diff --git a/.clang-format b/.clang-format index 46e7bbce94..ca8edf678f 100644 --- a/.clang-format +++ b/.clang-format @@ -37,7 +37,7 @@ BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: true -ColumnLimit: 120 +ColumnLimit: 100 CommentPragmas: "^ IWYU pragma:" ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000..d8a7cb1b6f --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,199 @@ +--- +Checks: "-*, + bugprone-argument-comment, + bugprone-assert-side-effect, + bugprone-bad-signal-to-kill-thread, + bugprone-bool-pointer-implicit-conversion, + bugprone-casting-through-void, + bugprone-chained-comparison, + bugprone-compare-pointer-to-member-virtual-function, + bugprone-copy-constructor-init, + bugprone-crtp-constructor-accessibility, + bugprone-dangling-handle, + bugprone-dynamic-static-initializers, + bugprone-empty-catch, + bugprone-fold-init-type, + bugprone-forward-declaration-namespace, + bugprone-inaccurate-erase, + bugprone-inc-dec-in-conditions, + bugprone-incorrect-enable-if, + bugprone-incorrect-roundings, + bugprone-infinite-loop, + bugprone-integer-division, + bugprone-lambda-function-name, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-operator-in-strlen-in-alloc, + bugprone-misplaced-pointer-arithmetic-in-alloc, + bugprone-misplaced-widening-cast, + bugprone-move-forwarding-reference, + bugprone-multi-level-implicit-pointer-conversion, + bugprone-multiple-new-in-one-expression, + bugprone-multiple-statement-macro, + bugprone-no-escape, + bugprone-non-zero-enum-to-bool-conversion, + bugprone-optional-value-conversion, + bugprone-parent-virtual-call, + bugprone-pointer-arithmetic-on-polymorphic-object, + bugprone-posix-return, + bugprone-redundant-branch-condition, + bugprone-reserved-identifier, + bugprone-return-const-ref-from-parameter, + bugprone-shared-ptr-array-mismatch, + bugprone-signal-handler, + bugprone-signed-char-misuse, + bugprone-sizeof-container, + bugprone-sizeof-expression, + bugprone-spuriously-wake-up-functions, + bugprone-standalone-empty, + bugprone-string-constructor, + bugprone-string-integer-assignment, + bugprone-string-literal-with-embedded-nul, + bugprone-stringview-nullptr, + bugprone-suspicious-enum-usage, + bugprone-suspicious-include, + bugprone-suspicious-memory-comparison, + bugprone-suspicious-memset-usage, + bugprone-suspicious-missing-comma, + bugprone-suspicious-realloc-usage, + bugprone-suspicious-semicolon, + bugprone-suspicious-string-compare, + bugprone-suspicious-stringview-data-usage, + bugprone-swapped-arguments, + bugprone-switch-missing-default-case, + bugprone-terminating-continue, + bugprone-throw-keyword-missing, + bugprone-too-small-loop-variable, + # bugprone-unchecked-optional-access, # see https://github.com/XRPLF/rippled/pull/6502 + bugprone-undefined-memory-manipulation, + bugprone-undelegated-constructor, + bugprone-unhandled-exception-at-new, + bugprone-unhandled-self-assignment, + bugprone-unique-ptr-array-mismatch, + bugprone-unsafe-functions, + bugprone-use-after-move, + bugprone-unused-raii, + bugprone-unused-return-value, + bugprone-unused-local-non-trivial-variable, + bugprone-virtual-near-miss, + cppcoreguidelines-init-variables, + cppcoreguidelines-misleading-capture-default-by-value, + cppcoreguidelines-no-suspend-with-lock, + cppcoreguidelines-pro-type-member-init, + cppcoreguidelines-pro-type-static-cast-downcast, + cppcoreguidelines-rvalue-reference-param-not-moved, + cppcoreguidelines-use-default-member-init, + cppcoreguidelines-virtual-class-destructor, + hicpp-ignored-remove-result, + misc-definitions-in-headers, + misc-header-include-cycle, + misc-misplaced-const, + misc-static-assert, + misc-throw-by-value-catch-by-reference, + misc-unused-alias-decls, + misc-unused-using-decls, + modernize-deprecated-headers, + modernize-make-shared, + modernize-make-unique, + performance-implicit-conversion-in-loop, + performance-move-constructor-init, + performance-trivially-destructible, + readability-avoid-nested-conditional-operator, + readability-avoid-return-with-void-value, + readability-braces-around-statements, + readability-const-return-type, + readability-container-contains, + readability-container-size-empty, + readability-duplicate-include, + readability-else-after-return, + readability-enum-initial-value, + readability-make-member-function-const, + readability-misleading-indentation, + readability-non-const-parameter, + readability-redundant-casting, + readability-redundant-declaration, + readability-redundant-inline-specifier, + readability-redundant-member-init, + readability-redundant-string-init, + readability-reference-to-constructed-temporary, + readability-static-definition-in-anonymous-namespace, + readability-use-std-min-max + " +# --- +# checks that have some issues that need to be resolved: +# +# llvm-namespace-comment, +# misc-const-correctness, +# misc-include-cleaner, +# misc-redundant-expression, +# +# readability-convert-member-functions-to-static, +# readability-implicit-bool-conversion, +# readability-inconsistent-declaration-parameter-name, +# readability-identifier-naming, +# readability-math-missing-parentheses, +# readability-simplify-boolean-expr, +# readability-suspicious-call-argument, +# readability-static-accessed-through-instance, +# +# modernize-concat-nested-namespaces, +# modernize-pass-by-value, +# modernize-type-traits, +# modernize-use-designated-initializers, +# modernize-use-emplace, +# modernize-use-equals-default, +# modernize-use-equals-delete, +# modernize-use-override, +# modernize-use-ranges, +# modernize-use-starts-ends-with, +# modernize-use-std-numbers, +# modernize-use-using, +# +# performance-faster-string-find, +# performance-for-range-copy, +# performance-inefficient-vector-operation, +# performance-move-const-arg, +# performance-no-automatic-move, +# --- +# +CheckOptions: + readability-braces-around-statements.ShortStatementLines: 2 + # readability-identifier-naming.MacroDefinitionCase: UPPER_CASE + # readability-identifier-naming.ClassCase: CamelCase + # readability-identifier-naming.StructCase: CamelCase + # readability-identifier-naming.UnionCase: CamelCase + # readability-identifier-naming.EnumCase: CamelCase + # readability-identifier-naming.EnumConstantCase: CamelCase + # readability-identifier-naming.ScopedEnumConstantCase: CamelCase + # readability-identifier-naming.GlobalConstantCase: UPPER_CASE + # readability-identifier-naming.GlobalConstantPrefix: "k" + # readability-identifier-naming.GlobalVariableCase: CamelCase + # readability-identifier-naming.GlobalVariablePrefix: "g" + # readability-identifier-naming.ConstexprFunctionCase: camelBack + # readability-identifier-naming.ConstexprMethodCase: camelBack + # readability-identifier-naming.ClassMethodCase: camelBack + # readability-identifier-naming.ClassMemberCase: camelBack + # readability-identifier-naming.ClassConstantCase: UPPER_CASE + # readability-identifier-naming.ClassConstantPrefix: "k" + # readability-identifier-naming.StaticConstantCase: UPPER_CASE + # readability-identifier-naming.StaticConstantPrefix: "k" + # readability-identifier-naming.StaticVariableCase: UPPER_CASE + # readability-identifier-naming.StaticVariablePrefix: "k" + # readability-identifier-naming.ConstexprVariableCase: UPPER_CASE + # readability-identifier-naming.ConstexprVariablePrefix: "k" + # readability-identifier-naming.LocalConstantCase: camelBack + # readability-identifier-naming.LocalVariableCase: camelBack + # readability-identifier-naming.TemplateParameterCase: CamelCase + # readability-identifier-naming.ParameterCase: camelBack + # readability-identifier-naming.FunctionCase: camelBack + # readability-identifier-naming.MemberCase: camelBack + # readability-identifier-naming.PrivateMemberSuffix: _ + # readability-identifier-naming.ProtectedMemberSuffix: _ + # readability-identifier-naming.PublicMemberSuffix: "" + # readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*" + bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true + bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc +# misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp' +# +# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$' +WarningsAsErrors: "*" diff --git a/.cmake-format.yaml b/.cmake-format.yaml deleted file mode 100644 index 1a3337fe8c..0000000000 --- a/.cmake-format.yaml +++ /dev/null @@ -1,247 +0,0 @@ -_help_parse: Options affecting listfile parsing -parse: - _help_additional_commands: - - Specify structure for custom cmake functions - additional_commands: - target_protobuf_sources: - pargs: - - target - - prefix - kwargs: - PROTOS: "*" - LANGUAGE: cpp - IMPORT_DIRS: "*" - GENERATE_EXTENSIONS: "*" - PLUGIN: "*" - _help_override_spec: - - Override configurations per-command where available - override_spec: {} - _help_vartags: - - Specify variable tags. - vartags: [] - _help_proptags: - - Specify property tags. - proptags: [] -_help_format: Options affecting formatting. -format: - _help_disable: - - Disable formatting entirely, making cmake-format a no-op - disable: false - _help_line_width: - - How wide to allow formatted cmake files - line_width: 120 - _help_tab_size: - - How many spaces to tab for indent - tab_size: 4 - _help_use_tabchars: - - If true, lines are indented using tab characters (utf-8 - - 0x09) instead of space characters (utf-8 0x20). - - In cases where the layout would require a fractional tab - - character, the behavior of the fractional indentation is - - governed by - use_tabchars: false - _help_fractional_tab_policy: - - If is True, then the value of this variable - - indicates how fractional indentions are handled during - - whitespace replacement. If set to 'use-space', fractional - - indentation is left as spaces (utf-8 0x20). If set to - - "`round-up` fractional indentation is replaced with a single" - - tab character (utf-8 0x09) effectively shifting the column - - to the next tabstop - fractional_tab_policy: use-space - _help_max_subgroups_hwrap: - - If an argument group contains more than this many sub-groups - - (parg or kwarg groups) then force it to a vertical layout. - max_subgroups_hwrap: 4 - _help_max_pargs_hwrap: - - If a positional argument group contains more than this many - - arguments, then force it to a vertical layout. - max_pargs_hwrap: 5 - _help_max_rows_cmdline: - - If a cmdline positional group consumes more than this many - - lines without nesting, then invalidate the layout (and nest) - max_rows_cmdline: 2 - _help_separate_ctrl_name_with_space: - - If true, separate flow control names from their parentheses - - with a space - separate_ctrl_name_with_space: true - _help_separate_fn_name_with_space: - - If true, separate function names from parentheses with a - - space - separate_fn_name_with_space: false - _help_dangle_parens: - - If a statement is wrapped to more than one line, than dangle - - the closing parenthesis on its own line. - dangle_parens: false - _help_dangle_align: - - If the trailing parenthesis must be 'dangled' on its on - - "line, then align it to this reference: `prefix`: the start" - - "of the statement, `prefix-indent`: the start of the" - - "statement, plus one indentation level, `child`: align to" - - the column of the arguments - dangle_align: prefix - _help_min_prefix_chars: - - If the statement spelling length (including space and - - parenthesis) is smaller than this amount, then force reject - - nested layouts. - min_prefix_chars: 18 - _help_max_prefix_chars: - - If the statement spelling length (including space and - - parenthesis) is larger than the tab width by more than this - - amount, then force reject un-nested layouts. - max_prefix_chars: 10 - _help_max_lines_hwrap: - - If a candidate layout is wrapped horizontally but it exceeds - - this many lines, then reject the layout. - max_lines_hwrap: 2 - _help_line_ending: - - What style line endings to use in the output. - line_ending: unix - _help_command_case: - - Format command names consistently as 'lower' or 'upper' case - command_case: canonical - _help_keyword_case: - - Format keywords consistently as 'lower' or 'upper' case - keyword_case: unchanged - _help_always_wrap: - - A list of command names which should always be wrapped - always_wrap: [] - _help_enable_sort: - - If true, the argument lists which are known to be sortable - - will be sorted lexicographicall - enable_sort: true - _help_autosort: - - If true, the parsers may infer whether or not an argument - - list is sortable (without annotation). - autosort: true - _help_require_valid_layout: - - By default, if cmake-format cannot successfully fit - - everything into the desired linewidth it will apply the - - last, most aggressive attempt that it made. If this flag is - - True, however, cmake-format will print error, exit with non- - - zero status code, and write-out nothing - require_valid_layout: false - _help_layout_passes: - - A dictionary mapping layout nodes to a list of wrap - - decisions. See the documentation for more information. - layout_passes: {} -_help_markup: Options affecting comment reflow and formatting. -markup: - _help_bullet_char: - - What character to use for bulleted lists - bullet_char: "-" - _help_enum_char: - - What character to use as punctuation after numerals in an - - enumerated list - enum_char: . - _help_first_comment_is_literal: - - If comment markup is enabled, don't reflow the first comment - - block in each listfile. Use this to preserve formatting of - - your copyright/license statements. - first_comment_is_literal: false - _help_literal_comment_pattern: - - If comment markup is enabled, don't reflow any comment block - - which matches this (regex) pattern. Default is `None` - - (disabled). - literal_comment_pattern: null - _help_fence_pattern: - - Regular expression to match preformat fences in comments - - default= ``r'^\s*([`~]{3}[`~]*)(.*)$'`` - fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$ - _help_ruler_pattern: - - Regular expression to match rulers in comments default= - - '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``' - ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$ - _help_explicit_trailing_pattern: - - If a comment line matches starts with this pattern then it - - is explicitly a trailing comment for the preceding - - argument. Default is '#<' - explicit_trailing_pattern: "#<" - _help_hashruler_min_length: - - If a comment line starts with at least this many consecutive - - hash characters, then don't lstrip() them off. This allows - - for lazy hash rulers where the first hash char is not - - separated by space - hashruler_min_length: 10 - _help_canonicalize_hashrulers: - - If true, then insert a space between the first hash char and - - remaining hash chars in a hash ruler, and normalize its - - length to fill the column - canonicalize_hashrulers: true - _help_enable_markup: - - enable comment markup parsing and reflow - enable_markup: false -_help_lint: Options affecting the linter -lint: - _help_disabled_codes: - - a list of lint codes to disable - disabled_codes: [] - _help_function_pattern: - - regular expression pattern describing valid function names - function_pattern: "[0-9a-z_]+" - _help_macro_pattern: - - regular expression pattern describing valid macro names - macro_pattern: "[0-9A-Z_]+" - _help_global_var_pattern: - - regular expression pattern describing valid names for - - variables with global (cache) scope - global_var_pattern: "[A-Z][0-9A-Z_]+" - _help_internal_var_pattern: - - regular expression pattern describing valid names for - - variables with global scope (but internal semantic) - internal_var_pattern: _[A-Z][0-9A-Z_]+ - _help_local_var_pattern: - - regular expression pattern describing valid names for - - variables with local scope - local_var_pattern: "[a-z][a-z0-9_]+" - _help_private_var_pattern: - - regular expression pattern describing valid names for - - privatedirectory variables - private_var_pattern: _[0-9a-z_]+ - _help_public_var_pattern: - - regular expression pattern describing valid names for public - - directory variables - public_var_pattern: "[A-Z][0-9A-Z_]+" - _help_argument_var_pattern: - - regular expression pattern describing valid names for - - function/macro arguments and loop variables. - argument_var_pattern: "[a-z][a-z0-9_]+" - _help_keyword_pattern: - - regular expression pattern describing valid names for - - keywords used in functions or macros - keyword_pattern: "[A-Z][0-9A-Z_]+" - _help_max_conditionals_custom_parser: - - In the heuristic for C0201, how many conditionals to match - - within a loop in before considering the loop a parser. - max_conditionals_custom_parser: 2 - _help_min_statement_spacing: - - Require at least this many newlines between statements - min_statement_spacing: 1 - _help_max_statement_spacing: - - Require no more than this many newlines between statements - max_statement_spacing: 2 - max_returns: 6 - max_branches: 12 - max_arguments: 5 - max_localvars: 15 - max_statements: 50 -_help_encode: Options affecting file encoding -encode: - _help_emit_byteorder_mark: - - If true, emit the unicode byte-order mark (BOM) at the start - - of the file - emit_byteorder_mark: false - _help_input_encoding: - - Specify the encoding of the input file. Defaults to utf-8 - input_encoding: utf-8 - _help_output_encoding: - - Specify the encoding of the output file. Defaults to utf-8. - - Note that cmake only claims to support utf-8 so be careful - - when using anything else - output_encoding: utf-8 -_help_misc: Miscellaneous configurations options. -misc: - _help_per_command: - - A dictionary containing any per-command configuration - - overrides. Currently only `command_case` is supported. - per_command: {} diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake new file mode 100644 index 0000000000..a16e330ffa --- /dev/null +++ b/.gersemi/definitions.cmake @@ -0,0 +1,101 @@ +# Custom CMake command definitions for gersemi formatting. +# These stubs teach gersemi the signatures of project-specific commands +# so it can format their invocations correctly. + +function(git_branch branch_val) +endfunction() + +function(isolate_headers target A B scope) +endfunction() + +function(create_symbolic_link target link) +endfunction() + +function(xrpl_add_test name) +endfunction() + +macro(exclude_from_default target_) +endmacro() + +macro(exclude_if_included target_) +endmacro() + +function(target_protobuf_sources target prefix) + set(options APPEND_PATH DESCRIPTORS) + set(oneValueArgs + LANGUAGE + OUT_VAR + EXPORT_MACRO + TARGET + PROTOC_OUT_DIR + PLUGIN + PLUGIN_OPTIONS + PROTOC_EXE + ) + set(multiValueArgs + PROTOS + IMPORT_DIRS + GENERATE_EXTENSIONS + PROTOC_OPTIONS + DEPENDENCIES + ) + cmake_parse_arguments( + THIS_FUNCTION_PREFIX + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) +endfunction() + +function(add_module parent name) +endfunction() + +function(setup_protocol_autogen) +endfunction() + +function(target_link_modules parent scope) +endfunction() + +function(setup_target_for_coverage_gcovr) + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME FORMAT) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments( + THIS_FUNCTION_PREFIX + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) +endfunction() + +function(add_code_coverage_to_target name scope) +endfunction() + +function(verbose_find_path variable name) + set(options + NO_CACHE + REQUIRED + OPTIONAL + NO_DEFAULT_PATH + NO_PACKAGE_ROOT_PATH + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_INSTALL_PREFIX + CMAKE_FIND_ROOT_PATH_BOTH + ONLY_CMAKE_FIND_ROOT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + set(oneValueArgs REGISTRY_VIEW VALIDATOR DOC) + set(multiValueArgs NAMES HINTS PATHS PATH_SUFFIXES) + cmake_parse_arguments( + THIS_FUNCTION_PREFIX + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) +endfunction() diff --git a/.gersemirc b/.gersemirc new file mode 100644 index 0000000000..5abd52ffd5 --- /dev/null +++ b/.gersemirc @@ -0,0 +1 @@ +definitions: [.gersemi] diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index cf50d48f95..0cf704b051 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,16 +1,79 @@ # This feature requires Git >= 2.24 # To use it by default in git blame: # git config blame.ignoreRevsFile .git-blame-ignore-revs -50760c693510894ca368e90369b0cc2dabfd07f3 -e2384885f5f630c8f0ffe4bf21a169b433a16858 -241b9ddde9e11beb7480600fd5ed90e1ef109b21 -760f16f56835663d9286bd29294d074de26a7ba6 -0eebe6a5f4246fced516d52b83ec4e7f47373edd -2189cc950c0cebb89e4e2fa3b2d8817205bf7cef -b9d007813378ad0ff45660dc07285b823c7e9855 -fe9a5365b8a52d4acc42eb27369247e6f238a4f9 -9a93577314e6a8d4b4a8368cc9d2b15a5d8303e8 -552377c76f55b403a1c876df873a23d780fcc81c -97f0747e103f13e26e45b731731059b32f7679ac -b13370ac0d207217354f1fc1c29aef87769fb8a1 + +# This file is sorted in reverse chronological order, with the most recent commits at the top. +# The commits listed here are ignored by git blame, which is useful for formatting-only commits that would otherwise obscure the history of changes to a file. + +# refactor: Enable remaining clang-tidy `cppcoreguidelines` checks (#6538) +72f4cb097f626b08b02fc3efcb4aa11cb2e7adb8 +# refactor: Rename system name from 'ripple' to 'xrpld' (#6347) +ffea3977f0b771fe8e43a8f74e4d393d63a7afd8 +# refactor: Update transaction folder structure (#6483) +5865bd017f777491b4a956f9210be0c4161f5442 +# chore: Use gersemi instead of ancient cmake-format (#6486) +0c74270b055133a57a497b5c9fc5a75f7647b1f4 +# chore: Apply clang-format width 100 (#6387) +2c1fad102353e11293e3edde1c043224e7d3e983 +# chore: Set clang-format width to 100 in config file (#6387) +25cca465538a56cce501477f9e5e2c1c7ea2d84c +# chore: Set cmake-format width to 100 (#6386) +469ce9f291a4480c38d4ee3baca5136b2f053cd0 +# refactor: Modularize app/tx (#6228) +0976b2b68b64972af8e6e7c497900b5bce9fe22f +# chore: Update clang-format to 21.1.8 (#6352) +958d8f375453d80bb1aa4c293b5102c045a3e4b4 +# refactor: Replace include guards by '#pragma once' (#6322) +34ef577604782ca8d6e1c17df8bd7470990a52ff +# chore: Format all cmake files without comments (#6294) +fe9c8d568fcf6ac21483024e01f58962dd5c8260 +# chore: Add cmake-format pre-commit hook (#6279) +a0e09187b9370805d027c611a7e9ff5a0125282a +# chore: Set ColumnLimit to 120 in clang-format (#6288) +5f638f55536def0d88b970d1018a465a238e55f4 +# refactor: Fix typos in comments, configure cspell (#6164) +3c9f5b62525cb1d6ca1153eeb10433db7d7379fd +# refactor: Rename `rippled.cfg` to `xrpld.cfg` (#6098) +3d1b3a49b3601a0a7037fa0b19d5df7b5e0e2fc1 +# refactor: Rename `ripple` namespace to `xrpl` (#5982) +1eb0fdac6543706b4b9ddca57fd4102928a1f871 +# refactor: Rename `rippled` binary to `xrpld` (#5983) +9eb84a561ef8bb066d89f098bd9b4ac71baed67c +# refactor: Replaces secp256k1 source by Conan package (#6089) +813bc4d9491b078bb950f8255f93b02f71320478 +# refactor: Remove unnecessary copyright notices already covered by LICENSE.md (#5929) +1d42c4f6de6bf01d1286fc7459b17a37a5189e88 +# refactor: Rename `RIPPLE_` and `RIPPLED_` definitions to `XRPL_` (#5821) +ada83564d894829424b0f4d922b0e737e07abbf7 +# refactor: Modularize shamap and nodestore (#5668) +8eb233c2ea8ad5a159be73b77f0f5e1496d547ac +# refactor: Modularise ledger (#5493) +dc8b37a52448b005153c13a7f046ad494128cf94 +# chore: Update clang-format and prettier with pre-commit (#5709) +c14ce956adeabe476ad73c18d73103f347c9c613 +# chore: Fix file formatting (#5718) 896b8c3b54a22b0497cb0d1ce95e1095f9a227ce +# chore: Reverts formatting changes to external files, adds formatting changes to proto files (#5711) +b13370ac0d207217354f1fc1c29aef87769fb8a1 +# chore: Run prettier on all files (#5657) +97f0747e103f13e26e45b731731059b32f7679ac +# Reformat code with clang-format-18 +552377c76f55b403a1c876df873a23d780fcc81c +# Recompute loops (#4997) +d028005aa6319338b0adae1aebf8abe113162960 +# Rewrite includes (#4997) +1d23148e6dd53957fcb6205c07a5c6cd7b64d50c +# Rearrange sources (#4997) +e416ee72ca26fa0c09d2aee1b68bdfb2b7046eed +# Move CMake directory (#4997) +2e902dee53aab2a8f27f32971047bb81e022f94f +# Rewrite includes +0eebe6a5f4246fced516d52b83ec4e7f47373edd +# Format formerly .hpp files +760f16f56835663d9286bd29294d074de26a7ba6 +# Rename .hpp to .h +241b9ddde9e11beb7480600fd5ed90e1ef109b21 +# Consolidate external libraries +e2384885f5f630c8f0ffe4bf21a169b433a16858 +# Format first-party source according to .clang-format +50760c693510894ca368e90369b0cc2dabfd07f3 diff --git a/.github/actions/generate-version/action.yml b/.github/actions/generate-version/action.yml index 6b84aac2f3..8edb7920c6 100644 --- a/.github/actions/generate-version/action.yml +++ b/.github/actions/generate-version/action.yml @@ -11,7 +11,7 @@ runs: steps: # When a tag is pushed, the version is used as-is. - name: Generate version for tag event - if: ${{ github.event_name == 'tag' }} + if: ${{ startsWith(github.ref, 'refs/tags/') }} shell: bash env: VERSION: ${{ github.ref_name }} @@ -22,7 +22,7 @@ runs: # We use a plus sign instead of a hyphen because Conan recipe versions do # not support two hyphens. - name: Generate version for non-tag event - if: ${{ github.event_name != 'tag' }} + if: ${{ !startsWith(github.ref, 'refs/tags/') }} shell: bash run: | echo 'Extracting version from BuildInfo.cpp.' diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..66e319e0e7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,56 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + time: "04:00" + timezone: Etc/GMT + commit-message: + prefix: "ci: [DEPENDABOT] " + target-branch: develop + + - package-ecosystem: github-actions + directory: .github/actions/build-deps/ + schedule: + interval: weekly + day: monday + time: "04:00" + timezone: Etc/GMT + commit-message: + prefix: "ci: [DEPENDABOT] " + target-branch: develop + + - package-ecosystem: github-actions + directory: .github/actions/generate-version/ + schedule: + interval: weekly + day: monday + time: "04:00" + timezone: Etc/GMT + commit-message: + prefix: "ci: [DEPENDABOT] " + target-branch: develop + + - package-ecosystem: github-actions + directory: .github/actions/print-env/ + schedule: + interval: weekly + day: monday + time: "04:00" + timezone: Etc/GMT + commit-message: + prefix: "ci: [DEPENDABOT] " + target-branch: develop + + - package-ecosystem: github-actions + directory: .github/actions/setup-conan/ + schedule: + interval: weekly + day: monday + time: "04:00" + timezone: Etc/GMT + commit-message: + prefix: "ci: [DEPENDABOT] " + target-branch: develop diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3ab3a38807..f1f7aa18f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,22 +29,6 @@ If a refactor, how is this better than the previous implementation? If there is a spec or design document for this feature, please link it here. --> -### Type of Change - - - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Refactor (non-breaking change that only restructures code) -- [ ] Performance (increase or change in throughput and/or latency) -- [ ] Tests (you added tests for code that already exists, or your new feature included in this PR) -- [ ] Documentation update -- [ ] Chore (no impact to binary, e.g. `.gitignore`, formatting, dropping support for older tooling) -- [ ] Release - ### API Impact ripple state entry - SLE::ref sleAccount, // --> the account being set. - bool const bAuth, // --> authorize account. - bool const bNoRipple, // --> others cannot ripple through - bool const bFreeze, // --> funds cannot leave - bool bDeepFreeze, // --> can neither receive nor send funds - STAmount const& saBalance, // --> balance of account being set. - // Issuer should be noAccount() - STAmount const& saLimit, // --> limit for account being set. - // Issuer should be the account being set. - std::uint32_t uSrcQualityIn, - std::uint32_t uSrcQualityOut, - beast::Journal j); - -[[nodiscard]] TER -removeEmptyHolding(ApplyView& view, AccountID const& accountID, Issue const& issue, beast::Journal journal); - -[[nodiscard]] TER -removeEmptyHolding(ApplyView& view, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal); - -[[nodiscard]] inline TER -removeEmptyHolding(ApplyView& view, AccountID const& accountID, Asset const& asset, beast::Journal journal) -{ - return std::visit( - [&](TIss const& issue) -> TER { - return removeEmptyHolding(view, accountID, issue, journal); - }, - asset.value()); -} - -[[nodiscard]] TER -trustDelete( - ApplyView& view, - std::shared_ptr const& sleRippleState, - AccountID const& uLowAccountID, - AccountID const& uHighAccountID, - beast::Journal j); - -/** Delete an offer. - - Requirements: - The passed `sle` be obtained from a prior - call to view.peek() -*/ -// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. -TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); - -//------------------------------------------------------------------------------ - -// -// Money Transfers -// - -// Direct send w/o fees: -// - Redeeming IOUs and/or sending sender's own IOUs. -// - Create trust line of needed. -// --> bCheckIssuer : normally require issuer to be involved. -// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. - -/** Calls static rippleCreditIOU if saAmount represents Issue. - * Calls static rippleCreditMPT if saAmount represents MPTIssue. - */ -TER -rippleCredit( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j); - -TER -rippleLockEscrowMPT(ApplyView& view, AccountID const& uGrantorID, STAmount const& saAmount, beast::Journal j); - -TER -rippleUnlockEscrowMPT( - ApplyView& view, - AccountID const& uGrantorID, - AccountID const& uGranteeID, - STAmount const& netAmount, - STAmount const& grossAmount, - beast::Journal j); - -/** Calls static accountSendIOU if saAmount represents Issue. - * Calls static accountSendMPT if saAmount represents MPTIssue. - */ -[[nodiscard]] TER -accountSend( - ApplyView& view, - AccountID const& from, - AccountID const& to, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee = WaiveTransferFee::No); - -using MultiplePaymentDestinations = std::vector>; -/** Like accountSend, except one account is sending multiple payments (with the - * same asset!) simultaneously - * - * Calls static accountSendMultiIOU if saAmount represents Issue. - * Calls static accountSendMultiMPT if saAmount represents MPTIssue. - */ -[[nodiscard]] TER -accountSendMulti( - ApplyView& view, - AccountID const& senderID, - Asset const& asset, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee = WaiveTransferFee::No); - -[[nodiscard]] TER -issueIOU(ApplyView& view, AccountID const& account, STAmount const& amount, Issue const& issue, beast::Journal j); - -[[nodiscard]] TER -redeemIOU(ApplyView& view, AccountID const& account, STAmount const& amount, Issue const& issue, beast::Journal j); - -[[nodiscard]] TER -transferXRP(ApplyView& view, AccountID const& from, AccountID const& to, STAmount const& amount, beast::Journal j); - -/* Check if MPToken (for MPT) or trust line (for IOU) exists: - * - StrongAuth - before checking if authorization is required - * - WeakAuth - * for MPT - after checking lsfMPTRequireAuth flag - * for IOU - do not check if trust line exists - * - Legacy - * for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth - * for IOU - do not check if trust line exists i.e. same as WeakAuth - */ -enum class AuthType { StrongAuth, WeakAuth, Legacy }; - -/** Check if the account lacks required authorization. - * - * Return tecNO_AUTH or tecNO_LINE if it does - * and tesSUCCESS otherwise. - * - * If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return - * tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the - * RippleState does exist, and the RippleState is not authorized. - * - * If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the - * RippleState exists, and is not authorized. Return tecNO_LINE if - * lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if - * WeakAuth and lsfRequireAuth is *not* set, this function will return - * tesSUCCESS even if RippleState does *not* exist. - * - * The default "Legacy" auth type is equivalent to WeakAuth. - */ -[[nodiscard]] TER -requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType = AuthType::Legacy); - -/** Check if the account lacks required authorization. - * - * This will also check for expired credentials. If it is called directly - * from preclaim, the user should convert result tecEXPIRED to tesSUCCESS and - * proceed to also check permissions with enforceMPTokenAuthorization inside - * doApply. This will ensure that any expired credentials are deleted. - * - * requireAuth check is recursive for MPT shares in a vault, descending to - * assets in the vault, up to maxAssetCheckDepth recursion depth. This is - * purely defensive, as we currently do not allow such vaults to be created. - * - * If StrongAuth then return tecNO_AUTH if MPToken doesn't exist or - * lsfMPTRequireAuth is set and MPToken is not authorized. Vault and LoanBroker - * pseudo-accounts are implicitly authorized. - * - * If WeakAuth then return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken - * doesn't exist or is not authorized (explicitly or via credentials, if - * DomainID is set in MPTokenIssuance). Consequently, if WeakAuth and - * lsfMPTRequireAuth is *not* set, this function will return true even if - * MPToken does *not* exist. - * - * The default "Legacy" auth type is equivalent to StrongAuth. - */ -[[nodiscard]] TER -requireAuth( - ReadView const& view, - MPTIssue const& mptIssue, - AccountID const& account, - AuthType authType = AuthType::Legacy, - int depth = 0); - -[[nodiscard]] TER inline requireAuth( - ReadView const& view, - Asset const& asset, - AccountID const& account, - AuthType authType = AuthType::Legacy) -{ - return std::visit( - [&](TIss const& issue_) { return requireAuth(view, issue_, account, authType); }, - asset.value()); -} - -/** Enforce account has MPToken to match its authorization. - * - * Called from doApply - it will check for expired (and delete if found any) - * credentials matching DomainID set in MPTokenIssuance. Must be called if - * requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim, - * which implies that preclaim should replace `tecEXPIRED` with `tesSUCCESS` - * in order for the transactor to proceed to doApply. - * - * This function will create MPToken (if needed) on the basis of any - * non-expired credentials and will delete any expired credentials, indirectly - * via verifyValidDomain, as per DomainID (if set in MPTokenIssuance). - * - * The caller does NOT need to ensure that DomainID is actually set - this - * function handles gracefully both cases when DomainID is set and when not. - * - * The caller does NOT need to look for existing MPToken to match - * mptIssue/account - this function checks lsfMPTAuthorized of an existing - * MPToken iff DomainID is not set. - * - * Do not use for accounts which hold implied permission e.g. object owners or - * if MPTokenIssuance does not require authorization. In both cases use - * MPTokenAuthorize::authorize if MPToken does not yet exist. - */ -[[nodiscard]] TER -enforceMPTokenAuthorization( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - XRPAmount const& priorBalance, - beast::Journal j); - -/** Check if the destination account is allowed - * to receive MPT. Return tecNO_AUTH if it doesn't - * and tesSUCCESS otherwise. - */ -[[nodiscard]] TER -canTransfer(ReadView const& view, MPTIssue const& mptIssue, AccountID const& from, AccountID const& to); - -[[nodiscard]] TER -canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to); - -[[nodiscard]] TER inline canTransfer( - ReadView const& view, - Asset const& asset, - AccountID const& from, - AccountID const& to) -{ - return std::visit( - [&](TIss const& issue) -> TER { return canTransfer(view, issue, from, to); }, - asset.value()); -} - /** Deleter function prototype. Returns the status of the entry deletion * (if should not be skipped) and if the entry should be skipped. The status * is always tesSUCCESS if the entry should be skipped. */ -using EntryDeleter = std::function(LedgerEntryType, uint256 const&, std::shared_ptr&)>; +using EntryDeleter = std::function< + std::pair(LedgerEntryType, uint256 const&, std::shared_ptr&)>; /** Cleanup owner directory entries on account delete. * Used for a regular and AMM accounts deletion. The caller * has to provide the deleter function, which handles details of @@ -934,57 +232,6 @@ cleanupOnAccountDelete( beast::Journal j, std::optional maxNodesToDelete = std::nullopt); -/** Delete trustline to AMM. The passed `sle` must be obtained from a prior - * call to view.peek(). Fail if neither side of the trustline is AMM or - * if ammAccountID is seated and is not one of the trustline's side. - */ -[[nodiscard]] TER -deleteAMMTrustLine( - ApplyView& view, - std::shared_ptr sleState, - std::optional const& ammAccountID, - beast::Journal j); - -// From the perspective of a vault, return the number of shares to give the -// depositor when they deposit a fixed amount of assets. Since shares are MPT -// this number is integral and always truncated in this calculation. -[[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets); - -// From the perspective of a vault, return the number of assets to take from -// depositor when they receive a fixed amount of shares. Note, since shares are -// MPT, they are always an integral number. -[[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); - -enum class TruncateShares : bool { no = false, yes = true }; - -// From the perspective of a vault, return the number of shares to demand from -// the depositor when they ask to withdraw a fixed amount of assets. Since -// shares are MPT this number is integral, and it will be rounded to nearest -// unless explicitly requested to be truncated instead. -[[nodiscard]] std::optional -assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets, - TruncateShares truncate = TruncateShares::no); - -// From the perspective of a vault, return the number of assets to give the -// depositor when they redeem a fixed amount of shares. Note, since shares are -// MPT, they are always an integral number. -[[nodiscard]] std::optional -sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); - /** Has the specified time passed? @param now the current time diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index e09f2f0e44..499b9204c6 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -23,11 +23,13 @@ public: static constexpr size_t initialBufferSize = kilobytes(256); RawStateTable() - : monotonic_resource_{std::make_unique(initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , items_{monotonic_resource_.get()} {}; RawStateTable(RawStateTable const& rhs) - : monotonic_resource_{std::make_unique(initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , items_{rhs.items_, monotonic_resource_.get()} , dropsDestroyed_{rhs.dropsDestroyed_} {}; diff --git a/include/xrpl/ledger/detail/ReadViewFwdRange.ipp b/include/xrpl/ledger/detail/ReadViewFwdRange.ipp index 212be52432..ce6754b39c 100644 --- a/include/xrpl/ledger/detail/ReadViewFwdRange.ipp +++ b/include/xrpl/ledger/detail/ReadViewFwdRange.ipp @@ -16,7 +16,9 @@ ReadViewFwdRange::iterator::iterator(iterator&& other) noexcept } template -ReadViewFwdRange::iterator::iterator(ReadView const* view, std::unique_ptr impl) +ReadViewFwdRange::iterator::iterator( + ReadView const* view, + std::unique_ptr impl) : view_(view), impl_(std::move(impl)) { } diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h new file mode 100644 index 0000000000..353c27fe41 --- /dev/null +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +/** Check if the issuer has the global freeze flag set. + @param issuer The account to check + @return true if the account has global freeze set +*/ +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, AccountID const& issuer); + +// Calculate liquid XRP balance for an account. +// This function may be used to calculate the amount of XRP that +// the holder is able to freely spend. It subtracts reserve requirements. +// +// ownerCountAdj adjusts the owner count in case the caller calculates +// before ledger entries are added or removed. Positive to add, negative +// to subtract. +// +// @param ownerCountAdj positive to add to count, negative to reduce count. +[[nodiscard]] XRPAmount +xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); + +/** Adjust the owner count up or down. */ +void +adjustOwnerCount( + ApplyView& view, + std::shared_ptr const& sle, + std::int32_t amount, + beast::Journal j); + +/** Returns IOU issuer transfer fee as Rate. Rate specifies + * the fee as fractions of 1 billion. For example, 1% transfer rate + * is represented as 1,010,000,000. + * @param issuer The IOU issuer + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, AccountID const& issuer); + +/** Generate a pseudo-account address from a pseudo owner key. + @param pseudoOwnerKey The key to generate the address from + @return The generated account ID +*/ +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); + +/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account + if set. + + The list is constructed during initialization and is const after that. + Pseudo-account designator fields MUST be maintained by including the + SField::sMD_PseudoAccount flag in the SField definition. +*/ +[[nodiscard]] std::vector const& +getPseudoAccountFields(); + +/** Returns true if and only if sleAcct is a pseudo-account or specific + pseudo-accounts in pseudoFieldFilter. + + Returns false if sleAcct is: + - NOT a pseudo-account OR + - NOT a ltACCOUNT_ROOT OR + - null pointer +*/ +[[nodiscard]] bool +isPseudoAccount( + std::shared_ptr sleAcct, + std::set const& pseudoFieldFilter = {}); + +/** Convenience overload that reads the account from the view. */ +[[nodiscard]] inline bool +isPseudoAccount( + ReadView const& view, + AccountID const& accountId, + std::set const& pseudoFieldFilter = {}) +{ + return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter); +} + +/** + * Create pseudo-account, storing pseudoOwnerKey into ownerField. + * + * The list of valid ownerField is maintained in AccountRootHelpers.cpp and + * the caller to this function must perform necessary amendment check(s) + * before using a field. The amendment check is **not** performed in + * createPseudoAccount. + */ +[[nodiscard]] Expected, TER> +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); + +/** Checks the destination and tag. + + - Checks that the SLE is not null. + - If the SLE requires a destination tag, checks that there is a tag. +*/ +[[nodiscard]] TER +checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag); + +} // namespace xrpl diff --git a/include/xrpl/ledger/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h similarity index 95% rename from include/xrpl/ledger/CredentialHelpers.h rename to include/xrpl/ledger/helpers/CredentialHelpers.h index 3744ff47eb..c60bd6c7fa 100644 --- a/include/xrpl/ledger/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -49,7 +49,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject); // This function is only called when we about to return tecNO_PERMISSION // because all the checks for the DepositPreauth authorization failed. TER -authorizedDepositPreauth(ApplyView const& view, STVector256 const& ctx, AccountID const& dst); +authorizedDepositPreauth(ReadView const& view, STVector256 const& ctx, AccountID const& dst); // Sort credentials array, return empty set if there are duplicates std::set> @@ -74,7 +74,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + std::shared_ptr const& sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h new file mode 100644 index 0000000000..189dfcd263 --- /dev/null +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -0,0 +1,223 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +namespace detail { + +template < + class V, + class N, + class = std::enable_if_t< + std::is_same_v, SLE> && std::is_base_of_v>> +bool +internalDirNext( + V& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + auto const& svIndexes = page->getFieldV256(sfIndexes); + XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range"); + + if (index >= svIndexes.size()) + { + auto const next = page->getFieldU64(sfIndexNext); + + if (!next) + { + entry.zero(); + return false; + } + + if constexpr (std::is_const_v) + { + page = view.read(keylet::page(root, next)); + } + else + { + page = view.peek(keylet::page(root, next)); + } + + XRPL_ASSERT(page, "xrpl::detail::internalDirNext : non-null root"); + + if (!page) + return false; + + index = 0; + + return internalDirNext(view, root, page, index, entry); + } + + entry = svIndexes[index++]; + return true; +} + +template < + class V, + class N, + class = std::enable_if_t< + std::is_same_v, SLE> && std::is_base_of_v>> +bool +internalDirFirst( + V& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + if constexpr (std::is_const_v) + { + page = view.read(keylet::page(root)); + } + else + { + page = view.peek(keylet::page(root)); + } + + if (!page) + return false; + + index = 0; + + return internalDirNext(view, root, page, index, entry); +} + +} // namespace detail + +/** @{ */ +/** Returns the first entry in the directory, advancing the index + + @deprecated These are legacy function that are considered deprecated + and will soon be replaced with an iterator-based model + that is easier to use. You should not use them in new code. + + @param view The view against which to operate + @param root The root (i.e. first page) of the directory to iterate + @param page The current page + @param index The index inside the current page + @param entry The entry at the current index + + @return true if the directory isn't empty; false otherwise + */ +bool +cdirFirst( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); + +bool +dirFirst( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); +/** @} */ + +/** @{ */ +/** Returns the next entry in the directory, advancing the index + + @deprecated These are legacy function that are considered deprecated + and will soon be replaced with an iterator-based model + that is easier to use. You should not use them in new code. + + @param view The view against which to operate + @param root The root (i.e. first page) of the directory to iterate + @param page The current page + @param index The index inside the current page + @param entry The entry at the current index + + @return true if the directory isn't empty; false otherwise + */ +bool +cdirNext( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); + +bool +dirNext( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry); +/** @} */ + +/** Iterate all items in the given directory. */ +void +forEachItem( + ReadView const& view, + Keylet const& root, + std::function const&)> const& f); + +/** Iterate all items after an item in the given directory. + @param after The key of the item to start after + @param hint The directory page containing `after` + @param limit The maximum number of items to return + @return `false` if the iteration failed +*/ +bool +forEachItemAfter( + ReadView const& view, + Keylet const& root, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> const& f); + +/** Iterate all items in an account's owner directory. */ +inline void +forEachItem( + ReadView const& view, + AccountID const& id, + std::function const&)> const& f) +{ + return forEachItem(view, keylet::ownerDir(id), f); +} + +/** Iterate all items after an item in an owner directory. + @param after The key of the item to start after + @param hint The directory page containing `after` + @param limit The maximum number of items to return + @return `false` if the iteration failed +*/ +inline bool +forEachItemAfter( + ReadView const& view, + AccountID const& id, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> const& f) +{ + return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); +} + +/** Returns `true` if the directory is empty + @param key The key of the directory +*/ +[[nodiscard]] bool +dirIsEmpty(ReadView const& view, Keylet const& k); + +/** Returns a function that sets the owner on a directory SLE */ +[[nodiscard]] std::function +describeOwnerDir(AccountID const& account); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h new file mode 100644 index 0000000000..ab487280b9 --- /dev/null +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Freeze checking (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue); + +[[nodiscard]] bool +isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); + +[[nodiscard]] bool +isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + MPTIssue const& mptIssue, + int depth = 0); + +//------------------------------------------------------------------------------ +// +// Transfer rate (MPT-specific) +// +//------------------------------------------------------------------------------ + +/** Returns MPT transfer fee as Rate. Rate specifies + * the fee as fractions of 1 billion. For example, 1% transfer rate + * is represented as 1,010,000,000. + * @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, MPTID const& issuanceID); + +//------------------------------------------------------------------------------ +// +// Holding checks (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, MPTIssue const& mptIssue); + +//------------------------------------------------------------------------------ +// +// Authorization (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +authorizeMPToken( + ApplyView& view, + XRPAmount const& priorBalance, + MPTID const& mptIssuanceID, + AccountID const& account, + beast::Journal journal, + std::uint32_t flags = 0, + std::optional holderID = std::nullopt); + +/** Check if the account lacks required authorization for MPT. + * + * requireAuth check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] TER +requireAuth( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& account, + AuthType authType = AuthType::Legacy, + int depth = 0); + +/** Enforce account has MPToken to match its authorization. + * + * Called from doApply - it will check for expired (and delete if found any) + * credentials matching DomainID set in MPTokenIssuance. Must be called if + * requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim. + */ +[[nodiscard]] TER +enforceMPTokenAuthorization( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + XRPAmount const& priorBalance, + beast::Journal j); + +/** Check if the destination account is allowed + * to receive MPT. Return tecNO_AUTH if it doesn't + * and tesSUCCESS otherwise. + */ +[[nodiscard]] TER +canTransfer( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& from, + AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Empty holding operations (MPT-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + MPTIssue const& mptIssue, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + MPTIssue const& mptIssue, + beast::Journal journal); + +//------------------------------------------------------------------------------ +// +// Escrow operations (MPT-specific) +// +//------------------------------------------------------------------------------ + +TER +rippleLockEscrowMPT( + ApplyView& view, + AccountID const& uGrantorID, + STAmount const& saAmount, + beast::Journal j); + +TER +rippleUnlockEscrowMPT( + ApplyView& view, + AccountID const& uGrantorID, + AccountID const& uGranteeID, + STAmount const& netAmount, + STAmount const& grossAmount, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h new file mode 100644 index 0000000000..9096071811 --- /dev/null +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace xrpl { + +/** Delete an offer. + + Requirements: + The offer must exist. + The caller must have already checked permissions. + + @param view The ApplyView to modify. + @param sle The offer to delete. + @param j Journal for logging. + + @return tesSUCCESS on success, otherwise an error code. +*/ +// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. +TER +offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h new file mode 100644 index 0000000000..3feba59d1f --- /dev/null +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -0,0 +1,255 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// +// RippleState (Trustline) helpers +// +//------------------------------------------------------------------------------ + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Credit functions (from Credit.h) +// +//------------------------------------------------------------------------------ + +/** Calculate the maximum amount of IOUs that an account can hold + @param view the ledger to check against. + @param account the account of interest. + @param issuer the issuer of the IOU. + @param currency the IOU to check. + @return The maximum amount that can be held. +*/ +/** @{ */ +STAmount +creditLimit( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency); + +IOUAmount +creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur); +/** @} */ + +/** Returns the amount of IOUs issued by issuer that are held by an account + @param view the ledger to check against. + @param account the account of interest. + @param issuer the issuer of the IOU. + @param currency the IOU to check. +*/ +/** @{ */ +STAmount +creditBalance( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency); +/** @} */ + +//------------------------------------------------------------------------------ +// +// Freeze checking (IOU-specific) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isIndividualFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isIndividualFrozen(view, account, issue.currency, issue.account); +} + +[[nodiscard]] bool +isFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isFrozen(view, account, issue.currency, issue.account); +} + +// Overload with depth parameter for uniformity with MPTIssue version. +// The depth parameter is ignored for IOUs since they don't have vault recursion. +[[nodiscard]] inline bool +isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int /*depth*/) +{ + return isFrozen(view, account, issue); +} + +[[nodiscard]] bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); + +[[nodiscard]] inline bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Issue const& issue, + int = 0 /*ignored*/) +{ + return isDeepFrozen(view, account, issue.currency, issue.account); +} + +[[nodiscard]] inline TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// Trust line operations +// +//------------------------------------------------------------------------------ + +/** Create a trust line + + This can set an initial balance. +*/ +[[nodiscard]] TER +trustCreate( + ApplyView& view, + bool const bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + bool const bAuth, // --> authorize account. + bool const bNoRipple, // --> others cannot ripple through + bool const bFreeze, // --> funds cannot leave + bool bDeepFreeze, // --> can neither receive nor send funds + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + std::uint32_t uQualityIn, + std::uint32_t uQualityOut, + beast::Journal j); + +[[nodiscard]] TER +trustDelete( + ApplyView& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID, + beast::Journal j); + +//------------------------------------------------------------------------------ +// +// IOU issuance/redemption +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +issueIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j); + +[[nodiscard]] TER +redeemIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j); + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (IOU-specific) +// +//------------------------------------------------------------------------------ + +/** Check if the account lacks required authorization. + * + * Return tecNO_AUTH or tecNO_LINE if it does + * and tesSUCCESS otherwise. + * + * If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return + * tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the + * RippleState does exist, and the RippleState is not authorized. + * + * If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the + * RippleState exists, and is not authorized. Return tecNO_LINE if + * lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if + * WeakAuth and lsfRequireAuth is *not* set, this function will return + * tesSUCCESS even if RippleState does *not* exist. + * + * The default "Legacy" auth type is equivalent to WeakAuth. + */ +[[nodiscard]] TER +requireAuth( + ReadView const& view, + Issue const& issue, + AccountID const& account, + AuthType authType = AuthType::Legacy); + +/** Check if the destination account is allowed + * to receive IOU. Return terNO_RIPPLE if rippling is + * disabled on both sides and tesSUCCESS otherwise. + */ +[[nodiscard]] TER +canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Empty holding operations (IOU-specific) +// +//------------------------------------------------------------------------------ + +/// Any transactors that call addEmptyHolding() in doApply must call +/// canAddHolding() in preflight with the same View and Asset +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Issue const& issue, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Issue const& issue, + beast::Journal journal); + +/** Delete trustline to AMM. The passed `sle` must be obtained from a prior + * call to view.peek(). Fail if neither side of the trustline is AMM or + * if ammAccountID is seated and is not one of the trustline's side. + */ +[[nodiscard]] TER +deleteAMMTrustLine( + ApplyView& view, + std::shared_ptr sleState, + std::optional const& ammAccountID, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h new file mode 100644 index 0000000000..74d1e4848e --- /dev/null +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Enums for token handling +// +//------------------------------------------------------------------------------ + +/** Controls the treatment of frozen account balances */ +enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN }; + +/** Controls the treatment of unauthorized MPT balances */ +enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED }; + +/** Controls whether to include the account's full spendable balance */ +enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE }; + +enum class WaiveTransferFee : bool { No = false, Yes }; + +/* Check if MPToken (for MPT) or trust line (for IOU) exists: + * - StrongAuth - before checking if authorization is required + * - WeakAuth + * for MPT - after checking lsfMPTRequireAuth flag + * for IOU - do not check if trust line exists + * - Legacy + * for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth + * for IOU - do not check if trust line exists i.e. same as WeakAuth + */ +enum class AuthType { StrongAuth, WeakAuth, Legacy }; + +//------------------------------------------------------------------------------ +// +// Freeze checking (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] bool +isGlobalFrozen(ReadView const& view, Asset const& asset); + +[[nodiscard]] bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +/** + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] bool +isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); + +[[nodiscard]] TER +checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Issue const& issue); + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Asset const& asset, + int depth = 0); + +[[nodiscard]] bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + int depth = 0); + +/** + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. + */ +[[nodiscard]] bool +isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); + +[[nodiscard]] TER +checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); + +[[nodiscard]] TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset); + +//------------------------------------------------------------------------------ +// +// Account balance functions (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +// Returns the amount an account can spend. +// +// If shSIMPLE_BALANCE is specified, this is the amount the account can spend +// without going into debt. +// +// If shFULL_BALANCE is specified, this is the amount the account can spend +// total. Specifically: +// * The account can go into debt if using a trust line, and the other side has +// a non-zero limit. +// * If the account is the asset issuer the limit is defined by the asset / +// issuance. +// +// <-- saAmount: amount of currency held by account. May be negative. +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Issue const& issue, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Asset const& asset, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance = shSIMPLE_BALANCE); + +// Returns the amount an account can spend of the currency type saDefault, or +// returns saDefault if this account is the issuer of the currency in +// question. Should be used in favor of accountHolds when questioning how much +// an account can spend while also allowing currency issuers to spend +// unlimited amounts of their own currency (since they can always issue more). +[[nodiscard]] STAmount +accountFunds( + ReadView const& view, + AccountID const& id, + STAmount const& saDefault, + FreezeHandling freezeHandling, + beast::Journal j); + +/** Returns the transfer fee as Rate based on the type of token + * @param view The ledger view + * @param amount The amount to transfer + */ +[[nodiscard]] Rate +transferRate(ReadView const& view, STAmount const& amount); + +//------------------------------------------------------------------------------ +// +// Holding operations (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Asset const& asset); + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Asset const& asset, + beast::Journal journal); + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Asset const& asset, + beast::Journal journal); + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +requireAuth( + ReadView const& view, + Asset const& asset, + AccountID const& account, + AuthType authType = AuthType::Legacy); + +[[nodiscard]] TER +canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to); + +//------------------------------------------------------------------------------ +// +// Money Transfers (Asset-based dispatchers) +// +//------------------------------------------------------------------------------ + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line of needed. +// --> bCheckIssuer : normally require issuer to be involved. +// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. + +/** Calls static rippleCreditIOU if saAmount represents Issue. + * Calls static rippleCreditMPT if saAmount represents MPTIssue. + */ +TER +rippleCredit( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j); + +/** Calls static accountSendIOU if saAmount represents Issue. + * Calls static accountSendMPT if saAmount represents MPTIssue. + */ +[[nodiscard]] TER +accountSend( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee = WaiveTransferFee::No); + +using MultiplePaymentDestinations = std::vector>; +/** Like accountSend, except one account is sending multiple payments (with the + * same asset!) simultaneously + * + * Calls static accountSendMultiIOU if saAmount represents Issue. + * Calls static accountSendMultiMPT if saAmount represents MPTIssue. + */ +[[nodiscard]] TER +accountSendMulti( + ApplyView& view, + AccountID const& senderID, + Asset const& asset, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee = WaiveTransferFee::No); + +[[nodiscard]] TER +transferXRP( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount, + beast::Journal j); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h new file mode 100644 index 0000000000..8aef30aa27 --- /dev/null +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#include +#include + +namespace xrpl { + +/** From the perspective of a vault, return the number of shares to give + depositor when they offer a fixed amount of assets. Note, since shares are + MPT, this number is integral and always truncated in this calculation. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param assets The amount of assets to convert. + + @return The number of shares, or nullopt on error. +*/ +[[nodiscard]] std::optional +assetsToSharesDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets); + +/** From the perspective of a vault, return the number of assets to take from + depositor when they receive a fixed amount of shares. Note, since shares are + MPT, they are always an integral number. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param shares The amount of shares to convert. + + @return The number of assets, or nullopt on error. +*/ +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares); + +/** Controls whether to truncate shares instead of rounding. */ +enum class TruncateShares : bool { no = false, yes = true }; + +/** From the perspective of a vault, return the number of shares to demand from + the depositor when they ask to withdraw a fixed amount of assets. Since + shares are MPT this number is integral, and it will be rounded to nearest + unless explicitly requested to be truncated instead. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param assets The amount of assets to convert. + @param truncate Whether to truncate instead of rounding. + + @return The number of shares, or nullopt on error. +*/ +[[nodiscard]] std::optional +assetsToSharesWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets, + TruncateShares truncate = TruncateShares::no); + +/** From the perspective of a vault, return the number of assets to give the + depositor when they redeem a fixed amount of shares. Note, since shares are + MPT, they are always an integral number. + + @param vault The vault SLE. + @param issuance The MPTokenIssuance SLE for the vault's shares. + @param shares The amount of shares to convert. + + @return The number of assets, or nullopt on error. +*/ +[[nodiscard]] std::optional +sharesToAssetsWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares); + +} // namespace xrpl diff --git a/include/xrpl/net/AutoSocket.h b/include/xrpl/net/AutoSocket.h index 85a3adb456..29cec23998 100644 --- a/include/xrpl/net/AutoSocket.h +++ b/include/xrpl/net/AutoSocket.h @@ -26,13 +26,20 @@ public: using callback = std::function; public: - AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c, bool secureOnly, bool plainOnly) - : mSecure(secureOnly), mBuffer((plainOnly || secureOnly) ? 0 : 4), j_{beast::Journal::getNullSink()} + AutoSocket( + boost::asio::io_context& s, + boost::asio::ssl::context& c, + bool secureOnly, + bool plainOnly) + : mSecure(secureOnly) + , mBuffer((plainOnly || secureOnly) ? 0 : 4) + , j_{beast::Journal::getNullSink()} { mSocket = std::make_unique(s, c); } - AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c) : AutoSocket(s, c, false, false) + AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c) + : AutoSocket(s, c, false, false) { } @@ -105,7 +112,12 @@ public: mSocket->next_layer().async_receive( boost::asio::buffer(mBuffer), boost::asio::socket_base::message_peek, - std::bind(&AutoSocket::handle_autodetect, this, cbFunc, std::placeholders::_1, std::placeholders::_2)); + std::bind( + &AutoSocket::handle_autodetect, + this, + cbFunc, + std::placeholders::_1, + std::placeholders::_2)); } } @@ -152,7 +164,10 @@ public: template void - async_read_until(boost::asio::basic_streambuf& buffers, std::string const& delim, Handler handler) + async_read_until( + boost::asio::basic_streambuf& buffers, + std::string const& delim, + Handler handler) { if (isSecure()) boost::asio::async_read_until(*mSocket, buffers, delim, handler); @@ -162,7 +177,10 @@ public: template void - async_read_until(boost::asio::basic_streambuf& buffers, MatchCondition cond, Handler handler) + async_read_until( + boost::asio::basic_streambuf& buffers, + MatchCondition cond, + Handler handler) { if (isSecure()) boost::asio::async_read_until(*mSocket, buffers, cond, handler); diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index 289ce8f6e3..c176e2d2e8 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -29,6 +29,18 @@ public: bool sslVerify, beast::Journal j); + /** Destroys the global SSL context created by initializeSSLContext(). + * + * This releases the underlying boost::asio::ssl::context and any + * associated OpenSSL resources. Must not be called while any + * HTTPClient requests are in flight. + * + * @note Currently only called from tests during teardown. In production, + * the SSL context lives for the lifetime of the process. + */ + static void + cleanupSSLContext(); + static void get(bool bSSL, boost::asio::io_context& io_context, @@ -37,8 +49,10 @@ public: std::string const& strPath, std::size_t responseMax, // if no Content-Length header std::chrono::seconds timeout, - std::function - complete, + std::function complete, beast::Journal& j); static void @@ -49,8 +63,10 @@ public: std::string const& strPath, std::size_t responseMax, // if no Content-Length header std::chrono::seconds timeout, - std::function - complete, + std::function complete, beast::Journal& j); static void @@ -62,8 +78,10 @@ public: std::function build, std::size_t responseMax, // if no Content-Length header std::chrono::seconds timeout, - std::function - complete, + std::function complete, beast::Journal& j); }; diff --git a/include/xrpl/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h index 1033b9a22b..80e5835f5e 100644 --- a/include/xrpl/net/HTTPClientSSLContext.h +++ b/include/xrpl/net/HTTPClientSSLContext.h @@ -30,8 +30,8 @@ public: registerSSLCerts(ssl_context_, ec, j_); if (ec && sslVerifyDir.empty()) - Throw( - boost::str(boost::format("Failed to set_default_verify_paths: %s") % ec.message())); + Throw(boost::str( + boost::format("Failed to set_default_verify_paths: %s") % ec.message())); } else { @@ -43,7 +43,8 @@ public: ssl_context_.add_verify_path(sslVerifyDir, ec); if (ec) - Throw(boost::str(boost::format("Failed to add verify path: %s") % ec.message())); + Throw( + boost::str(boost::format("Failed to add verify path: %s") % ec.message())); } } @@ -114,7 +115,9 @@ public: if (!ec) { strm.set_verify_callback( - std::bind(&rfc6125_verify, host, std::placeholders::_1, std::placeholders::_2, j_), ec); + std::bind( + &rfc6125_verify, host, std::placeholders::_1, std::placeholders::_2, j_), + ec); } } @@ -131,12 +134,17 @@ public: * @param j journal for logging */ static bool - rfc6125_verify(std::string const& domain, bool preverified, boost::asio::ssl::verify_context& ctx, beast::Journal j) + rfc6125_verify( + std::string const& domain, + bool preverified, + boost::asio::ssl::verify_context& ctx, + beast::Journal j) { if (boost::asio::ssl::host_name_verification(domain)(preverified, ctx)) return true; - JLOG(j.warn()) << "Outbound SSL connection to " << domain << " fails certificate verification"; + JLOG(j.warn()) << "Outbound SSL connection to " << domain + << " fails certificate verification"; return false; } diff --git a/include/xrpl/nodestore/Backend.h b/include/xrpl/nodestore/Backend.h index 6af292d5b3..36fd36ec00 100644 --- a/include/xrpl/nodestore/Backend.h +++ b/include/xrpl/nodestore/Backend.h @@ -63,7 +63,8 @@ public: virtual void open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) { - Throw("Deterministic appType/uid/salt not supported by backend " + getName()); + Throw( + "Deterministic appType/uid/salt not supported by backend " + getName()); } /** Close the backend. @@ -76,16 +77,16 @@ public: If the object is not found or an error is encountered, the result will indicate the condition. @note This will be called concurrently. - @param key A pointer to the key data. + @param hash The hash of the object. @param pObject [out] The created object if successful. @return The result of the operation. */ virtual Status - fetch(void const* key, std::shared_ptr* pObject) = 0; + fetch(uint256 const& hash, std::shared_ptr* pObject) = 0; /** Fetch a batch synchronously. */ virtual std::pair>, Status> - fetchBatch(std::vector const& hashes) = 0; + fetchBatch(std::vector const& hashes) = 0; /** Store a single object. Depending on the implementation this may happen immediately diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index d1f5b1bda2..e33b8a3a5c 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -243,7 +243,10 @@ private: std::condition_variable readCondVar_; // reads to do - std::map const&)>>>> + std::map< + uint256, + std::vector< + std::pair const&)>>>> read_; std::atomic readStopping_ = false; @@ -251,7 +254,11 @@ private: std::atomic runningThreads_ = 0; virtual std::shared_ptr - fetchNodeObject(uint256 const& hash, std::uint32_t ledgerSeq, FetchReport& fetchReport, bool duplicate) = 0; + fetchNodeObject( + uint256 const& hash, + std::uint32_t ledgerSeq, + FetchReport& fetchReport, + bool duplicate) = 0; /** Visit every object in the database This is usually called during import. diff --git a/include/xrpl/nodestore/DatabaseRotating.h b/include/xrpl/nodestore/DatabaseRotating.h index e20b49805c..23a749f972 100644 --- a/include/xrpl/nodestore/DatabaseRotating.h +++ b/include/xrpl/nodestore/DatabaseRotating.h @@ -13,7 +13,11 @@ namespace NodeStore { class DatabaseRotating : public Database { public: - DatabaseRotating(Scheduler& scheduler, int readThreads, Section const& config, beast::Journal journal) + DatabaseRotating( + Scheduler& scheduler, + int readThreads, + Section const& config, + beast::Journal journal) : Database(scheduler, readThreads, config, journal) { } @@ -28,7 +32,8 @@ public: virtual void rotate( std::unique_ptr&& newBackend, - std::function const& f) = 0; + std::function const& + f) = 0; }; } // namespace NodeStore diff --git a/include/xrpl/nodestore/Manager.h b/include/xrpl/nodestore/Manager.h index 9553407881..ff00ee3ee1 100644 --- a/include/xrpl/nodestore/Manager.h +++ b/include/xrpl/nodestore/Manager.h @@ -38,7 +38,11 @@ public: /** Create a backend. */ virtual std::unique_ptr - make_Backend(Section const& parameters, std::size_t burstSize, Scheduler& scheduler, beast::Journal journal) = 0; + make_Backend( + Section const& parameters, + std::size_t burstSize, + Scheduler& scheduler, + beast::Journal journal) = 0; /** Construct a NodeStore database. diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index 94859c4d22..21f06fef8d 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -82,7 +82,8 @@ private: std::shared_ptr backend_; std::shared_ptr - fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) override; + fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) + override; void for_each(std::function)> f) override diff --git a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h index 1a378c54c7..8d4cd9ddbc 100644 --- a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h +++ b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h @@ -31,7 +31,8 @@ public: void rotate( std::unique_ptr&& newBackend, - std::function const& f) override; + std::function const& + f) override; std::string getName() const override; @@ -61,7 +62,8 @@ private: mutable std::mutex mutex_; std::shared_ptr - fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) override; + fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) + override; void for_each(std::function)> f) override; diff --git a/include/xrpl/nodestore/detail/ManagerImp.h b/include/xrpl/nodestore/detail/ManagerImp.h index f46a7d56d0..cb10a740c1 100644 --- a/include/xrpl/nodestore/detail/ManagerImp.h +++ b/include/xrpl/nodestore/detail/ManagerImp.h @@ -33,8 +33,11 @@ public: erase(Factory& factory) override; std::unique_ptr - make_Backend(Section const& parameters, std::size_t burstSize, Scheduler& scheduler, beast::Journal journal) - override; + make_Backend( + Section const& parameters, + std::size_t burstSize, + Scheduler& scheduler, + beast::Journal journal) override; std::unique_ptr make_Database( diff --git a/include/xrpl/nodestore/detail/codec.h b/include/xrpl/nodestore/detail/codec.h index 1d08beff59..8a337ca4c3 100644 --- a/include/xrpl/nodestore/detail/codec.h +++ b/include/xrpl/nodestore/detail/codec.h @@ -62,8 +62,8 @@ lz4_compress(void const* in, std::size_t in_size, BufferFactory&& bf) std::uint8_t* out = reinterpret_cast(bf(n + out_max)); result.first = out; std::memcpy(out, vi.data(), n); - auto const out_size = - LZ4_compress_default(reinterpret_cast(in), reinterpret_cast(out + n), in_size, out_max); + auto const out_size = LZ4_compress_default( + reinterpret_cast(in), reinterpret_cast(out + n), in_size, out_max); if (out_size == 0) Throw("lz4 compress"); result.second = n + out_size; @@ -137,8 +137,9 @@ nodeobject_decompress(void const* in, std::size_t in_size, BufferFactory&& bf) { if (in_size < 32) Throw( - "nodeobject codec v1: short inner node subsize: " + std::string("in_size = ") + - std::to_string(in_size) + " i = " + std::to_string(i)); + "nodeobject codec v1: short inner node subsize: " + + std::string("in_size = ") + std::to_string(in_size) + + " i = " + std::to_string(i)); std::memcpy(os.data(32), is(32), 32); in_size -= 32; } @@ -148,14 +149,16 @@ nodeobject_decompress(void const* in, std::size_t in_size, BufferFactory&& bf) } } if (in_size > 0) - Throw("nodeobject codec v1: long inner node, in_size = " + std::to_string(in_size)); + Throw( + "nodeobject codec v1: long inner node, in_size = " + std::to_string(in_size)); break; } case 3: // full v1 inner node { if (in_size != 16 * 32) // hashes Throw( - "nodeobject codec v1: short full inner node, in_size = " + std::to_string(in_size)); + "nodeobject codec v1: short full inner node, in_size = " + + std::to_string(in_size)); istream is(p, in_size); result.second = 525; void* const out = bf(result.second); diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index a2c531f361..1a0252d67a 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -17,7 +17,8 @@ std::uint16_t constexpr AUCTION_SLOT_MAX_AUTH_ACCOUNTS = 4; std::uint32_t constexpr AUCTION_SLOT_FEE_SCALE_FACTOR = 100000; std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION = 10; std::uint32_t constexpr AUCTION_SLOT_MIN_FEE_FRACTION = 25; -std::uint32_t constexpr AUCTION_SLOT_INTERVAL_DURATION = TOTAL_TIME_SLOT_SECS / AUCTION_SLOT_TIME_INTERVALS; +std::uint32_t constexpr AUCTION_SLOT_INTERVAL_DURATION = + TOTAL_TIME_SLOT_SECS / AUCTION_SLOT_TIME_INTERVALS; // Votes std::uint16_t constexpr VOTE_MAX_SLOTS = 8; @@ -49,7 +50,9 @@ invalidAMMAmount( bool validZero = false); NotTEC -invalidAMMAsset(Issue const& issue, std::optional> const& pair = std::nullopt); +invalidAMMAsset( + Issue const& issue, + std::optional> const& pair = std::nullopt); NotTEC invalidAMMAssetPair( diff --git a/include/xrpl/protocol/AmountConversions.h b/include/xrpl/protocol/AmountConversions.h index 0807e96c49..e1a224dac9 100644 --- a/include/xrpl/protocol/AmountConversions.h +++ b/include/xrpl/protocol/AmountConversions.h @@ -53,7 +53,8 @@ inline IOUAmount toAmount(STAmount const& amt) { XRPL_ASSERT( - amt.mantissa() < std::numeric_limits::max(), "xrpl::toAmount : maximum mantissa"); + amt.mantissa() < std::numeric_limits::max(), + "xrpl::toAmount : maximum mantissa"); bool const isNeg = amt.negative(); std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa(); @@ -66,7 +67,8 @@ inline XRPAmount toAmount(STAmount const& amt) { XRPL_ASSERT( - amt.mantissa() < std::numeric_limits::max(), "xrpl::toAmount : maximum mantissa"); + amt.mantissa() < std::numeric_limits::max(), + "xrpl::toAmount : maximum mantissa"); bool const isNeg = amt.negative(); std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa(); diff --git a/include/xrpl/protocol/ApiVersion.h b/include/xrpl/protocol/ApiVersion.h index 756a41d25e..8772b5a49d 100644 --- a/include/xrpl/protocol/ApiVersion.h +++ b/include/xrpl/protocol/ApiVersion.h @@ -47,9 +47,11 @@ constexpr static auto apiMaximumValidVersion = apiBetaVersion; static_assert(apiInvalidVersion < apiMinimumSupportedVersion); static_assert( - apiVersionIfUnspecified >= apiMinimumSupportedVersion && apiVersionIfUnspecified <= apiMaximumSupportedVersion); + apiVersionIfUnspecified >= apiMinimumSupportedVersion && + apiVersionIfUnspecified <= apiMaximumSupportedVersion); static_assert( - apiCommandLineVersion >= apiMinimumSupportedVersion && apiCommandLineVersion <= apiMaximumSupportedVersion); + apiCommandLineVersion >= apiMinimumSupportedVersion && + apiCommandLineVersion <= apiMaximumSupportedVersion); static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion); static_assert(apiBetaVersion >= apiMaximumSupportedVersion); static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion); @@ -97,7 +99,8 @@ inline unsigned int getAPIVersionNumber(Json::Value const& jv, bool betaEnabled) { static Json::Value const minVersion(RPC::apiMinimumSupportedVersion); - Json::Value const maxVersion(betaEnabled ? RPC::apiBetaVersion : RPC::apiMaximumSupportedVersion); + Json::Value const maxVersion( + betaEnabled ? RPC::apiBetaVersion : RPC::apiMaximumSupportedVersion); if (jv.isObject()) { @@ -135,7 +138,9 @@ forApiVersions(Fn const& fn, Args&&... args) { constexpr auto size = maxVer + 1 - minVer; [&](std::index_sequence) { - (((void)fn(std::integral_constant{}, std::forward(args)...)), ...); + (((void)fn( + std::integral_constant{}, std::forward(args)...)), + ...); }(std::make_index_sequence{}); } @@ -143,10 +148,12 @@ template void forAllApiVersions(Fn const& fn, Args&&... args) requires requires { - forApiVersions(fn, std::forward(args)...); + forApiVersions( + fn, std::forward(args)...); } { - forApiVersions(fn, std::forward(args)...); + forApiVersions( + fn, std::forward(args)...); } } // namespace xrpl diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index 3c42a4323f..cc7633cfe1 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -49,7 +49,7 @@ getFullVersionString(); @return the encoded version in a 64-bit integer */ std::uint64_t -encodeSoftwareVersion(char const* const versionStr); +encodeSoftwareVersion(std::string_view versionStr); /** Returns this server's version packed in a 64-bit integer. */ std::uint64_t diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index 08c034e38f..c138d5d39a 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -167,7 +167,8 @@ namespace RPC { struct ErrorInfo { // Default ctor needed to produce an empty std::array during constexpr eval. - constexpr ErrorInfo() : code(rpcUNKNOWN), token("unknown"), message("An unknown error code."), http_status(200) + constexpr ErrorInfo() + : code(rpcUNKNOWN), token("unknown"), message("An unknown error code."), http_status(200) { } @@ -176,7 +177,11 @@ struct ErrorInfo { } - constexpr ErrorInfo(error_code_i code_, char const* token_, char const* message_, int http_status_) + constexpr ErrorInfo( + error_code_i code_, + char const* token_, + char const* message_, + int http_status_) : code(code_), token(token_), message(message_), http_status(http_status_) { } diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 6d674fbef2..0ff02fdac2 100644 --- a/include/xrpl/protocol/Feature.h +++ b/include/xrpl/protocol/Feature.h @@ -64,6 +64,49 @@ namespace xrpl { +// Feature names must not exceed this length (in characters, excluding the null terminator). +static constexpr std::size_t maxFeatureNameSize = 63; +// Reserve this exact feature-name length (in characters/bytes, excluding the null terminator) +// so that a 32-byte uint256 (for example, in WASM or other interop contexts) can be used +// as a compact, fixed-size feature selector without conflicting with human-readable names. +static constexpr std::size_t reservedFeatureNameSize = 32; + +// Both validFeatureNameSize and validFeatureName are consteval functions that can be used in +// static_asserts to validate feature names at compile time. They are only used inside +// enforceValidFeatureName in Feature.cpp, but are exposed here for testing. The expected +// parameter `auto fn` is a constexpr lambda which returns a const char*, making it available +// for compile-time evaluation. Read more in https://accu.org/journals/overload/30/172/wu/ +consteval auto +validFeatureNameSize(auto fn) -> bool +{ + constexpr char const* n = fn(); + // Note, std::strlen is not constexpr, we need to implement our own here. + constexpr std::size_t N = [](auto n) { + std::size_t ret = 0; + for (auto ptr = n; *ptr != '\0'; ret++, ++ptr) + ; + return ret; + }(n); + return N != reservedFeatureNameSize && // + N <= maxFeatureNameSize; +} + +consteval auto +validFeatureName(auto fn) -> bool +{ + constexpr char const* n = fn(); + // Prevent the use of visually confusable characters and enforce that feature names + // are always valid ASCII. This is needed because C++ allows Unicode identifiers. + // Characters below 0x20 are nonprintable control characters, and characters with the 0x80 bit + // set are non-ASCII (e.g. UTF-8 encoding of Unicode), so both are disallowed. + for (auto ptr = n; *ptr != '\0'; ++ptr) + { + if (*ptr & 0x80 || *ptr < 0x20) + return false; + } + return true; +} + enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes }; enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported }; @@ -296,7 +339,7 @@ public: friend FeatureBitset operator^(FeatureBitset const& lhs, uint256 const& rhs) { - return lhs ^ FeatureBitset { rhs }; + return lhs ^ FeatureBitset{rhs}; } friend FeatureBitset diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index 52c2272da0..2ee453f575 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -95,7 +95,8 @@ inline IOUAmount::IOUAmount(beast::Zero) *this = beast::zero; } -inline IOUAmount::IOUAmount(mantissa_type mantissa, exponent_type exponent) : mantissa_(mantissa), exponent_(exponent) +inline IOUAmount::IOUAmount(mantissa_type mantissa, exponent_type exponent) + : mantissa_(mantissa), exponent_(exponent) { normalize(); } @@ -110,7 +111,8 @@ IOUAmount::operator=(beast::Zero) return *this; } -inline IOUAmount::operator Number() const +inline IOUAmount:: +operator Number() const { return Number{mantissa_, exponent_}; } @@ -140,7 +142,8 @@ IOUAmount::operator<(IOUAmount const& other) const return Number{*this} < Number{other}; } -inline IOUAmount::operator bool() const noexcept +inline IOUAmount:: +operator bool() const noexcept { return mantissa_ != 0; } diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 6868f416d7..ddb763208f 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -169,7 +169,9 @@ Keylet depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept; Keylet -depositPreauth(AccountID const& owner, std::set> const& authCreds) noexcept; +depositPreauth( + AccountID const& owner, + std::set> const& authCreds) noexcept; inline Keylet depositPreauth(uint256 const& key) noexcept diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index 67701c91ca..c454683e19 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -30,9 +30,11 @@ public: Item( char const* name, KeyType type, - std::initializer_list uniqueFields, - std::initializer_list commonFields) - : soTemplate_(uniqueFields, commonFields), name_(name), type_(type) + std::vector uniqueFields, + std::vector commonFields) + : soTemplate_(std::move(uniqueFields), std::move(commonFields)) + , name_(name) + , type_(type) { // Verify that KeyType is appropriate. static_assert( @@ -98,7 +100,8 @@ public: if (auto const result = findByName(name)) return result->getType(); Throw( - name_ + ": Unknown format name '" + name.substr(0, std::min(name.size(), std::size_t(32))) + "'"); + name_ + ": Unknown format name '" + + name.substr(0, std::min(name.size(), std::size_t(32))) + "'"); } /** Retrieve a format based on its type. @@ -141,23 +144,25 @@ protected: @param name The name of this format. @param type The type of this format. - @param uniqueFields An std::initializer_list of unique fields - @param commonFields An std::initializer_list of common fields + @param uniqueFields A std::vector of unique fields + @param commonFields A std::vector of common fields @return The created format. */ Item const& add(char const* name, KeyType type, - std::initializer_list uniqueFields, - std::initializer_list commonFields = {}) + std::vector uniqueFields, + std::vector commonFields = {}) { if (auto const item = findByType(type)) { - LogicError(std::string("Duplicate key for item '") + name + "': already maps to " + item->getName()); + LogicError( + std::string("Duplicate key for item '") + name + "': already maps to " + + item->getName()); } - formats_.emplace_front(name, type, uniqueFields, commonFields); + formats_.emplace_front(name, type, std::move(uniqueFields), std::move(commonFields)); Item const& item{formats_.front()}; names_[name] = &item; diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index fa39c15843..a43f6a7134 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -2,36 +2,34 @@ #include -namespace xrpl { +#include +#include +#include +namespace xrpl { /** Identifiers for on-ledger objects. - Each ledger object requires a unique type identifier, which is stored - within the object itself; this makes it possible to iterate the entire - ledger and determine each object's type and verify that the object you - retrieved from a given hash matches the expected type. + Each ledger object requires a unique type identifier, which is stored within the object itself; + this makes it possible to iterate the entire ledger and determine each object's type and verify + that the object you retrieved from a given hash matches the expected type. - @warning Since these values are stored inside objects stored on the ledger - they are part of the protocol. **Changing them should be avoided - because without special handling, this will result in a hard + @warning Since these values are stored inside objects stored on the ledger they are part of the + protocol. + **Changing them should be avoided because without special handling, this will result in a hard fork.** - @note Values outside this range may be used internally by the code for - various purposes, but attempting to use such values to identify - on-ledger objects will results in an invariant failure. + @note Values outside this range may be used internally by the code for various purposes, but + attempting to use such values to identify on-ledger objects will result in an invariant failure. - @note When retiring types, the specific values should not be removed but - should be marked as [[deprecated]]. This is to avoid accidental - reuse of identifiers. + @note When retiring types, the specific values should not be removed but should be marked as + [[deprecated]]. This is to avoid accidental reuse of identifiers. - @todo The C++ language does not enable checking for duplicate values - here. If it becomes possible then we should do this. + @todo The C++ language does not enable checking for duplicate values here. + If it becomes possible then we should do this. @ingroup protocol */ -// clang-format off -enum LedgerEntryType : std::uint16_t -{ +enum LedgerEntryType : std::uint16_t { #pragma push_macro("LEDGER_ENTRY") #undef LEDGER_ENTRY @@ -46,12 +44,10 @@ enum LedgerEntryType : std::uint16_t //--------------------------------------------------------------------------- /** A special type, matching any ledger entry type. - The value does not represent a concrete type, but rather is used in - contexts where the specific type of a ledger object is unimportant, - unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + specific type of a ledger object is unimportant, unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::unchecked */ @@ -59,12 +55,11 @@ enum LedgerEntryType : std::uint16_t /** A special type, matching any ledger type except directory nodes. - The value does not represent a concrete type, but rather is used in - contexts where the ledger object must not be a directory node but - its specific type is otherwise unimportant, unknown or unavailable. + The value does not represent a concrete type, but rather is used in contexts where the + ledger object must not be a directory node but its specific type is otherwise unimportant, + unknown or unavailable. - Objects with this special type cannot be created or stored on the - ledger. + Objects with this special type cannot be created or stored on the ledger. \sa keylet::child */ @@ -93,104 +88,188 @@ enum LedgerEntryType : std::uint16_t Support for this type of object was never implemented. No objects of this type were ever created. */ - ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067, + ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = + 0x0067, }; -// clang-format off -/** +/** Ledger object flags. + + These flags are specified in ledger objects and modify their behavior. + + @warning Ledger object flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in a hard + fork.** + @ingroup protocol */ -enum LedgerSpecificFlags { - // ltACCOUNT_ROOT - lsfPasswordSpent = 0x00010000, // True, if password set fee is spent. - lsfRequireDestTag = - 0x00020000, // True, to require a DestinationTag for payments. - lsfRequireAuth = - 0x00040000, // True, to require a authorization to hold IOUs. - lsfDisallowXRP = 0x00080000, // True, to disallow sending XRP. - lsfDisableMaster = 0x00100000, // True, force regular key - lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states - lsfGlobalFreeze = 0x00400000, // True, all assets frozen - lsfDefaultRipple = - 0x00800000, // True, incoming trust lines allow rippling by default - lsfDepositAuth = 0x01000000, // True, all deposits require authorization -/* // reserved for Hooks amendment - lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks -*/ - lsfDisallowIncomingNFTokenOffer = - 0x04000000, // True, reject new incoming NFT offers - lsfDisallowIncomingCheck = - 0x08000000, // True, reject new checks - lsfDisallowIncomingPayChan = - 0x10000000, // True, reject new paychans - lsfDisallowIncomingTrustline = - 0x20000000, // True, reject new trustlines (only if no issued assets) - lsfAllowTrustLineLocking = - 0x40000000, // True, enable trustline locking - lsfAllowTrustLineClawback = - 0x80000000, // True, enable clawback +#pragma push_macro("XMACRO") +#pragma push_macro("TO_VALUE") +#pragma push_macro("VALUE_TO_MAP") +#pragma push_macro("NULL_NAME") +#pragma push_macro("TO_MAP") +#pragma push_macro("ALL_LEDGER_FLAGS") - // ltOFFER - lsfPassive = 0x00010000, - lsfSell = 0x00020000, // True, offer was placed as a sell. - lsfHybrid = 0x00040000, // True, offer is hybrid. +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef TO_MAP - // ltRIPPLE_STATE - lsfLowReserve = 0x00010000, // True, if entry counts toward reserve. - lsfHighReserve = 0x00020000, - lsfLowAuth = 0x00040000, - lsfHighAuth = 0x00080000, - lsfLowNoRipple = 0x00100000, - lsfHighNoRipple = 0x00200000, - lsfLowFreeze = 0x00400000, // True, low side has set freeze flag - lsfHighFreeze = 0x00800000, // True, high side has set freeze flag - lsfLowDeepFreeze = 0x02000000, // True, low side has set deep freeze flag - lsfHighDeepFreeze = 0x04000000, // True, high side has set deep freeze flag - lsfAMMNode = 0x01000000, // True, trust line to AMM. Used by client - // apps to identify payments via AMM. +#undef ALL_LEDGER_FLAGS - // ltSIGNER_LIST - lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount +// clang-format off - // ltDIR_NODE - lsfNFTokenBuyOffers = 0x00000001, - lsfNFTokenSellOffers = 0x00000002, +#define XMACRO(LEDGER_OBJECT, LSF_FLAG, LSF_FLAG2) \ + LEDGER_OBJECT(AccountRoot, \ + LSF_FLAG(lsfPasswordSpent, 0x00010000) /* True, if password set fee is spent. */ \ + LSF_FLAG(lsfRequireDestTag, 0x00020000) /* True, to require a DestinationTag for payments. */ \ + LSF_FLAG(lsfRequireAuth, 0x00040000) /* True, to require a authorization to hold IOUs. */ \ + LSF_FLAG(lsfDisallowXRP, 0x00080000) /* True, to disallow sending XRP. */ \ + LSF_FLAG(lsfDisableMaster, 0x00100000) /* True, force regular key */ \ + LSF_FLAG(lsfNoFreeze, 0x00200000) /* True, cannot freeze ripple states */ \ + LSF_FLAG(lsfGlobalFreeze, 0x00400000) /* True, all assets frozen */ \ + LSF_FLAG(lsfDefaultRipple, 0x00800000) /* True, incoming trust lines allow rippling by default */ \ + LSF_FLAG(lsfDepositAuth, 0x01000000) /* True, all deposits require authorization */ \ + LSF_FLAG(lsfDisallowIncomingNFTokenOffer, 0x04000000) /* True, reject new incoming NFT offers */ \ + LSF_FLAG(lsfDisallowIncomingCheck, 0x08000000) /* True, reject new checks */ \ + LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \ + LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \ + LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \ + LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \ + \ + LEDGER_OBJECT(Offer, \ + LSF_FLAG(lsfPassive, 0x00010000) \ + LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \ + LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \ + \ + LEDGER_OBJECT(RippleState, \ + LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \ + LSF_FLAG(lsfHighReserve, 0x00020000) \ + LSF_FLAG(lsfLowAuth, 0x00040000) \ + LSF_FLAG(lsfHighAuth, 0x00080000) \ + LSF_FLAG(lsfLowNoRipple, 0x00100000) \ + LSF_FLAG(lsfHighNoRipple, 0x00200000) \ + LSF_FLAG(lsfLowFreeze, 0x00400000) /* True, low side has set freeze flag */ \ + LSF_FLAG(lsfHighFreeze, 0x00800000) /* True, high side has set freeze flag */ \ + LSF_FLAG(lsfAMMNode, 0x01000000) /* True, trust line to AMM. */ \ + /* Used by client apps to identify payments via AMM. */ \ + LSF_FLAG(lsfLowDeepFreeze, 0x02000000) /* True, low side has set deep freeze flag */ \ + LSF_FLAG(lsfHighDeepFreeze, 0x04000000)) /* True, high side has set deep freeze flag */ \ + \ + LEDGER_OBJECT(SignerList, \ + LSF_FLAG(lsfOneOwnerCount, 0x00010000)) /* True, uses only one OwnerCount */ \ + \ + LEDGER_OBJECT(DirNode, \ + LSF_FLAG(lsfNFTokenBuyOffers, 0x00000001) \ + LSF_FLAG(lsfNFTokenSellOffers, 0x00000002)) \ + \ + LEDGER_OBJECT(NFTokenOffer, \ + LSF_FLAG(lsfSellNFToken, 0x00000001)) \ + \ + LEDGER_OBJECT(MPTokenIssuance, \ + LSF_FLAG(lsfMPTLocked, 0x00000001) /* Also used in ltMPTOKEN */ \ + LSF_FLAG(lsfMPTCanLock, 0x00000002) \ + LSF_FLAG(lsfMPTRequireAuth, 0x00000004) \ + LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \ + LSF_FLAG(lsfMPTCanTrade, 0x00000010) \ + LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \ + LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \ + \ + LEDGER_OBJECT(MPTokenIssuanceMutable, \ + LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \ + LSF_FLAG(lsmfMPTCanMutateRequireAuth, 0x00000004) \ + LSF_FLAG(lsmfMPTCanMutateCanEscrow, 0x00000008) \ + LSF_FLAG(lsmfMPTCanMutateCanTrade, 0x00000010) \ + LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \ + LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \ + LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \ + LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \ + \ + LEDGER_OBJECT(MPToken, \ + LSF_FLAG2(lsfMPTLocked, 0x00000001) \ + LSF_FLAG(lsfMPTAuthorized, 0x00000002)) \ + \ + LEDGER_OBJECT(Credential, \ + LSF_FLAG(lsfAccepted, 0x00010000)) \ + \ + LEDGER_OBJECT(Vault, \ + LSF_FLAG(lsfVaultPrivate, 0x00010000)) \ + \ + LEDGER_OBJECT(Loan, \ + LSF_FLAG(lsfLoanDefault, 0x00010000) \ + LSF_FLAG(lsfLoanImpaired, 0x00020000) \ + LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ - // ltNFTOKEN_OFFER - lsfSellNFToken = 0x00000001, +// clang-format on - // ltMPTOKEN_ISSUANCE - lsfMPTLocked = 0x00000001, // Also used in ltMPTOKEN - lsfMPTCanLock = 0x00000002, - lsfMPTRequireAuth = 0x00000004, - lsfMPTCanEscrow = 0x00000008, - lsfMPTCanTrade = 0x00000010, - lsfMPTCanTransfer = 0x00000020, - lsfMPTCanClawback = 0x00000040, +// Create all the flag values as an enum. +// +// example: +// enum LedgerSpecificFlags { +// lsfPasswordSpent = 0x00010000, +// lsfRequireDestTag = 0x00020000, +// ... +// }; +#define TO_VALUE(name, value) name = value, +#define NULL_NAME(name, values) values +#define NULL_OUTPUT(name, value) +enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) }; - lsmfMPTCanMutateCanLock = 0x00000002, - lsmfMPTCanMutateRequireAuth = 0x00000004, - lsmfMPTCanMutateCanEscrow = 0x00000008, - lsmfMPTCanMutateCanTrade = 0x00000010, - lsmfMPTCanMutateCanTransfer = 0x00000020, - lsmfMPTCanMutateCanClawback = 0x00000040, - lsmfMPTCanMutateMetadata = 0x00010000, - lsmfMPTCanMutateTransferFee = 0x00020000, +// Create getter functions for each set of flags using Meyer's singleton pattern. +// This avoids static initialization order fiasco while still providing efficient access. +// This is used below in `getAllLedgerFlags()` to generate the server_definitions RPC output. +// +// example: +// inline LedgerFlagMap const& getAccountRootFlags() { +// static LedgerFlagMap const flags = { +// {"lsfPasswordSpent", 0x00010000}, +// {"lsfRequireDestTag", 0x00020000}, +// ...}; +// return flags; +// } +using LedgerFlagMap = std::map; +#define VALUE_TO_MAP(name, value) {#name, value}, +#define TO_MAP(name, values) \ + inline LedgerFlagMap const& get##name##Flags() \ + { \ + static LedgerFlagMap const flags = {values}; \ + return flags; \ + } +XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP) - // ltMPTOKEN - lsfMPTAuthorized = 0x00000002, +// Create a getter function for all ledger flag maps using Meyer's singleton pattern. +// This is used to generate the server_definitions RPC output. +// +// example: +// inline std::vector> const& getAllLedgerFlags() { +// static std::vector> const flags = { +// {"AccountRoot", getAccountRootFlags()}, +// ...}; +// return flags; +// } +#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()}, +inline std::vector> const& +getAllLedgerFlags() +{ + static std::vector> const flags = { + XMACRO(ALL_LEDGER_FLAGS, NULL_OUTPUT, NULL_OUTPUT)}; + return flags; +} - // ltCREDENTIAL - lsfAccepted = 0x00010000, +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef ALL_LEDGER_FLAGS - // ltVAULT - lsfVaultPrivate = 0x00010000, - - // ltLOAN - lsfLoanDefault = 0x00010000, - lsfLoanImpaired = 0x00020000, - lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments -}; +#pragma pop_macro("XMACRO") +#pragma pop_macro("TO_VALUE") +#pragma pop_macro("VALUE_TO_MAP") +#pragma pop_macro("NULL_NAME") +#pragma pop_macro("TO_MAP") +#pragma pop_macro("ALL_LEDGER_FLAGS") //------------------------------------------------------------------------------ @@ -207,6 +286,10 @@ private: public: static LedgerFormats const& getInstance(); + + // Fields shared by all ledger entry formats: + static std::vector const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/LedgerShortcut.h b/include/xrpl/protocol/LedgerShortcut.h new file mode 100644 index 0000000000..68c31c4c3c --- /dev/null +++ b/include/xrpl/protocol/LedgerShortcut.h @@ -0,0 +1,22 @@ +#pragma once + +namespace xrpl { + +/** + * @brief Enumeration of ledger shortcuts for specifying which ledger to use. + * + * These shortcuts provide a convenient way to reference commonly used ledgers + * without needing to specify their exact hash or sequence number. + */ +enum class LedgerShortcut { + /** The current working ledger (open, not yet closed) */ + Current, + + /** The most recently closed ledger (may not be validated) */ + Closed, + + /** The most recently validated ledger */ + Validated +}; + +} // namespace xrpl diff --git a/include/xrpl/protocol/MPTAmount.h b/include/xrpl/protocol/MPTAmount.h index 66027185b3..5c1642ae5c 100644 --- a/include/xrpl/protocol/MPTAmount.h +++ b/include/xrpl/protocol/MPTAmount.h @@ -93,7 +93,8 @@ MPTAmount::operator=(beast::Zero) } /** Returns true if the amount is not zero */ -constexpr MPTAmount::operator bool() const noexcept +constexpr MPTAmount:: +operator bool() const noexcept { return value_ != 0; } diff --git a/include/xrpl/protocol/MultiApiJson.h b/include/xrpl/protocol/MultiApiJson.h index ad7a6b8f0d..1ebe72f15e 100644 --- a/include/xrpl/protocol/MultiApiJson.h +++ b/include/xrpl/protocol/MultiApiJson.h @@ -86,8 +86,16 @@ struct MultiApiJson template requires std::same_as, MultiApiJson> auto - operator()(Json& json, std::integral_constant const version, Fn fn, Args&&... args) const - -> std::invoke_result_t, Args&&...> + operator()( + Json& json, + std::integral_constant const version, + Fn fn, + Args&&... args) const + -> std::invoke_result_t< + Fn, + decltype(json.val[0]), + std::integral_constant, + Args&&...> { static_assert(valid(Version) && index(Version) >= 0 && index(Version) < size); return std::invoke(fn, json.val[index(Version)], version, std::forward(args)...); @@ -107,7 +115,7 @@ struct MultiApiJson // unsigned int version, extra arguments template requires(!some_integral_constant) && std::convertible_to && - std::same_as, MultiApiJson> + std::same_as, MultiApiJson> auto operator()(Json& json, Version version, Fn fn, Args&&... args) const -> std::invoke_result_t @@ -122,9 +130,10 @@ struct MultiApiJson // unsigned int version, Json only template requires(!some_integral_constant) && std::convertible_to && - std::same_as, MultiApiJson> + std::same_as, MultiApiJson> auto - operator()(Json& json, Version version, Fn fn) const -> std::invoke_result_t + operator()(Json& json, Version version, Fn fn) const + -> std::invoke_result_t { XRPL_ASSERT( valid(version) && index(version) >= 0 && index(version) < size, @@ -137,7 +146,9 @@ struct MultiApiJson visit() { return [self = this](auto... args) - requires requires { visitor(std::declval(), std::declval()...); } + requires requires { + visitor(std::declval(), std::declval()...); + } { return visitor(*self, std::forward(args)...); }; } @@ -145,14 +156,17 @@ struct MultiApiJson visit() const { return [self = this](auto... args) - requires requires { visitor(std::declval(), std::declval()...); } + requires requires { + visitor(std::declval(), std::declval()...); + } { return visitor(*self, std::forward(args)...); }; } template auto visit(Args... args) -> std::invoke_result_t - requires(sizeof...(args) > 0) && requires { visitor(*this, std::forward(args)...); } + requires(sizeof...(args) > 0) && + requires { visitor(*this, std::forward(args)...); } { return visitor(*this, std::forward(args)...); } @@ -160,7 +174,8 @@ struct MultiApiJson template auto visit(Args... args) const -> std::invoke_result_t - requires(sizeof...(args) > 0) && requires { visitor(*this, std::forward(args)...); } + requires(sizeof...(args) > 0) && + requires { visitor(*this, std::forward(args)...); } { return visitor(*this, std::forward(args)...); } @@ -169,6 +184,7 @@ struct MultiApiJson } // namespace detail // Wrapper for Json for all supported API versions. -using MultiApiJson = detail::MultiApiJson; +using MultiApiJson = + detail::MultiApiJson; } // namespace xrpl diff --git a/include/xrpl/protocol/NFTokenID.h b/include/xrpl/protocol/NFTokenID.h index f3635d6fac..4bdacc1095 100644 --- a/include/xrpl/protocol/NFTokenID.h +++ b/include/xrpl/protocol/NFTokenID.h @@ -29,7 +29,10 @@ std::vector getNFTokenIDFromDeletedOffer(TxMeta const& transactionMeta); void -insertNFTokenID(Json::Value& response, std::shared_ptr const& transaction, TxMeta const& transactionMeta); +insertNFTokenID( + Json::Value& response, + std::shared_ptr const& transaction, + TxMeta const& transactionMeta); /** @} */ } // namespace xrpl diff --git a/include/xrpl/protocol/NFTokenOfferID.h b/include/xrpl/protocol/NFTokenOfferID.h index c3117db197..01b0ced638 100644 --- a/include/xrpl/protocol/NFTokenOfferID.h +++ b/include/xrpl/protocol/NFTokenOfferID.h @@ -18,7 +18,9 @@ namespace xrpl { @{ */ bool -canHaveNFTokenOfferID(std::shared_ptr const& serializedTx, TxMeta const& transactionMeta); +canHaveNFTokenOfferID( + std::shared_ptr const& serializedTx, + TxMeta const& transactionMeta); std::optional getOfferIDFromCreatedOffer(TxMeta const& transactionMeta); diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index e8800a41d2..65861addc7 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -65,7 +65,7 @@ public: std::optional getGranularTxType(GranularPermissionType const& gpType) const; - std::optional> const + std::optional> getTxFeature(TxType txType) const; bool diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index 2bafeaeda5..1a28349540 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -114,7 +114,8 @@ namespace Lending { Valid values are between 0 and 10% inclusive. */ -TenthBips16 constexpr maxManagementFeeRate(unsafe_cast(percentageToTenthBips(10).value())); +TenthBips16 constexpr maxManagementFeeRate( + unsafe_cast(percentageToTenthBips(10).value())); static_assert(maxManagementFeeRate == TenthBips16(std::uint16_t(10'000u))); /** The maximum coverage rate required of a loan broker in 1/10 bips. @@ -208,7 +209,7 @@ std::size_t constexpr maxDIDDocumentLength = 256; std::size_t constexpr maxDIDURILength = 256; /** The maximum length of an Attestation inside a DID */ -std::size_t constexpr maxDIDAttestationLength = 256; +std::size_t constexpr maxDIDDataLength = 256; /** The maximum length of a domain */ std::size_t constexpr maxDomainLength = 256; @@ -250,16 +251,25 @@ std::uint8_t constexpr vaultMaximumIOUScale = 18; * another vault; counted from 0 */ std::uint8_t constexpr maxAssetCheckDepth = 5; -/** The maximum length of a Data field in Escrow object that can be updated by - * Wasm code */ -std::size_t constexpr maxWasmDataLength = 4 * 1024; +/** Maximum length of a Data field in Escrow object that can be updated by WASM code. */ +std::size_t constexpr maxWasmDataLength = 4 * 1024; // 4KB -/** The maximum length of a parameters passed from Wasm code*/ -std::size_t constexpr maxWasmParamLength = 1024; +/** Maximum length of parameters passed from WASM code to host functions. */ +std::size_t constexpr maxWasmParamLength = 1024; // 1KB /** A ledger index. */ using LedgerIndex = std::uint32_t; +std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 256; + +/** Returns true if the given ledgerIndex is a voting ledgerIndex */ +bool +isVotingLedger(LedgerIndex seq); + +/** Returns true if the given ledgerIndex is a flag ledgerIndex */ +bool +isFlagLedger(LedgerIndex seq); + /** A transaction identifier. The value is computed as the hash of the canonicalized, serialized transaction object. diff --git a/include/xrpl/protocol/PublicKey.h b/include/xrpl/protocol/PublicKey.h index eff2af6839..67e55ca136 100644 --- a/include/xrpl/protocol/PublicKey.h +++ b/include/xrpl/protocol/PublicKey.h @@ -44,7 +44,7 @@ protected: // All the constructed public keys are valid, non-empty and contain 33 // bytes of data. static constexpr std::size_t size_ = 33; - std::uint8_t buf_[size_]; // should be large enough + std::uint8_t buf_[size_]{}; // should be large enough public: using const_iterator = std::uint8_t const*; @@ -125,7 +125,8 @@ operator==(PublicKey const& lhs, PublicKey const& rhs) inline bool operator<(PublicKey const& lhs, PublicKey const& rhs) { - return std::lexicographical_compare(lhs.data(), lhs.data() + lhs.size(), rhs.data(), rhs.data() + rhs.size()); + return std::lexicographical_compare( + lhs.data(), lhs.data() + lhs.size(), rhs.data(), rhs.data() + rhs.size()); } template diff --git a/include/xrpl/protocol/Quality.h b/include/xrpl/protocol/Quality.h index 4d3fcbfcc0..09b0f4e7cb 100644 --- a/include/xrpl/protocol/Quality.h +++ b/include/xrpl/protocol/Quality.h @@ -114,7 +114,8 @@ public: /** Create a quality from the ratio of two amounts. */ template - explicit Quality(TAmounts const& amount) : Quality(Amounts(toSTAmount(amount.in), toSTAmount(amount.out))) + explicit Quality(TAmounts const& amount) + : Quality(Amounts(toSTAmount(amount.in), toSTAmount(amount.out))) { } @@ -263,7 +264,8 @@ public: friend double relativeDistance(Quality const& q1, Quality const& q2) { - XRPL_ASSERT(q1.m_value > 0 && q2.m_value > 0, "xrpl::Quality::relativeDistance : minimum inputs"); + XRPL_ASSERT( + q1.m_value > 0 && q2.m_value > 0, "xrpl::Quality::relativeDistance : minimum inputs"); if (q1.m_value == q2.m_value) // make expected common case fast return 0; @@ -278,7 +280,8 @@ public: auto const expDiff = exponent(maxV) - exponent(minV); double const minVD = static_cast(minVMantissa); - double const maxVD = expDiff ? maxVMantissa * pow(10, expDiff) : static_cast(maxVMantissa); + double const maxVD = + expDiff ? maxVMantissa * pow(10, expDiff) : static_cast(maxVMantissa); // maxVD and minVD are scaled so they have the same exponents. Dividing // cancels out the exponents, so we only need to deal with the (scaled) @@ -312,7 +315,8 @@ TAmounts Quality::ceil_in(TAmounts const& amount, In const& limit) const { // Construct a function pointer to the function we want to call. - static constexpr Amounts (Quality::*ceil_in_fn_ptr)(Amounts const&, STAmount const&) const = &Quality::ceil_in; + static constexpr Amounts (Quality::*ceil_in_fn_ptr)(Amounts const&, STAmount const&) const = + &Quality::ceil_in; return ceil_TAmounts_helper(amount, limit, amount.in, ceil_in_fn_ptr); } @@ -322,8 +326,8 @@ TAmounts Quality::ceil_in_strict(TAmounts const& amount, In const& limit, bool roundUp) const { // Construct a function pointer to the function we want to call. - static constexpr Amounts (Quality::*ceil_in_fn_ptr)(Amounts const&, STAmount const&, bool) const = - &Quality::ceil_in_strict; + static constexpr Amounts (Quality::*ceil_in_fn_ptr)(Amounts const&, STAmount const&, bool) + const = &Quality::ceil_in_strict; return ceil_TAmounts_helper(amount, limit, amount.in, ceil_in_fn_ptr, roundUp); } @@ -333,7 +337,8 @@ TAmounts Quality::ceil_out(TAmounts const& amount, Out const& limit) const { // Construct a function pointer to the function we want to call. - static constexpr Amounts (Quality::*ceil_out_fn_ptr)(Amounts const&, STAmount const&) const = &Quality::ceil_out; + static constexpr Amounts (Quality::*ceil_out_fn_ptr)(Amounts const&, STAmount const&) const = + &Quality::ceil_out; return ceil_TAmounts_helper(amount, limit, amount.out, ceil_out_fn_ptr); } @@ -343,8 +348,8 @@ TAmounts Quality::ceil_out_strict(TAmounts const& amount, Out const& limit, bool roundUp) const { // Construct a function pointer to the function we want to call. - static constexpr Amounts (Quality::*ceil_out_fn_ptr)(Amounts const&, STAmount const&, bool) const = - &Quality::ceil_out_strict; + static constexpr Amounts (Quality::*ceil_out_fn_ptr)(Amounts const&, STAmount const&, bool) + const = &Quality::ceil_out_strict; return ceil_TAmounts_helper(amount, limit, amount.out, ceil_out_fn_ptr, roundUp); } diff --git a/include/xrpl/protocol/QualityFunction.h b/include/xrpl/protocol/QualityFunction.h index 4f76f4df0a..15865a6e07 100644 --- a/include/xrpl/protocol/QualityFunction.h +++ b/include/xrpl/protocol/QualityFunction.h @@ -67,7 +67,10 @@ public: }; template -QualityFunction::QualityFunction(TAmounts const& amounts, std::uint32_t tfee, QualityFunction::AMMTag) +QualityFunction::QualityFunction( + TAmounts const& amounts, + std::uint32_t tfee, + QualityFunction::AMMTag) { if (amounts.in <= beast::zero || amounts.out <= beast::zero) Throw("QualityFunction amounts are 0."); diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index 7d4a458fce..11ca8eb72a 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -49,7 +49,9 @@ private: makeRulesGivenLedger(DigestAwareReadView const& ledger, Rules const& current); friend Rules - makeRulesGivenLedger(DigestAwareReadView const& ledger, std::unordered_set> const& presets); + makeRulesGivenLedger( + DigestAwareReadView const& ledger, + std::unordered_set> const& presets); Rules( std::unordered_set> const& presets, diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 794fc86c11..41cea7936c 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace xrpl { @@ -53,7 +54,10 @@ public: template requires(std::is_same_v || std::is_same_v) - SOElement(TypedField const& fieldName, SOEStyle style, SOETxMPTIssue supportMpt = soeMPTNotSupported) + SOElement( + TypedField const& fieldName, + SOEStyle style, + SOETxMPTIssue supportMpt = soeMPTNotSupported) : sField_(fieldName), style_(style), supportMpt_(supportMpt) { init(fieldName); @@ -94,10 +98,16 @@ public: operator=(SOTemplate&& other) = default; /** Create a template populated with all fields. - After creating the template fields cannot be - added, modified, or removed. + After creating the template fields cannot be added, modified, or removed. */ - SOTemplate(std::initializer_list uniqueFields, std::initializer_list commonFields = {}); + SOTemplate(std::vector uniqueFields, std::vector commonFields = {}); + + /** Create a template populated with all fields. + Note: Defers to the vector constructor above. + */ + SOTemplate( + std::initializer_list uniqueFields, + std::initializer_list commonFields = {}); /* Provide for the enumeration of fields */ std::vector::const_iterator diff --git a/include/xrpl/protocol/STAccount.h b/include/xrpl/protocol/STAccount.h index 3fdb2e015e..76c8f24b7b 100644 --- a/include/xrpl/protocol/STAccount.h +++ b/include/xrpl/protocol/STAccount.h @@ -24,7 +24,7 @@ public: STAccount(); STAccount(SField const& n); - STAccount(SField const& n, Buffer&& v); + STAccount(SField const& n, Buffer const& v); STAccount(SerialIter& sit, SField const& name); STAccount(SField const& n, AccountID const& v); diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index df2b1c19a0..dadeec096f 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -80,7 +80,12 @@ public: unchecked); template - STAmount(A const& asset, mantissa_type mantissa, exponent_type exponent, bool negative, unchecked); + STAmount( + A const& asset, + mantissa_type mantissa, + exponent_type exponent, + bool negative, + unchecked); // Call canonicalize template @@ -300,13 +305,23 @@ STAmount::STAmount( } template -STAmount::STAmount(A const& asset, mantissa_type mantissa, exponent_type exponent, bool negative, unchecked) +STAmount::STAmount( + A const& asset, + mantissa_type mantissa, + exponent_type exponent, + bool negative, + unchecked) : mAsset(asset), mValue(mantissa), mOffset(exponent), mIsNegative(negative) { } template -STAmount::STAmount(SField const& name, A const& asset, std::uint64_t mantissa, int exponent, bool negative) +STAmount::STAmount( + SField const& name, + A const& asset, + std::uint64_t mantissa, + int exponent, + bool negative) : STBase(name), mAsset(asset), mValue(mantissa), mOffset(exponent), mIsNegative(negative) { // mValue is uint64, but needs to fit in the range of int64 @@ -326,7 +341,8 @@ STAmount::STAmount(SField const& name, A const& asset, std::uint64_t mantissa, i } template -STAmount::STAmount(A const& asset, std::int64_t mantissa, int exponent) : mAsset(asset), mOffset(exponent) +STAmount::STAmount(A const& asset, std::int64_t mantissa, int exponent) + : mAsset(asset), mOffset(exponent) { set(mantissa); canonicalize(); @@ -480,12 +496,14 @@ STAmount::zeroed() const return STAmount(mAsset); } -inline STAmount::operator bool() const noexcept +inline STAmount:: +operator bool() const noexcept { return *this != beast::zero; } -inline STAmount::operator Number() const +inline STAmount:: +operator Number() const { if (native()) return xrp(); @@ -658,7 +676,10 @@ getRate(STAmount const& offerOut, STAmount const& offerIn); * */ [[nodiscard]] STAmount -roundToScale(STAmount const& value, std::int32_t scale, Number::rounding_mode rounding = Number::getround()); +roundToScale( + STAmount const& value, + std::int32_t scale, + Number::rounding_mode rounding = Number::getround()); /** Round an arbitrary precision Number IN PLACE to the precision of a given * Asset. diff --git a/include/xrpl/protocol/STArray.h b/include/xrpl/protocol/STArray.h index d9c5306d14..045b682f88 100644 --- a/include/xrpl/protocol/STArray.h +++ b/include/xrpl/protocol/STArray.h @@ -23,12 +23,14 @@ public: template < class Iter, - class = std::enable_if_t::reference, STObject>>> + class = std::enable_if_t< + std::is_convertible_v::reference, STObject>>> explicit STArray(Iter first, Iter last); template < class Iter, - class = std::enable_if_t::reference, STObject>>> + class = std::enable_if_t< + std::is_convertible_v::reference, STObject>>> STArray(SField const& f, Iter first, Iter last); STArray& diff --git a/include/xrpl/protocol/STBitString.h b/include/xrpl/protocol/STBitString.h index e8b6e7282b..7bd270a98c 100644 --- a/include/xrpl/protocol/STBitString.h +++ b/include/xrpl/protocol/STBitString.h @@ -83,7 +83,8 @@ inline STBitString::STBitString(SField const& n, value_type const& v) : ST } template -inline STBitString::STBitString(SerialIter& sit, SField const& name) : STBitString(name, sit.getBitString()) +inline STBitString::STBitString(SerialIter& sit, SField const& name) + : STBitString(name, sit.getBitString()) { } @@ -169,7 +170,8 @@ STBitString::value() const } template -STBitString::operator value_type() const +STBitString:: +operator value_type() const { return value_; } diff --git a/include/xrpl/protocol/STBlob.h b/include/xrpl/protocol/STBlob.h index 15a9bd5f78..71d1a04478 100644 --- a/include/xrpl/protocol/STBlob.h +++ b/include/xrpl/protocol/STBlob.h @@ -68,11 +68,13 @@ private: friend class detail::STVar; }; -inline STBlob::STBlob(STBlob const& rhs) : STBase(rhs), CountedObject(rhs), value_(rhs.data(), rhs.size()) +inline STBlob::STBlob(STBlob const& rhs) + : STBase(rhs), CountedObject(rhs), value_(rhs.data(), rhs.size()) { } -inline STBlob::STBlob(SField const& f, void const* data, std::size_t size) : STBase(f), value_(data, size) +inline STBlob::STBlob(SField const& f, void const* data, std::size_t size) + : STBase(f), value_(data, size) { } diff --git a/include/xrpl/protocol/STInteger.h b/include/xrpl/protocol/STInteger.h index b5c4dbf6cf..f4bbd6e73d 100644 --- a/include/xrpl/protocol/STInteger.h +++ b/include/xrpl/protocol/STInteger.h @@ -134,7 +134,8 @@ STInteger::setValue(Integer v) } template -inline STInteger::operator Integer() const +inline STInteger:: +operator Integer() const { return value_; } diff --git a/include/xrpl/protocol/STLedgerEntry.h b/include/xrpl/protocol/STLedgerEntry.h index 6ba3b36c6b..dde7d49ebd 100644 --- a/include/xrpl/protocol/STLedgerEntry.h +++ b/include/xrpl/protocol/STLedgerEntry.h @@ -55,7 +55,11 @@ public: isThreadedType(Rules const& rules) const; bool - thread(uint256 const& txID, std::uint32_t ledgerSeq, uint256& prevTxID, std::uint32_t& prevLedgerID); + thread( + uint256 const& txID, + std::uint32_t ledgerSeq, + uint256& prevTxID, + std::uint32_t& prevLedgerID); private: /* Make STObject comply with the template for this SLE type @@ -77,11 +81,13 @@ private: using SLE = STLedgerEntry; -inline STLedgerEntry::STLedgerEntry(LedgerEntryType type, uint256 const& key) : STLedgerEntry(Keylet(type, key)) +inline STLedgerEntry::STLedgerEntry(LedgerEntryType type, uint256 const& key) + : STLedgerEntry(Keylet(type, key)) { } -inline STLedgerEntry::STLedgerEntry(SerialIter&& sit, uint256 const& index) : STLedgerEntry(sit, index) +inline STLedgerEntry::STLedgerEntry(SerialIter&& sit, uint256 const& index) + : STLedgerEntry(sit, index) { } diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 5d5d829c99..ecfae0086b 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -57,7 +57,7 @@ class STObject : public STBase, public CountedObject using list_type = std::vector; list_type v_; - SOTemplate const* mType; + SOTemplate const* mType{}; public: using iterator = boost::transform_iterator; @@ -147,9 +147,12 @@ public: int getCount() const; - bool setFlag(std::uint32_t); - bool clearFlag(std::uint32_t); - bool isFlag(std::uint32_t) const; + bool + setFlag(std::uint32_t); + bool + clearFlag(std::uint32_t); + bool + isFlag(std::uint32_t) const; std::uint32_t getFlags() const; @@ -398,7 +401,7 @@ public: getStyle(SField const& field) const; bool - hasMatchingEntry(STBase const&); + hasMatchingEntry(STBase const&) const; bool operator==(STObject const& o) const; @@ -431,8 +434,8 @@ private: // by value. template < typename T, - typename V = - typename std::remove_cv().value())>::type>::type> + typename V = typename std::remove_cv< + typename std::remove_reference().value())>::type>::type> V getFieldByValue(SField const& field) const; @@ -507,10 +510,14 @@ protected: // Constraint += and -= ValueProxy operators // to value types that support arithmetic operations template -concept IsArithmeticNumber = std::is_arithmetic_v || std::is_same_v || std::is_same_v; -template -concept IsArithmeticValueUnit = - std::is_same_v> && IsArithmeticNumber && std::is_class_v; +concept IsArithmeticNumber = + std::is_arithmetic_v || std::is_same_v || std::is_same_v; +template < + typename U, + typename Value = typename U::value_type, + typename Unit = typename U::unit_type> +concept IsArithmeticValueUnit = std::is_same_v> && + IsArithmeticNumber && std::is_class_v; template concept IsArithmeticST = !IsArithmeticValueUnit && IsArithmeticNumber; template @@ -519,7 +526,8 @@ concept IsArithmetic = IsArithmeticNumber || IsArithmeticST || IsArithmeti template concept Addable = requires(T t, U u) { t = t + u; }; template -concept IsArithmeticCompatible = IsArithmetic && Addable; +concept IsArithmeticCompatible = + IsArithmetic && Addable; template class STObject::ValueProxy : public Proxy @@ -799,7 +807,8 @@ STObject::ValueProxy::operator-=(U const& u) } template -STObject::ValueProxy::operator value_type() const +STObject::ValueProxy:: +operator value_type() const { return this->value(); } @@ -812,13 +821,15 @@ STObject::ValueProxy::ValueProxy(STObject* st, TypedField const* f) : Prox //------------------------------------------------------------------------------ template -STObject::OptionalProxy::operator bool() const noexcept +STObject::OptionalProxy:: +operator bool() const noexcept { return engaged(); } template -STObject::OptionalProxy::operator typename STObject::OptionalProxy::optional_type() const +STObject::OptionalProxy:: +operator typename STObject::OptionalProxy::optional_type() const { return optional_value(); } @@ -1050,12 +1061,15 @@ STObject::at(TypedField const& f) const return u->value(); XRPL_ASSERT(mType, "xrpl::STObject::at(TypedField auto) : field template non-null"); - XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(TypedField auto) : type not present"); + XRPL_ASSERT( + b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(TypedField auto) : type not present"); if (mType->style(f) == soeOPTIONAL) Throw("Missing optional field: " + f.getName()); - XRPL_ASSERT(mType->style(f) == soeDEFAULT, "xrpl::STObject::at(TypedField auto) : template style is default"); + XRPL_ASSERT( + mType->style(f) == soeDEFAULT, + "xrpl::STObject::at(TypedField auto) : template style is default"); // Used to help handle the case where value_type is a const reference, // otherwise we would return the address of a temporary. @@ -1077,7 +1091,9 @@ STObject::at(OptionaledField const& of) const mType, "xrpl::STObject::at(OptionaledField auto) : field template " "non-null"); - XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(OptionaledField auto) : type not present"); + XRPL_ASSERT( + b->getSType() == STI_NOTPRESENT, + "xrpl::STObject::at(OptionaledField auto) : type not present"); if (mType->style(*of.f) == soeOPTIONAL) return std::nullopt; XRPL_ASSERT( diff --git a/include/xrpl/protocol/STPathSet.h b/include/xrpl/protocol/STPathSet.h index 1bab654d3f..2ccbc3d657 100644 --- a/include/xrpl/protocol/STPathSet.h +++ b/include/xrpl/protocol/STPathSet.h @@ -49,7 +49,11 @@ public: AccountID const& issuer, bool forceCurrency = false); - STPathElement(unsigned int uType, AccountID const& account, Currency const& currency, AccountID const& issuer); + STPathElement( + unsigned int uType, + AccountID const& account, + Currency const& currency, + AccountID const& issuer); auto getNodeType() const; @@ -230,7 +234,8 @@ inline STPathElement::STPathElement( is_offer_ = false; mAccountID = *account; mType |= typeAccount; - XRPL_ASSERT(mAccountID != noAccount(), "xrpl::STPathElement::STPathElement : account is set"); + XRPL_ASSERT( + mAccountID != noAccount(), "xrpl::STPathElement::STPathElement : account is set"); } if (currency) @@ -254,7 +259,11 @@ inline STPathElement::STPathElement( Currency const& currency, AccountID const& issuer, bool forceCurrency) - : mType(typeNone), mAccountID(account), mCurrencyID(currency), mIssuerID(issuer), is_offer_(isXRP(mAccountID)) + : mType(typeNone) + , mAccountID(account) + , mCurrencyID(currency) + , mIssuerID(issuer) + , is_offer_(isXRP(mAccountID)) { if (!is_offer_) mType |= typeAccount; @@ -273,7 +282,11 @@ inline STPathElement::STPathElement( AccountID const& account, Currency const& currency, AccountID const& issuer) - : mType(uType), mAccountID(account), mCurrencyID(currency), mIssuerID(issuer), is_offer_(isXRP(mAccountID)) + : mType(uType) + , mAccountID(account) + , mCurrencyID(currency) + , mIssuerID(issuer) + , is_offer_(isXRP(mAccountID)) { hash_value_ = get_hash(*this); } diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index a12c658dc2..f200c19eac 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -15,7 +15,7 @@ namespace xrpl { -enum TxnSql : char { +enum class TxnSql : char { txnSqlNew = 'N', txnSqlConflict = 'C', txnSqlHeld = 'H', @@ -83,6 +83,9 @@ public: std::uint32_t getSeqValue() const; + AccountID + getFeePayer() const; + boost::container::flat_set getMentionedAccounts() const; @@ -119,7 +122,11 @@ public: getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) const; std::string - getMetaSQL(Serializer rawTxn, std::uint32_t inLedger, char status, std::string const& escapedMetaData) const; + getMetaSQL( + Serializer rawTxn, + std::uint32_t inLedger, + TxnSql status, + std::string const& escapedMetaData) const; std::vector const& getBatchTransactionIDs() const; diff --git a/include/xrpl/protocol/STValidation.h b/include/xrpl/protocol/STValidation.h index aa48161f68..861e2152f8 100644 --- a/include/xrpl/protocol/STValidation.h +++ b/include/xrpl/protocol/STValidation.h @@ -64,7 +64,12 @@ public: @param f callback function to "fill" the validation with necessary data */ template - STValidation(NetClock::time_point signTime, PublicKey const& pk, SecretKey const& sk, NodeID const& nodeID, F&& f); + STValidation( + NetClock::time_point signTime, + PublicKey const& pk, + SecretKey const& sk, + NodeID const& nodeID, + F&& f); // Hash of the validated ledger uint256 @@ -117,10 +122,13 @@ public: render() const { std::stringstream ss; - ss << "validation: " << " ledger_hash: " << getLedgerHash() << " consensus_hash: " << getConsensusHash() - << " sign_time: " << to_string(getSignTime()) << " seen_time: " << to_string(getSeenTime()) - << " signer_public_key: " << getSignerPublic() << " node_id: " << getNodeID() << " is_valid: " << isValid() - << " is_full: " << isFull() << " is_trusted: " << isTrusted() << " signing_hash: " << getSigningHash() + ss << "validation: " << " ledger_hash: " << getLedgerHash() + << " consensus_hash: " << getConsensusHash() + << " sign_time: " << to_string(getSignTime()) + << " seen_time: " << to_string(getSeenTime()) + << " signer_public_key: " << getSignerPublic() << " node_id: " << getNodeID() + << " is_valid: " << isValid() << " is_full: " << isFull() + << " is_trusted: " << isTrusted() << " signing_hash: " << getSigningHash() << " base58: " << toBase58(TokenType::NodePublic, getSignerPublic()); return ss.str(); } @@ -152,7 +160,8 @@ STValidation::STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, bool ch { if (checkSignature && !isValid()) { - JLOG(debugLog().error()) << "Invalid signature in validation: " << getJson(JsonOptions::none); + JLOG(debugLog().error()) << "Invalid signature in validation: " + << getJson(JsonOptions::none); Throw("Invalid signature in validation"); } @@ -174,7 +183,10 @@ STValidation::STValidation( SecretKey const& sk, NodeID const& nodeID, F&& f) - : STObject(validationFormat(), sfValidation), signingPubKey_(pk), nodeID_(nodeID), seenTime_(signTime) + : STObject(validationFormat(), sfValidation) + , signingPubKey_(pk) + , nodeID_(nodeID) + , seenTime_(signTime) { XRPL_ASSERT( nodeID_.isNonZero(), diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index c83d981bfc..dd77067b7c 100644 --- a/include/xrpl/protocol/STVector256.h +++ b/include/xrpl/protocol/STVector256.h @@ -110,7 +110,8 @@ inline STVector256::STVector256(std::vector const& vector) : mValue(vec { } -inline STVector256::STVector256(SField const& n, std::vector const& vector) : STBase(n), mValue(vector) +inline STVector256::STVector256(SField const& n, std::vector const& vector) + : STBase(n), mValue(vector) { } @@ -135,7 +136,8 @@ STVector256::setValue(STVector256 const& v) } /** Retrieve a copy of the vector we contain */ -inline STVector256::operator std::vector() const +inline STVector256:: +operator std::vector() const { return mValue; } diff --git a/include/xrpl/protocol/STXChainBridge.h b/include/xrpl/protocol/STXChainBridge.h index 709d3886b7..f74cc02041 100644 --- a/include/xrpl/protocol/STXChainBridge.h +++ b/include/xrpl/protocol/STXChainBridge.h @@ -114,15 +114,31 @@ private: inline bool operator==(STXChainBridge const& lhs, STXChainBridge const& rhs) { - return std::tie(lhs.lockingChainDoor_, lhs.lockingChainIssue_, lhs.issuingChainDoor_, lhs.issuingChainIssue_) == - std::tie(rhs.lockingChainDoor_, rhs.lockingChainIssue_, rhs.issuingChainDoor_, rhs.issuingChainIssue_); + return std::tie( + lhs.lockingChainDoor_, + lhs.lockingChainIssue_, + lhs.issuingChainDoor_, + lhs.issuingChainIssue_) == + std::tie( + rhs.lockingChainDoor_, + rhs.lockingChainIssue_, + rhs.issuingChainDoor_, + rhs.issuingChainIssue_); } inline bool operator<(STXChainBridge const& lhs, STXChainBridge const& rhs) { - return std::tie(lhs.lockingChainDoor_, lhs.lockingChainIssue_, lhs.issuingChainDoor_, lhs.issuingChainIssue_) < - std::tie(rhs.lockingChainDoor_, rhs.lockingChainIssue_, rhs.issuingChainDoor_, rhs.issuingChainIssue_); + return std::tie( + lhs.lockingChainDoor_, + lhs.lockingChainIssue_, + lhs.issuingChainDoor_, + lhs.issuingChainIssue_) < + std::tie( + rhs.lockingChainDoor_, + rhs.lockingChainIssue_, + rhs.issuingChainDoor_, + rhs.issuingChainIssue_); } inline AccountID const& diff --git a/include/xrpl/protocol/SecretKey.h b/include/xrpl/protocol/SecretKey.h index a59b1150c1..dd5566915f 100644 --- a/include/xrpl/protocol/SecretKey.h +++ b/include/xrpl/protocol/SecretKey.h @@ -16,8 +16,11 @@ namespace xrpl { /** A secret key. */ class SecretKey { +public: + static constexpr std::size_t size_ = 32; + private: - std::uint8_t buf_[32]; + std::uint8_t buf_[size_]{}; public: using const_iterator = std::uint8_t const*; @@ -27,9 +30,14 @@ public: SecretKey& operator=(SecretKey const&) = default; + bool + operator==(SecretKey const&) = delete; + bool + operator!=(SecretKey const&) = delete; + ~SecretKey(); - SecretKey(std::array const& data); + SecretKey(std::array const& data); SecretKey(Slice const& slice); std::uint8_t const* @@ -78,16 +86,10 @@ public: }; inline bool -operator==(SecretKey const& lhs, SecretKey const& rhs) -{ - return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0; -} +operator==(SecretKey const& lhs, SecretKey const& rhs) = delete; inline bool -operator!=(SecretKey const& lhs, SecretKey const& rhs) -{ - return !(lhs == rhs); -} +operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/Seed.h b/include/xrpl/protocol/Seed.h index 1fad94e2bd..5b490be12e 100644 --- a/include/xrpl/protocol/Seed.h +++ b/include/xrpl/protocol/Seed.h @@ -13,7 +13,7 @@ namespace xrpl { class Seed { private: - std::array buf_; + std::array buf_{}; public: using const_iterator = std::array::const_iterator; diff --git a/include/xrpl/protocol/Sign.h b/include/xrpl/protocol/Sign.h index 9f0bf2f1ca..0b5b5d7239 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -19,7 +19,12 @@ namespace xrpl { @note If a signature already exists, it is overwritten. */ void -sign(STObject& st, HashPrefix const& prefix, KeyType type, SecretKey const& sk, SF_VL const& sigField = sfSignature); +sign( + STObject& st, + HashPrefix const& prefix, + KeyType type, + SecretKey const& sk, + SF_VL const& sigField = sfSignature); /** Returns `true` if STObject contains valid signature @@ -30,7 +35,11 @@ sign(STObject& st, HashPrefix const& prefix, KeyType type, SecretKey const& sk, If not specified the value defaults to `sfSignature`. */ bool -verify(STObject const& st, HashPrefix const& prefix, PublicKey const& pk, SF_VL const& sigField = sfSignature); +verify( + STObject const& st, + HashPrefix const& prefix, + PublicKey const& pk, + SF_VL const& sigField = sfSignature); /** Return a Serializer suitable for computing a multisigning TxnSignature. */ Serializer diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index 70ab67c56f..b51e011533 100644 --- a/include/xrpl/protocol/SystemParameters.h +++ b/include/xrpl/protocol/SystemParameters.h @@ -14,7 +14,7 @@ namespace xrpl { static inline std::string const& systemName() { - static std::string const name = "ripple"; + static std::string const name = "xrpld"; return name; } diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index e5be836a0f..bc7c0a7739 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -422,7 +422,9 @@ public: } // Trait tells enable_if which types are allowed for construction. - template >>::value>> + template < + typename T, + typename = std::enable_if_t>>::value>> constexpr TERSubset(T rhs) : code_(TERtoInt(rhs)) { } @@ -490,60 +492,60 @@ public: // Only enabled if both arguments return int if TERtiInt is called with them. template constexpr auto -operator==(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator==(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) == TERtoInt(rhs); } template constexpr auto -operator!=(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator!=(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) != TERtoInt(rhs); } template constexpr auto -operator<(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator<(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) < TERtoInt(rhs); } template constexpr auto -operator<=(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator<=(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) <= TERtoInt(rhs); } template constexpr auto -operator>(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator>(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) > TERtoInt(rhs); } template constexpr auto -operator>=(L const& lhs, R const& rhs) - -> std::enable_if_t< - std::is_same::value && std::is_same::value, - bool> +operator>=(L const& lhs, R const& rhs) -> std::enable_if_t< + std::is_same::value && + std::is_same::value, + bool> { return TERtoInt(lhs) >= TERtoInt(rhs); } diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 6c6ef5e369..7c2085109f 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -3,294 +3,444 @@ #include #include +#include +#include +#include +#include namespace xrpl { /** Transaction flags. - These flags are specified in a transaction's 'Flags' field and modify the - behavior of that transaction. + These flags are specified in a transaction's 'Flags' field and modify + the behavior of that transaction. There are two types of flags: - (1) Universal flags: these are flags which apply to, and are interpreted - the same way by, all transactions, except, perhaps, - to special pseudo-transactions. + (1) Universal flags: these are flags which apply to, and are interpreted the same way by, + all transactions, except, perhaps, to special pseudo-transactions. - (2) Tx-Specific flags: these are flags which are interpreted according - to the type of the transaction being executed. - That is, the same numerical flag value may have - different effects, depending on the transaction - being executed. + (2) Tx-Specific flags: these are flags which are interpreted according to the type of the + transaction being executed. That is, the same numerical flag value may have different + effects, depending on the transaction being executed. - @note The universal transaction flags occupy the high-order 8 bits. The - tx-specific flags occupy the remaining 24 bits. + @note The universal transaction flags occupy the high-order 8 bits. + The tx-specific flags occupy the remaining 24 bits. - @warning Transaction flags form part of the protocol. **Changing them - should be avoided because without special handling, this will - result in a hard fork.** + @warning Transaction flags form part of the protocol. + **Changing them should be avoided because without special handling, this will result in + a hard fork.** @ingroup protocol */ -// Formatting equals sign aligned 4 spaces after longest prefix, except for -// wrapped lines -// clang-format off +using FlagValue = std::uint32_t; + // Universal Transaction flags: -constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000; -constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; -constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; -constexpr std::uint32_t tfUniversalMask = ~tfUniversal; +inline constexpr FlagValue tfFullyCanonicalSig = 0x80000000; +inline constexpr FlagValue tfInnerBatchTxn = 0x40000000; +inline constexpr FlagValue tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; +inline constexpr FlagValue tfUniversalMask = ~tfUniversal; -// AccountSet flags: -constexpr std::uint32_t tfRequireDestTag = 0x00010000; -constexpr std::uint32_t tfOptionalDestTag = 0x00020000; -constexpr std::uint32_t tfRequireAuth = 0x00040000; -constexpr std::uint32_t tfOptionalAuth = 0x00080000; -constexpr std::uint32_t tfDisallowXRP = 0x00100000; -constexpr std::uint32_t tfAllowXRP = 0x00200000; -constexpr std::uint32_t tfAccountSetMask = - ~(tfUniversal | tfRequireDestTag | tfOptionalDestTag | tfRequireAuth | - tfOptionalAuth | tfDisallowXRP | tfAllowXRP); +#pragma push_macro("XMACRO") +#pragma push_macro("TO_VALUE") +#pragma push_macro("VALUE_TO_MAP") +#pragma push_macro("NULL_NAME") +#pragma push_macro("NULL_OUTPUT") +#pragma push_macro("TO_MAP") +#pragma push_macro("TO_MASK") +#pragma push_macro("VALUE_TO_MASK") +#pragma push_macro("ALL_TX_FLAGS") +#pragma push_macro("NULL_MASK_ADJ") +#pragma push_macro("MASK_ADJ_TO_MASK") -// AccountSet SetFlag/ClearFlag values -constexpr std::uint32_t asfRequireDest = 1; -constexpr std::uint32_t asfRequireAuth = 2; -constexpr std::uint32_t asfDisallowXRP = 3; -constexpr std::uint32_t asfDisableMaster = 4; -constexpr std::uint32_t asfAccountTxnID = 5; -constexpr std::uint32_t asfNoFreeze = 6; -constexpr std::uint32_t asfGlobalFreeze = 7; -constexpr std::uint32_t asfDefaultRipple = 8; -constexpr std::uint32_t asfDepositAuth = 9; -constexpr std::uint32_t asfAuthorizedNFTokenMinter = 10; -/* // reserved for Hooks amendment -constexpr std::uint32_t asfTshCollect = 11; -*/ -constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12; -constexpr std::uint32_t asfDisallowIncomingCheck = 13; -constexpr std::uint32_t asfDisallowIncomingPayChan = 14; -constexpr std::uint32_t asfDisallowIncomingTrustline = 15; -constexpr std::uint32_t asfAllowTrustLineClawback = 16; -constexpr std::uint32_t asfAllowTrustLineLocking = 17; +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef TO_MASK +#undef VALUE_TO_MASK +#undef NULL_MASK_ADJ +#undef MASK_ADJ_TO_MASK -// OfferCreate flags: -constexpr std::uint32_t tfPassive = 0x00010000; -constexpr std::uint32_t tfImmediateOrCancel = 0x00020000; -constexpr std::uint32_t tfFillOrKill = 0x00040000; -constexpr std::uint32_t tfSell = 0x00080000; -constexpr std::uint32_t tfHybrid = 0x00100000; -constexpr std::uint32_t tfOfferCreateMask = - ~(tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell | tfHybrid); +// clang-format off +#undef ALL_TX_FLAGS -// Payment flags: -constexpr std::uint32_t tfNoRippleDirect = 0x00010000; -constexpr std::uint32_t tfPartialPayment = 0x00020000; -constexpr std::uint32_t tfLimitQuality = 0x00040000; -constexpr std::uint32_t tfPaymentMask = - ~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect); -constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); - -// TrustSet flags: -constexpr std::uint32_t tfSetfAuth = 0x00010000; -constexpr std::uint32_t tfSetNoRipple = 0x00020000; -constexpr std::uint32_t tfClearNoRipple = 0x00040000; -constexpr std::uint32_t tfSetFreeze = 0x00100000; -constexpr std::uint32_t tfClearFreeze = 0x00200000; -constexpr std::uint32_t tfSetDeepFreeze = 0x00400000; -constexpr std::uint32_t tfClearDeepFreeze = 0x00800000; -constexpr std::uint32_t tfTrustSetMask = - ~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze | - tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze); -constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); - -// EnableAmendment flags: -constexpr std::uint32_t tfGotMajority = 0x00010000; -constexpr std::uint32_t tfLostMajority = 0x00020000; -constexpr std::uint32_t tfChangeMask = - ~( tfUniversal | tfGotMajority | tfLostMajority); - -// PaymentChannelClaim flags: -constexpr std::uint32_t tfRenew = 0x00010000; -constexpr std::uint32_t tfClose = 0x00020000; -constexpr std::uint32_t tfPayChanClaimMask = ~(tfUniversal | tfRenew | tfClose); - -// NFTokenMint flags: -constexpr std::uint32_t const tfBurnable = 0x00000001; -constexpr std::uint32_t const tfOnlyXRP = 0x00000002; -constexpr std::uint32_t const tfTrustLine = 0x00000004; -constexpr std::uint32_t const tfTransferable = 0x00000008; -constexpr std::uint32_t const tfMutable = 0x00000010; - -// MPTokenIssuanceCreate flags: -// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction -// is not allowed to modify it. -constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock; -constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth; -constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow; -constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade; -constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer; -constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback; -constexpr std::uint32_t const tfMPTokenIssuanceCreateMask = - ~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback); - -// MPTokenIssuanceCreate MutableFlags: -// Indicating specific fields or flags may be changed after issuance. -constexpr std::uint32_t const tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock; -constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth; -constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow; -constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade; -constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer; -constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback; -constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata; -constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee; -constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask = - ~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade - | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee); - -// MPTokenAuthorize flags: -constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001; -constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize); - -// MPTokenIssuanceSet flags: -constexpr std::uint32_t const tfMPTLock = 0x00000001; -constexpr std::uint32_t const tfMPTUnlock = 0x00000002; -constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); -constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); - -// MPTokenIssuanceSet MutableFlags: -// Set or Clear flags. -constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001; -constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002; -constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004; -constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008; -constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010; -constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020; -constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040; -constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080; -constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100; -constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200; -constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400; -constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800; -constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock | - tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | - tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | - tmfMPTSetCanClawback | tmfMPTClearCanClawback); - -// MPTokenIssuanceDestroy flags: -constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal; - -// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between -// accounts allowed a TrustLine to be added to the issuer of that token -// without explicit permission from that issuer. This was enabled by -// minting the NFToken with the tfTrustLine flag set. +// XMACRO parameters: +// - TRANSACTION: handles the transaction name, its flags, and mask adjustment +// - TF_FLAG: defines a new flag constant +// - TF_FLAG2: references an existing flag constant (no new definition) +// - MASK_ADJ: specifies flags to add back to the mask (making them invalid for this tx type) // -// That capability could be used to attack the NFToken issuer. It -// would be possible for two accounts to trade the NFToken back and forth -// building up any number of TrustLines on the issuer, increasing the -// issuer's reserve without bound. +// Note: MASK_ADJ is used when a universal flag should be invalid for a specific transaction. +// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) because the outer Batch transaction +// must not have tfInnerBatchTxn set (only inner transactions should have it). // -// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the -// tfTrustLine flag as a way to prevent the attack. But until the -// amendment passes we still need to keep the old behavior available. -constexpr std::uint32_t const tfNFTokenMintMask = - ~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable); - -constexpr std::uint32_t const tfNFTokenMintOldMask = - ~( ~tfNFTokenMintMask | tfTrustLine); - -// if featureDynamicNFT enabled then new flag allowing mutable URI available. -constexpr std::uint32_t const tfNFTokenMintOldMaskWithMutable = - ~( ~tfNFTokenMintOldMask | tfMutable); - -constexpr std::uint32_t const tfNFTokenMintMaskWithMutable = - ~( ~tfNFTokenMintMask | tfMutable); - -// NFTokenCreateOffer flags: -constexpr std::uint32_t const tfSellNFToken = 0x00000001; -constexpr std::uint32_t const tfNFTokenCreateOfferMask = - ~(tfUniversal | tfSellNFToken); - -// NFTokenCancelOffer flags: -constexpr std::uint32_t const tfNFTokenCancelOfferMask = ~tfUniversal; - -// NFTokenAcceptOffer flags: -constexpr std::uint32_t const tfNFTokenAcceptOfferMask = ~tfUniversal; - -// Clawback flags: -constexpr std::uint32_t const tfClawbackMask = ~tfUniversal; - -// AMM Flags: -constexpr std::uint32_t tfLPToken = 0x00010000; -constexpr std::uint32_t tfWithdrawAll = 0x00020000; -constexpr std::uint32_t tfOneAssetWithdrawAll = 0x00040000; -constexpr std::uint32_t tfSingleAsset = 0x00080000; -constexpr std::uint32_t tfTwoAsset = 0x00100000; -constexpr std::uint32_t tfOneAssetLPToken = 0x00200000; -constexpr std::uint32_t tfLimitLPToken = 0x00400000; -constexpr std::uint32_t tfTwoAssetIfEmpty = 0x00800000; -constexpr std::uint32_t tfWithdrawSubTx = - tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | - tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll; -constexpr std::uint32_t tfDepositSubTx = - tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | - tfLimitLPToken | tfTwoAssetIfEmpty; -constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx); -constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx); - -// AMMClawback flags: -constexpr std::uint32_t tfClawTwoAssets = 0x00000001; -constexpr std::uint32_t tfAMMClawbackMask = ~(tfUniversal | tfClawTwoAssets); - -// BridgeModify flags: -constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000; -constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount); - -// VaultCreate flags: -constexpr std::uint32_t const tfVaultPrivate = 0x00010000; -static_assert(tfVaultPrivate == lsfVaultPrivate); -constexpr std::uint32_t const tfVaultShareNonTransferable = 0x00020000; -constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate | tfVaultShareNonTransferable); - -// Batch Flags: -constexpr std::uint32_t tfAllOrNothing = 0x00010000; -constexpr std::uint32_t tfOnlyOne = 0x00020000; -constexpr std::uint32_t tfUntilFailure = 0x00040000; -constexpr std::uint32_t tfIndependent = 0x00080000; -/** - * @note If nested Batch transactions are supported in the future, the tfInnerBatchTxn flag - * will need to be removed from this mask to allow Batch transaction to be inside - * the sfRawTransactions array. - */ -constexpr std::uint32_t const tfBatchMask = - ~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn; - -// LoanSet and LoanPay flags: -// LoanSet: True, indicates the loan supports overpayments -// LoanPay: True, indicates any excess in this payment can be used -// as an overpayment. False, no overpayments will be taken. -constexpr std::uint32_t const tfLoanOverpayment = 0x00010000; -// LoanPay exclusive flags: -// tfLoanFullPayment: True, indicates that the payment is an early -// full payment. It must pay the entire loan including close -// interest and fees, or it will fail. False: Not a full payment. -constexpr std::uint32_t const tfLoanFullPayment = 0x00020000; -// tfLoanLatePayment: True, indicates that the payment is late, -// and includes late interest and fees. If the loan is not late, -// it will fail. False: not a late payment. If the current payment -// is overdue, the transaction will fail. -constexpr std::uint32_t const tfLoanLatePayment = 0x00040000; -constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal | - tfLoanOverpayment); -constexpr std::uint32_t const tfLoanPayMask = ~(tfUniversal | - tfLoanOverpayment | tfLoanFullPayment | tfLoanLatePayment); - -// LoanManage flags: -constexpr std::uint32_t const tfLoanDefault = 0x00010000; -constexpr std::uint32_t const tfLoanImpair = 0x00020000; -constexpr std::uint32_t const tfLoanUnimpair = 0x00040000; -constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair); +// TODO: Consider rewriting this using reflection in C++26 or later. Alternatively this could be a DSL processed by a script at build time. +#define XMACRO(TRANSACTION, TF_FLAG, TF_FLAG2, MASK_ADJ) \ + TRANSACTION(AccountSet, \ + TF_FLAG(tfRequireDestTag, 0x00010000) \ + TF_FLAG(tfOptionalDestTag, 0x00020000) \ + TF_FLAG(tfRequireAuth, 0x00040000) \ + TF_FLAG(tfOptionalAuth, 0x00080000) \ + TF_FLAG(tfDisallowXRP, 0x00100000) \ + TF_FLAG(tfAllowXRP, 0x00200000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(OfferCreate, \ + TF_FLAG(tfPassive, 0x00010000) \ + TF_FLAG(tfImmediateOrCancel, 0x00020000) \ + TF_FLAG(tfFillOrKill, 0x00040000) \ + TF_FLAG(tfSell, 0x00080000) \ + TF_FLAG(tfHybrid, 0x00100000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(Payment, \ + TF_FLAG(tfNoRippleDirect, 0x00010000) \ + TF_FLAG(tfPartialPayment, 0x00020000) \ + TF_FLAG(tfLimitQuality, 0x00040000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(TrustSet, \ + TF_FLAG(tfSetfAuth, 0x00010000) \ + TF_FLAG(tfSetNoRipple, 0x00020000) \ + TF_FLAG(tfClearNoRipple, 0x00040000) \ + TF_FLAG(tfSetFreeze, 0x00100000) \ + TF_FLAG(tfClearFreeze, 0x00200000) \ + TF_FLAG(tfSetDeepFreeze, 0x00400000) \ + TF_FLAG(tfClearDeepFreeze, 0x00800000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(EnableAmendment, \ + TF_FLAG(tfGotMajority, 0x00010000) \ + TF_FLAG(tfLostMajority, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(PaymentChannelClaim, \ + TF_FLAG(tfRenew, 0x00010000) \ + TF_FLAG(tfClose, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(NFTokenMint, \ + TF_FLAG(tfBurnable, 0x00000001) \ + TF_FLAG(tfOnlyXRP, 0x00000002) \ + /* deprecated TF_FLAG(tfTrustLine, 0x00000004) */ \ + TF_FLAG(tfTransferable, 0x00000008) \ + TF_FLAG(tfMutable, 0x00000010), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenIssuanceCreate, \ + /* Note: tf/lsfMPTLocked is intentionally omitted since this transaction is not allowed to modify it. */ \ + TF_FLAG(tfMPTCanLock, lsfMPTCanLock) \ + TF_FLAG(tfMPTRequireAuth, lsfMPTRequireAuth) \ + TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \ + TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \ + TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \ + TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenAuthorize, \ + TF_FLAG(tfMPTUnauthorize, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(MPTokenIssuanceSet, \ + TF_FLAG(tfMPTLock, 0x00000001) \ + TF_FLAG(tfMPTUnlock, 0x00000002), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(NFTokenCreateOffer, \ + TF_FLAG(tfSellNFToken, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMDeposit, \ + TF_FLAG(tfLPToken, 0x00010000) \ + TF_FLAG(tfSingleAsset, 0x00080000) \ + TF_FLAG(tfTwoAsset, 0x00100000) \ + TF_FLAG(tfOneAssetLPToken, 0x00200000) \ + TF_FLAG(tfLimitLPToken, 0x00400000) \ + TF_FLAG(tfTwoAssetIfEmpty, 0x00800000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMWithdraw, \ + TF_FLAG2(tfLPToken, 0x00010000) \ + TF_FLAG(tfWithdrawAll, 0x00020000) \ + TF_FLAG(tfOneAssetWithdrawAll, 0x00040000) \ + TF_FLAG2(tfSingleAsset, 0x00080000) \ + TF_FLAG2(tfTwoAsset, 0x00100000) \ + TF_FLAG2(tfOneAssetLPToken, 0x00200000) \ + TF_FLAG2(tfLimitLPToken, 0x00400000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(AMMClawback, \ + TF_FLAG(tfClawTwoAssets, 0x00000001), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(XChainModifyBridge, \ + TF_FLAG(tfClearAccountCreateAmount, 0x00010000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(VaultCreate, \ + TF_FLAG(tfVaultPrivate, lsfVaultPrivate) \ + TF_FLAG(tfVaultShareNonTransferable, 0x00020000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(Batch, \ + TF_FLAG(tfAllOrNothing, 0x00010000) \ + TF_FLAG(tfOnlyOne, 0x00020000) \ + TF_FLAG(tfUntilFailure, 0x00040000) \ + TF_FLAG(tfIndependent, 0x00080000), \ + MASK_ADJ(tfInnerBatchTxn)) /* Batch must reject tfInnerBatchTxn - only inner transactions should have this flag */ \ + \ + TRANSACTION(LoanSet, /* True indicates the loan supports overpayments */ \ + TF_FLAG(tfLoanOverpayment, 0x00010000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(LoanPay, /* True indicates any excess in this payment can be used as an overpayment. */ \ + /* False: no overpayments will be taken. */ \ + TF_FLAG2(tfLoanOverpayment, 0x00010000) \ + TF_FLAG(tfLoanFullPayment, 0x00020000) /* True indicates that the payment is an early full payment. */ \ + /* It must pay the entire loan including close interest and fees, or it will fail. */ \ + /* False: Not a full payment. */ \ + TF_FLAG(tfLoanLatePayment, 0x00040000), /* True indicates that the payment is late, and includes late interest and fees. */ \ + /* If the loan is not late, it will fail. */ \ + /* False: not a late payment. If the current payment is overdue, the transaction will fail.*/ \ + MASK_ADJ(0)) \ + \ + TRANSACTION(LoanManage, \ + TF_FLAG(tfLoanDefault, 0x00010000) \ + TF_FLAG(tfLoanImpair, 0x00020000) \ + TF_FLAG(tfLoanUnimpair, 0x00040000), \ + MASK_ADJ(0)) // clang-format on +// Create all the flag values. +// +// example: +// inline constexpr FlagValue tfAccountSetRequireDestTag = 0x00010000; +#define TO_VALUE(name, value) inline constexpr FlagValue name = value; +#define NULL_NAME(name, values, maskAdj) values +#define NULL_OUTPUT(name, value) +#define NULL_MASK_ADJ(value) +XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT, NULL_MASK_ADJ) + +// Create masks for each transaction type that has flags. +// +// example: +// inline constexpr FlagValue tfAccountSetMask = ~(tfUniversal | tfRequireDestTag | +// tfOptionalDestTag | tfRequireAuth | tfOptionalAuth | tfDisallowXRP | tfAllowXRP); +// +// The mask adjustment (maskAdj) allows adding flags back to the mask, making them invalid. +// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) to reject tfInnerBatchTxn on outer Batch. +#define TO_MASK(name, values, maskAdj) \ + inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | maskAdj; +#define VALUE_TO_MASK(name, value) | name +#define MASK_ADJ_TO_MASK(value) value +XMACRO(TO_MASK, VALUE_TO_MASK, VALUE_TO_MASK, MASK_ADJ_TO_MASK) + +// Verify that tfBatchMask correctly rejects tfInnerBatchTxn. +// The outer Batch transaction must NOT have tfInnerBatchTxn set; only inner transactions should +// have it. +static_assert( + (tfBatchMask & tfInnerBatchTxn) == tfInnerBatchTxn, + "tfBatchMask must include tfInnerBatchTxn to reject it on outer Batch"); + +// Verify that other transaction masks correctly allow tfInnerBatchTxn. +// Inner transactions need tfInnerBatchTxn to be valid, so these masks must not reject it. +static_assert( + (tfPaymentMask & tfInnerBatchTxn) == 0, + "tfPaymentMask must not reject tfInnerBatchTxn"); +static_assert( + (tfAccountSetMask & tfInnerBatchTxn) == 0, + "tfAccountSetMask must not reject tfInnerBatchTxn"); + +// Create getter functions for each set of flags using Meyer's singleton pattern. +// This avoids static initialization order fiasco while still providing efficient access. +// This is used below in `getAllTxFlags()` to generate the server_definitions RPC +// output. +// +// example: +// inline FlagMap const& getAccountSetFlags() { +// static FlagMap const flags = { +// {"tfRequireDestTag", 0x00010000}, +// {"tfOptionalDestTag", 0x00020000}, +// ...}; +// return flags; +// } +using FlagMap = std::map; +#define VALUE_TO_MAP(name, value) {#name, value}, +#define TO_MAP(name, values, maskAdj) \ + inline FlagMap const& get##name##Flags() \ + { \ + static FlagMap const flags = {values}; \ + return flags; \ + } +XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP, NULL_MASK_ADJ) + +inline FlagMap const& +getUniversalFlags() +{ + static FlagMap const flags = { + {"tfFullyCanonicalSig", tfFullyCanonicalSig}, {"tfInnerBatchTxn", tfInnerBatchTxn}}; + return flags; +} + +// Create a getter function for all transaction flag maps using Meyer's singleton pattern. +// This is used to generate the server_definitions RPC output. +// +// example: +// inline FlagMapPairList const& getAllTxFlags() { +// static FlagMapPairList const flags = { +// {"AccountSet", getAccountSetFlags()}, +// ...}; +// return flags; +// } +using FlagMapPairList = std::vector>; +#define ALL_TX_FLAGS(name, values, maskAdj) {#name, get##name##Flags()}, +inline FlagMapPairList const& +getAllTxFlags() +{ + static FlagMapPairList const flags = { + {"universal", getUniversalFlags()}, + XMACRO(ALL_TX_FLAGS, NULL_OUTPUT, NULL_OUTPUT, NULL_MASK_ADJ)}; + return flags; +} + +#undef XMACRO +#undef TO_VALUE +#undef VALUE_TO_MAP +#undef NULL_NAME +#undef NULL_OUTPUT +#undef TO_MAP +#undef TO_MASK +#undef VALUE_TO_MASK +#undef ALL_TX_FLAGS +#undef NULL_MASK_ADJ +#undef MASK_ADJ_TO_MASK + +#pragma pop_macro("XMACRO") +#pragma pop_macro("TO_VALUE") +#pragma pop_macro("VALUE_TO_MAP") +#pragma pop_macro("NULL_NAME") +#pragma pop_macro("NULL_OUTPUT") +#pragma pop_macro("TO_MAP") +#pragma pop_macro("TO_MASK") +#pragma pop_macro("VALUE_TO_MASK") +#pragma pop_macro("ALL_TX_FLAGS") +#pragma pop_macro("NULL_MASK_ADJ") +#pragma pop_macro("MASK_ADJ_TO_MASK") + +// Additional transaction masks and combos +inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); +inline constexpr FlagValue tfTrustSetPermissionMask = + ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); + +// MPTokenIssuanceCreate MutableFlags: +// Indicating specific fields or flags may be changed after issuance. +inline constexpr FlagValue tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock; +inline constexpr FlagValue tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth; +inline constexpr FlagValue tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow; +inline constexpr FlagValue tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade; +inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer; +inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback; +inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata; +inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee; +inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = + ~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | + tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | + tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee); + +// MPTokenIssuanceSet MutableFlags: +// Set or Clear flags. + +inline constexpr FlagValue tmfMPTSetCanLock = 0x00000001; +inline constexpr FlagValue tmfMPTClearCanLock = 0x00000002; +inline constexpr FlagValue tmfMPTSetRequireAuth = 0x00000004; +inline constexpr FlagValue tmfMPTClearRequireAuth = 0x00000008; +inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000010; +inline constexpr FlagValue tmfMPTClearCanEscrow = 0x00000020; +inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000040; +inline constexpr FlagValue tmfMPTClearCanTrade = 0x00000080; +inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100; +inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200; +inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400; +inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800; +inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~( + tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | + tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade | + tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback); + +// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a +// TrustLine to be added to the issuer of that token without explicit permission from that issuer. +// This was enabled by minting the NFToken with the tfTrustLine flag set. +// +// That capability could be used to attack the NFToken issuer. +// It would be possible for two accounts to trade the NFToken back and forth building up any number +// of TrustLines on the issuer, increasing the issuer's reserve without bound. +// +// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the tfTrustLine flag as a way +// to prevent the attack. But until the amendment passes we still need to keep the old behavior +// available. +inline constexpr FlagValue tfTrustLine = 0x00000004; // needed for backwards compatibility +inline constexpr FlagValue tfNFTokenMintMaskWithoutMutable = + ~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable); + +inline constexpr FlagValue tfNFTokenMintOldMask = ~(~tfNFTokenMintMaskWithoutMutable | tfTrustLine); + +// if featureDynamicNFT enabled then new flag allowing mutable URI available. +inline constexpr FlagValue tfNFTokenMintOldMaskWithMutable = ~(~tfNFTokenMintOldMask | tfMutable); + +inline constexpr FlagValue tfWithdrawSubTx = tfLPToken | tfSingleAsset | tfTwoAsset | + tfOneAssetLPToken | tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll; +inline constexpr FlagValue tfDepositSubTx = + tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | tfLimitLPToken | tfTwoAssetIfEmpty; + +#pragma push_macro("ACCOUNTSET_FLAGS") +#pragma push_macro("ACCOUNTSET_FLAG_TO_VALUE") +#pragma push_macro("ACCOUNTSET_FLAG_TO_MAP") + +// AccountSet SetFlag/ClearFlag values +#define ACCOUNTSET_FLAGS(ASF_FLAG) \ + ASF_FLAG(asfRequireDest, 1) \ + ASF_FLAG(asfRequireAuth, 2) \ + ASF_FLAG(asfDisallowXRP, 3) \ + ASF_FLAG(asfDisableMaster, 4) \ + ASF_FLAG(asfAccountTxnID, 5) \ + ASF_FLAG(asfNoFreeze, 6) \ + ASF_FLAG(asfGlobalFreeze, 7) \ + ASF_FLAG(asfDefaultRipple, 8) \ + ASF_FLAG(asfDepositAuth, 9) \ + ASF_FLAG(asfAuthorizedNFTokenMinter, 10) \ + /* 11 is reserved for Hooks amendment */ \ + /* ASF_FLAG(asfTshCollect, 11) */ \ + ASF_FLAG(asfDisallowIncomingNFTokenOffer, 12) \ + ASF_FLAG(asfDisallowIncomingCheck, 13) \ + ASF_FLAG(asfDisallowIncomingPayChan, 14) \ + ASF_FLAG(asfDisallowIncomingTrustline, 15) \ + ASF_FLAG(asfAllowTrustLineClawback, 16) \ + ASF_FLAG(asfAllowTrustLineLocking, 17) + +#define ACCOUNTSET_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value; +#define ACCOUNTSET_FLAG_TO_MAP(name, value) {#name, value}, + +ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_VALUE) + +inline std::map const& +getAsfFlagMap() +{ + static std::map const flags = { + ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_MAP)}; + return flags; +} + +#undef ACCOUNTSET_FLAG_TO_VALUE +#undef ACCOUNTSET_FLAG_TO_MAP +#undef ACCOUNTSET_FLAGS + +#pragma pop_macro("ACCOUNTSET_FLAG_TO_VALUE") +#pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP") +#pragma pop_macro("ACCOUNTSET_FLAGS") + } // namespace xrpl diff --git a/include/xrpl/protocol/TxFormats.h b/include/xrpl/protocol/TxFormats.h index b76eef7585..563a28f39f 100644 --- a/include/xrpl/protocol/TxFormats.h +++ b/include/xrpl/protocol/TxFormats.h @@ -2,6 +2,8 @@ #include +#include + namespace xrpl { /** Transaction type identifiers. @@ -73,6 +75,9 @@ private: public: static TxFormats const& getInstance(); + + static std::vector const& + getCommonFields(); }; } // namespace xrpl diff --git a/include/xrpl/protocol/TxSearched.h b/include/xrpl/protocol/TxSearched.h new file mode 100644 index 0000000000..e085bff315 --- /dev/null +++ b/include/xrpl/protocol/TxSearched.h @@ -0,0 +1,7 @@ +#pragma once + +namespace xrpl { + +enum class TxSearched { all, some, unknown }; + +} diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index ecf0bf7798..b377c80e26 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -35,8 +35,8 @@ class TenthBipsTag; // namespace. template -concept Valid = - std::is_class_v && std::is_object_v && std::is_object_v; +concept Valid = std::is_class_v && std::is_object_v && + std::is_object_v; /** `Usable` is checked to ensure that only values with known valid type tags can be used (sometimes transparently) in @@ -47,12 +47,15 @@ concept Valid = */ template concept Usable = Valid && - (std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || + (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || std::is_same_v); template -concept Compatible = Valid && std::is_arithmetic_v && std::is_arithmetic_v && +concept Compatible = + Valid && std::is_arithmetic_v && std::is_arithmetic_v && std::is_convertible_v; template @@ -62,8 +65,8 @@ template concept IntegralValue = Integral; template -concept CastableValue = - IntegralValue && IntegralValue && std::is_same_v; +concept CastableValue = IntegralValue && IntegralValue && + std::is_same_v; template class ValueUnit : private boost::totally_ordered>, @@ -292,7 +295,8 @@ public: { if constexpr (std::is_integral_v) { - using jsontype = std::conditional_t, Json::Int, Json::UInt>; + using jsontype = + std::conditional_t, Json::Int, Json::UInt>; constexpr auto min = std::numeric_limits::min(); constexpr auto max = std::numeric_limits::max(); @@ -343,7 +347,8 @@ to_string(ValueUnit const& amount) } template -concept muldivSource = Valid && std::is_convertible_v; +concept muldivSource = + Valid && std::is_convertible_v; template concept muldivDest = muldivSource && // Dest is also a source @@ -359,8 +364,8 @@ concept muldivable = muldivSources && muldivDest; // Source and Dest can be the same by default template -concept muldivCommutable = - muldivable && !std::is_same_v; +concept muldivCommutable = muldivable && + !std::is_same_v; template ValueUnit @@ -400,7 +405,10 @@ mulDivU(Source1 value, Dest mul, Source2 div) using namespace boost::multiprecision; uint128_t product; - product = multiply(product, static_cast(value.value()), static_cast(mul.value())); + product = multiply( + product, + static_cast(value.value()), + static_cast(mul.value())); auto quotient = product / div.value(); diff --git a/include/xrpl/protocol/XChainAttestations.h b/include/xrpl/protocol/XChainAttestations.h index fc3c464b92..06eff0fcf2 100644 --- a/include/xrpl/protocol/XChainAttestations.h +++ b/include/xrpl/protocol/XChainAttestations.h @@ -339,7 +339,9 @@ struct XChainCreateAccountAttestation match(MatchFields const& rhs) const; friend bool - operator==(XChainCreateAccountAttestation const& lhs, XChainCreateAccountAttestation const& rhs); + operator==( + XChainCreateAccountAttestation const& lhs, + XChainCreateAccountAttestation const& rhs); }; // Attestations from witness servers for a particular claim ID and bridge. @@ -408,7 +410,9 @@ public: template [[nodiscard]] inline bool -operator==(XChainAttestationsBase const& lhs, XChainAttestationsBase const& rhs) +operator==( + XChainAttestationsBase const& lhs, + XChainAttestationsBase const& rhs) { return lhs.attestations() == rhs.attestations(); } @@ -456,7 +460,8 @@ class XChainClaimAttestations final : public XChainAttestationsBase +class XChainCreateAccountAttestations final + : public XChainAttestationsBase { using TBase = XChainAttestationsBase; using TBase::TBase; diff --git a/include/xrpl/protocol/XRPAmount.h b/include/xrpl/protocol/XRPAmount.h index c1af338e4b..6e4133361b 100644 --- a/include/xrpl/protocol/XRPAmount.h +++ b/include/xrpl/protocol/XRPAmount.h @@ -166,7 +166,8 @@ public: std::optional dropsAs() const { - if ((drops_ > std::numeric_limits::max()) || (!std::numeric_limits::is_signed && drops_ < 0) || + if ((drops_ > std::numeric_limits::max()) || + (!std::numeric_limits::is_signed && drops_ < 0) || (std::numeric_limits::is_signed && drops_ < std::numeric_limits::lowest())) { return std::nullopt; diff --git a/include/xrpl/protocol/detail/STVar.h b/include/xrpl/protocol/detail/STVar.h index b5373d1a4a..bab2c2e024 100644 --- a/include/xrpl/protocol/detail/STVar.h +++ b/include/xrpl/protocol/detail/STVar.h @@ -44,7 +44,7 @@ private: // The largest "small object" we can accommodate static std::size_t constexpr max_size = 72; - std::aligned_storage::type d_; + std::aligned_storage::type d_ = {}; STBase* p_ = nullptr; public: diff --git a/include/xrpl/protocol/detail/b58_utils.h b/include/xrpl/protocol/detail/b58_utils.h index a34104555e..adb889ee53 100644 --- a/include/xrpl/protocol/detail/b58_utils.h +++ b/include/xrpl/protocol/detail/b58_utils.h @@ -122,7 +122,8 @@ inplace_bigint_div_rem(std::span numerator, std::uint64_t divisor) unsigned __int128 const low128 = low; return ((high128 << 64) | low128); }; - auto div_rem_64 = [](unsigned __int128 num, std::uint64_t denom) -> std::tuple { + auto div_rem_64 = [](unsigned __int128 num, + std::uint64_t denom) -> std::tuple { unsigned __int128 const denom128 = denom; unsigned __int128 const d = num / denom128; unsigned __int128 const r = num - (denom128 * d); diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 9b61d5a651..c2149321dc 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -19,7 +19,7 @@ XRPL_FEATURE(SmartEscrow, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo) @@ -33,7 +33,7 @@ XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo) -XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(Batch, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(SingleAssetVault, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index be21c36bde..a8ce412bb3 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -8,11 +8,11 @@ * To ease maintenance, you may replace any unneeded values with "..." * e.g. #define TRANSACTION(tag, value, name, ...) * - * You must define a transactor class in the `ripple` namespace named `name`, + * You must define a transactor class in the `xrpl` namespace named `name`, * and include its header alongside the TRANSACTOR definition using this * format: * #if TRANSACTION_INCLUDE - * # include + * # include * #endif * * The `privileges` parameter of the TRANSACTION macro is a bitfield @@ -22,7 +22,7 @@ /** This transaction type executes a payment. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegable, @@ -42,7 +42,7 @@ TRANSACTION(ttPAYMENT, 0, Payment, /** This transaction type creates an escrow object. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegable, @@ -60,6 +60,9 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, })) /** This transaction type completes an existing escrow. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegable, uint256{}, @@ -76,7 +79,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, /** This transaction type adjusts various account settings. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegable, @@ -97,7 +100,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, /** This transaction type cancels an existing escrow. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegable, @@ -110,7 +113,7 @@ TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, /** This transaction type sets or clears an account's "regular key". */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegable, @@ -124,7 +127,7 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, /** This transaction type creates an offer to trade one asset for another. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegable, @@ -140,7 +143,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, /** This transaction type cancels existing offers to trade one asset for another. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegable, @@ -154,7 +157,7 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, /** This transaction type creates a new set of tickets. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegable, @@ -170,7 +173,7 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, // The SignerEntries are optional because a SignerList is deleted by // setting the SignerQuorum to zero and omitting SignerEntries. #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegable, @@ -183,7 +186,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, /** This transaction type creates a new unidirectional XRP payment channel. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegable, @@ -199,6 +202,9 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, })) /** This transaction type funds an existing unidirectional XRP payment channel. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegable, uint256{}, @@ -210,6 +216,9 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, })) /** This transaction type submits a claim against an existing unidirectional payment channel. */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegable, uint256{}, @@ -225,7 +234,7 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, /** This transaction type creates a new check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegable, @@ -241,7 +250,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, /** This transaction type cashes an existing check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegable, @@ -255,7 +264,7 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash, /** This transaction type cancels an existing check. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegable, @@ -267,7 +276,7 @@ TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, /** This transaction type grants or revokes authorization to transfer funds. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegable, @@ -282,7 +291,7 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, /** This transaction type modifies a trustline between two accounts. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegable, @@ -296,7 +305,7 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet, /** This transaction type deletes an existing account. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegable, @@ -312,7 +321,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, /** This transaction mints a new NFT. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegable, @@ -330,7 +339,7 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, /** This transaction burns (i.e. destroys) an existing NFT. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegable, @@ -343,7 +352,7 @@ TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, /** This transaction creates a new offer to buy or sell an NFT. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegable, @@ -359,7 +368,7 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, /** This transaction cancels an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegable, @@ -371,7 +380,7 @@ TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, /** This transaction accepts an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegable, @@ -385,7 +394,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, /** This transaction claws back issued tokens. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegable, @@ -398,7 +407,7 @@ TRANSACTION(ttCLAWBACK, 30, Clawback, /** This transaction claws back tokens from an AMM pool. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegable, @@ -413,7 +422,7 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, /** This transaction type creates an AMM instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegable, @@ -427,7 +436,7 @@ TRANSACTION(ttAMM_CREATE, 35, AMMCreate, /** This transaction type deposits into an AMM instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegable, @@ -445,7 +454,7 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, /** This transaction type withdraws from an AMM instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegable, @@ -462,7 +471,7 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, /** This transaction type votes for the trading fee */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegable, @@ -476,7 +485,7 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote, /** This transaction type bids for the auction slot */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegable, @@ -492,7 +501,7 @@ TRANSACTION(ttAMM_BID, 39, AMMBid, /** This transaction type deletes AMM in the empty state */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegable, @@ -505,7 +514,7 @@ TRANSACTION(ttAMM_DELETE, 40, AMMDelete, /** This transactions creates a crosschain sequence number */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegable, @@ -620,7 +629,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, /** This transaction type creates or updates a DID */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegable, @@ -633,6 +642,9 @@ TRANSACTION(ttDID_SET, 49, DIDSet, })) /** This transaction type deletes a DID */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegable, featureDID, @@ -641,7 +653,7 @@ TRANSACTION(ttDID_DELETE, 50, DIDDelete, /** This transaction type creates an Oracle instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegable, @@ -658,7 +670,7 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, /** This transaction type deletes an Oracle instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegable, @@ -670,7 +682,7 @@ TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, /** This transaction type fixes a problem in the ledger state */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegable, @@ -683,7 +695,7 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, /** This transaction type creates a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegable, @@ -700,7 +712,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, /** This transaction type destroys a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegable, @@ -712,7 +724,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, /** This transaction type sets flags on a MPTokensIssuance or MPToken instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegable, @@ -729,7 +741,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, /** This transaction type authorizes a MPToken instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegable, @@ -742,7 +754,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, /** This transaction type create an Credential instance */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegable, @@ -756,6 +768,9 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, })) /** This transaction type accept an Credential object */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegable, featureCredentials, @@ -766,6 +781,9 @@ TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, })) /** This transaction type delete an Credential object */ +#if TRANSACTION_INCLUDE +# include +#endif TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegable, featureCredentials, @@ -778,7 +796,7 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, /** This transaction type modify a NFToken */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegable, @@ -792,7 +810,7 @@ TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, /** This transaction type creates or modifies a Permissioned Domain */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegable, @@ -805,7 +823,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, /** This transaction type deletes a Permissioned Domain */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegable, @@ -817,7 +835,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, /** This transaction type delegates authorized account specified permissions */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegable, @@ -830,10 +848,10 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, /** This transaction creates a single asset vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, createPseudoAcct | createMPTIssuance | mustModifyVault, ({ @@ -848,10 +866,10 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, /** This transaction updates a single asset vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_SET, 66, VaultSet, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustModifyVault, ({ @@ -863,10 +881,10 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, /** This transaction deletes a single asset vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mustDeleteAcct | destroyMPTIssuance | mustModifyVault, ({ @@ -875,10 +893,10 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, /** This transaction trades assets for shares with a vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayAuthorizeMPT | mustModifyVault, ({ @@ -888,10 +906,10 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, /** This transaction trades shares for assets with a vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mayAuthorizeMPT | mustModifyVault, ({ @@ -903,10 +921,10 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, /** This transaction claws back tokens from a vault. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, - Delegation::delegable, + Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mustModifyVault, ({ @@ -917,7 +935,7 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, /** This transaction type batches together transactions. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegable, @@ -932,10 +950,10 @@ TRANSACTION(ttBATCH, 71, Batch, /** This transaction creates and updates a Loan Broker */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, createPseudoAcct | mayAuthorizeMPT, ({ {sfVaultID, soeREQUIRED}, @@ -949,10 +967,10 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, /** This transaction deletes a Loan Broker */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mustDeleteAcct | mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -960,10 +978,10 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, /** This transaction deposits First Loss Capital into a Loan Broker */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -972,10 +990,10 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, /** This transaction withdraws First Loss Capital from a Loan Broker */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -987,10 +1005,10 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, /** This transaction claws back First Loss Capital from a Loan Broker to the issuer of the capital */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanBrokerID, soeOPTIONAL}, @@ -999,10 +1017,10 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, /** This transaction creates a Loan */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_SET, 80, LoanSet, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanBrokerID, soeREQUIRED}, @@ -1026,10 +1044,10 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet, /** This transaction deletes an existing Loan */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, noPriv, ({ {sfLoanID, soeREQUIRED}, @@ -1037,10 +1055,10 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, /** This transaction is used to change the delinquency status of an existing Loan */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, // All of the LoanManage options will modify the vault, but the // transaction can succeed without options, essentially making it @@ -1051,10 +1069,10 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, /** The Borrower uses this transaction to make a Payment on the Loan. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttLOAN_PAY, 84, LoanPay, - Delegation::delegable, + Delegation::notDelegable, featureLendingProtocol, mayAuthorizeMPT | mustModifyVault, ({ {sfLoanID, soeREQUIRED}, @@ -1066,7 +1084,7 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay, For details, see: https://xrpl.org/amendments.html */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegable, diff --git a/include/xrpl/protocol/digest.h b/include/xrpl/protocol/digest.h index b5ec277e7b..9e6606f51f 100644 --- a/include/xrpl/protocol/digest.h +++ b/include/xrpl/protocol/digest.h @@ -40,7 +40,7 @@ public: operator result_type() noexcept; private: - char ctx_[96]; + char ctx_[96]{}; }; /** SHA-512 digest @@ -63,7 +63,7 @@ public: operator result_type() noexcept; private: - char ctx_[216]; + char ctx_[216]{}; }; /** SHA-256 digest @@ -86,7 +86,7 @@ public: operator result_type() noexcept; private: - char ctx_[112]; + char ctx_[112]{}; }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/json_get_or_throw.h b/include/xrpl/protocol/json_get_or_throw.h index 7cdf481ad3..336d875450 100644 --- a/include/xrpl/protocol/json_get_or_throw.h +++ b/include/xrpl/protocol/json_get_or_throw.h @@ -34,7 +34,8 @@ struct JsonTypeMismatchError : std::exception char const* const key; std::string const expectedType; mutable std::string msg; - JsonTypeMismatchError(Json::StaticString const& k, std::string et) : key{k.c_str()}, expectedType{std::move(et)} + JsonTypeMismatchError(Json::StaticString const& k, std::string et) + : key{k.c_str()}, expectedType{std::move(et)} { } char const* @@ -42,7 +43,8 @@ struct JsonTypeMismatchError : std::exception { if (msg.empty()) { - msg = std::string("Type mismatch on json key: ") + key + "; expected type: " + expectedType; + msg = std::string("Type mismatch on json key: ") + key + + "; expected type: " + expectedType; } return msg.c_str(); } diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 05942353dd..8b810d50d1 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -25,6 +25,7 @@ namespace jss { JSS(AL_size); // out: GetCounts JSS(AL_hit_rate); // out: GetCounts JSS(AcceptedCredentials); // out: AccountObjects +JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions JSS(Account); // in: TransactionSign; field. JSS(AMMID); // field JSS(Amount); // in: TransactionSign; field. @@ -111,6 +112,7 @@ JSS(accounts); // in: LedgerEntry, Subscribe, // handlers/Ledger, Unsubscribe JSS(accounts_proposed); // in: Subscribe, Unsubscribe JSS(action); +JSS(active); // out: OverlayImpl JSS(acquiring); // out: LedgerRequest JSS(address); // out: PeerImp JSS(affected); // out: AcceptedLedgerTx @@ -187,6 +189,7 @@ JSS(closed_ledger); // out: NetworkOPs JSS(cluster); // out: PeerImp JSS(code); // out: errors JSS(command); // in: RPCHandler +JSS(common); // out: RPC server_definitions JSS(complete); // out: NetworkOPs, InboundLedger JSS(complete_ledgers); // out: NetworkOPs, PeerImp JSS(consensus); // out: NetworkOPs, LedgerConsensus @@ -301,6 +304,7 @@ JSS(id); // websocket. JSS(ident); // in: AccountCurrencies, AccountInfo, // OwnerInfo JSS(ignore_default); // in: AccountLines +JSS(in); // out: OverlayImpl JSS(inLedger); // out: tx/Transaction JSS(inbound); // out: PeerImp JSS(index); // in: LedgerEntry @@ -359,6 +363,8 @@ JSS(ledger_min); // in, out: AccountTx* JSS(ledger_time); // out: NetworkOPs JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions +JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions JSS(levels); // LogLevels JSS(limit); // in/out: AccountTx*, AccountOffers, // AccountLines, AccountObjects @@ -460,8 +466,10 @@ JSS(open); // out: handlers/Ledger JSS(open_ledger_cost); // out: SubmitTransaction JSS(open_ledger_fee); // out: TxQ JSS(open_ledger_level); // out: TxQ +JSS(optionality); // out: server_definitions JSS(oracles); // in: get_aggregate_price JSS(oracle_document_id); // in: get_aggregate_price +JSS(out); // out: OverlayImpl JSS(owner); // in: LedgerEntry, out: NetworkOPs JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx JSS(page_index); @@ -619,6 +627,8 @@ JSS(TRANSACTION_RESULTS); // out: RPC server_definitions // matches definitions.json format JSS(TRANSACTION_TYPES); // out: RPC server_definitions // matches definitions.json format +JSS(TRANSACTION_FLAGS); // out: RPC server_definitions +JSS(TRANSACTION_FORMATS); // out: RPC server_definitions JSS(TYPES); // out: RPC server_definitions // matches definitions.json format JSS(transfer_rate); // out: nft_info (clio) diff --git a/include/xrpl/protocol/nftPageMask.h b/include/xrpl/protocol/nftPageMask.h index 05a42ce294..2f7337c328 100644 --- a/include/xrpl/protocol/nftPageMask.h +++ b/include/xrpl/protocol/nftPageMask.h @@ -9,7 +9,8 @@ namespace nft { // NFT directory pages order their contents based only on the low 96 bits of // the NFToken value. This mask provides easy access to the necessary mask. -uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff")); +uint256 constexpr pageMask( + std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff")); } // namespace nft } // namespace xrpl diff --git a/include/xrpl/protocol/tokens.h b/include/xrpl/protocol/tokens.h index 8cda8efc86..043db02825 100644 --- a/include/xrpl/protocol/tokens.h +++ b/include/xrpl/protocol/tokens.h @@ -74,7 +74,10 @@ namespace b58_fast { // Use the fast version (10-15x faster) is using gcc extensions (int128 in // particular) [[nodiscard]] B58Result> -encodeBase58Token(TokenType token_type, std::span input, std::span out); +encodeBase58Token( + TokenType token_type, + std::span input, + std::span out); [[nodiscard]] B58Result> decodeBase58Token(TokenType type, std::string_view s, std::span outBuf); diff --git a/include/xrpl/protocol_autogen/LedgerEntryBase.h b/include/xrpl/protocol_autogen/LedgerEntryBase.h new file mode 100644 index 0000000000..0c5b367391 --- /dev/null +++ b/include/xrpl/protocol_autogen/LedgerEntryBase.h @@ -0,0 +1,152 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +/** + * @brief Base class for type-safe ledger entry wrappers. + * + * This class provides common functionality for all ledger entry types, + * including access to common fields (sfLedgerIndex, sfLedgerEntryType, sfFlags). + * + * This is an immutable wrapper around SLE (Serialized Ledger Entry). + * Use the corresponding Builder classes to construct new ledger entries. + */ +class LedgerEntryBase +{ +public: + /** + * @brief Construct a ledger entry wrapper from an existing SLE object. + * @param sle The underlying serialized ledger entry to wrap + */ + explicit LedgerEntryBase(std::shared_ptr sle) : sle_(std::move(sle)) + { + } + + /** + * @brief Validate the ledger entry + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate() const + { + if (!sle_->isFieldPresent(sfLedgerEntryType)) + { + return false; // LCOV_EXCL_LINE + } + auto ledgerEntryType = static_cast(sle_->getFieldU16(sfLedgerEntryType)); + return protocol_autogen::validateSTObject( + *sle_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate()); + } + + /** + * @brief Get the ledger entry type. + * @return The type of this ledger entry + */ + [[nodiscard]] + LedgerEntryType + getType() const + { + return sle_->getType(); + } + + /** + * @brief Get the key (index) of this ledger entry. + * + * The key uniquely identifies this ledger entry in the ledger state. + * @return A constant reference to the 256-bit key + */ + [[nodiscard]] + uint256 const& + getKey() const + { + return sle_->key(); + } + + // Common field getters (from LedgerFormats.cpp commonFields) + + /** + * @brief Get the ledger index (sfLedgerIndex). + * + * This field is OPTIONAL and represents the index of the ledger entry. + * @return The ledger index if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getLedgerIndex() const + { + if (sle_->isFieldPresent(sfLedgerIndex)) + { + return sle_->at(sfLedgerIndex); + } + return std::nullopt; + } + + /** + * @brief Check if the ledger entry has a ledger index. + * @return true if sfLedgerIndex is present, false otherwise + */ + [[nodiscard]] + bool + hasLedgerIndex() const + { + return sle_->isFieldPresent(sfLedgerIndex); + } + + /** + * @brief Get the ledger entry type field (sfLedgerEntryType). + * + * This field is REQUIRED for all ledger entries and indicates the type + * of the ledger entry (e.g., AccountRoot, RippleState, Offer, etc.). + * @return The ledger entry type as a 16-bit unsigned integer + */ + [[nodiscard]] + uint16_t + getLedgerEntryType() const + { + return sle_->at(sfLedgerEntryType); + } + + /** + * @brief Get the flags field (sfFlags). + * + * This field is REQUIRED for all ledger entries and contains + * type-specific flags that modify the behavior of the ledger entry. + * @return The flags value as a 32-bit unsigned integer + */ + [[nodiscard]] + std::uint32_t + getFlags() const + { + return sle_->at(sfFlags); + } + + /** + * @brief Get the underlying SLE object. + * + * Provides direct access to the wrapped serialized ledger entry object + * for cases where the type-safe accessors are insufficient. + * @return A constant reference to the underlying SLE object + */ + [[nodiscard]] + std::shared_ptr + getSle() const + { + return sle_; + } + +protected: + /** @brief The underlying serialized ledger entry being wrapped. */ + std::shared_ptr sle_; +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h b/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h new file mode 100644 index 0000000000..e2f05c31ab --- /dev/null +++ b/include/xrpl/protocol_autogen/LedgerEntryBuilderBase.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::ledger_entries { + +/** + * Base class for all ledger entry builders. + * Provides common field setters that are available for all ledger entry types. + */ +template +class LedgerEntryBuilderBase +{ +public: + LedgerEntryBuilderBase() = default; + + LedgerEntryBuilderBase( + SF_UINT16::type::value_type ledgerEntryType, + SF_UINT32::type::value_type flags = 0) + { + // Don't call object_.set(soTemplate) - keep object_ as a free object. + // This avoids creating STBase placeholders for soeDEFAULT fields, + // which would cause applyTemplate() to throw "may not be explicitly + // set to default" when building the SLE. + // The SLE constructor will call applyTemplate() which properly + // handles missing fields. + object_[sfLedgerEntryType] = ledgerEntryType; + object_[sfFlags] = flags; + } + + /** + * @brief Validate the ledger entry + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate() const + { + if (!object_.isFieldPresent(sfLedgerEntryType)) + { + return false; // LCOV_EXCL_LINE + } + auto ledgerEntryType = static_cast(object_.getFieldU16(sfLedgerEntryType)); + return protocol_autogen::validateSTObject( + object_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate()); + } + + /** + * Set the ledger index. + * @param value Ledger index + * @return Reference to the derived builder for method chaining. + */ + Derived& + setLedgerIndex(uint256 const& value) + { + object_[sfLedgerIndex] = value; + return static_cast(*this); + } + + /** + * Set the flags. + * @param value Flags value + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFlags(uint32_t value) + { + object_.setFieldU32(sfFlags, value); + return static_cast(*this); + } + +protected: + STObject object_{sfLedgerEntry}; +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/README.md b/include/xrpl/protocol_autogen/README.md new file mode 100644 index 0000000000..be90788b05 --- /dev/null +++ b/include/xrpl/protocol_autogen/README.md @@ -0,0 +1,79 @@ +# Protocol Autogen + +This directory contains auto-generated C++ wrapper classes for XRP Ledger protocol types. + +## Generated Files + +The files in this directory are automatically generated at **CMake configure time** from macro definition files: + +- **Transaction classes** (in `transactions/`): Generated from `include/xrpl/protocol/detail/transactions.macro` by `scripts/generate_tx_classes.py` +- **Ledger entry classes** (in `ledger_entries/`): Generated from `include/xrpl/protocol/detail/ledger_entries.macro` by `scripts/generate_ledger_classes.py` + +## Generation Process + +The generation happens automatically when you **configure** the project (not during build). When you run CMake, the system: + +1. Creates a Python virtual environment in the build directory (`codegen_venv`) +2. Installs Python dependencies from `scripts/requirements.txt` into the venv (only if needed) +3. Runs the Python generation scripts using the venv Python interpreter +4. Parses the macro files to extract type definitions +5. Generates type-safe C++ wrapper classes using Mako templates +6. Places the generated headers in this directory + +### When Regeneration Happens + +The code is regenerated when: + +- You run CMake configure for the first time +- The Python virtual environment doesn't exist +- `scripts/requirements.txt` has been modified + +To force regeneration, delete the build directory and reconfigure. + +### Python Dependencies + +The code generation requires the following Python packages (automatically installed): + +- `pcpp` - C preprocessor for Python +- `pyparsing` - Parser combinator library +- `Mako` - Template engine + +These are isolated in a virtual environment and won't affect your system Python installation. + +## Version Control + +The generated `.h` files **are checked into version control**. This means: + +- Developers without Python 3 can still build the project using the committed files +- CI/CD systems don't need to run code generation if files are up to date +- Changes to generated files are visible in code review + +## Modifying Generated Code + +**Do not manually edit generated files.** Any changes will be overwritten the next time CMake configure runs. + +To modify the generated classes: + +- Edit the macro files in `include/xrpl/protocol/detail/` +- Edit the Mako templates in `scripts/templates/` +- Edit the generation scripts in `scripts/` +- Update Python dependencies in `scripts/requirements.txt` +- Run CMake configure to regenerate + +## Adding Common Fields + +If you add a new common field to `TxFormats.cpp` or `LedgerFormats.cpp`, you should also update the corresponding base classes and templates manually: + +Base classes: + +- `TransactionBase.h` - Add getters for new common transaction fields +- `TransactionBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters +- `LedgerEntryBase.h` - Add getters for new common ledger entry fields +- `LedgerEntryBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters + +Templates (update to pass required common fields to base class constructors): + +- `scripts/templates/Transaction.h.mako` +- `scripts/templates/LedgerEntry.h.mako` + +These files are **not auto-generated** and must be updated by hand. diff --git a/include/xrpl/protocol_autogen/STObjectValidation.h b/include/xrpl/protocol_autogen/STObjectValidation.h new file mode 100644 index 0000000000..cd0633af5d --- /dev/null +++ b/include/xrpl/protocol_autogen/STObjectValidation.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace xrpl::protocol_autogen { + +[[nodiscard]] +inline bool +validateSTObject(STObject const& obj, SOTemplate const& format) +{ + for (auto const& field : format) + { + if (!obj.isFieldPresent(field.sField()) && field.style() == soeREQUIRED) + { + return false; // LCOV_EXCL_LINE + } + + if (field.supportMPT() == soeMPTNotSupported && obj.isFieldPresent(field.sField())) + { + if (field.sField().fieldType == STI_AMOUNT) + { + auto const& amount = obj.getFieldAmount(field.sField()); + + if (amount.asset().holds()) + return false; // LCOV_EXCL_LINE + } + else if (field.sField().fieldType == STI_ISSUE) + { + auto issue = dynamic_cast(obj.peekAtPField(field.sField())); + if (!issue) + return false; // LCOV_EXCL_LINE + + if (issue->holds()) + return false; // LCOV_EXCL_LINE + } + } + } + + return true; +} + +} // namespace xrpl::protocol_autogen diff --git a/include/xrpl/protocol_autogen/TransactionBase.h b/include/xrpl/protocol_autogen/TransactionBase.h new file mode 100644 index 0000000000..161d718c66 --- /dev/null +++ b/include/xrpl/protocol_autogen/TransactionBase.h @@ -0,0 +1,457 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +/** + * @brief Base class for all transaction wrapper types. + * + * Provides type-safe read-only accessors for common transaction fields. + * This is an immutable wrapper around STTx. Use the corresponding Builder classes + * to construct new transactions. + */ +class TransactionBase +{ +public: + /** + * @brief Construct a transaction wrapper from an existing STTx object. + * @param tx The underlying transaction object to wrap + */ + explicit TransactionBase(std::shared_ptr tx) : tx_(std::move(tx)) + { + } + + /** + * @brief Validate the transaction + * @return true if validation passes, false otherwise + */ + [[nodiscard]] + bool + validate(std::string& reason) const + { + if (!protocol_autogen::validateSTObject( + *tx_, TxFormats::getInstance().findByType(tx_->getTxnType())->getSOTemplate())) + { + // LCOV_EXCL_START + reason = "Transaction failed schema validation"; + return false; + // LCOV_EXCL_STOP + } + + // Pseudo transactions are not submitted to the network + if (isPseudoTx(*tx_)) + { + return true; + } + return passesLocalChecks(*tx_, reason); + } + + /** + * @brief Get the transaction type. + * @return The type of this transaction + */ + [[nodiscard]] + xrpl::TxType + getTransactionType() const + { + return tx_->getTxnType(); + } + + /** + * @brief Get the account initiating the transaction (sfAccount). + * + * This field is REQUIRED for all transactions. + * @return The account ID of the transaction sender + */ + [[nodiscard]] + AccountID + getAccount() const + { + return tx_->at(sfAccount); + } + + /** + * @brief Get the sequence number of the transaction (sfSequence). + * + * This field is REQUIRED for all transactions. + * @return The sequence number + */ + [[nodiscard]] + std::uint32_t + getSequence() const + { + return tx_->at(sfSequence); + } + + /** + * @brief Get the transaction fee (sfFee). + * + * This field is REQUIRED for all transactions. + * @return The fee amount + */ + [[nodiscard]] + STAmount + getFee() const + { + return tx_->at(sfFee); + } + + /** + * @brief Get the signing public key (sfSigningPubKey). + * + * This field is REQUIRED for all transactions. + * @return The public key used for signing as a blob + */ + [[nodiscard]] + Blob + getSigningPubKey() const + { + return tx_->getFieldVL(sfSigningPubKey); + } + + /** + * @brief Get the transaction flags (sfFlags). + * + * This field is OPTIONAL. + * @return The flags value if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getFlags() const + { + if (tx_->isFieldPresent(sfFlags)) + return tx_->at(sfFlags); + return std::nullopt; + } + + /** + * @brief Check if the transaction has flags set. + * @return true if sfFlags is present, false otherwise + */ + [[nodiscard]] + bool + hasFlags() const + { + return tx_->isFieldPresent(sfFlags); + } + + /** + * @brief Get the source tag (sfSourceTag). + * + * This field is OPTIONAL and used to identify the source of a payment. + * @return The source tag value if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getSourceTag() const + { + if (tx_->isFieldPresent(sfSourceTag)) + return tx_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a source tag. + * @return true if sfSourceTag is present, false otherwise + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return tx_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get the previous transaction ID (sfPreviousTxnID). + * + * This field is OPTIONAL and used for transaction chaining. + * @return The previous transaction ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getPreviousTxnID() const + { + if (tx_->isFieldPresent(sfPreviousTxnID)) + return tx_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a previous transaction ID. + * @return true if sfPreviousTxnID is present, false otherwise + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return tx_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get the last ledger sequence (sfLastLedgerSequence). + * + * This field is OPTIONAL and specifies the latest ledger sequence + * in which this transaction can be included. + * @return The last ledger sequence if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getLastLedgerSequence() const + { + if (tx_->isFieldPresent(sfLastLedgerSequence)) + return tx_->at(sfLastLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a last ledger sequence. + * @return true if sfLastLedgerSequence is present, false otherwise + */ + [[nodiscard]] + bool + hasLastLedgerSequence() const + { + return tx_->isFieldPresent(sfLastLedgerSequence); + } + + /** + * @brief Get the account transaction ID (sfAccountTxnID). + * + * This field is OPTIONAL and used to track transaction sequences. + * @return The account transaction ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getAccountTxnID() const + { + if (tx_->isFieldPresent(sfAccountTxnID)) + return tx_->at(sfAccountTxnID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has an account transaction ID. + * @return true if sfAccountTxnID is present, false otherwise + */ + [[nodiscard]] + bool + hasAccountTxnID() const + { + return tx_->isFieldPresent(sfAccountTxnID); + } + + /** + * @brief Get the operation limit (sfOperationLimit). + * + * This field is OPTIONAL and limits the number of operations in a transaction. + * @return The operation limit if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getOperationLimit() const + { + if (tx_->isFieldPresent(sfOperationLimit)) + return tx_->at(sfOperationLimit); + return std::nullopt; + } + + /** + * @brief Check if the transaction has an operation limit. + * @return true if sfOperationLimit is present, false otherwise + */ + [[nodiscard]] + bool + hasOperationLimit() const + { + return tx_->isFieldPresent(sfOperationLimit); + } + + /** + * @brief Get the memos array (sfMemos). + * + * This field is OPTIONAL and contains arbitrary data attached to the transaction. + * @note This is an untyped field (STArray). + * @return A reference wrapper to the memos array if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional> + getMemos() const + { + if (tx_->isFieldPresent(sfMemos)) + return tx_->getFieldArray(sfMemos); + return std::nullopt; + } + + /** + * @brief Check if the transaction has memos. + * @return true if sfMemos is present, false otherwise + */ + [[nodiscard]] + bool + hasMemos() const + { + return tx_->isFieldPresent(sfMemos); + } + + /** + * @brief Get the ticket sequence (sfTicketSequence). + * + * This field is OPTIONAL and used when consuming a ticket instead of a sequence number. + * @return The ticket sequence if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getTicketSequence() const + { + if (tx_->isFieldPresent(sfTicketSequence)) + return tx_->at(sfTicketSequence); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a ticket sequence. + * @return true if sfTicketSequence is present, false otherwise + */ + [[nodiscard]] + bool + hasTicketSequence() const + { + return tx_->isFieldPresent(sfTicketSequence); + } + + /** + * @brief Get the transaction signature (sfTxnSignature). + * + * This field is OPTIONAL and contains the signature for single-signed transactions. + * @return The transaction signature as a blob if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getTxnSignature() const + { + if (tx_->isFieldPresent(sfTxnSignature)) + return tx_->getFieldVL(sfTxnSignature); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a transaction signature. + * @return true if sfTxnSignature is present, false otherwise + */ + [[nodiscard]] + bool + hasTxnSignature() const + { + return tx_->isFieldPresent(sfTxnSignature); + } + + /** + * @brief Get the signers array (sfSigners). + * + * This field is OPTIONAL and contains the list of signers for multi-signed transactions. + * @note This is an untyped field (STArray). + * @return A reference wrapper to the signers array if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional> + getSigners() const + { + if (tx_->isFieldPresent(sfSigners)) + return tx_->getFieldArray(sfSigners); + return std::nullopt; + } + + /** + * @brief Check if the transaction has signers. + * @return true if sfSigners is present, false otherwise + */ + [[nodiscard]] + bool + hasSigners() const + { + return tx_->isFieldPresent(sfSigners); + } + + /** + * @brief Get the network ID (sfNetworkID). + * + * This field is OPTIONAL and identifies the network this transaction is intended for. + * @return The network ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getNetworkID() const + { + if (tx_->isFieldPresent(sfNetworkID)) + return tx_->at(sfNetworkID); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a network ID. + * @return true if sfNetworkID is present, false otherwise + */ + [[nodiscard]] + bool + hasNetworkID() const + { + return tx_->isFieldPresent(sfNetworkID); + } + + /** + * @brief Get the delegate account (sfDelegate). + * + * This field is OPTIONAL and specifies a delegate account for the transaction. + * @return The delegate account ID if present, std::nullopt otherwise + */ + [[nodiscard]] + std::optional + getDelegate() const + { + if (tx_->isFieldPresent(sfDelegate)) + return tx_->at(sfDelegate); + return std::nullopt; + } + + /** + * @brief Check if the transaction has a delegate account. + * @return true if sfDelegate is present, false otherwise + */ + [[nodiscard]] + bool + hasDelegate() const + { + return tx_->isFieldPresent(sfDelegate); + } + + /** + * @brief Get the underlying STTx object. + * + * Provides direct access to the wrapped transaction object for cases + * where the type-safe accessors are insufficient. + * @return A constant reference to the underlying STTx object + */ + [[nodiscard]] + std::shared_ptr + getSTTx() const + { + return tx_; + } + +protected: + /** @brief The underlying transaction object being wrapped. */ + std::shared_ptr tx_; +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/TransactionBuilderBase.h b/include/xrpl/protocol_autogen/TransactionBuilderBase.h new file mode 100644 index 0000000000..3abfd07a1c --- /dev/null +++ b/include/xrpl/protocol_autogen/TransactionBuilderBase.h @@ -0,0 +1,269 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::transactions { + +/** + * Base class for all transaction builders. + * Provides common field setters that are available for all transaction types. + */ +template +class TransactionBuilderBase +{ +public: + TransactionBuilderBase() = default; + + TransactionBuilderBase( + SF_UINT16::type::value_type transactionType, + SF_ACCOUNT::type::value_type account, + std::optional sequence, + std::optional fee) + { + // Don't call object_.set(soTemplate) - keep object_ as a free object. + // This avoids creating STBase placeholders for soeDEFAULT fields, + // which would cause applyTemplate() to throw "may not be explicitly + // set to default" when building the STTx. + // The STTx constructor will call applyTemplate() which properly + // handles missing fields. + object_[sfTransactionType] = transactionType; + setAccount(account); + + if (sequence) + { + setSequence(*sequence); + } + if (fee) + { + setFee(*fee); + } + } + + /** + * Set the account that is sending the transaction. + * @param value Account address (typically as a string) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setAccount(AccountID const& value) + { + object_[sfAccount] = value; + return static_cast(*this); + } + + /** + * Set the transaction fee. + * @param value Fee in drops (typically as a string or number) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFee(STAmount const& value) + { + object_[sfFee] = value; + return static_cast(*this); + } + + /** + * Set the sequence number. + * @param value Sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSequence(std::uint32_t const& value) + { + object_[sfSequence] = value; + return static_cast(*this); + } + + /** + * Set the ticket sequence to use for this transaction. + * When using a ticket, the regular sequence number is set to 0. + * @param value Ticket sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setTicketSequence(std::uint32_t const& value) + { + object_[sfSequence] = 0u; + object_[sfTicketSequence] = value; + return static_cast(*this); + } + + /** + * Set transaction flags. + * @param value Flags value + * @return Reference to the derived builder for method chaining. + */ + Derived& + setFlags(std::uint32_t const& value) + { + object_[sfFlags] = value; + return static_cast(*this); + } + + /** + * Set the source tag. + * @param value Source tag + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSourceTag(std::uint32_t const& value) + { + object_[sfSourceTag] = value; + return static_cast(*this); + } + + /** + * Set the last ledger sequence. + * @param value Last ledger sequence number + * @return Reference to the derived builder for method chaining. + */ + Derived& + setLastLedgerSequence(std::uint32_t const& value) + { + object_[sfLastLedgerSequence] = value; + return static_cast(*this); + } + + /** + * Set the account transaction ID. + * @param value Account transaction ID (typically as a hex string) + * @return Reference to the derived builder for method chaining. + */ + Derived& + setAccountTxnID(uint256 const& value) + { + object_[sfAccountTxnID] = value; + return static_cast(*this); + } + + /** + * Set the previous transaction ID. + * Used for emulate027 compatibility. + * @param value Previous transaction ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setPreviousTxnID(uint256 const& value) + { + object_[sfPreviousTxnID] = value; + return static_cast(*this); + } + + /** + * Set the operation limit. + * @param value Operation limit + * @return Reference to the derived builder for method chaining. + */ + Derived& + setOperationLimit(std::uint32_t const& value) + { + object_[sfOperationLimit] = value; + return static_cast(*this); + } + + /** + * Set the memos array. + * @param value Array of memo objects + * @return Reference to the derived builder for method chaining. + */ + Derived& + setMemos(STArray const& value) + { + object_.setFieldArray(sfMemos, value); + return static_cast(*this); + } + + /** + * Set the signers array for multi-signing. + * @param value Array of signer objects + * @return Reference to the derived builder for method chaining. + */ + Derived& + setSigners(STArray const& value) + { + object_.setFieldArray(sfSigners, value); + return static_cast(*this); + } + + /** + * Set the network ID. + * @param value Network ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setNetworkID(std::uint32_t const& value) + { + object_[sfNetworkID] = value; + return static_cast(*this); + } + + /** + * Set the delegate account for delegated transactions. + * @param value Delegate account ID + * @return Reference to the derived builder for method chaining. + */ + Derived& + setDelegate(AccountID const& value) + { + object_[sfDelegate] = value; + return static_cast(*this); + } + + /** + * Get the underlying STObject. + * @return The STObject + */ + STObject const& + getSTObject() const + { + return object_; + } + +protected: + /** + * Sign the transaction with the given keys. + * + * This sets the SigningPubKey field and computes the TxnSignature. + * + * @param publicKey The public key for signing + * @param secretKey The secret key for signing + * @return Reference to the derived builder for method chaining. + */ + Derived& + sign(PublicKey const& publicKey, SecretKey const& secretKey) + { + // Set the signing public key + object_.setFieldVL(sfSigningPubKey, publicKey.slice()); + + // Build the signing data: HashPrefix::txSign + serialized object + // (without signing fields) + Serializer s; + s.add32(HashPrefix::txSign); + object_.addWithoutSigningFields(s); + + // Sign and set the signature + auto const sig = xrpl::sign(publicKey, secretKey, s.slice()); + object_.setFieldVL(sfTxnSignature, sig); + + return static_cast(*this); + } + + STObject object_{sfTransaction}; +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/Utils.h b/include/xrpl/protocol_autogen/Utils.h new file mode 100644 index 0000000000..057c685209 --- /dev/null +++ b/include/xrpl/protocol_autogen/Utils.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace xrpl::protocol_autogen { + +template +using Optional = std::conditional_t< + std::is_reference_v, + std::optional>>, + std::optional>; + +} diff --git a/include/xrpl/protocol_autogen/ledger_entries/AMM.h b/include/xrpl/protocol_autogen/ledger_entries/AMM.h new file mode 100644 index 0000000000..f92f15eaa8 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/AMM.h @@ -0,0 +1,392 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AMMBuilder; + +/** + * @brief Ledger Entry: AMM + * + * Type: ltAMM (0x0079) + * RPC Name: amm + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AMMBuilder to construct new ledger entries. + */ +class AMM : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltAMM; + + /** + * @brief Construct a AMM ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit AMM(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for AMM"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfTradingFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTradingFee() const + { + if (hasTradingFee()) + return this->sle_->at(sfTradingFee); + return std::nullopt; + } + + /** + * @brief Check if sfTradingFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTradingFee() const + { + return this->sle_->isFieldPresent(sfTradingFee); + } + + /** + * @brief Get sfVoteSlots (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getVoteSlots() const + { + if (this->sle_->isFieldPresent(sfVoteSlots)) + return this->sle_->getFieldArray(sfVoteSlots); + return std::nullopt; + } + + /** + * @brief Check if sfVoteSlots is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasVoteSlots() const + { + return this->sle_->isFieldPresent(sfVoteSlots); + } + + /** + * @brief Get sfAuctionSlot (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional + getAuctionSlot() const + { + if (this->sle_->isFieldPresent(sfAuctionSlot)) + return this->sle_->getFieldObject(sfAuctionSlot); + return std::nullopt; + } + + /** + * @brief Check if sfAuctionSlot is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuctionSlot() const + { + return this->sle_->isFieldPresent(sfAuctionSlot); + } + + /** + * @brief Get sfLPTokenBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getLPTokenBalance() const + { + return this->sle_->at(sfLPTokenBalance); + } + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->sle_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->sle_->at(sfAsset2); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for AMM ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AMMBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AMMBuilder with required fields. + * @param account The sfAccount field value. + * @param lPTokenBalance The sfLPTokenBalance field value. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param ownerNode The sfOwnerNode field value. + */ + AMMBuilder(std::decay_t const& account,std::decay_t const& lPTokenBalance,std::decay_t const& asset,std::decay_t const& asset2,std::decay_t const& ownerNode) + : LedgerEntryBuilderBase(ltAMM) + { + setAccount(account); + setLPTokenBalance(lPTokenBalance); + setAsset(asset); + setAsset2(asset2); + setOwnerNode(ownerNode); + } + + /** + * @brief Construct a AMMBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AMMBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltAMM) + { + throw std::runtime_error("Invalid ledger entry type for AMM"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Set sfVoteSlots (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setVoteSlots(STArray const& value) + { + object_.setFieldArray(sfVoteSlots, value); + return *this; + } + + /** + * @brief Set sfAuctionSlot (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAuctionSlot(STObject const& value) + { + object_.setFieldObject(sfAuctionSlot, value); + return *this; + } + + /** + * @brief Set sfLPTokenBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setLPTokenBalance(std::decay_t const& value) + { + object_[sfLPTokenBalance] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed AMM wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + AMM + build(uint256 const& index) + { + return AMM{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h new file mode 100644 index 0000000000..15cf14b21a --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -0,0 +1,834 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AccountRootBuilder; + +/** + * @brief Ledger Entry: AccountRoot + * + * Type: ltACCOUNT_ROOT (0x0061) + * RPC Name: account + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AccountRootBuilder to construct new ledger entries. + */ +class AccountRoot : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltACCOUNT_ROOT; + + /** + * @brief Construct a AccountRoot ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit AccountRoot(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for AccountRoot"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfOwnerCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOwnerCount() const + { + return this->sle_->at(sfOwnerCount); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfAccountTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAccountTxnID() const + { + if (hasAccountTxnID()) + return this->sle_->at(sfAccountTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfAccountTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAccountTxnID() const + { + return this->sle_->isFieldPresent(sfAccountTxnID); + } + + /** + * @brief Get sfRegularKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRegularKey() const + { + if (hasRegularKey()) + return this->sle_->at(sfRegularKey); + return std::nullopt; + } + + /** + * @brief Check if sfRegularKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRegularKey() const + { + return this->sle_->isFieldPresent(sfRegularKey); + } + + /** + * @brief Get sfEmailHash (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEmailHash() const + { + if (hasEmailHash()) + return this->sle_->at(sfEmailHash); + return std::nullopt; + } + + /** + * @brief Check if sfEmailHash is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEmailHash() const + { + return this->sle_->isFieldPresent(sfEmailHash); + } + + /** + * @brief Get sfWalletLocator (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletLocator() const + { + if (hasWalletLocator()) + return this->sle_->at(sfWalletLocator); + return std::nullopt; + } + + /** + * @brief Check if sfWalletLocator is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletLocator() const + { + return this->sle_->isFieldPresent(sfWalletLocator); + } + + /** + * @brief Get sfWalletSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletSize() const + { + if (hasWalletSize()) + return this->sle_->at(sfWalletSize); + return std::nullopt; + } + + /** + * @brief Check if sfWalletSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletSize() const + { + return this->sle_->isFieldPresent(sfWalletSize); + } + + /** + * @brief Get sfMessageKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMessageKey() const + { + if (hasMessageKey()) + return this->sle_->at(sfMessageKey); + return std::nullopt; + } + + /** + * @brief Check if sfMessageKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMessageKey() const + { + return this->sle_->isFieldPresent(sfMessageKey); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + return this->sle_->at(sfTransferRate); + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->sle_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfDomain (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomain() const + { + if (hasDomain()) + return this->sle_->at(sfDomain); + return std::nullopt; + } + + /** + * @brief Check if sfDomain is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomain() const + { + return this->sle_->isFieldPresent(sfDomain); + } + + /** + * @brief Get sfTickSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTickSize() const + { + if (hasTickSize()) + return this->sle_->at(sfTickSize); + return std::nullopt; + } + + /** + * @brief Check if sfTickSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTickSize() const + { + return this->sle_->isFieldPresent(sfTickSize); + } + + /** + * @brief Get sfTicketCount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTicketCount() const + { + if (hasTicketCount()) + return this->sle_->at(sfTicketCount); + return std::nullopt; + } + + /** + * @brief Check if sfTicketCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTicketCount() const + { + return this->sle_->isFieldPresent(sfTicketCount); + } + + /** + * @brief Get sfNFTokenMinter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenMinter() const + { + if (hasNFTokenMinter()) + return this->sle_->at(sfNFTokenMinter); + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenMinter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenMinter() const + { + return this->sle_->isFieldPresent(sfNFTokenMinter); + } + + /** + * @brief Get sfMintedNFTokens (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMintedNFTokens() const + { + if (hasMintedNFTokens()) + return this->sle_->at(sfMintedNFTokens); + return std::nullopt; + } + + /** + * @brief Check if sfMintedNFTokens is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMintedNFTokens() const + { + return this->sle_->isFieldPresent(sfMintedNFTokens); + } + + /** + * @brief Get sfBurnedNFTokens (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBurnedNFTokens() const + { + if (hasBurnedNFTokens()) + return this->sle_->at(sfBurnedNFTokens); + return std::nullopt; + } + + /** + * @brief Check if sfBurnedNFTokens is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBurnedNFTokens() const + { + return this->sle_->isFieldPresent(sfBurnedNFTokens); + } + + /** + * @brief Get sfFirstNFTokenSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFirstNFTokenSequence() const + { + if (hasFirstNFTokenSequence()) + return this->sle_->at(sfFirstNFTokenSequence); + return std::nullopt; + } + + /** + * @brief Check if sfFirstNFTokenSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFirstNFTokenSequence() const + { + return this->sle_->isFieldPresent(sfFirstNFTokenSequence); + } + + /** + * @brief Get sfAMMID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAMMID() const + { + if (hasAMMID()) + return this->sle_->at(sfAMMID); + return std::nullopt; + } + + /** + * @brief Check if sfAMMID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAMMID() const + { + return this->sle_->isFieldPresent(sfAMMID); + } + + /** + * @brief Get sfVaultID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getVaultID() const + { + if (hasVaultID()) + return this->sle_->at(sfVaultID); + return std::nullopt; + } + + /** + * @brief Check if sfVaultID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasVaultID() const + { + return this->sle_->isFieldPresent(sfVaultID); + } + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + return this->sle_->at(sfLoanBrokerID); + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->sle_->isFieldPresent(sfLoanBrokerID); + } +}; + +/** + * @brief Builder for AccountRoot ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AccountRootBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AccountRootBuilder with required fields. + * @param account The sfAccount field value. + * @param sequence The sfSequence field value. + * @param balance The sfBalance field value. + * @param ownerCount The sfOwnerCount field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + AccountRootBuilder(std::decay_t const& account,std::decay_t const& sequence,std::decay_t const& balance,std::decay_t const& ownerCount,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltACCOUNT_ROOT) + { + setAccount(account); + setSequence(sequence); + setBalance(balance); + setOwnerCount(ownerCount); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a AccountRootBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AccountRootBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltACCOUNT_ROOT) + { + throw std::runtime_error("Invalid ledger entry type for AccountRoot"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfOwnerCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setOwnerCount(std::decay_t const& value) + { + object_[sfOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfAccountTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAccountTxnID(std::decay_t const& value) + { + object_[sfAccountTxnID] = value; + return *this; + } + + /** + * @brief Set sfRegularKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setRegularKey(std::decay_t const& value) + { + object_[sfRegularKey] = value; + return *this; + } + + /** + * @brief Set sfEmailHash (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setEmailHash(std::decay_t const& value) + { + object_[sfEmailHash] = value; + return *this; + } + + /** + * @brief Set sfWalletLocator (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setWalletLocator(std::decay_t const& value) + { + object_[sfWalletLocator] = value; + return *this; + } + + /** + * @brief Set sfWalletSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setWalletSize(std::decay_t const& value) + { + object_[sfWalletSize] = value; + return *this; + } + + /** + * @brief Set sfMessageKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setMessageKey(std::decay_t const& value) + { + object_[sfMessageKey] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfDomain (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setDomain(std::decay_t const& value) + { + object_[sfDomain] = value; + return *this; + } + + /** + * @brief Set sfTickSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTickSize(std::decay_t const& value) + { + object_[sfTickSize] = value; + return *this; + } + + /** + * @brief Set sfTicketCount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setTicketCount(std::decay_t const& value) + { + object_[sfTicketCount] = value; + return *this; + } + + /** + * @brief Set sfNFTokenMinter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setNFTokenMinter(std::decay_t const& value) + { + object_[sfNFTokenMinter] = value; + return *this; + } + + /** + * @brief Set sfMintedNFTokens (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setMintedNFTokens(std::decay_t const& value) + { + object_[sfMintedNFTokens] = value; + return *this; + } + + /** + * @brief Set sfBurnedNFTokens (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setBurnedNFTokens(std::decay_t const& value) + { + object_[sfBurnedNFTokens] = value; + return *this; + } + + /** + * @brief Set sfFirstNFTokenSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setFirstNFTokenSequence(std::decay_t const& value) + { + object_[sfFirstNFTokenSequence] = value; + return *this; + } + + /** + * @brief Set sfAMMID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setAMMID(std::decay_t const& value) + { + object_[sfAMMID] = value; + return *this; + } + + /** + * @brief Set sfVaultID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Build and return the completed AccountRoot wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + AccountRoot + build(uint256 const& index) + { + return AccountRoot{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h new file mode 100644 index 0000000000..2b51b83747 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h @@ -0,0 +1,236 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class AmendmentsBuilder; + +/** + * @brief Ledger Entry: Amendments + * + * Type: ltAMENDMENTS (0x0066) + * RPC Name: amendments + * + * Immutable wrapper around SLE providing type-safe field access. + * Use AmendmentsBuilder to construct new ledger entries. + */ +class Amendments : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltAMENDMENTS; + + /** + * @brief Construct a Amendments ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Amendments(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Amendments"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAmendments (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmendments() const + { + if (hasAmendments()) + return this->sle_->at(sfAmendments); + return std::nullopt; + } + + /** + * @brief Check if sfAmendments is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmendments() const + { + return this->sle_->isFieldPresent(sfAmendments); + } + + /** + * @brief Get sfMajorities (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getMajorities() const + { + if (this->sle_->isFieldPresent(sfMajorities)) + return this->sle_->getFieldArray(sfMajorities); + return std::nullopt; + } + + /** + * @brief Check if sfMajorities is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMajorities() const + { + return this->sle_->isFieldPresent(sfMajorities); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Amendments ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class AmendmentsBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new AmendmentsBuilder with required fields. + */ + AmendmentsBuilder() + : LedgerEntryBuilderBase(ltAMENDMENTS) + { + } + + /** + * @brief Construct a AmendmentsBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + AmendmentsBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltAMENDMENTS) + { + throw std::runtime_error("Invalid ledger entry type for Amendments"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAmendments (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setAmendments(std::decay_t const& value) + { + object_[sfAmendments] = value; + return *this; + } + + /** + * @brief Set sfMajorities (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setMajorities(STArray const& value) + { + object_.setFieldArray(sfMajorities, value); + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AmendmentsBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Amendments wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Amendments + build(uint256 const& index) + { + return Amendments{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h new file mode 100644 index 0000000000..3926ff65fa --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h @@ -0,0 +1,346 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class BridgeBuilder; + +/** + * @brief Ledger Entry: Bridge + * + * Type: ltBRIDGE (0x0069) + * RPC Name: bridge + * + * Immutable wrapper around SLE providing type-safe field access. + * Use BridgeBuilder to construct new ledger entries. + */ +class Bridge : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltBRIDGE; + + /** + * @brief Construct a Bridge ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Bridge(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Bridge"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->sle_->at(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + return this->sle_->at(sfMinAccountCreateAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->sle_->isFieldPresent(sfMinAccountCreateAmount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->sle_->at(sfXChainClaimID); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->sle_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfXChainAccountClaimCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountClaimCount() const + { + return this->sle_->at(sfXChainAccountClaimCount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Bridge ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class BridgeBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new BridgeBuilder with required fields. + * @param account The sfAccount field value. + * @param signatureReward The sfSignatureReward field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param xChainAccountClaimCount The sfXChainAccountClaimCount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + BridgeBuilder(std::decay_t const& account,std::decay_t const& signatureReward,std::decay_t const& xChainBridge,std::decay_t const& xChainClaimID,std::decay_t const& xChainAccountCreateCount,std::decay_t const& xChainAccountClaimCount,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltBRIDGE) + { + setAccount(account); + setSignatureReward(signatureReward); + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setXChainAccountCreateCount(xChainAccountCreateCount); + setXChainAccountClaimCount(xChainAccountClaimCount); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a BridgeBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + BridgeBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltBRIDGE) + { + throw std::runtime_error("Invalid ledger entry type for Bridge"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountClaimCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setXChainAccountClaimCount(std::decay_t const& value) + { + object_[sfXChainAccountClaimCount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BridgeBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Bridge wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Bridge + build(uint256 const& index) + { + return Bridge{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Check.h b/include/xrpl/protocol_autogen/ledger_entries/Check.h new file mode 100644 index 0000000000..c071186afb --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Check.h @@ -0,0 +1,427 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class CheckBuilder; + +/** + * @brief Ledger Entry: Check + * + * Type: ltCHECK (0x0043) + * RPC Name: check + * + * Immutable wrapper around SLE providing type-safe field access. + * Use CheckBuilder to construct new ledger entries. + */ +class Check : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltCHECK; + + /** + * @brief Construct a Check ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Check(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Check"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfSendMax (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSendMax() const + { + return this->sle_->at(sfSendMax); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfDestinationNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getDestinationNode() const + { + return this->sle_->at(sfDestinationNode); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + return this->sle_->at(sfInvoiceID); + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->sle_->isFieldPresent(sfInvoiceID); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->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->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Check ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class CheckBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new CheckBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param sendMax The sfSendMax field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param destinationNode The sfDestinationNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + CheckBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& sendMax,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& destinationNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltCHECK) + { + setAccount(account); + setDestination(destination); + setSendMax(sendMax); + setSequence(sequence); + setOwnerNode(ownerNode); + setDestinationNode(destinationNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a CheckBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + CheckBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltCHECK) + { + throw std::runtime_error("Invalid ledger entry type for Check"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Check wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Check + build(uint256 const& index) + { + return Check{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Credential.h b/include/xrpl/protocol_autogen/ledger_entries/Credential.h new file mode 100644 index 0000000000..4c45f80a8d --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Credential.h @@ -0,0 +1,344 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class CredentialBuilder; + +/** + * @brief Ledger Entry: Credential + * + * Type: ltCREDENTIAL (0x0081) + * RPC Name: credential + * + * Immutable wrapper around SLE providing type-safe field access. + * Use CredentialBuilder to construct new ledger entries. + */ +class Credential : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltCREDENTIAL; + + /** + * @brief Construct a Credential ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Credential(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Credential"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfSubject (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getSubject() const + { + return this->sle_->at(sfSubject); + } + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->sle_->at(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->sle_->at(sfCredentialType); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfIssuerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getIssuerNode() const + { + return this->sle_->at(sfIssuerNode); + } + + /** + * @brief Get sfSubjectNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSubjectNode() const + { + if (hasSubjectNode()) + return this->sle_->at(sfSubjectNode); + return std::nullopt; + } + + /** + * @brief Check if sfSubjectNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSubjectNode() const + { + return this->sle_->isFieldPresent(sfSubjectNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Credential ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class CredentialBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new CredentialBuilder with required fields. + * @param subject The sfSubject field value. + * @param issuer The sfIssuer field value. + * @param credentialType The sfCredentialType field value. + * @param issuerNode The sfIssuerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + CredentialBuilder(std::decay_t const& subject,std::decay_t const& issuer,std::decay_t const& credentialType,std::decay_t const& issuerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltCREDENTIAL) + { + setSubject(subject); + setIssuer(issuer); + setCredentialType(credentialType); + setIssuerNode(issuerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a CredentialBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + CredentialBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltCREDENTIAL) + { + throw std::runtime_error("Invalid ledger entry type for Credential"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfSubject (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfIssuerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setIssuerNode(std::decay_t const& value) + { + object_[sfIssuerNode] = value; + return *this; + } + + /** + * @brief Set sfSubjectNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setSubjectNode(std::decay_t const& value) + { + object_[sfSubjectNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Credential wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Credential + build(uint256 const& index) + { + return Credential{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DID.h b/include/xrpl/protocol_autogen/ledger_entries/DID.h new file mode 100644 index 0000000000..fc88e988df --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DID.h @@ -0,0 +1,296 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DIDBuilder; + +/** + * @brief Ledger Entry: DID + * + * Type: ltDID (0x0049) + * RPC Name: did + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DIDBuilder to construct new ledger entries. + */ +class DID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDID; + + /** + * @brief Construct a DID ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit DID(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDIDDocument (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDIDDocument() const + { + if (hasDIDDocument()) + return this->sle_->at(sfDIDDocument); + return std::nullopt; + } + + /** + * @brief Check if sfDIDDocument is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDIDDocument() const + { + return this->sle_->isFieldPresent(sfDIDDocument); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for DID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DIDBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DIDBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDID) + { + setAccount(account); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDID) + { + throw std::runtime_error("Invalid ledger entry type for DID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDIDDocument (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setDIDDocument(std::decay_t const& value) + { + object_[sfDIDDocument] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed DID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DID + build(uint256 const& index) + { + return DID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h new file mode 100644 index 0000000000..4d937eb99a --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DelegateBuilder; + +/** + * @brief Ledger Entry: Delegate + * + * Type: ltDELEGATE (0x0083) + * RPC Name: delegate + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DelegateBuilder to construct new ledger entries. + */ +class Delegate : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDELEGATE; + + /** + * @brief Construct a Delegate ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Delegate(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Delegate"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfAuthorize (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAuthorize() const + { + return this->sle_->at(sfAuthorize); + } + + /** + * @brief Get sfPermissions (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPermissions() const + { + return this->sle_->getFieldArray(sfPermissions); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Delegate ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DelegateBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DelegateBuilder with required fields. + * @param account The sfAccount field value. + * @param authorize The sfAuthorize field value. + * @param permissions The sfPermissions field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DelegateBuilder(std::decay_t const& account,std::decay_t const& authorize,STArray const& permissions,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDELEGATE) + { + setAccount(account); + setAuthorize(authorize); + setPermissions(permissions); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DelegateBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DelegateBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDELEGATE) + { + throw std::runtime_error("Invalid ledger entry type for Delegate"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfAuthorize (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfPermissions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPermissions(STArray const& value) + { + object_.setFieldArray(sfPermissions, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Delegate wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Delegate + build(uint256 const& index) + { + return Delegate{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h new file mode 100644 index 0000000000..533caba619 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h @@ -0,0 +1,262 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DepositPreauthBuilder; + +/** + * @brief Ledger Entry: DepositPreauth + * + * Type: ltDEPOSIT_PREAUTH (0x0070) + * RPC Name: deposit_preauth + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DepositPreauthBuilder to construct new ledger entries. + */ +class DepositPreauth : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDEPOSIT_PREAUTH; + + /** + * @brief Construct a DepositPreauth ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit DepositPreauth(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DepositPreauth"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfAuthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuthorize() const + { + if (hasAuthorize()) + return this->sle_->at(sfAuthorize); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorize() const + { + return this->sle_->isFieldPresent(sfAuthorize); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfAuthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthorizeCredentials() const + { + if (this->sle_->isFieldPresent(sfAuthorizeCredentials)) + return this->sle_->getFieldArray(sfAuthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorizeCredentials() const + { + return this->sle_->isFieldPresent(sfAuthorizeCredentials); + } +}; + +/** + * @brief Builder for DepositPreauth ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DepositPreauthBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DepositPreauthBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + DepositPreauthBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltDEPOSIT_PREAUTH) + { + setAccount(account); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a DepositPreauthBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DepositPreauthBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDEPOSIT_PREAUTH) + { + throw std::runtime_error("Invalid ledger entry type for DepositPreauth"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfAuthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfAuthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfAuthorizeCredentials, value); + return *this; + } + + /** + * @brief Build and return the completed DepositPreauth wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DepositPreauth + build(uint256 const& index) + { + return DepositPreauth{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h new file mode 100644 index 0000000000..545542ec6c --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h @@ -0,0 +1,563 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class DirectoryNodeBuilder; + +/** + * @brief Ledger Entry: DirectoryNode + * + * Type: ltDIR_NODE (0x0064) + * RPC Name: directory + * + * Immutable wrapper around SLE providing type-safe field access. + * Use DirectoryNodeBuilder to construct new ledger entries. + */ +class DirectoryNode : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltDIR_NODE; + + /** + * @brief Construct a DirectoryNode ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit DirectoryNode(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for DirectoryNode"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + return this->sle_->at(sfOwner); + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->sle_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfTakerPaysCurrency (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerPaysCurrency() const + { + if (hasTakerPaysCurrency()) + return this->sle_->at(sfTakerPaysCurrency); + return std::nullopt; + } + + /** + * @brief Check if sfTakerPaysCurrency is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerPaysCurrency() const + { + return this->sle_->isFieldPresent(sfTakerPaysCurrency); + } + + /** + * @brief Get sfTakerPaysIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerPaysIssuer() const + { + if (hasTakerPaysIssuer()) + return this->sle_->at(sfTakerPaysIssuer); + return std::nullopt; + } + + /** + * @brief Check if sfTakerPaysIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerPaysIssuer() const + { + return this->sle_->isFieldPresent(sfTakerPaysIssuer); + } + + /** + * @brief Get sfTakerGetsCurrency (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerGetsCurrency() const + { + if (hasTakerGetsCurrency()) + return this->sle_->at(sfTakerGetsCurrency); + return std::nullopt; + } + + /** + * @brief Check if sfTakerGetsCurrency is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerGetsCurrency() const + { + return this->sle_->isFieldPresent(sfTakerGetsCurrency); + } + + /** + * @brief Get sfTakerGetsIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTakerGetsIssuer() const + { + if (hasTakerGetsIssuer()) + return this->sle_->at(sfTakerGetsIssuer); + return std::nullopt; + } + + /** + * @brief Check if sfTakerGetsIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTakerGetsIssuer() const + { + return this->sle_->isFieldPresent(sfTakerGetsIssuer); + } + + /** + * @brief Get sfExchangeRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExchangeRate() const + { + if (hasExchangeRate()) + return this->sle_->at(sfExchangeRate); + return std::nullopt; + } + + /** + * @brief Check if sfExchangeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExchangeRate() const + { + return this->sle_->isFieldPresent(sfExchangeRate); + } + + /** + * @brief Get sfIndexes (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getIndexes() const + { + return this->sle_->at(sfIndexes); + } + + /** + * @brief Get sfRootIndex (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getRootIndex() const + { + return this->sle_->at(sfRootIndex); + } + + /** + * @brief Get sfIndexNext (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIndexNext() const + { + if (hasIndexNext()) + return this->sle_->at(sfIndexNext); + return std::nullopt; + } + + /** + * @brief Check if sfIndexNext is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIndexNext() const + { + return this->sle_->isFieldPresent(sfIndexNext); + } + + /** + * @brief Get sfIndexPrevious (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIndexPrevious() const + { + if (hasIndexPrevious()) + return this->sle_->at(sfIndexPrevious); + return std::nullopt; + } + + /** + * @brief Check if sfIndexPrevious is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIndexPrevious() const + { + return this->sle_->isFieldPresent(sfIndexPrevious); + } + + /** + * @brief Get sfNFTokenID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenID() const + { + if (hasNFTokenID()) + return this->sle_->at(sfNFTokenID); + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenID() const + { + return this->sle_->isFieldPresent(sfNFTokenID); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for DirectoryNode ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class DirectoryNodeBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new DirectoryNodeBuilder with required fields. + * @param indexes The sfIndexes field value. + * @param rootIndex The sfRootIndex field value. + */ + DirectoryNodeBuilder(std::decay_t const& indexes,std::decay_t const& rootIndex) + : LedgerEntryBuilderBase(ltDIR_NODE) + { + setIndexes(indexes); + setRootIndex(rootIndex); + } + + /** + * @brief Construct a DirectoryNodeBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + DirectoryNodeBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltDIR_NODE) + { + throw std::runtime_error("Invalid ledger entry type for DirectoryNode"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfTakerPaysCurrency (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerPaysCurrency(std::decay_t const& value) + { + object_[sfTakerPaysCurrency] = value; + return *this; + } + + /** + * @brief Set sfTakerPaysIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerPaysIssuer(std::decay_t const& value) + { + object_[sfTakerPaysIssuer] = value; + return *this; + } + + /** + * @brief Set sfTakerGetsCurrency (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerGetsCurrency(std::decay_t const& value) + { + object_[sfTakerGetsCurrency] = value; + return *this; + } + + /** + * @brief Set sfTakerGetsIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setTakerGetsIssuer(std::decay_t const& value) + { + object_[sfTakerGetsIssuer] = value; + return *this; + } + + /** + * @brief Set sfExchangeRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setExchangeRate(std::decay_t const& value) + { + object_[sfExchangeRate] = value; + return *this; + } + + /** + * @brief Set sfIndexes (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexes(std::decay_t const& value) + { + object_[sfIndexes] = value; + return *this; + } + + /** + * @brief Set sfRootIndex (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setRootIndex(std::decay_t const& value) + { + object_[sfRootIndex] = value; + return *this; + } + + /** + * @brief Set sfIndexNext (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexNext(std::decay_t const& value) + { + object_[sfIndexNext] = value; + return *this; + } + + /** + * @brief Set sfIndexPrevious (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setIndexPrevious(std::decay_t const& value) + { + object_[sfIndexPrevious] = value; + return *this; + } + + /** + * @brief Set sfNFTokenID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DirectoryNodeBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the completed DirectoryNode wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + DirectoryNode + build(uint256 const& index) + { + return DirectoryNode{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h new file mode 100644 index 0000000000..fd15b2a713 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h @@ -0,0 +1,554 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class EscrowBuilder; + +/** + * @brief Ledger Entry: Escrow + * + * Type: ltESCROW (0x0075) + * RPC Name: escrow + * + * Immutable wrapper around SLE providing type-safe field access. + * Use EscrowBuilder to construct new ledger entries. + */ +class Escrow : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltESCROW; + + /** + * @brief Construct a Escrow ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Escrow(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Escrow"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSequence() const + { + if (hasSequence()) + return this->sle_->at(sfSequence); + return std::nullopt; + } + + /** + * @brief Check if sfSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSequence() const + { + return this->sle_->isFieldPresent(sfSequence); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + return this->sle_->at(sfCondition); + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->sle_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + return this->sle_->at(sfCancelAfter); + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->sle_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfFinishAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFinishAfter() const + { + if (hasFinishAfter()) + return this->sle_->at(sfFinishAfter); + return std::nullopt; + } + + /** + * @brief Check if sfFinishAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFinishAfter() const + { + return this->sle_->isFieldPresent(sfFinishAfter); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->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->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDestinationNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationNode() const + { + if (hasDestinationNode()) + return this->sle_->at(sfDestinationNode); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationNode() const + { + return this->sle_->isFieldPresent(sfDestinationNode); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + return this->sle_->at(sfTransferRate); + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->sle_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfIssuerNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuerNode() const + { + if (hasIssuerNode()) + return this->sle_->at(sfIssuerNode); + return std::nullopt; + } + + /** + * @brief Check if sfIssuerNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuerNode() const + { + return this->sle_->isFieldPresent(sfIssuerNode); + } +}; + +/** + * @brief Builder for Escrow ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class EscrowBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new EscrowBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + EscrowBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& amount,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltESCROW) + { + setAccount(account); + setDestination(destination); + setAmount(amount); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a EscrowBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + EscrowBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltESCROW) + { + throw std::runtime_error("Invalid ledger entry type for Escrow"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfFinishAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setFinishAfter(std::decay_t const& value) + { + object_[sfFinishAfter] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfIssuerNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowBuilder& + setIssuerNode(std::decay_t const& value) + { + object_[sfIssuerNode] = value; + return *this; + } + + /** + * @brief Build and return the completed Escrow wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Escrow + build(uint256 const& index) + { + return Escrow{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h new file mode 100644 index 0000000000..e3c9ca0091 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h @@ -0,0 +1,410 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class FeeSettingsBuilder; + +/** + * @brief Ledger Entry: FeeSettings + * + * Type: ltFEE_SETTINGS (0x0073) + * RPC Name: fee + * + * Immutable wrapper around SLE providing type-safe field access. + * Use FeeSettingsBuilder to construct new ledger entries. + */ +class FeeSettings : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltFEE_SETTINGS; + + /** + * @brief Construct a FeeSettings ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit FeeSettings(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for FeeSettings"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfBaseFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFee() const + { + if (hasBaseFee()) + return this->sle_->at(sfBaseFee); + return std::nullopt; + } + + /** + * @brief Check if sfBaseFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFee() const + { + return this->sle_->isFieldPresent(sfBaseFee); + } + + /** + * @brief Get sfReferenceFeeUnits (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReferenceFeeUnits() const + { + if (hasReferenceFeeUnits()) + return this->sle_->at(sfReferenceFeeUnits); + return std::nullopt; + } + + /** + * @brief Check if sfReferenceFeeUnits is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReferenceFeeUnits() const + { + return this->sle_->isFieldPresent(sfReferenceFeeUnits); + } + + /** + * @brief Get sfReserveBase (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBase() const + { + if (hasReserveBase()) + return this->sle_->at(sfReserveBase); + return std::nullopt; + } + + /** + * @brief Check if sfReserveBase is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBase() const + { + return this->sle_->isFieldPresent(sfReserveBase); + } + + /** + * @brief Get sfReserveIncrement (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrement() const + { + if (hasReserveIncrement()) + return this->sle_->at(sfReserveIncrement); + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrement is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrement() const + { + return this->sle_->isFieldPresent(sfReserveIncrement); + } + + /** + * @brief Get sfBaseFeeDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFeeDrops() const + { + if (hasBaseFeeDrops()) + return this->sle_->at(sfBaseFeeDrops); + return std::nullopt; + } + + /** + * @brief Check if sfBaseFeeDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFeeDrops() const + { + return this->sle_->isFieldPresent(sfBaseFeeDrops); + } + + /** + * @brief Get sfReserveBaseDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBaseDrops() const + { + if (hasReserveBaseDrops()) + return this->sle_->at(sfReserveBaseDrops); + return std::nullopt; + } + + /** + * @brief Check if sfReserveBaseDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBaseDrops() const + { + return this->sle_->isFieldPresent(sfReserveBaseDrops); + } + + /** + * @brief Get sfReserveIncrementDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrementDrops() const + { + if (hasReserveIncrementDrops()) + return this->sle_->at(sfReserveIncrementDrops); + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrementDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrementDrops() const + { + return this->sle_->isFieldPresent(sfReserveIncrementDrops); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for FeeSettings ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class FeeSettingsBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new FeeSettingsBuilder with required fields. + */ + FeeSettingsBuilder() + : LedgerEntryBuilderBase(ltFEE_SETTINGS) + { + } + + /** + * @brief Construct a FeeSettingsBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + FeeSettingsBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltFEE_SETTINGS) + { + throw std::runtime_error("Invalid ledger entry type for FeeSettings"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfBaseFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setBaseFee(std::decay_t const& value) + { + object_[sfBaseFee] = value; + return *this; + } + + /** + * @brief Set sfReferenceFeeUnits (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReferenceFeeUnits(std::decay_t const& value) + { + object_[sfReferenceFeeUnits] = value; + return *this; + } + + /** + * @brief Set sfReserveBase (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveBase(std::decay_t const& value) + { + object_[sfReserveBase] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrement (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveIncrement(std::decay_t const& value) + { + object_[sfReserveIncrement] = value; + return *this; + } + + /** + * @brief Set sfBaseFeeDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setBaseFeeDrops(std::decay_t const& value) + { + object_[sfBaseFeeDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveBaseDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveBaseDrops(std::decay_t const& value) + { + object_[sfReserveBaseDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrementDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setReserveIncrementDrops(std::decay_t const& value) + { + object_[sfReserveIncrementDrops] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + FeeSettingsBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed FeeSettings wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + FeeSettings + build(uint256 const& index) + { + return FeeSettings{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h new file mode 100644 index 0000000000..c95ca303ba --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h @@ -0,0 +1,189 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LedgerHashesBuilder; + +/** + * @brief Ledger Entry: LedgerHashes + * + * Type: ltLEDGER_HASHES (0x0068) + * RPC Name: hashes + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LedgerHashesBuilder to construct new ledger entries. + */ +class LedgerHashes : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLEDGER_HASHES; + + /** + * @brief Construct a LedgerHashes ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit LedgerHashes(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for LedgerHashes"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfFirstLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFirstLedgerSequence() const + { + if (hasFirstLedgerSequence()) + return this->sle_->at(sfFirstLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if sfFirstLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFirstLedgerSequence() const + { + return this->sle_->isFieldPresent(sfFirstLedgerSequence); + } + + /** + * @brief Get sfLastLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLastLedgerSequence() const + { + if (hasLastLedgerSequence()) + return this->sle_->at(sfLastLedgerSequence); + return std::nullopt; + } + + /** + * @brief Check if sfLastLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLastLedgerSequence() const + { + return this->sle_->isFieldPresent(sfLastLedgerSequence); + } + + /** + * @brief Get sfHashes (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getHashes() const + { + return this->sle_->at(sfHashes); + } +}; + +/** + * @brief Builder for LedgerHashes ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LedgerHashesBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LedgerHashesBuilder with required fields. + * @param hashes The sfHashes field value. + */ + LedgerHashesBuilder(std::decay_t const& hashes) + : LedgerEntryBuilderBase(ltLEDGER_HASHES) + { + setHashes(hashes); + } + + /** + * @brief Construct a LedgerHashesBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LedgerHashesBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLEDGER_HASHES) + { + throw std::runtime_error("Invalid ledger entry type for LedgerHashes"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfFirstLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setFirstLedgerSequence(std::decay_t const& value) + { + object_[sfFirstLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfLastLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setLastLedgerSequence(std::decay_t const& value) + { + object_[sfLastLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfHashes (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LedgerHashesBuilder& + setHashes(std::decay_t const& value) + { + object_[sfHashes] = value; + return *this; + } + + /** + * @brief Build and return the completed LedgerHashes wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + LedgerHashes + build(uint256 const& index) + { + return LedgerHashes{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Loan.h b/include/xrpl/protocol_autogen/ledger_entries/Loan.h new file mode 100644 index 0000000000..73b37ea892 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Loan.h @@ -0,0 +1,930 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LoanBuilder; + +/** + * @brief Ledger Entry: Loan + * + * Type: ltLOAN (0x0089) + * RPC Name: loan + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LoanBuilder to construct new ledger entries. + */ +class Loan : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLOAN; + + /** + * @brief Construct a Loan ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Loan(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Loan"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfLoanBrokerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getLoanBrokerNode() const + { + return this->sle_->at(sfLoanBrokerNode); + } + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->sle_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfLoanSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLoanSequence() const + { + return this->sle_->at(sfLoanSequence); + } + + /** + * @brief Get sfBorrower (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getBorrower() const + { + return this->sle_->at(sfBorrower); + } + + /** + * @brief Get sfLoanOriginationFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanOriginationFee() const + { + if (hasLoanOriginationFee()) + return this->sle_->at(sfLoanOriginationFee); + return std::nullopt; + } + + /** + * @brief Check if sfLoanOriginationFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanOriginationFee() const + { + return this->sle_->isFieldPresent(sfLoanOriginationFee); + } + + /** + * @brief Get sfLoanServiceFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanServiceFee() const + { + if (hasLoanServiceFee()) + return this->sle_->at(sfLoanServiceFee); + return std::nullopt; + } + + /** + * @brief Check if sfLoanServiceFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanServiceFee() const + { + return this->sle_->isFieldPresent(sfLoanServiceFee); + } + + /** + * @brief Get sfLatePaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLatePaymentFee() const + { + if (hasLatePaymentFee()) + return this->sle_->at(sfLatePaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfLatePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLatePaymentFee() const + { + return this->sle_->isFieldPresent(sfLatePaymentFee); + } + + /** + * @brief Get sfClosePaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClosePaymentFee() const + { + if (hasClosePaymentFee()) + return this->sle_->at(sfClosePaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfClosePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClosePaymentFee() const + { + return this->sle_->isFieldPresent(sfClosePaymentFee); + } + + /** + * @brief Get sfOverpaymentFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentFee() const + { + if (hasOverpaymentFee()) + return this->sle_->at(sfOverpaymentFee); + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentFee() const + { + return this->sle_->isFieldPresent(sfOverpaymentFee); + } + + /** + * @brief Get sfInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInterestRate() const + { + if (hasInterestRate()) + return this->sle_->at(sfInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInterestRate() const + { + return this->sle_->isFieldPresent(sfInterestRate); + } + + /** + * @brief Get sfLateInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLateInterestRate() const + { + if (hasLateInterestRate()) + return this->sle_->at(sfLateInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfLateInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLateInterestRate() const + { + return this->sle_->isFieldPresent(sfLateInterestRate); + } + + /** + * @brief Get sfCloseInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCloseInterestRate() const + { + if (hasCloseInterestRate()) + return this->sle_->at(sfCloseInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfCloseInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCloseInterestRate() const + { + return this->sle_->isFieldPresent(sfCloseInterestRate); + } + + /** + * @brief Get sfOverpaymentInterestRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentInterestRate() const + { + if (hasOverpaymentInterestRate()) + return this->sle_->at(sfOverpaymentInterestRate); + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentInterestRate() const + { + return this->sle_->isFieldPresent(sfOverpaymentInterestRate); + } + + /** + * @brief Get sfStartDate (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getStartDate() const + { + return this->sle_->at(sfStartDate); + } + + /** + * @brief Get sfPaymentInterval (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPaymentInterval() const + { + return this->sle_->at(sfPaymentInterval); + } + + /** + * @brief Get sfGracePeriod (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getGracePeriod() const + { + if (hasGracePeriod()) + return this->sle_->at(sfGracePeriod); + return std::nullopt; + } + + /** + * @brief Check if sfGracePeriod is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasGracePeriod() const + { + return this->sle_->isFieldPresent(sfGracePeriod); + } + + /** + * @brief Get sfPreviousPaymentDueDate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousPaymentDueDate() const + { + if (hasPreviousPaymentDueDate()) + return this->sle_->at(sfPreviousPaymentDueDate); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousPaymentDueDate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousPaymentDueDate() const + { + return this->sle_->isFieldPresent(sfPreviousPaymentDueDate); + } + + /** + * @brief Get sfNextPaymentDueDate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNextPaymentDueDate() const + { + if (hasNextPaymentDueDate()) + return this->sle_->at(sfNextPaymentDueDate); + return std::nullopt; + } + + /** + * @brief Check if sfNextPaymentDueDate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNextPaymentDueDate() const + { + return this->sle_->isFieldPresent(sfNextPaymentDueDate); + } + + /** + * @brief Get sfPaymentRemaining (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentRemaining() const + { + if (hasPaymentRemaining()) + return this->sle_->at(sfPaymentRemaining); + return std::nullopt; + } + + /** + * @brief Check if sfPaymentRemaining is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentRemaining() const + { + return this->sle_->isFieldPresent(sfPaymentRemaining); + } + + /** + * @brief Get sfPeriodicPayment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_NUMBER::type::value_type + getPeriodicPayment() const + { + return this->sle_->at(sfPeriodicPayment); + } + + /** + * @brief Get sfPrincipalOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPrincipalOutstanding() const + { + if (hasPrincipalOutstanding()) + return this->sle_->at(sfPrincipalOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfPrincipalOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPrincipalOutstanding() const + { + return this->sle_->isFieldPresent(sfPrincipalOutstanding); + } + + /** + * @brief Get sfTotalValueOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTotalValueOutstanding() const + { + if (hasTotalValueOutstanding()) + return this->sle_->at(sfTotalValueOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfTotalValueOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTotalValueOutstanding() const + { + return this->sle_->isFieldPresent(sfTotalValueOutstanding); + } + + /** + * @brief Get sfManagementFeeOutstanding (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeOutstanding() const + { + if (hasManagementFeeOutstanding()) + return this->sle_->at(sfManagementFeeOutstanding); + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeOutstanding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeOutstanding() const + { + return this->sle_->isFieldPresent(sfManagementFeeOutstanding); + } + + /** + * @brief Get sfLoanScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanScale() const + { + if (hasLoanScale()) + return this->sle_->at(sfLoanScale); + return std::nullopt; + } + + /** + * @brief Check if sfLoanScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanScale() const + { + return this->sle_->isFieldPresent(sfLoanScale); + } +}; + +/** + * @brief Builder for Loan ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LoanBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LoanBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param ownerNode The sfOwnerNode field value. + * @param loanBrokerNode The sfLoanBrokerNode field value. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param loanSequence The sfLoanSequence field value. + * @param borrower The sfBorrower field value. + * @param startDate The sfStartDate field value. + * @param paymentInterval The sfPaymentInterval field value. + * @param periodicPayment The sfPeriodicPayment field value. + */ + LoanBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& ownerNode,std::decay_t const& loanBrokerNode,std::decay_t const& loanBrokerID,std::decay_t const& loanSequence,std::decay_t const& borrower,std::decay_t const& startDate,std::decay_t const& paymentInterval,std::decay_t const& periodicPayment) + : LedgerEntryBuilderBase(ltLOAN) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setOwnerNode(ownerNode); + setLoanBrokerNode(loanBrokerNode); + setLoanBrokerID(loanBrokerID); + setLoanSequence(loanSequence); + setBorrower(borrower); + setStartDate(startDate); + setPaymentInterval(paymentInterval); + setPeriodicPayment(periodicPayment); + } + + /** + * @brief Construct a LoanBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LoanBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLOAN) + { + throw std::runtime_error("Invalid ledger entry type for Loan"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanBrokerNode(std::decay_t const& value) + { + object_[sfLoanBrokerNode] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfLoanSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanSequence(std::decay_t const& value) + { + object_[sfLoanSequence] = value; + return *this; + } + + /** + * @brief Set sfBorrower (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setBorrower(std::decay_t const& value) + { + object_[sfBorrower] = value; + return *this; + } + + /** + * @brief Set sfLoanOriginationFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanOriginationFee(std::decay_t const& value) + { + object_[sfLoanOriginationFee] = value; + return *this; + } + + /** + * @brief Set sfLoanServiceFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanServiceFee(std::decay_t const& value) + { + object_[sfLoanServiceFee] = value; + return *this; + } + + /** + * @brief Set sfLatePaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLatePaymentFee(std::decay_t const& value) + { + object_[sfLatePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfClosePaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setClosePaymentFee(std::decay_t const& value) + { + object_[sfClosePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOverpaymentFee(std::decay_t const& value) + { + object_[sfOverpaymentFee] = value; + return *this; + } + + /** + * @brief Set sfInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setInterestRate(std::decay_t const& value) + { + object_[sfInterestRate] = value; + return *this; + } + + /** + * @brief Set sfLateInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLateInterestRate(std::decay_t const& value) + { + object_[sfLateInterestRate] = value; + return *this; + } + + /** + * @brief Set sfCloseInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setCloseInterestRate(std::decay_t const& value) + { + object_[sfCloseInterestRate] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentInterestRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setOverpaymentInterestRate(std::decay_t const& value) + { + object_[sfOverpaymentInterestRate] = value; + return *this; + } + + /** + * @brief Set sfStartDate (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setStartDate(std::decay_t const& value) + { + object_[sfStartDate] = value; + return *this; + } + + /** + * @brief Set sfPaymentInterval (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPaymentInterval(std::decay_t const& value) + { + object_[sfPaymentInterval] = value; + return *this; + } + + /** + * @brief Set sfGracePeriod (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setGracePeriod(std::decay_t const& value) + { + object_[sfGracePeriod] = value; + return *this; + } + + /** + * @brief Set sfPreviousPaymentDueDate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPreviousPaymentDueDate(std::decay_t const& value) + { + object_[sfPreviousPaymentDueDate] = value; + return *this; + } + + /** + * @brief Set sfNextPaymentDueDate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setNextPaymentDueDate(std::decay_t const& value) + { + object_[sfNextPaymentDueDate] = value; + return *this; + } + + /** + * @brief Set sfPaymentRemaining (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPaymentRemaining(std::decay_t const& value) + { + object_[sfPaymentRemaining] = value; + return *this; + } + + /** + * @brief Set sfPeriodicPayment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPeriodicPayment(std::decay_t const& value) + { + object_[sfPeriodicPayment] = value; + return *this; + } + + /** + * @brief Set sfPrincipalOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setPrincipalOutstanding(std::decay_t const& value) + { + object_[sfPrincipalOutstanding] = value; + return *this; + } + + /** + * @brief Set sfTotalValueOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setTotalValueOutstanding(std::decay_t const& value) + { + object_[sfTotalValueOutstanding] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeOutstanding (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setManagementFeeOutstanding(std::decay_t const& value) + { + object_[sfManagementFeeOutstanding] = value; + return *this; + } + + /** + * @brief Set sfLoanScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBuilder& + setLoanScale(std::decay_t const& value) + { + object_[sfLoanScale] = value; + return *this; + } + + /** + * @brief Build and return the completed Loan wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Loan + build(uint256 const& index) + { + return Loan{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h new file mode 100644 index 0000000000..99083d6fce --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h @@ -0,0 +1,591 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class LoanBrokerBuilder; + +/** + * @brief Ledger Entry: LoanBroker + * + * Type: ltLOAN_BROKER (0x0088) + * RPC Name: loan_broker + * + * Immutable wrapper around SLE providing type-safe field access. + * Use LoanBrokerBuilder to construct new ledger entries. + */ +class LoanBroker : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltLOAN_BROKER; + + /** + * @brief Construct a LoanBroker ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit LoanBroker(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for LoanBroker"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfVaultNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getVaultNode() const + { + return this->sle_->at(sfVaultNode); + } + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->sle_->at(sfVaultID); + } + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfLoanSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLoanSequence() const + { + return this->sle_->at(sfLoanSequence); + } + + /** + * @brief Get sfData (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfManagementFeeRate (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeRate() const + { + if (hasManagementFeeRate()) + return this->sle_->at(sfManagementFeeRate); + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeRate() const + { + return this->sle_->isFieldPresent(sfManagementFeeRate); + } + + /** + * @brief Get sfOwnerCount (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwnerCount() const + { + if (hasOwnerCount()) + return this->sle_->at(sfOwnerCount); + return std::nullopt; + } + + /** + * @brief Check if sfOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwnerCount() const + { + return this->sle_->isFieldPresent(sfOwnerCount); + } + + /** + * @brief Get sfDebtTotal (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtTotal() const + { + if (hasDebtTotal()) + return this->sle_->at(sfDebtTotal); + return std::nullopt; + } + + /** + * @brief Check if sfDebtTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtTotal() const + { + return this->sle_->isFieldPresent(sfDebtTotal); + } + + /** + * @brief Get sfDebtMaximum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtMaximum() const + { + if (hasDebtMaximum()) + return this->sle_->at(sfDebtMaximum); + return std::nullopt; + } + + /** + * @brief Check if sfDebtMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtMaximum() const + { + return this->sle_->isFieldPresent(sfDebtMaximum); + } + + /** + * @brief Get sfCoverAvailable (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverAvailable() const + { + if (hasCoverAvailable()) + return this->sle_->at(sfCoverAvailable); + return std::nullopt; + } + + /** + * @brief Check if sfCoverAvailable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverAvailable() const + { + return this->sle_->isFieldPresent(sfCoverAvailable); + } + + /** + * @brief Get sfCoverRateMinimum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateMinimum() const + { + if (hasCoverRateMinimum()) + return this->sle_->at(sfCoverRateMinimum); + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateMinimum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateMinimum() const + { + return this->sle_->isFieldPresent(sfCoverRateMinimum); + } + + /** + * @brief Get sfCoverRateLiquidation (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateLiquidation() const + { + if (hasCoverRateLiquidation()) + return this->sle_->at(sfCoverRateLiquidation); + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateLiquidation is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateLiquidation() const + { + return this->sle_->isFieldPresent(sfCoverRateLiquidation); + } +}; + +/** + * @brief Builder for LoanBroker ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class LoanBrokerBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param vaultNode The sfVaultNode field value. + * @param vaultID The sfVaultID field value. + * @param account The sfAccount field value. + * @param owner The sfOwner field value. + * @param loanSequence The sfLoanSequence field value. + */ + LoanBrokerBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& vaultNode,std::decay_t const& vaultID,std::decay_t const& account,std::decay_t const& owner,std::decay_t const& loanSequence) + : LedgerEntryBuilderBase(ltLOAN_BROKER) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setSequence(sequence); + setOwnerNode(ownerNode); + setVaultNode(vaultNode); + setVaultID(vaultID); + setAccount(account); + setOwner(owner); + setLoanSequence(loanSequence); + } + + /** + * @brief Construct a LoanBrokerBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + LoanBrokerBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltLOAN_BROKER) + { + throw std::runtime_error("Invalid ledger entry type for LoanBroker"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfVaultNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setVaultNode(std::decay_t const& value) + { + object_[sfVaultNode] = value; + return *this; + } + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfLoanSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setLoanSequence(std::decay_t const& value) + { + object_[sfLoanSequence] = value; + return *this; + } + + /** + * @brief Set sfData (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeRate (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setManagementFeeRate(std::decay_t const& value) + { + object_[sfManagementFeeRate] = value; + return *this; + } + + /** + * @brief Set sfOwnerCount (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setOwnerCount(std::decay_t const& value) + { + object_[sfOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfDebtTotal (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setDebtTotal(std::decay_t const& value) + { + object_[sfDebtTotal] = value; + return *this; + } + + /** + * @brief Set sfDebtMaximum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setDebtMaximum(std::decay_t const& value) + { + object_[sfDebtMaximum] = value; + return *this; + } + + /** + * @brief Set sfCoverAvailable (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverAvailable(std::decay_t const& value) + { + object_[sfCoverAvailable] = value; + return *this; + } + + /** + * @brief Set sfCoverRateMinimum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverRateMinimum(std::decay_t const& value) + { + object_[sfCoverRateMinimum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateLiquidation (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + LoanBrokerBuilder& + setCoverRateLiquidation(std::decay_t const& value) + { + object_[sfCoverRateLiquidation] = value; + return *this; + } + + /** + * @brief Build and return the completed LoanBroker wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + LoanBroker + build(uint256 const& index) + { + return LoanBroker{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h new file mode 100644 index 0000000000..d11a8effd3 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h @@ -0,0 +1,285 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class MPTokenBuilder; + +/** + * @brief Ledger Entry: MPToken + * + * Type: ltMPTOKEN (0x007f) + * RPC Name: mptoken + * + * Immutable wrapper around SLE providing type-safe field access. + * Use MPTokenBuilder to construct new ledger entries. + */ +class MPToken : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltMPTOKEN; + + /** + * @brief Construct a MPToken ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit MPToken(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for MPToken"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->sle_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfMPTAmount (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTAmount() const + { + if (hasMPTAmount()) + return this->sle_->at(sfMPTAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMPTAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTAmount() const + { + return this->sle_->isFieldPresent(sfMPTAmount); + } + + /** + * @brief Get sfLockedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLockedAmount() const + { + if (hasLockedAmount()) + return this->sle_->at(sfLockedAmount); + return std::nullopt; + } + + /** + * @brief Check if sfLockedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLockedAmount() const + { + return this->sle_->isFieldPresent(sfLockedAmount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for MPToken ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class MPTokenBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenBuilder with required fields. + * @param account The sfAccount field value. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + MPTokenBuilder(std::decay_t const& account,std::decay_t const& mPTokenIssuanceID,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltMPTOKEN) + { + setAccount(account); + setMPTokenIssuanceID(mPTokenIssuanceID); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a MPTokenBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + MPTokenBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltMPTOKEN) + { + throw std::runtime_error("Invalid ledger entry type for MPToken"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfMPTAmount (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setMPTAmount(std::decay_t const& value) + { + object_[sfMPTAmount] = value; + return *this; + } + + /** + * @brief Set sfLockedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setLockedAmount(std::decay_t const& value) + { + object_[sfLockedAmount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed MPToken wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + MPToken + build(uint256 const& index) + { + return MPToken{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h new file mode 100644 index 0000000000..23b8a05015 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -0,0 +1,484 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class MPTokenIssuanceBuilder; + +/** + * @brief Ledger Entry: MPTokenIssuance + * + * Type: ltMPTOKEN_ISSUANCE (0x007e) + * RPC Name: mpt_issuance + * + * Immutable wrapper around SLE providing type-safe field access. + * Use MPTokenIssuanceBuilder to construct new ledger entries. + */ +class MPTokenIssuance : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltMPTOKEN_ISSUANCE; + + /** + * @brief Construct a MPTokenIssuance ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit MPTokenIssuance(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->sle_->at(sfIssuer); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfTransferFee (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + return this->sle_->at(sfTransferFee); + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->sle_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfAssetScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetScale() const + { + if (hasAssetScale()) + return this->sle_->at(sfAssetScale); + return std::nullopt; + } + + /** + * @brief Check if sfAssetScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetScale() const + { + return this->sle_->isFieldPresent(sfAssetScale); + } + + /** + * @brief Get sfMaximumAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaximumAmount() const + { + if (hasMaximumAmount()) + return this->sle_->at(sfMaximumAmount); + return std::nullopt; + } + + /** + * @brief Check if sfMaximumAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaximumAmount() const + { + return this->sle_->isFieldPresent(sfMaximumAmount); + } + + /** + * @brief Get sfOutstandingAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOutstandingAmount() const + { + return this->sle_->at(sfOutstandingAmount); + } + + /** + * @brief Get sfLockedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLockedAmount() const + { + if (hasLockedAmount()) + return this->sle_->at(sfLockedAmount); + return std::nullopt; + } + + /** + * @brief Check if sfLockedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLockedAmount() const + { + return this->sle_->isFieldPresent(sfLockedAmount); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + return this->sle_->at(sfMPTokenMetadata); + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->sle_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMutableFlags (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + return this->sle_->at(sfMutableFlags); + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->sle_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuance ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class MPTokenIssuanceBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceBuilder with required fields. + * @param issuer The sfIssuer field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param outstandingAmount The sfOutstandingAmount field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + MPTokenIssuanceBuilder(std::decay_t const& issuer,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& outstandingAmount,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltMPTOKEN_ISSUANCE) + { + setIssuer(issuer); + setSequence(sequence); + setOwnerNode(ownerNode); + setOutstandingAmount(outstandingAmount); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a MPTokenIssuanceBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + MPTokenIssuanceBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltMPTOKEN_ISSUANCE) + { + throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfAssetScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setAssetScale(std::decay_t const& value) + { + object_[sfAssetScale] = value; + return *this; + } + + /** + * @brief Set sfMaximumAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMaximumAmount(std::decay_t const& value) + { + object_[sfMaximumAmount] = value; + return *this; + } + + /** + * @brief Set sfOutstandingAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setOutstandingAmount(std::decay_t const& value) + { + object_[sfOutstandingAmount] = value; + return *this; + } + + /** + * @brief Set sfLockedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setLockedAmount(std::decay_t const& value) + { + object_[sfLockedAmount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the completed MPTokenIssuance wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + MPTokenIssuance + build(uint256 const& index) + { + return MPTokenIssuance{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h new file mode 100644 index 0000000000..c500cde14c --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h @@ -0,0 +1,333 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NFTokenOfferBuilder; + +/** + * @brief Ledger Entry: NFTokenOffer + * + * Type: ltNFTOKEN_OFFER (0x0037) + * RPC Name: nft_offer + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NFTokenOfferBuilder to construct new ledger entries. + */ +class NFTokenOffer : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNFTOKEN_OFFER; + + /** + * @brief Construct a NFTokenOffer ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit NFTokenOffer(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenOffer"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->sle_->at(sfNFTokenID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfNFTokenOfferNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getNFTokenOfferNode() const + { + return this->sle_->at(sfNFTokenOfferNode); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + return this->sle_->at(sfDestination); + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->sle_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NFTokenOffer ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NFTokenOfferBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenOfferBuilder with required fields. + * @param owner The sfOwner field value. + * @param nFTokenID The sfNFTokenID field value. + * @param amount The sfAmount field value. + * @param ownerNode The sfOwnerNode field value. + * @param nFTokenOfferNode The sfNFTokenOfferNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + NFTokenOfferBuilder(std::decay_t const& owner,std::decay_t const& nFTokenID,std::decay_t const& amount,std::decay_t const& ownerNode,std::decay_t const& nFTokenOfferNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltNFTOKEN_OFFER) + { + setOwner(owner); + setNFTokenID(nFTokenID); + setAmount(amount); + setOwnerNode(ownerNode); + setNFTokenOfferNode(nFTokenOfferNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a NFTokenOfferBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NFTokenOfferBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNFTOKEN_OFFER) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenOffer"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfNFTokenOfferNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setNFTokenOfferNode(std::decay_t const& value) + { + object_[sfNFTokenOfferNode] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenOfferBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NFTokenOffer wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NFTokenOffer + build(uint256 const& index) + { + return NFTokenOffer{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h new file mode 100644 index 0000000000..fa8c415495 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h @@ -0,0 +1,238 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NFTokenPageBuilder; + +/** + * @brief Ledger Entry: NFTokenPage + * + * Type: ltNFTOKEN_PAGE (0x0050) + * RPC Name: nft_page + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NFTokenPageBuilder to construct new ledger entries. + */ +class NFTokenPage : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNFTOKEN_PAGE; + + /** + * @brief Construct a NFTokenPage ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit NFTokenPage(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenPage"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousPageMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousPageMin() const + { + if (hasPreviousPageMin()) + return this->sle_->at(sfPreviousPageMin); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousPageMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousPageMin() const + { + return this->sle_->isFieldPresent(sfPreviousPageMin); + } + + /** + * @brief Get sfNextPageMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNextPageMin() const + { + if (hasNextPageMin()) + return this->sle_->at(sfNextPageMin); + return std::nullopt; + } + + /** + * @brief Check if sfNextPageMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNextPageMin() const + { + return this->sle_->isFieldPresent(sfNextPageMin); + } + + /** + * @brief Get sfNFTokens (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getNFTokens() const + { + return this->sle_->getFieldArray(sfNFTokens); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NFTokenPage ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NFTokenPageBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenPageBuilder with required fields. + * @param nFTokens The sfNFTokens field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + NFTokenPageBuilder(STArray const& nFTokens,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltNFTOKEN_PAGE) + { + setNFTokens(nFTokens); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a NFTokenPageBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NFTokenPageBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNFTOKEN_PAGE) + { + throw std::runtime_error("Invalid ledger entry type for NFTokenPage"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousPageMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousPageMin(std::decay_t const& value) + { + object_[sfPreviousPageMin] = value; + return *this; + } + + /** + * @brief Set sfNextPageMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setNextPageMin(std::decay_t const& value) + { + object_[sfNextPageMin] = value; + return *this; + } + + /** + * @brief Set sfNFTokens (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setNFTokens(STArray const& value) + { + object_.setFieldArray(sfNFTokens, value); + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenPageBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NFTokenPage wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NFTokenPage + build(uint256 const& index) + { + return NFTokenPage{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h new file mode 100644 index 0000000000..8ac2db5683 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h @@ -0,0 +1,271 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class NegativeUNLBuilder; + +/** + * @brief Ledger Entry: NegativeUNL + * + * Type: ltNEGATIVE_UNL (0x004e) + * RPC Name: nunl + * + * Immutable wrapper around SLE providing type-safe field access. + * Use NegativeUNLBuilder to construct new ledger entries. + */ +class NegativeUNL : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltNEGATIVE_UNL; + + /** + * @brief Construct a NegativeUNL ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit NegativeUNL(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for NegativeUNL"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfDisabledValidators (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getDisabledValidators() const + { + if (this->sle_->isFieldPresent(sfDisabledValidators)) + return this->sle_->getFieldArray(sfDisabledValidators); + return std::nullopt; + } + + /** + * @brief Check if sfDisabledValidators is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDisabledValidators() const + { + return this->sle_->isFieldPresent(sfDisabledValidators); + } + + /** + * @brief Get sfValidatorToDisable (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getValidatorToDisable() const + { + if (hasValidatorToDisable()) + return this->sle_->at(sfValidatorToDisable); + return std::nullopt; + } + + /** + * @brief Check if sfValidatorToDisable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasValidatorToDisable() const + { + return this->sle_->isFieldPresent(sfValidatorToDisable); + } + + /** + * @brief Get sfValidatorToReEnable (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getValidatorToReEnable() const + { + if (hasValidatorToReEnable()) + return this->sle_->at(sfValidatorToReEnable); + return std::nullopt; + } + + /** + * @brief Check if sfValidatorToReEnable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasValidatorToReEnable() const + { + return this->sle_->isFieldPresent(sfValidatorToReEnable); + } + + /** + * @brief Get sfPreviousTxnID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnID() const + { + if (hasPreviousTxnID()) + return this->sle_->at(sfPreviousTxnID); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnID() const + { + return this->sle_->isFieldPresent(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPreviousTxnLgrSeq() const + { + if (hasPreviousTxnLgrSeq()) + return this->sle_->at(sfPreviousTxnLgrSeq); + return std::nullopt; + } + + /** + * @brief Check if sfPreviousTxnLgrSeq is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPreviousTxnLgrSeq() const + { + return this->sle_->isFieldPresent(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for NegativeUNL ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class NegativeUNLBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new NegativeUNLBuilder with required fields. + */ + NegativeUNLBuilder() + : LedgerEntryBuilderBase(ltNEGATIVE_UNL) + { + } + + /** + * @brief Construct a NegativeUNLBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + NegativeUNLBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltNEGATIVE_UNL) + { + throw std::runtime_error("Invalid ledger entry type for NegativeUNL"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfDisabledValidators (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setDisabledValidators(STArray const& value) + { + object_.setFieldArray(sfDisabledValidators, value); + return *this; + } + + /** + * @brief Set sfValidatorToDisable (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setValidatorToDisable(std::decay_t const& value) + { + object_[sfValidatorToDisable] = value; + return *this; + } + + /** + * @brief Set sfValidatorToReEnable (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setValidatorToReEnable(std::decay_t const& value) + { + object_[sfValidatorToReEnable] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NegativeUNLBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed NegativeUNL wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + NegativeUNL + build(uint256 const& index) + { + return NegativeUNL{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Offer.h b/include/xrpl/protocol_autogen/ledger_entries/Offer.h new file mode 100644 index 0000000000..cccd9183a1 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Offer.h @@ -0,0 +1,417 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class OfferBuilder; + +/** + * @brief Ledger Entry: Offer + * + * Type: ltOFFER (0x006f) + * RPC Name: offer + * + * Immutable wrapper around SLE providing type-safe field access. + * Use OfferBuilder to construct new ledger entries. + */ +class Offer : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltOFFER; + + /** + * @brief Construct a Offer ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Offer(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Offer"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfTakerPays (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerPays() const + { + return this->sle_->at(sfTakerPays); + } + + /** + * @brief Get sfTakerGets (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerGets() const + { + return this->sle_->at(sfTakerGets); + } + + /** + * @brief Get sfBookDirectory (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getBookDirectory() const + { + return this->sle_->at(sfBookDirectory); + } + + /** + * @brief Get sfBookNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getBookNode() const + { + return this->sle_->at(sfBookNode); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + return this->sle_->at(sfDomainID); + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->sle_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfAdditionalBooks (soeOPTIONAL) + * @note This is an untyped field (unknown). + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAdditionalBooks() const + { + if (this->sle_->isFieldPresent(sfAdditionalBooks)) + return this->sle_->getFieldArray(sfAdditionalBooks); + return std::nullopt; + } + + /** + * @brief Check if sfAdditionalBooks is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAdditionalBooks() const + { + return this->sle_->isFieldPresent(sfAdditionalBooks); + } +}; + +/** + * @brief Builder for Offer ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class OfferBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new OfferBuilder with required fields. + * @param account The sfAccount field value. + * @param sequence The sfSequence field value. + * @param takerPays The sfTakerPays field value. + * @param takerGets The sfTakerGets field value. + * @param bookDirectory The sfBookDirectory field value. + * @param bookNode The sfBookNode field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + OfferBuilder(std::decay_t const& account,std::decay_t const& sequence,std::decay_t const& takerPays,std::decay_t const& takerGets,std::decay_t const& bookDirectory,std::decay_t const& bookNode,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltOFFER) + { + setAccount(account); + setSequence(sequence); + setTakerPays(takerPays); + setTakerGets(takerGets); + setBookDirectory(bookDirectory); + setBookNode(bookNode); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a OfferBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + OfferBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltOFFER) + { + throw std::runtime_error("Invalid ledger entry type for Offer"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfTakerPays (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setTakerPays(std::decay_t const& value) + { + object_[sfTakerPays] = value; + return *this; + } + + /** + * @brief Set sfTakerGets (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setTakerGets(std::decay_t const& value) + { + object_[sfTakerGets] = value; + return *this; + } + + /** + * @brief Set sfBookDirectory (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setBookDirectory(std::decay_t const& value) + { + object_[sfBookDirectory] = value; + return *this; + } + + /** + * @brief Set sfBookNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setBookNode(std::decay_t const& value) + { + object_[sfBookNode] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfAdditionalBooks (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferBuilder& + setAdditionalBooks(STArray const& value) + { + object_.setFieldArray(sfAdditionalBooks, value); + return *this; + } + + /** + * @brief Build and return the completed Offer wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Offer + build(uint256 const& index) + { + return Offer{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h new file mode 100644 index 0000000000..4da93773e6 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h @@ -0,0 +1,358 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class OracleBuilder; + +/** + * @brief Ledger Entry: Oracle + * + * Type: ltORACLE (0x0080) + * RPC Name: oracle + * + * Immutable wrapper around SLE providing type-safe field access. + * Use OracleBuilder to construct new ledger entries. + */ +class Oracle : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltORACLE; + + /** + * @brief Construct a Oracle ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Oracle(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Oracle"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfOracleDocumentID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOracleDocumentID() const + { + if (hasOracleDocumentID()) + return this->sle_->at(sfOracleDocumentID); + return std::nullopt; + } + + /** + * @brief Check if sfOracleDocumentID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOracleDocumentID() const + { + return this->sle_->isFieldPresent(sfOracleDocumentID); + } + + /** + * @brief Get sfProvider (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getProvider() const + { + return this->sle_->at(sfProvider); + } + + /** + * @brief Get sfPriceDataSeries (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPriceDataSeries() const + { + return this->sle_->getFieldArray(sfPriceDataSeries); + } + + /** + * @brief Get sfAssetClass (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getAssetClass() const + { + return this->sle_->at(sfAssetClass); + } + + /** + * @brief Get sfLastUpdateTime (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLastUpdateTime() const + { + return this->sle_->at(sfLastUpdateTime); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + return this->sle_->at(sfURI); + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->sle_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Oracle ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class OracleBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new OracleBuilder with required fields. + * @param owner The sfOwner field value. + * @param provider The sfProvider field value. + * @param priceDataSeries The sfPriceDataSeries field value. + * @param assetClass The sfAssetClass field value. + * @param lastUpdateTime The sfLastUpdateTime field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + OracleBuilder(std::decay_t const& owner,std::decay_t const& provider,STArray const& priceDataSeries,std::decay_t const& assetClass,std::decay_t const& lastUpdateTime,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltORACLE) + { + setOwner(owner); + setProvider(provider); + setPriceDataSeries(priceDataSeries); + setAssetClass(assetClass); + setLastUpdateTime(lastUpdateTime); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a OracleBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + OracleBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltORACLE) + { + throw std::runtime_error("Invalid ledger entry type for Oracle"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOracleDocumentID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Set sfProvider (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setProvider(std::decay_t const& value) + { + object_[sfProvider] = value; + return *this; + } + + /** + * @brief Set sfPriceDataSeries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPriceDataSeries(STArray const& value) + { + object_.setFieldArray(sfPriceDataSeries, value); + return *this; + } + + /** + * @brief Set sfAssetClass (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setAssetClass(std::decay_t const& value) + { + object_[sfAssetClass] = value; + return *this; + } + + /** + * @brief Set sfLastUpdateTime (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setLastUpdateTime(std::decay_t const& value) + { + object_[sfLastUpdateTime] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Oracle wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Oracle + build(uint256 const& index) + { + return Oracle{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h new file mode 100644 index 0000000000..50245bc0d8 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h @@ -0,0 +1,521 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class PayChannelBuilder; + +/** + * @brief Ledger Entry: PayChannel + * + * Type: ltPAYCHAN (0x0078) + * RPC Name: payment_channel + * + * Immutable wrapper around SLE providing type-safe field access. + * Use PayChannelBuilder to construct new ledger entries. + */ +class PayChannel : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltPAYCHAN; + + /** + * @brief Construct a PayChannel ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit PayChannel(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for PayChannel"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->sle_->at(sfDestination); + } + + /** + * @brief Get sfSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSequence() const + { + if (hasSequence()) + return this->sle_->at(sfSequence); + return std::nullopt; + } + + /** + * @brief Check if sfSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSequence() const + { + return this->sle_->isFieldPresent(sfSequence); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->sle_->at(sfAmount); + } + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->sle_->at(sfPublicKey); + } + + /** + * @brief Get sfSettleDelay (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSettleDelay() const + { + return this->sle_->at(sfSettleDelay); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + return this->sle_->at(sfExpiration); + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->sle_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + return this->sle_->at(sfCancelAfter); + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->sle_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfSourceTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSourceTag() const + { + if (hasSourceTag()) + return this->sle_->at(sfSourceTag); + return std::nullopt; + } + + /** + * @brief Check if sfSourceTag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSourceTag() const + { + return this->sle_->isFieldPresent(sfSourceTag); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationTag() const + { + if (hasDestinationTag()) + return this->sle_->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->sle_->isFieldPresent(sfDestinationTag); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfDestinationNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestinationNode() const + { + if (hasDestinationNode()) + return this->sle_->at(sfDestinationNode); + return std::nullopt; + } + + /** + * @brief Check if sfDestinationNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestinationNode() const + { + return this->sle_->isFieldPresent(sfDestinationNode); + } +}; + +/** + * @brief Builder for PayChannel ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class PayChannelBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new PayChannelBuilder with required fields. + * @param account The sfAccount field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param balance The sfBalance field value. + * @param publicKey The sfPublicKey field value. + * @param settleDelay The sfSettleDelay field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + PayChannelBuilder(std::decay_t const& account,std::decay_t const& destination,std::decay_t const& amount,std::decay_t const& balance,std::decay_t const& publicKey,std::decay_t const& settleDelay,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltPAYCHAN) + { + setAccount(account); + setDestination(destination); + setAmount(amount); + setBalance(balance); + setPublicKey(publicKey); + setSettleDelay(settleDelay); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a PayChannelBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + PayChannelBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltPAYCHAN) + { + throw std::runtime_error("Invalid ledger entry type for PayChannel"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSettleDelay (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSettleDelay(std::decay_t const& value) + { + object_[sfSettleDelay] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfSourceTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setSourceTag(std::decay_t const& value) + { + object_[sfSourceTag] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfDestinationNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PayChannelBuilder& + setDestinationNode(std::decay_t const& value) + { + object_[sfDestinationNode] = value; + return *this; + } + + /** + * @brief Build and return the completed PayChannel wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + PayChannel + build(uint256 const& index) + { + return PayChannel{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h new file mode 100644 index 0000000000..630f46517f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class PermissionedDomainBuilder; + +/** + * @brief Ledger Entry: PermissionedDomain + * + * Type: ltPERMISSIONED_DOMAIN (0x0082) + * RPC Name: permissioned_domain + * + * Immutable wrapper around SLE providing type-safe field access. + * Use PermissionedDomainBuilder to construct new ledger entries. + */ +class PermissionedDomain : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltPERMISSIONED_DOMAIN; + + /** + * @brief Construct a PermissionedDomain ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit PermissionedDomain(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for PermissionedDomain"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfAcceptedCredentials (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getAcceptedCredentials() const + { + return this->sle_->getFieldArray(sfAcceptedCredentials); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for PermissionedDomain ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class PermissionedDomainBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainBuilder with required fields. + * @param owner The sfOwner field value. + * @param sequence The sfSequence field value. + * @param acceptedCredentials The sfAcceptedCredentials field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + PermissionedDomainBuilder(std::decay_t const& owner,std::decay_t const& sequence,STArray const& acceptedCredentials,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltPERMISSIONED_DOMAIN) + { + setOwner(owner); + setSequence(sequence); + setAcceptedCredentials(acceptedCredentials); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a PermissionedDomainBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + PermissionedDomainBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltPERMISSIONED_DOMAIN) + { + throw std::runtime_error("Invalid ledger entry type for PermissionedDomain"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfAcceptedCredentials (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setAcceptedCredentials(STArray const& value) + { + object_.setFieldArray(sfAcceptedCredentials, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed PermissionedDomain wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + PermissionedDomain + build(uint256 const& index) + { + return PermissionedDomain{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h new file mode 100644 index 0000000000..993496d38f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -0,0 +1,425 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class RippleStateBuilder; + +/** + * @brief Ledger Entry: RippleState + * + * Type: ltRIPPLE_STATE (0x0072) + * RPC Name: state + * + * Immutable wrapper around SLE providing type-safe field access. + * Use RippleStateBuilder to construct new ledger entries. + */ +class RippleState : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltRIPPLE_STATE; + + /** + * @brief Construct a RippleState ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit RippleState(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for RippleState"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfBalance (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getBalance() const + { + return this->sle_->at(sfBalance); + } + + /** + * @brief Get sfLowLimit (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getLowLimit() const + { + return this->sle_->at(sfLowLimit); + } + + /** + * @brief Get sfHighLimit (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getHighLimit() const + { + return this->sle_->at(sfHighLimit); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfLowNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowNode() const + { + if (hasLowNode()) + return this->sle_->at(sfLowNode); + return std::nullopt; + } + + /** + * @brief Check if sfLowNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowNode() const + { + return this->sle_->isFieldPresent(sfLowNode); + } + + /** + * @brief Get sfLowQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowQualityIn() const + { + if (hasLowQualityIn()) + return this->sle_->at(sfLowQualityIn); + return std::nullopt; + } + + /** + * @brief Check if sfLowQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowQualityIn() const + { + return this->sle_->isFieldPresent(sfLowQualityIn); + } + + /** + * @brief Get sfLowQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowQualityOut() const + { + if (hasLowQualityOut()) + return this->sle_->at(sfLowQualityOut); + return std::nullopt; + } + + /** + * @brief Check if sfLowQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowQualityOut() const + { + return this->sle_->isFieldPresent(sfLowQualityOut); + } + + /** + * @brief Get sfHighNode (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighNode() const + { + if (hasHighNode()) + return this->sle_->at(sfHighNode); + return std::nullopt; + } + + /** + * @brief Check if sfHighNode is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighNode() const + { + return this->sle_->isFieldPresent(sfHighNode); + } + + /** + * @brief Get sfHighQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighQualityIn() const + { + if (hasHighQualityIn()) + return this->sle_->at(sfHighQualityIn); + return std::nullopt; + } + + /** + * @brief Check if sfHighQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighQualityIn() const + { + return this->sle_->isFieldPresent(sfHighQualityIn); + } + + /** + * @brief Get sfHighQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighQualityOut() const + { + if (hasHighQualityOut()) + return this->sle_->at(sfHighQualityOut); + return std::nullopt; + } + + /** + * @brief Check if sfHighQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighQualityOut() const + { + return this->sle_->isFieldPresent(sfHighQualityOut); + } +}; + +/** + * @brief Builder for RippleState ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class RippleStateBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new RippleStateBuilder with required fields. + * @param balance The sfBalance field value. + * @param lowLimit The sfLowLimit field value. + * @param highLimit The sfHighLimit field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + RippleStateBuilder(std::decay_t const& balance,std::decay_t const& lowLimit,std::decay_t const& highLimit,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltRIPPLE_STATE) + { + setBalance(balance); + setLowLimit(lowLimit); + setHighLimit(highLimit); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a RippleStateBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + RippleStateBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltRIPPLE_STATE) + { + throw std::runtime_error("Invalid ledger entry type for RippleState"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfBalance (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfLowLimit (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowLimit(std::decay_t const& value) + { + object_[sfLowLimit] = value; + return *this; + } + + /** + * @brief Set sfHighLimit (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighLimit(std::decay_t const& value) + { + object_[sfHighLimit] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfLowNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowNode(std::decay_t const& value) + { + object_[sfLowNode] = value; + return *this; + } + + /** + * @brief Set sfLowQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowQualityIn(std::decay_t const& value) + { + object_[sfLowQualityIn] = value; + return *this; + } + + /** + * @brief Set sfLowQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowQualityOut(std::decay_t const& value) + { + object_[sfLowQualityOut] = value; + return *this; + } + + /** + * @brief Set sfHighNode (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighNode(std::decay_t const& value) + { + object_[sfHighNode] = value; + return *this; + } + + /** + * @brief Set sfHighQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighQualityIn(std::decay_t const& value) + { + object_[sfHighQualityIn] = value; + return *this; + } + + /** + * @brief Set sfHighQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighQualityOut(std::decay_t const& value) + { + object_[sfHighQualityOut] = value; + return *this; + } + + /** + * @brief Build and return the completed RippleState wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + RippleState + build(uint256 const& index) + { + return RippleState{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h new file mode 100644 index 0000000000..57d706bc28 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h @@ -0,0 +1,275 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class SignerListBuilder; + +/** + * @brief Ledger Entry: SignerList + * + * Type: ltSIGNER_LIST (0x0053) + * RPC Name: signer_list + * + * Immutable wrapper around SLE providing type-safe field access. + * Use SignerListBuilder to construct new ledger entries. + */ +class SignerList : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltSIGNER_LIST; + + /** + * @brief Construct a SignerList ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit SignerList(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for SignerList"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + return this->sle_->at(sfOwner); + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->sle_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfSignerQuorum (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerQuorum() const + { + return this->sle_->at(sfSignerQuorum); + } + + /** + * @brief Get sfSignerEntries (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getSignerEntries() const + { + return this->sle_->getFieldArray(sfSignerEntries); + } + + /** + * @brief Get sfSignerListID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerListID() const + { + return this->sle_->at(sfSignerListID); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for SignerList ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class SignerListBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new SignerListBuilder with required fields. + * @param ownerNode The sfOwnerNode field value. + * @param signerQuorum The sfSignerQuorum field value. + * @param signerEntries The sfSignerEntries field value. + * @param signerListID The sfSignerListID field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + SignerListBuilder(std::decay_t const& ownerNode,std::decay_t const& signerQuorum,STArray const& signerEntries,std::decay_t const& signerListID,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltSIGNER_LIST) + { + setOwnerNode(ownerNode); + setSignerQuorum(signerQuorum); + setSignerEntries(signerEntries); + setSignerListID(signerListID); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a SignerListBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + SignerListBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltSIGNER_LIST) + { + throw std::runtime_error("Invalid ledger entry type for SignerList"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfSignerQuorum (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerQuorum(std::decay_t const& value) + { + object_[sfSignerQuorum] = value; + return *this; + } + + /** + * @brief Set sfSignerEntries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerEntries(STArray const& value) + { + object_.setFieldArray(sfSignerEntries, value); + return *this; + } + + /** + * @brief Set sfSignerListID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setSignerListID(std::decay_t const& value) + { + object_[sfSignerListID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed SignerList wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + SignerList + build(uint256 const& index) + { + return SignerList{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h new file mode 100644 index 0000000000..f13e8a398f --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h @@ -0,0 +1,215 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class TicketBuilder; + +/** + * @brief Ledger Entry: Ticket + * + * Type: ltTICKET (0x0054) + * RPC Name: ticket + * + * Immutable wrapper around SLE providing type-safe field access. + * Use TicketBuilder to construct new ledger entries. + */ +class Ticket : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltTICKET; + + /** + * @brief Construct a Ticket ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Ticket(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Ticket"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfTicketSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getTicketSequence() const + { + return this->sle_->at(sfTicketSequence); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for Ticket ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class TicketBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new TicketBuilder with required fields. + * @param account The sfAccount field value. + * @param ownerNode The sfOwnerNode field value. + * @param ticketSequence The sfTicketSequence field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + TicketBuilder(std::decay_t const& account,std::decay_t const& ownerNode,std::decay_t const& ticketSequence,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltTICKET) + { + setAccount(account); + setOwnerNode(ownerNode); + setTicketSequence(ticketSequence); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a TicketBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + TicketBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltTICKET) + { + throw std::runtime_error("Invalid ledger entry type for Ticket"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfTicketSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setTicketSequence(std::decay_t const& value) + { + object_[sfTicketSequence] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed Ticket wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Ticket + build(uint256 const& index) + { + return Ticket{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/Vault.h b/include/xrpl/protocol_autogen/ledger_entries/Vault.h new file mode 100644 index 0000000000..e24e73fab3 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Vault.h @@ -0,0 +1,521 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class VaultBuilder; + +/** + * @brief Ledger Entry: Vault + * + * Type: ltVAULT (0x0084) + * RPC Name: vault + * + * Immutable wrapper around SLE providing type-safe field access. + * Use VaultBuilder to construct new ledger entries. + */ +class Vault : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltVAULT; + + /** + * @brief Construct a Vault ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Vault(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Vault"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSequence() const + { + return this->sle_->at(sfSequence); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->sle_->at(sfAsset); + } + + /** + * @brief Get sfAssetsTotal (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsTotal() const + { + if (hasAssetsTotal()) + return this->sle_->at(sfAssetsTotal); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsTotal() const + { + return this->sle_->isFieldPresent(sfAssetsTotal); + } + + /** + * @brief Get sfAssetsAvailable (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsAvailable() const + { + if (hasAssetsAvailable()) + return this->sle_->at(sfAssetsAvailable); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsAvailable is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsAvailable() const + { + return this->sle_->isFieldPresent(sfAssetsAvailable); + } + + /** + * @brief Get sfAssetsMaximum (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + return this->sle_->at(sfAssetsMaximum); + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->sle_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfLossUnrealized (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLossUnrealized() const + { + if (hasLossUnrealized()) + return this->sle_->at(sfLossUnrealized); + return std::nullopt; + } + + /** + * @brief Check if sfLossUnrealized is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLossUnrealized() const + { + return this->sle_->isFieldPresent(sfLossUnrealized); + } + + /** + * @brief Get sfShareMPTID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getShareMPTID() const + { + return this->sle_->at(sfShareMPTID); + } + + /** + * @brief Get sfWithdrawalPolicy (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWithdrawalPolicy() const + { + return this->sle_->at(sfWithdrawalPolicy); + } + + /** + * @brief Get sfScale (soeDEFAULT) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getScale() const + { + if (hasScale()) + return this->sle_->at(sfScale); + return std::nullopt; + } + + /** + * @brief Check if sfScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasScale() const + { + return this->sle_->isFieldPresent(sfScale); + } +}; + +/** + * @brief Builder for Vault ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class VaultBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new VaultBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param sequence The sfSequence field value. + * @param ownerNode The sfOwnerNode field value. + * @param owner The sfOwner field value. + * @param account The sfAccount field value. + * @param asset The sfAsset field value. + * @param shareMPTID The sfShareMPTID field value. + * @param withdrawalPolicy The sfWithdrawalPolicy field value. + */ + VaultBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& sequence,std::decay_t const& ownerNode,std::decay_t const& owner,std::decay_t const& account,std::decay_t const& asset,std::decay_t const& shareMPTID,std::decay_t const& withdrawalPolicy) + : LedgerEntryBuilderBase(ltVAULT) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setSequence(sequence); + setOwnerNode(ownerNode); + setOwner(owner); + setAccount(account); + setAsset(asset); + setShareMPTID(shareMPTID); + setWithdrawalPolicy(withdrawalPolicy); + } + + /** + * @brief Construct a VaultBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + VaultBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltVAULT) + { + throw std::runtime_error("Invalid ledger entry type for Vault"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setSequence(std::decay_t const& value) + { + object_[sfSequence] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAssetsTotal (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsTotal(std::decay_t const& value) + { + object_[sfAssetsTotal] = value; + return *this; + } + + /** + * @brief Set sfAssetsAvailable (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsAvailable(std::decay_t const& value) + { + object_[sfAssetsAvailable] = value; + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfLossUnrealized (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setLossUnrealized(std::decay_t const& value) + { + object_[sfLossUnrealized] = value; + return *this; + } + + /** + * @brief Set sfShareMPTID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setShareMPTID(std::decay_t const& value) + { + object_[sfShareMPTID] = value; + return *this; + } + + /** + * @brief Set sfWithdrawalPolicy (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setWithdrawalPolicy(std::decay_t const& value) + { + object_[sfWithdrawalPolicy] = value; + return *this; + } + + /** + * @brief Set sfScale (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + VaultBuilder& + setScale(std::decay_t const& value) + { + object_[sfScale] = value; + return *this; + } + + /** + * @brief Build and return the completed Vault wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Vault + build(uint256 const& index) + { + return Vault{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h new file mode 100644 index 0000000000..3309228951 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h @@ -0,0 +1,312 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class XChainOwnedClaimIDBuilder; + +/** + * @brief Ledger Entry: XChainOwnedClaimID + * + * Type: ltXCHAIN_OWNED_CLAIM_ID (0x0071) + * RPC Name: xchain_owned_claim_id + * + * Immutable wrapper around SLE providing type-safe field access. + * Use XChainOwnedClaimIDBuilder to construct new ledger entries. + */ +class XChainOwnedClaimID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CLAIM_ID; + + /** + * @brief Construct a XChainOwnedClaimID ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit XChainOwnedClaimID(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->sle_->at(sfXChainClaimID); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->sle_->at(sfOtherChainSource); + } + + /** + * @brief Get sfXChainClaimAttestations (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getXChainClaimAttestations() const + { + return this->sle_->getFieldArray(sfXChainClaimAttestations); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->sle_->at(sfSignatureReward); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for XChainOwnedClaimID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class XChainOwnedClaimIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new XChainOwnedClaimIDBuilder with required fields. + * @param account The sfAccount field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param xChainClaimAttestations The sfXChainClaimAttestations field value. + * @param signatureReward The sfSignatureReward field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + XChainOwnedClaimIDBuilder(std::decay_t const& account,std::decay_t const& xChainBridge,std::decay_t const& xChainClaimID,std::decay_t const& otherChainSource,STArray const& xChainClaimAttestations,std::decay_t const& signatureReward,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltXCHAIN_OWNED_CLAIM_ID) + { + setAccount(account); + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setOtherChainSource(otherChainSource); + setXChainClaimAttestations(xChainClaimAttestations); + setSignatureReward(signatureReward); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a XChainOwnedClaimIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + XChainOwnedClaimIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CLAIM_ID) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimAttestations (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setXChainClaimAttestations(STArray const& value) + { + object_.setFieldArray(sfXChainClaimAttestations, value); + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedClaimIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed XChainOwnedClaimID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + XChainOwnedClaimID + build(uint256 const& index) + { + return XChainOwnedClaimID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h new file mode 100644 index 0000000000..5415d52884 --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class XChainOwnedCreateAccountClaimIDBuilder; + +/** + * @brief Ledger Entry: XChainOwnedCreateAccountClaimID + * + * Type: ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID (0x0074) + * RPC Name: xchain_owned_create_account_claim_id + * + * Immutable wrapper around SLE providing type-safe field access. + * Use XChainOwnedCreateAccountClaimIDBuilder to construct new ledger entries. + */ +class XChainOwnedCreateAccountClaimID : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID; + + /** + * @brief Construct a XChainOwnedCreateAccountClaimID ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit XChainOwnedCreateAccountClaimID(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAccount() const + { + return this->sle_->at(sfAccount); + } + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->sle_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->sle_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfXChainCreateAccountAttestations (soeREQUIRED) + * @note This is an untyped field (unknown). + * @return The field value. + */ + [[nodiscard]] + STArray const& + getXChainCreateAccountAttestations() const + { + return this->sle_->getFieldArray(sfXChainCreateAccountAttestations); + } + + /** + * @brief Get sfOwnerNode (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfPreviousTxnID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } +}; + +/** + * @brief Builder for XChainOwnedCreateAccountClaimID ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class XChainOwnedCreateAccountClaimIDBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new XChainOwnedCreateAccountClaimIDBuilder with required fields. + * @param account The sfAccount field value. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param xChainCreateAccountAttestations The sfXChainCreateAccountAttestations field value. + * @param ownerNode The sfOwnerNode field value. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + */ + XChainOwnedCreateAccountClaimIDBuilder(std::decay_t const& account,std::decay_t const& xChainBridge,std::decay_t const& xChainAccountCreateCount,STArray const& xChainCreateAccountAttestations,std::decay_t const& ownerNode,std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq) + : LedgerEntryBuilderBase(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) + { + setAccount(account); + setXChainBridge(xChainBridge); + setXChainAccountCreateCount(xChainAccountCreateCount); + setXChainCreateAccountAttestations(xChainCreateAccountAttestations); + setOwnerNode(ownerNode); + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + } + + /** + * @brief Construct a XChainOwnedCreateAccountClaimIDBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + XChainOwnedCreateAccountClaimIDBuilder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) + { + throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setAccount(std::decay_t const& value) + { + object_[sfAccount] = value; + return *this; + } + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfXChainCreateAccountAttestations (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setXChainCreateAccountAttestations(STArray const& value) + { + object_.setFieldArray(sfXChainCreateAccountAttestations, value); + return *this; + } + + /** + * @brief Set sfOwnerNode (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainOwnedCreateAccountClaimIDBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Build and return the completed XChainOwnedCreateAccountClaimID wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + XChainOwnedCreateAccountClaimID + build(uint256 const& index) + { + return XChainOwnedCreateAccountClaimID{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/transactions/AMMBid.h b/include/xrpl/protocol_autogen/transactions/AMMBid.h new file mode 100644 index 0000000000..d3f8cba8f7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMBid.h @@ -0,0 +1,262 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMBidBuilder; + +/** + * @brief Transaction: AMMBid + * + * Type: ttAMM_BID (39) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMBidBuilder to construct new transactions. + */ +class AMMBid : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_BID; + + /** + * @brief Construct a AMMBid transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMBid(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMBid"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfBidMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBidMin() const + { + if (hasBidMin()) + { + return this->tx_->at(sfBidMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfBidMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBidMin() const + { + return this->tx_->isFieldPresent(sfBidMin); + } + + /** + * @brief Get sfBidMax (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBidMax() const + { + if (hasBidMax()) + { + return this->tx_->at(sfBidMax); + } + return std::nullopt; + } + + /** + * @brief Check if sfBidMax is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBidMax() const + { + return this->tx_->isFieldPresent(sfBidMax); + } + /** + * @brief Get sfAuthAccounts (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthAccounts() const + { + if (this->tx_->isFieldPresent(sfAuthAccounts)) + return this->tx_->getFieldArray(sfAuthAccounts); + return std::nullopt; + } + + /** + * @brief Check if sfAuthAccounts is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthAccounts() const + { + return this->tx_->isFieldPresent(sfAuthAccounts); + } +}; + +/** + * @brief Builder for AMMBid transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMBidBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMBidBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMBidBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_BID, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMBidBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMBidBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_BID) + { + throw std::runtime_error("Invalid transaction type for AMMBidBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfBidMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setBidMin(std::decay_t const& value) + { + object_[sfBidMin] = value; + return *this; + } + + /** + * @brief Set sfBidMax (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setBidMax(std::decay_t const& value) + { + object_[sfBidMax] = value; + return *this; + } + + /** + * @brief Set sfAuthAccounts (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMBidBuilder& + setAuthAccounts(STArray const& value) + { + object_.setFieldArray(sfAuthAccounts, value); + return *this; + } + + /** + * @brief Build and return the AMMBid wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMBid + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMBid{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMClawback.h b/include/xrpl/protocol_autogen/transactions/AMMClawback.h new file mode 100644 index 0000000000..9603cad6ef --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMClawback.h @@ -0,0 +1,214 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMClawbackBuilder; + +/** + * @brief Transaction: AMMClawback + * + * Type: ttAMM_CLAWBACK (31) + * Delegable: Delegation::delegable + * Amendment: featureAMMClawback + * Privileges: mayDeleteAcct | overrideFreeze + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMClawbackBuilder to construct new transactions. + */ +class AMMClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_CLAWBACK; + + /** + * @brief Construct a AMMClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfHolder (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getHolder() const + { + return this->tx_->at(sfHolder); + } + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for AMMClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param holder The sfHolder field value. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& holder, std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_CLAWBACK, account, sequence, fee) + { + setHolder(holder); + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for AMMClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfHolder (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the AMMClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMCreate.h b/include/xrpl/protocol_autogen/transactions/AMMCreate.h new file mode 100644 index 0000000000..311d19a3c4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMCreate.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMCreateBuilder; + +/** + * @brief Transaction: AMMCreate + * + * Type: ttAMM_CREATE (35) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: createPseudoAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMCreateBuilder to construct new transactions. + */ +class AMMCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_CREATE; + + /** + * @brief Construct a AMMCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount2() const + { + return this->tx_->at(sfAmount2); + } + + /** + * @brief Get sfTradingFee (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getTradingFee() const + { + return this->tx_->at(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param amount The sfAmount field value. + * @param amount2 The sfAmount2 field value. + * @param tradingFee The sfTradingFee field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& amount, std::decay_t const& amount2, std::decay_t const& tradingFee, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_CREATE, account, sequence, fee) + { + setAmount(amount); + setAmount2(amount2); + setTradingFee(tradingFee); + } + + /** + * @brief Construct a AMMCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_CREATE) + { + throw std::runtime_error("Invalid transaction type for AMMCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMCreateBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMDelete.h b/include/xrpl/protocol_autogen/transactions/AMMDelete.h new file mode 100644 index 0000000000..bc61434d0b --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMDelete.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMDeleteBuilder; + +/** + * @brief Transaction: AMMDelete + * + * Type: ttAMM_DELETE (40) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: mustDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMDeleteBuilder to construct new transactions. + */ +class AMMDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_DELETE; + + /** + * @brief Construct a AMMDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } +}; + +/** + * @brief Builder for AMMDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_DELETE, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_DELETE) + { + throw std::runtime_error("Invalid transaction type for AMMDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDeleteBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDeleteBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Build and return the AMMDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h new file mode 100644 index 0000000000..8e86339b0a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h @@ -0,0 +1,338 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMDepositBuilder; + +/** + * @brief Transaction: AMMDeposit + * + * Type: ttAMM_DEPOSIT (36) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMDepositBuilder to construct new transactions. + */ +class AMMDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_DEPOSIT; + + /** + * @brief Construct a AMMDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount2() const + { + if (hasAmount2()) + { + return this->tx_->at(sfAmount2); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount2 is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount2() const + { + return this->tx_->isFieldPresent(sfAmount2); + } + + /** + * @brief Get sfEPrice (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEPrice() const + { + if (hasEPrice()) + { + return this->tx_->at(sfEPrice); + } + return std::nullopt; + } + + /** + * @brief Check if sfEPrice is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEPrice() const + { + return this->tx_->isFieldPresent(sfEPrice); + } + + /** + * @brief Get sfLPTokenOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLPTokenOut() const + { + if (hasLPTokenOut()) + { + return this->tx_->at(sfLPTokenOut); + } + return std::nullopt; + } + + /** + * @brief Check if sfLPTokenOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLPTokenOut() const + { + return this->tx_->isFieldPresent(sfLPTokenOut); + } + + /** + * @brief Get sfTradingFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTradingFee() const + { + if (hasTradingFee()) + { + return this->tx_->at(sfTradingFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTradingFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTradingFee() const + { + return this->tx_->isFieldPresent(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_DEPOSIT, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for AMMDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfEPrice (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setEPrice(std::decay_t const& value) + { + object_[sfEPrice] = value; + return *this; + } + + /** + * @brief Set sfLPTokenOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setLPTokenOut(std::decay_t const& value) + { + object_[sfLPTokenOut] = value; + return *this; + } + + /** + * @brief Set sfTradingFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMDepositBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMVote.h b/include/xrpl/protocol_autogen/transactions/AMMVote.h new file mode 100644 index 0000000000..a4e58c9aa4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMVote.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMVoteBuilder; + +/** + * @brief Transaction: AMMVote + * + * Type: ttAMM_VOTE (38) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMVoteBuilder to construct new transactions. + */ +class AMMVote : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_VOTE; + + /** + * @brief Construct a AMMVote transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMVote(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMVote"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfTradingFee (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getTradingFee() const + { + return this->tx_->at(sfTradingFee); + } +}; + +/** + * @brief Builder for AMMVote transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMVoteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMVoteBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param tradingFee The sfTradingFee field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMVoteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::decay_t const& tradingFee, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_VOTE, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + setTradingFee(tradingFee); + } + + /** + * @brief Construct a AMMVoteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMVoteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_VOTE) + { + throw std::runtime_error("Invalid transaction type for AMMVoteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfTradingFee (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMVoteBuilder& + setTradingFee(std::decay_t const& value) + { + object_[sfTradingFee] = value; + return *this; + } + + /** + * @brief Build and return the AMMVote wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMVote + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMVote{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h new file mode 100644 index 0000000000..da19c546ee --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h @@ -0,0 +1,301 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AMMWithdrawBuilder; + +/** + * @brief Transaction: AMMWithdraw + * + * Type: ttAMM_WITHDRAW (37) + * Delegable: Delegation::delegable + * Amendment: featureAMM + * Privileges: mayDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AMMWithdrawBuilder to construct new transactions. + */ +class AMMWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMM_WITHDRAW; + + /** + * @brief Construct a AMMWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AMMWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AMMWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAsset2 (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset2() const + { + return this->tx_->at(sfAsset2); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfAmount2 (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount2() const + { + if (hasAmount2()) + { + return this->tx_->at(sfAmount2); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount2 is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount2() const + { + return this->tx_->isFieldPresent(sfAmount2); + } + + /** + * @brief Get sfEPrice (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEPrice() const + { + if (hasEPrice()) + { + return this->tx_->at(sfEPrice); + } + return std::nullopt; + } + + /** + * @brief Check if sfEPrice is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEPrice() const + { + return this->tx_->isFieldPresent(sfEPrice); + } + + /** + * @brief Get sfLPTokenIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLPTokenIn() const + { + if (hasLPTokenIn()) + { + return this->tx_->at(sfLPTokenIn); + } + return std::nullopt; + } + + /** + * @brief Check if sfLPTokenIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLPTokenIn() const + { + return this->tx_->isFieldPresent(sfLPTokenIn); + } +}; + +/** + * @brief Builder for AMMWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AMMWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AMMWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param asset2 The sfAsset2 field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AMMWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::decay_t const& asset2, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMM_WITHDRAW, account, sequence, fee) + { + setAsset(asset); + setAsset2(asset2); + } + + /** + * @brief Construct a AMMWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AMMWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMM_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for AMMWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAsset2 (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAsset2(std::decay_t const& value) + { + object_[sfAsset2] = STIssue(sfAsset2, value); + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAmount2 (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setAmount2(std::decay_t const& value) + { + object_[sfAmount2] = value; + return *this; + } + + /** + * @brief Set sfEPrice (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setEPrice(std::decay_t const& value) + { + object_[sfEPrice] = value; + return *this; + } + + /** + * @brief Set sfLPTokenIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AMMWithdrawBuilder& + setLPTokenIn(std::decay_t const& value) + { + object_[sfLPTokenIn] = value; + return *this; + } + + /** + * @brief Build and return the AMMWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AMMWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AMMWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AccountDelete.h b/include/xrpl/protocol_autogen/transactions/AccountDelete.h new file mode 100644 index 0000000000..86ae9af546 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AccountDelete.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AccountDeleteBuilder; + +/** + * @brief Transaction: AccountDelete + * + * Type: ttACCOUNT_DELETE (21) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: mustDeleteAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AccountDeleteBuilder to construct new transactions. + */ +class AccountDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttACCOUNT_DELETE; + + /** + * @brief Construct a AccountDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AccountDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AccountDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for AccountDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AccountDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AccountDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AccountDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttACCOUNT_DELETE, account, sequence, fee) + { + setDestination(destination); + } + + /** + * @brief Construct a AccountDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AccountDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttACCOUNT_DELETE) + { + throw std::runtime_error("Invalid transaction type for AccountDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountDeleteBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the AccountDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AccountDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AccountDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/AccountSet.h b/include/xrpl/protocol_autogen/transactions/AccountSet.h new file mode 100644 index 0000000000..c00142bd1e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/AccountSet.h @@ -0,0 +1,475 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class AccountSetBuilder; + +/** + * @brief Transaction: AccountSet + * + * Type: ttACCOUNT_SET (3) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use AccountSetBuilder to construct new transactions. + */ +class AccountSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttACCOUNT_SET; + + /** + * @brief Construct a AccountSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit AccountSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for AccountSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfEmailHash (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getEmailHash() const + { + if (hasEmailHash()) + { + return this->tx_->at(sfEmailHash); + } + return std::nullopt; + } + + /** + * @brief Check if sfEmailHash is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasEmailHash() const + { + return this->tx_->isFieldPresent(sfEmailHash); + } + + /** + * @brief Get sfWalletLocator (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletLocator() const + { + if (hasWalletLocator()) + { + return this->tx_->at(sfWalletLocator); + } + return std::nullopt; + } + + /** + * @brief Check if sfWalletLocator is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletLocator() const + { + return this->tx_->isFieldPresent(sfWalletLocator); + } + + /** + * @brief Get sfWalletSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWalletSize() const + { + if (hasWalletSize()) + { + return this->tx_->at(sfWalletSize); + } + return std::nullopt; + } + + /** + * @brief Check if sfWalletSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWalletSize() const + { + return this->tx_->isFieldPresent(sfWalletSize); + } + + /** + * @brief Get sfMessageKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMessageKey() const + { + if (hasMessageKey()) + { + return this->tx_->at(sfMessageKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfMessageKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMessageKey() const + { + return this->tx_->isFieldPresent(sfMessageKey); + } + + /** + * @brief Get sfDomain (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomain() const + { + if (hasDomain()) + { + return this->tx_->at(sfDomain); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomain is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomain() const + { + return this->tx_->isFieldPresent(sfDomain); + } + + /** + * @brief Get sfTransferRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferRate() const + { + if (hasTransferRate()) + { + return this->tx_->at(sfTransferRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferRate() const + { + return this->tx_->isFieldPresent(sfTransferRate); + } + + /** + * @brief Get sfSetFlag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSetFlag() const + { + if (hasSetFlag()) + { + return this->tx_->at(sfSetFlag); + } + return std::nullopt; + } + + /** + * @brief Check if sfSetFlag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSetFlag() const + { + return this->tx_->isFieldPresent(sfSetFlag); + } + + /** + * @brief Get sfClearFlag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClearFlag() const + { + if (hasClearFlag()) + { + return this->tx_->at(sfClearFlag); + } + return std::nullopt; + } + + /** + * @brief Check if sfClearFlag is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClearFlag() const + { + return this->tx_->isFieldPresent(sfClearFlag); + } + + /** + * @brief Get sfTickSize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTickSize() const + { + if (hasTickSize()) + { + return this->tx_->at(sfTickSize); + } + return std::nullopt; + } + + /** + * @brief Check if sfTickSize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTickSize() const + { + return this->tx_->isFieldPresent(sfTickSize); + } + + /** + * @brief Get sfNFTokenMinter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenMinter() const + { + if (hasNFTokenMinter()) + { + return this->tx_->at(sfNFTokenMinter); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenMinter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenMinter() const + { + return this->tx_->isFieldPresent(sfNFTokenMinter); + } +}; + +/** + * @brief Builder for AccountSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class AccountSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new AccountSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + AccountSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttACCOUNT_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a AccountSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + AccountSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttACCOUNT_SET) + { + throw std::runtime_error("Invalid transaction type for AccountSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfEmailHash (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setEmailHash(std::decay_t const& value) + { + object_[sfEmailHash] = value; + return *this; + } + + /** + * @brief Set sfWalletLocator (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setWalletLocator(std::decay_t const& value) + { + object_[sfWalletLocator] = value; + return *this; + } + + /** + * @brief Set sfWalletSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setWalletSize(std::decay_t const& value) + { + object_[sfWalletSize] = value; + return *this; + } + + /** + * @brief Set sfMessageKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setMessageKey(std::decay_t const& value) + { + object_[sfMessageKey] = value; + return *this; + } + + /** + * @brief Set sfDomain (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setDomain(std::decay_t const& value) + { + object_[sfDomain] = value; + return *this; + } + + /** + * @brief Set sfTransferRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setTransferRate(std::decay_t const& value) + { + object_[sfTransferRate] = value; + return *this; + } + + /** + * @brief Set sfSetFlag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setSetFlag(std::decay_t const& value) + { + object_[sfSetFlag] = value; + return *this; + } + + /** + * @brief Set sfClearFlag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setClearFlag(std::decay_t const& value) + { + object_[sfClearFlag] = value; + return *this; + } + + /** + * @brief Set sfTickSize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setTickSize(std::decay_t const& value) + { + object_[sfTickSize] = value; + return *this; + } + + /** + * @brief Set sfNFTokenMinter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + AccountSetBuilder& + setNFTokenMinter(std::decay_t const& value) + { + object_[sfNFTokenMinter] = value; + return *this; + } + + /** + * @brief Build and return the AccountSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + AccountSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return AccountSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Batch.h b/include/xrpl/protocol_autogen/transactions/Batch.h new file mode 100644 index 0000000000..0bb638435c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Batch.h @@ -0,0 +1,164 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class BatchBuilder; + +/** + * @brief Transaction: Batch + * + * Type: ttBATCH (71) + * Delegable: Delegation::notDelegable + * Amendment: featureBatch + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use BatchBuilder to construct new transactions. + */ +class Batch : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttBATCH; + + /** + * @brief Construct a Batch transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Batch(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Batch"); + } + } + + // Transaction-specific field getters + /** + * @brief Get sfRawTransactions (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getRawTransactions() const + { + return this->tx_->getFieldArray(sfRawTransactions); + } + /** + * @brief Get sfBatchSigners (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getBatchSigners() const + { + if (this->tx_->isFieldPresent(sfBatchSigners)) + return this->tx_->getFieldArray(sfBatchSigners); + return std::nullopt; + } + + /** + * @brief Check if sfBatchSigners is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBatchSigners() const + { + return this->tx_->isFieldPresent(sfBatchSigners); + } +}; + +/** + * @brief Builder for Batch transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class BatchBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new BatchBuilder with required fields. + * @param account The account initiating the transaction. + * @param rawTransactions The sfRawTransactions field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + BatchBuilder(SF_ACCOUNT::type::value_type account, + STArray const& rawTransactions, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttBATCH, account, sequence, fee) + { + setRawTransactions(rawTransactions); + } + + /** + * @brief Construct a BatchBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + BatchBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttBATCH) + { + throw std::runtime_error("Invalid transaction type for BatchBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfRawTransactions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + BatchBuilder& + setRawTransactions(STArray const& value) + { + object_.setFieldArray(sfRawTransactions, value); + return *this; + } + + /** + * @brief Set sfBatchSigners (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + BatchBuilder& + setBatchSigners(STArray const& value) + { + object_.setFieldArray(sfBatchSigners, value); + return *this; + } + + /** + * @brief Build and return the Batch wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Batch + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Batch{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCancel.h b/include/xrpl/protocol_autogen/transactions/CheckCancel.h new file mode 100644 index 0000000000..2409e7064c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCancel.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCancelBuilder; + +/** + * @brief Transaction: CheckCancel + * + * Type: ttCHECK_CANCEL (18) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCancelBuilder to construct new transactions. + */ +class CheckCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CANCEL; + + /** + * @brief Construct a CheckCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfCheckID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getCheckID() const + { + return this->tx_->at(sfCheckID); + } +}; + +/** + * @brief Builder for CheckCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param checkID The sfCheckID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& checkID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CANCEL, account, sequence, fee) + { + setCheckID(checkID); + } + + /** + * @brief Construct a CheckCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CANCEL) + { + throw std::runtime_error("Invalid transaction type for CheckCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfCheckID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCancelBuilder& + setCheckID(std::decay_t const& value) + { + object_[sfCheckID] = value; + return *this; + } + + /** + * @brief Build and return the CheckCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCash.h b/include/xrpl/protocol_autogen/transactions/CheckCash.h new file mode 100644 index 0000000000..2a2bc9c7af --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCash.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCashBuilder; + +/** + * @brief Transaction: CheckCash + * + * Type: ttCHECK_CASH (17) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCashBuilder to construct new transactions. + */ +class CheckCash : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CASH; + + /** + * @brief Construct a CheckCash transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCash(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCash"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfCheckID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getCheckID() const + { + return this->tx_->at(sfCheckID); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfDeliverMin (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDeliverMin() const + { + if (hasDeliverMin()) + { + return this->tx_->at(sfDeliverMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfDeliverMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDeliverMin() const + { + return this->tx_->isFieldPresent(sfDeliverMin); + } +}; + +/** + * @brief Builder for CheckCash transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCashBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCashBuilder with required fields. + * @param account The account initiating the transaction. + * @param checkID The sfCheckID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCashBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& checkID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CASH, account, sequence, fee) + { + setCheckID(checkID); + } + + /** + * @brief Construct a CheckCashBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCashBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CASH) + { + throw std::runtime_error("Invalid transaction type for CheckCashBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfCheckID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setCheckID(std::decay_t const& value) + { + object_[sfCheckID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDeliverMin (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCashBuilder& + setDeliverMin(std::decay_t const& value) + { + object_[sfDeliverMin] = value; + return *this; + } + + /** + * @brief Build and return the CheckCash wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCash + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCash{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CheckCreate.h b/include/xrpl/protocol_autogen/transactions/CheckCreate.h new file mode 100644 index 0000000000..ba209a7fc4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CheckCreate.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CheckCreateBuilder; + +/** + * @brief Transaction: CheckCreate + * + * Type: ttCHECK_CREATE (16) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CheckCreateBuilder to construct new transactions. + */ +class CheckCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCHECK_CREATE; + + /** + * @brief Construct a CheckCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CheckCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CheckCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfSendMax (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSendMax() const + { + return this->tx_->at(sfSendMax); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + { + return this->tx_->at(sfInvoiceID); + } + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->tx_->isFieldPresent(sfInvoiceID); + } +}; + +/** + * @brief Builder for CheckCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CheckCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CheckCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param sendMax The sfSendMax field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CheckCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& sendMax, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCHECK_CREATE, account, sequence, fee) + { + setDestination(destination); + setSendMax(sendMax); + } + + /** + * @brief Construct a CheckCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CheckCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCHECK_CREATE) + { + throw std::runtime_error("Invalid transaction type for CheckCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CheckCreateBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Build and return the CheckCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CheckCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CheckCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Clawback.h b/include/xrpl/protocol_autogen/transactions/Clawback.h new file mode 100644 index 0000000000..6c34ceff11 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Clawback.h @@ -0,0 +1,168 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ClawbackBuilder; + +/** + * @brief Transaction: Clawback + * + * Type: ttCLAWBACK (30) + * Delegable: Delegation::delegable + * Amendment: featureClawback + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ClawbackBuilder to construct new transactions. + */ +class Clawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCLAWBACK; + + /** + * @brief Construct a Clawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Clawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Clawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } +}; + +/** + * @brief Builder for Clawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCLAWBACK, account, sequence, fee) + { + setAmount(amount); + } + + /** + * @brief Construct a ClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCLAWBACK) + { + throw std::runtime_error("Invalid transaction type for ClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + ClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Build and return the Clawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Clawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Clawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h new file mode 100644 index 0000000000..c739e4abcb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialAcceptBuilder; + +/** + * @brief Transaction: CredentialAccept + * + * Type: ttCREDENTIAL_ACCEPT (59) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialAcceptBuilder to construct new transactions. + */ +class CredentialAccept : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_ACCEPT; + + /** + * @brief Construct a CredentialAccept transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialAccept(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialAccept"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfIssuer (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getIssuer() const + { + return this->tx_->at(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } +}; + +/** + * @brief Builder for CredentialAccept transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialAcceptBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialAcceptBuilder with required fields. + * @param account The account initiating the transaction. + * @param issuer The sfIssuer field value. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialAcceptBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& issuer, std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_ACCEPT, account, sequence, fee) + { + setIssuer(issuer); + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialAcceptBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialAcceptBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_ACCEPT) + { + throw std::runtime_error("Invalid transaction type for CredentialAcceptBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfIssuer (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialAcceptBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialAcceptBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Build and return the CredentialAccept wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialAccept + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialAccept{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h new file mode 100644 index 0000000000..4da5b3ff47 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h @@ -0,0 +1,227 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialCreateBuilder; + +/** + * @brief Transaction: CredentialCreate + * + * Type: ttCREDENTIAL_CREATE (58) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialCreateBuilder to construct new transactions. + */ +class CredentialCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_CREATE; + + /** + * @brief Construct a CredentialCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSubject (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getSubject() const + { + return this->tx_->at(sfSubject); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } +}; + +/** + * @brief Builder for CredentialCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param subject The sfSubject field value. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& subject, std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_CREATE, account, sequence, fee) + { + setSubject(subject); + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_CREATE) + { + throw std::runtime_error("Invalid transaction type for CredentialCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSubject (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialCreateBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Build and return the CredentialCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h new file mode 100644 index 0000000000..bd840ed104 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class CredentialDeleteBuilder; + +/** + * @brief Transaction: CredentialDelete + * + * Type: ttCREDENTIAL_DELETE (60) + * Delegable: Delegation::delegable + * Amendment: featureCredentials + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use CredentialDeleteBuilder to construct new transactions. + */ +class CredentialDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCREDENTIAL_DELETE; + + /** + * @brief Construct a CredentialDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit CredentialDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for CredentialDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSubject (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSubject() const + { + if (hasSubject()) + { + return this->tx_->at(sfSubject); + } + return std::nullopt; + } + + /** + * @brief Check if sfSubject is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSubject() const + { + return this->tx_->isFieldPresent(sfSubject); + } + + /** + * @brief Get sfIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuer() const + { + if (hasIssuer()) + { + return this->tx_->at(sfIssuer); + } + return std::nullopt; + } + + /** + * @brief Check if sfIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuer() const + { + return this->tx_->isFieldPresent(sfIssuer); + } + + /** + * @brief Get sfCredentialType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getCredentialType() const + { + return this->tx_->at(sfCredentialType); + } +}; + +/** + * @brief Builder for CredentialDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class CredentialDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new CredentialDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param credentialType The sfCredentialType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + CredentialDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& credentialType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttCREDENTIAL_DELETE, account, sequence, fee) + { + setCredentialType(credentialType); + } + + /** + * @brief Construct a CredentialDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + CredentialDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCREDENTIAL_DELETE) + { + throw std::runtime_error("Invalid transaction type for CredentialDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSubject (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setSubject(std::decay_t const& value) + { + object_[sfSubject] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfCredentialType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + CredentialDeleteBuilder& + setCredentialType(std::decay_t const& value) + { + object_[sfCredentialType] = value; + return *this; + } + + /** + * @brief Build and return the CredentialDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + CredentialDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return CredentialDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DIDDelete.h b/include/xrpl/protocol_autogen/transactions/DIDDelete.h new file mode 100644 index 0000000000..c4e61eb19e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DIDDelete.h @@ -0,0 +1,105 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DIDDeleteBuilder; + +/** + * @brief Transaction: DIDDelete + * + * Type: ttDID_DELETE (50) + * Delegable: Delegation::delegable + * Amendment: featureDID + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DIDDeleteBuilder to construct new transactions. + */ +class DIDDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDID_DELETE; + + /** + * @brief Construct a DIDDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DIDDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DIDDelete"); + } + } + + // Transaction-specific field getters +}; + +/** + * @brief Builder for DIDDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DIDDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DIDDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DIDDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDID_DELETE, account, sequence, fee) + { + } + + /** + * @brief Construct a DIDDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DIDDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDID_DELETE) + { + throw std::runtime_error("Invalid transaction type for DIDDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Build and return the DIDDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DIDDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DIDDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DIDSet.h b/include/xrpl/protocol_autogen/transactions/DIDSet.h new file mode 100644 index 0000000000..33313d384c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DIDSet.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DIDSetBuilder; + +/** + * @brief Transaction: DIDSet + * + * Type: ttDID_SET (49) + * Delegable: Delegation::delegable + * Amendment: featureDID + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DIDSetBuilder to construct new transactions. + */ +class DIDSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDID_SET; + + /** + * @brief Construct a DIDSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DIDSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DIDSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDIDDocument (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDIDDocument() const + { + if (hasDIDDocument()) + { + return this->tx_->at(sfDIDDocument); + } + return std::nullopt; + } + + /** + * @brief Check if sfDIDDocument is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDIDDocument() const + { + return this->tx_->isFieldPresent(sfDIDDocument); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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); + } +}; + +/** + * @brief Builder for DIDSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DIDSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DIDSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DIDSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDID_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a DIDSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DIDSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDID_SET) + { + throw std::runtime_error("Invalid transaction type for DIDSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDIDDocument (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setDIDDocument(std::decay_t const& value) + { + object_[sfDIDDocument] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DIDSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Build and return the DIDSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DIDSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DIDSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DelegateSet.h b/include/xrpl/protocol_autogen/transactions/DelegateSet.h new file mode 100644 index 0000000000..cac4b3abef --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DelegateSet.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DelegateSetBuilder; + +/** + * @brief Transaction: DelegateSet + * + * Type: ttDELEGATE_SET (64) + * Delegable: Delegation::notDelegable + * Amendment: featurePermissionDelegationV1_1 + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DelegateSetBuilder to construct new transactions. + */ +class DelegateSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDELEGATE_SET; + + /** + * @brief Construct a DelegateSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DelegateSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DelegateSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAuthorize (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAuthorize() const + { + return this->tx_->at(sfAuthorize); + } + /** + * @brief Get sfPermissions (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPermissions() const + { + return this->tx_->getFieldArray(sfPermissions); + } +}; + +/** + * @brief Builder for DelegateSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DelegateSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DelegateSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param authorize The sfAuthorize field value. + * @param permissions The sfPermissions field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DelegateSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& authorize, STArray const& permissions, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDELEGATE_SET, account, sequence, fee) + { + setAuthorize(authorize); + setPermissions(permissions); + } + + /** + * @brief Construct a DelegateSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DelegateSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDELEGATE_SET) + { + throw std::runtime_error("Invalid transaction type for DelegateSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAuthorize (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateSetBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfPermissions (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + DelegateSetBuilder& + setPermissions(STArray const& value) + { + object_.setFieldArray(sfPermissions, value); + return *this; + } + + /** + * @brief Build and return the DelegateSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DelegateSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DelegateSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h new file mode 100644 index 0000000000..eae8b113a9 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h @@ -0,0 +1,249 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class DepositPreauthBuilder; + +/** + * @brief Transaction: DepositPreauth + * + * Type: ttDEPOSIT_PREAUTH (19) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use DepositPreauthBuilder to construct new transactions. + */ +class DepositPreauth : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttDEPOSIT_PREAUTH; + + /** + * @brief Construct a DepositPreauth transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit DepositPreauth(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for DepositPreauth"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAuthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuthorize() const + { + if (hasAuthorize()) + { + return this->tx_->at(sfAuthorize); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorize() const + { + return this->tx_->isFieldPresent(sfAuthorize); + } + + /** + * @brief Get sfUnauthorize (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getUnauthorize() const + { + if (hasUnauthorize()) + { + return this->tx_->at(sfUnauthorize); + } + return std::nullopt; + } + + /** + * @brief Check if sfUnauthorize is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasUnauthorize() const + { + return this->tx_->isFieldPresent(sfUnauthorize); + } + /** + * @brief Get sfAuthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getAuthorizeCredentials() const + { + if (this->tx_->isFieldPresent(sfAuthorizeCredentials)) + return this->tx_->getFieldArray(sfAuthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfAuthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuthorizeCredentials() const + { + return this->tx_->isFieldPresent(sfAuthorizeCredentials); + } + /** + * @brief Get sfUnauthorizeCredentials (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getUnauthorizeCredentials() const + { + if (this->tx_->isFieldPresent(sfUnauthorizeCredentials)) + return this->tx_->getFieldArray(sfUnauthorizeCredentials); + return std::nullopt; + } + + /** + * @brief Check if sfUnauthorizeCredentials is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasUnauthorizeCredentials() const + { + return this->tx_->isFieldPresent(sfUnauthorizeCredentials); + } +}; + +/** + * @brief Builder for DepositPreauth transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class DepositPreauthBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new DepositPreauthBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + DepositPreauthBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttDEPOSIT_PREAUTH, account, sequence, fee) + { + } + + /** + * @brief Construct a DepositPreauthBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + DepositPreauthBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttDEPOSIT_PREAUTH) + { + throw std::runtime_error("Invalid transaction type for DepositPreauthBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAuthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorize(std::decay_t const& value) + { + object_[sfAuthorize] = value; + return *this; + } + + /** + * @brief Set sfUnauthorize (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setUnauthorize(std::decay_t const& value) + { + object_[sfUnauthorize] = value; + return *this; + } + + /** + * @brief Set sfAuthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setAuthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfAuthorizeCredentials, value); + return *this; + } + + /** + * @brief Set sfUnauthorizeCredentials (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + DepositPreauthBuilder& + setUnauthorizeCredentials(STArray const& value) + { + object_.setFieldArray(sfUnauthorizeCredentials, value); + return *this; + } + + /** + * @brief Build and return the DepositPreauth wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + DepositPreauth + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return DepositPreauth{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h new file mode 100644 index 0000000000..0bf3dfd575 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EnableAmendmentBuilder; + +/** + * @brief Transaction: EnableAmendment + * + * Type: ttAMENDMENT (100) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EnableAmendmentBuilder to construct new transactions. + */ +class EnableAmendment : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttAMENDMENT; + + /** + * @brief Construct a EnableAmendment transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EnableAmendment(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EnableAmendment"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLedgerSequence() const + { + return this->tx_->at(sfLedgerSequence); + } + + /** + * @brief Get sfAmendment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getAmendment() const + { + return this->tx_->at(sfAmendment); + } +}; + +/** + * @brief Builder for EnableAmendment transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EnableAmendmentBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EnableAmendmentBuilder with required fields. + * @param account The account initiating the transaction. + * @param ledgerSequence The sfLedgerSequence field value. + * @param amendment The sfAmendment field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EnableAmendmentBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ledgerSequence, std::decay_t const& amendment, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttAMENDMENT, account, sequence, fee) + { + setLedgerSequence(ledgerSequence); + setAmendment(amendment); + } + + /** + * @brief Construct a EnableAmendmentBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EnableAmendmentBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttAMENDMENT) + { + throw std::runtime_error("Invalid transaction type for EnableAmendmentBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EnableAmendmentBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfAmendment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EnableAmendmentBuilder& + setAmendment(std::decay_t const& value) + { + object_[sfAmendment] = value; + return *this; + } + + /** + * @brief Build and return the EnableAmendment wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EnableAmendment + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EnableAmendment{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h new file mode 100644 index 0000000000..a92d5b10ae --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h @@ -0,0 +1,153 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowCancelBuilder; + +/** + * @brief Transaction: EscrowCancel + * + * Type: ttESCROW_CANCEL (4) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowCancelBuilder to construct new transactions. + */ +class EscrowCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_CANCEL; + + /** + * @brief Construct a EscrowCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->tx_->at(sfOwner); + } + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } +}; + +/** + * @brief Builder for EscrowCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param owner The sfOwner field value. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& owner, std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_CANCEL, account, sequence, fee) + { + setOwner(owner); + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a EscrowCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_CANCEL) + { + throw std::runtime_error("Invalid transaction type for EscrowCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCancelBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCancelBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Build and return the EscrowCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h new file mode 100644 index 0000000000..32dae6cc57 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h @@ -0,0 +1,303 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowCreateBuilder; + +/** + * @brief Transaction: EscrowCreate + * + * Type: ttESCROW_CREATE (1) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowCreateBuilder to construct new transactions. + */ +class EscrowCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_CREATE; + + /** + * @brief Construct a EscrowCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + { + return this->tx_->at(sfCondition); + } + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->tx_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + { + return this->tx_->at(sfCancelAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->tx_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfFinishAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFinishAfter() const + { + if (hasFinishAfter()) + { + return this->tx_->at(sfFinishAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfFinishAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFinishAfter() const + { + return this->tx_->isFieldPresent(sfFinishAfter); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 Builder for EscrowCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_CREATE, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a EscrowCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_CREATE) + { + throw std::runtime_error("Invalid transaction type for EscrowCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfFinishAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setFinishAfter(std::decay_t const& value) + { + object_[sfFinishAfter] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the EscrowCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h new file mode 100644 index 0000000000..20e9a089fb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class EscrowFinishBuilder; + +/** + * @brief Transaction: EscrowFinish + * + * Type: ttESCROW_FINISH (2) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use EscrowFinishBuilder to construct new transactions. + */ +class EscrowFinish : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttESCROW_FINISH; + + /** + * @brief Construct a EscrowFinish transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit EscrowFinish(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for EscrowFinish"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOwner (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->tx_->at(sfOwner); + } + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } + + /** + * @brief Get sfFulfillment (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFulfillment() const + { + if (hasFulfillment()) + { + return this->tx_->at(sfFulfillment); + } + return std::nullopt; + } + + /** + * @brief Check if sfFulfillment is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFulfillment() const + { + return this->tx_->isFieldPresent(sfFulfillment); + } + + /** + * @brief Get sfCondition (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCondition() const + { + if (hasCondition()) + { + return this->tx_->at(sfCondition); + } + return std::nullopt; + } + + /** + * @brief Check if sfCondition is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCondition() const + { + return this->tx_->isFieldPresent(sfCondition); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for EscrowFinish transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class EscrowFinishBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new EscrowFinishBuilder with required fields. + * @param account The account initiating the transaction. + * @param owner The sfOwner field value. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + EscrowFinishBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& owner, std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttESCROW_FINISH, account, sequence, fee) + { + setOwner(owner); + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a EscrowFinishBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + EscrowFinishBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttESCROW_FINISH) + { + throw std::runtime_error("Invalid transaction type for EscrowFinishBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOwner (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Set sfFulfillment (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setFulfillment(std::decay_t const& value) + { + object_[sfFulfillment] = value; + return *this; + } + + /** + * @brief Set sfCondition (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setCondition(std::decay_t const& value) + { + object_[sfCondition] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + EscrowFinishBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the EscrowFinish wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + EscrowFinish + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return EscrowFinish{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h new file mode 100644 index 0000000000..6f60b81a04 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LedgerStateFixBuilder; + +/** + * @brief Transaction: LedgerStateFix + * + * Type: ttLEDGER_STATE_FIX (53) + * Delegable: Delegation::delegable + * Amendment: fixNFTokenPageLinks + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LedgerStateFixBuilder to construct new transactions. + */ +class LedgerStateFix : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLEDGER_STATE_FIX; + + /** + * @brief Construct a LedgerStateFix transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LedgerStateFix(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LedgerStateFix"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerFixType (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT16::type::value_type + getLedgerFixType() const + { + return this->tx_->at(sfLedgerFixType); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } +}; + +/** + * @brief Builder for LedgerStateFix transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LedgerStateFixBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LedgerStateFixBuilder with required fields. + * @param account The account initiating the transaction. + * @param ledgerFixType The sfLedgerFixType field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LedgerStateFixBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ledgerFixType, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLEDGER_STATE_FIX, account, sequence, fee) + { + setLedgerFixType(ledgerFixType); + } + + /** + * @brief Construct a LedgerStateFixBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LedgerStateFixBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLEDGER_STATE_FIX) + { + throw std::runtime_error("Invalid transaction type for LedgerStateFixBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerFixType (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LedgerStateFixBuilder& + setLedgerFixType(std::decay_t const& value) + { + object_[sfLedgerFixType] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LedgerStateFixBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Build and return the LedgerStateFix wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LedgerStateFix + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LedgerStateFix{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h new file mode 100644 index 0000000000..d8a9d9d52a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h @@ -0,0 +1,181 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverClawbackBuilder; + +/** + * @brief Transaction: LoanBrokerCoverClawback + * + * Type: ttLOAN_BROKER_COVER_CLAWBACK (78) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverClawbackBuilder to construct new transactions. + */ +class LoanBrokerCoverClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_CLAWBACK; + + /** + * @brief Construct a LoanBrokerCoverClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + { + return this->tx_->at(sfLoanBrokerID); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->tx_->isFieldPresent(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for LoanBrokerCoverClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_CLAWBACK, account, sequence, fee) + { + } + + /** + * @brief Construct a LoanBrokerCoverClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverClawbackBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h new file mode 100644 index 0000000000..baa567a7ac --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverDepositBuilder; + +/** + * @brief Transaction: LoanBrokerCoverDeposit + * + * Type: ttLOAN_BROKER_COVER_DEPOSIT (76) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverDepositBuilder to construct new transactions. + */ +class LoanBrokerCoverDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_DEPOSIT; + + /** + * @brief Construct a LoanBrokerCoverDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for LoanBrokerCoverDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_DEPOSIT, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setAmount(amount); + } + + /** + * @brief Construct a LoanBrokerCoverDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverDepositBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h new file mode 100644 index 0000000000..3690b6a40a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h @@ -0,0 +1,229 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerCoverWithdrawBuilder; + +/** + * @brief Transaction: LoanBrokerCoverWithdraw + * + * Type: ttLOAN_BROKER_COVER_WITHDRAW (77) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerCoverWithdrawBuilder to construct new transactions. + */ +class LoanBrokerCoverWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_COVER_WITHDRAW; + + /** + * @brief Construct a LoanBrokerCoverWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerCoverWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 Builder for LoanBrokerCoverWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerCoverWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerCoverWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerCoverWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_COVER_WITHDRAW, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setAmount(amount); + } + + /** + * @brief Construct a LoanBrokerCoverWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerCoverWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_COVER_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerCoverWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerCoverWithdrawBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerCoverWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerCoverWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerCoverWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h new file mode 100644 index 0000000000..2c174ae500 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerDeleteBuilder; + +/** + * @brief Transaction: LoanBrokerDelete + * + * Type: ttLOAN_BROKER_DELETE (75) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mustDeleteAcct | mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerDeleteBuilder to construct new transactions. + */ +class LoanBrokerDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_DELETE; + + /** + * @brief Construct a LoanBrokerDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } +}; + +/** + * @brief Builder for LoanBrokerDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_DELETE, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + } + + /** + * @brief Construct a LoanBrokerDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_DELETE) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerDeleteBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h new file mode 100644 index 0000000000..ba6ca06266 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h @@ -0,0 +1,351 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanBrokerSetBuilder; + +/** + * @brief Transaction: LoanBrokerSet + * + * Type: ttLOAN_BROKER_SET (74) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: createPseudoAcct | mayAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanBrokerSetBuilder to construct new transactions. + */ +class LoanBrokerSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_BROKER_SET; + + /** + * @brief Construct a LoanBrokerSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanBrokerSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfLoanBrokerID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanBrokerID() const + { + if (hasLoanBrokerID()) + { + return this->tx_->at(sfLoanBrokerID); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanBrokerID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanBrokerID() const + { + return this->tx_->isFieldPresent(sfLoanBrokerID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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); + } + + /** + * @brief Get sfManagementFeeRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getManagementFeeRate() const + { + if (hasManagementFeeRate()) + { + return this->tx_->at(sfManagementFeeRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfManagementFeeRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasManagementFeeRate() const + { + return this->tx_->isFieldPresent(sfManagementFeeRate); + } + + /** + * @brief Get sfDebtMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDebtMaximum() const + { + if (hasDebtMaximum()) + { + return this->tx_->at(sfDebtMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfDebtMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDebtMaximum() const + { + return this->tx_->isFieldPresent(sfDebtMaximum); + } + + /** + * @brief Get sfCoverRateMinimum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateMinimum() const + { + if (hasCoverRateMinimum()) + { + return this->tx_->at(sfCoverRateMinimum); + } + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateMinimum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateMinimum() const + { + return this->tx_->isFieldPresent(sfCoverRateMinimum); + } + + /** + * @brief Get sfCoverRateLiquidation (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCoverRateLiquidation() const + { + if (hasCoverRateLiquidation()) + { + return this->tx_->at(sfCoverRateLiquidation); + } + return std::nullopt; + } + + /** + * @brief Check if sfCoverRateLiquidation is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCoverRateLiquidation() const + { + return this->tx_->isFieldPresent(sfCoverRateLiquidation); + } +}; + +/** + * @brief Builder for LoanBrokerSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanBrokerSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanBrokerSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanBrokerSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_BROKER_SET, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a LoanBrokerSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanBrokerSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_BROKER_SET) + { + throw std::runtime_error("Invalid transaction type for LoanBrokerSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfLoanBrokerID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfManagementFeeRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setManagementFeeRate(std::decay_t const& value) + { + object_[sfManagementFeeRate] = value; + return *this; + } + + /** + * @brief Set sfDebtMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setDebtMaximum(std::decay_t const& value) + { + object_[sfDebtMaximum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateMinimum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setCoverRateMinimum(std::decay_t const& value) + { + object_[sfCoverRateMinimum] = value; + return *this; + } + + /** + * @brief Set sfCoverRateLiquidation (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanBrokerSetBuilder& + setCoverRateLiquidation(std::decay_t const& value) + { + object_[sfCoverRateLiquidation] = value; + return *this; + } + + /** + * @brief Build and return the LoanBrokerSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanBrokerSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanBrokerSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanDelete.h b/include/xrpl/protocol_autogen/transactions/LoanDelete.h new file mode 100644 index 0000000000..df1b49d17e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanDeleteBuilder; + +/** + * @brief Transaction: LoanDelete + * + * Type: ttLOAN_DELETE (81) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanDeleteBuilder to construct new transactions. + */ +class LoanDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_DELETE; + + /** + * @brief Construct a LoanDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } +}; + +/** + * @brief Builder for LoanDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_DELETE, account, sequence, fee) + { + setLoanID(loanID); + } + + /** + * @brief Construct a LoanDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_DELETE) + { + throw std::runtime_error("Invalid transaction type for LoanDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanDeleteBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Build and return the LoanDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanManage.h b/include/xrpl/protocol_autogen/transactions/LoanManage.h new file mode 100644 index 0000000000..3da31e4487 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanManage.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanManageBuilder; + +/** + * @brief Transaction: LoanManage + * + * Type: ttLOAN_MANAGE (82) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanManageBuilder to construct new transactions. + */ +class LoanManage : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_MANAGE; + + /** + * @brief Construct a LoanManage transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanManage(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanManage"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } +}; + +/** + * @brief Builder for LoanManage transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanManageBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanManageBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanManageBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_MANAGE, account, sequence, fee) + { + setLoanID(loanID); + } + + /** + * @brief Construct a LoanManageBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanManageBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_MANAGE) + { + throw std::runtime_error("Invalid transaction type for LoanManageBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanManageBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Build and return the LoanManage wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanManage + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanManage{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanPay.h b/include/xrpl/protocol_autogen/transactions/LoanPay.h new file mode 100644 index 0000000000..e0b4376e18 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanPay.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanPayBuilder; + +/** + * @brief Transaction: LoanPay + * + * Type: ttLOAN_PAY (84) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanPayBuilder to construct new transactions. + */ +class LoanPay : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_PAY; + + /** + * @brief Construct a LoanPay transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanPay(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanPay"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanID() const + { + return this->tx_->at(sfLoanID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for LoanPay transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanPayBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanPayBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanID The sfLoanID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanPayBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_PAY, account, sequence, fee) + { + setLoanID(loanID); + setAmount(amount); + } + + /** + * @brief Construct a LoanPayBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanPayBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_PAY) + { + throw std::runtime_error("Invalid transaction type for LoanPayBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanPayBuilder& + setLoanID(std::decay_t const& value) + { + object_[sfLoanID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + LoanPayBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the LoanPay wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanPay + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanPay{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/LoanSet.h b/include/xrpl/protocol_autogen/transactions/LoanSet.h new file mode 100644 index 0000000000..7d6181f517 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/LoanSet.h @@ -0,0 +1,706 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class LoanSetBuilder; + +/** + * @brief Transaction: LoanSet + * + * Type: ttLOAN_SET (80) + * Delegable: Delegation::notDelegable + * Amendment: featureLendingProtocol + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use LoanSetBuilder to construct new transactions. + */ +class LoanSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttLOAN_SET; + + /** + * @brief Construct a LoanSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit LoanSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for LoanSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLoanBrokerID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getLoanBrokerID() const + { + return this->tx_->at(sfLoanBrokerID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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); + } + + /** + * @brief Get sfCounterparty (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCounterparty() const + { + if (hasCounterparty()) + { + return this->tx_->at(sfCounterparty); + } + return std::nullopt; + } + + /** + * @brief Check if sfCounterparty is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCounterparty() const + { + return this->tx_->isFieldPresent(sfCounterparty); + } + /** + * @brief Get sfCounterpartySignature (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional + getCounterpartySignature() const + { + if (this->tx_->isFieldPresent(sfCounterpartySignature)) + return this->tx_->getFieldObject(sfCounterpartySignature); + return std::nullopt; + } + + /** + * @brief Check if sfCounterpartySignature is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCounterpartySignature() const + { + return this->tx_->isFieldPresent(sfCounterpartySignature); + } + + /** + * @brief Get sfLoanOriginationFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanOriginationFee() const + { + if (hasLoanOriginationFee()) + { + return this->tx_->at(sfLoanOriginationFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanOriginationFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanOriginationFee() const + { + return this->tx_->isFieldPresent(sfLoanOriginationFee); + } + + /** + * @brief Get sfLoanServiceFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLoanServiceFee() const + { + if (hasLoanServiceFee()) + { + return this->tx_->at(sfLoanServiceFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLoanServiceFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLoanServiceFee() const + { + return this->tx_->isFieldPresent(sfLoanServiceFee); + } + + /** + * @brief Get sfLatePaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLatePaymentFee() const + { + if (hasLatePaymentFee()) + { + return this->tx_->at(sfLatePaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfLatePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLatePaymentFee() const + { + return this->tx_->isFieldPresent(sfLatePaymentFee); + } + + /** + * @brief Get sfClosePaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getClosePaymentFee() const + { + if (hasClosePaymentFee()) + { + return this->tx_->at(sfClosePaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfClosePaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasClosePaymentFee() const + { + return this->tx_->isFieldPresent(sfClosePaymentFee); + } + + /** + * @brief Get sfOverpaymentFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentFee() const + { + if (hasOverpaymentFee()) + { + return this->tx_->at(sfOverpaymentFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentFee() const + { + return this->tx_->isFieldPresent(sfOverpaymentFee); + } + + /** + * @brief Get sfInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInterestRate() const + { + if (hasInterestRate()) + { + return this->tx_->at(sfInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInterestRate() const + { + return this->tx_->isFieldPresent(sfInterestRate); + } + + /** + * @brief Get sfLateInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLateInterestRate() const + { + if (hasLateInterestRate()) + { + return this->tx_->at(sfLateInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfLateInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLateInterestRate() const + { + return this->tx_->isFieldPresent(sfLateInterestRate); + } + + /** + * @brief Get sfCloseInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCloseInterestRate() const + { + if (hasCloseInterestRate()) + { + return this->tx_->at(sfCloseInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfCloseInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCloseInterestRate() const + { + return this->tx_->isFieldPresent(sfCloseInterestRate); + } + + /** + * @brief Get sfOverpaymentInterestRate (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOverpaymentInterestRate() const + { + if (hasOverpaymentInterestRate()) + { + return this->tx_->at(sfOverpaymentInterestRate); + } + return std::nullopt; + } + + /** + * @brief Check if sfOverpaymentInterestRate is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOverpaymentInterestRate() const + { + return this->tx_->isFieldPresent(sfOverpaymentInterestRate); + } + + /** + * @brief Get sfPrincipalRequested (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_NUMBER::type::value_type + getPrincipalRequested() const + { + return this->tx_->at(sfPrincipalRequested); + } + + /** + * @brief Get sfPaymentTotal (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentTotal() const + { + if (hasPaymentTotal()) + { + return this->tx_->at(sfPaymentTotal); + } + return std::nullopt; + } + + /** + * @brief Check if sfPaymentTotal is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentTotal() const + { + return this->tx_->isFieldPresent(sfPaymentTotal); + } + + /** + * @brief Get sfPaymentInterval (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPaymentInterval() const + { + if (hasPaymentInterval()) + { + return this->tx_->at(sfPaymentInterval); + } + return std::nullopt; + } + + /** + * @brief Check if sfPaymentInterval is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaymentInterval() const + { + return this->tx_->isFieldPresent(sfPaymentInterval); + } + + /** + * @brief Get sfGracePeriod (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getGracePeriod() const + { + if (hasGracePeriod()) + { + return this->tx_->at(sfGracePeriod); + } + return std::nullopt; + } + + /** + * @brief Check if sfGracePeriod is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasGracePeriod() const + { + return this->tx_->isFieldPresent(sfGracePeriod); + } +}; + +/** + * @brief Builder for LoanSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class LoanSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new LoanSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param loanBrokerID The sfLoanBrokerID field value. + * @param principalRequested The sfPrincipalRequested field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + LoanSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& loanBrokerID, std::decay_t const& principalRequested, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttLOAN_SET, account, sequence, fee) + { + setLoanBrokerID(loanBrokerID); + setPrincipalRequested(principalRequested); + } + + /** + * @brief Construct a LoanSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + LoanSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttLOAN_SET) + { + throw std::runtime_error("Invalid transaction type for LoanSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLoanBrokerID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanBrokerID(std::decay_t const& value) + { + object_[sfLoanBrokerID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfCounterparty (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCounterparty(std::decay_t const& value) + { + object_[sfCounterparty] = value; + return *this; + } + + /** + * @brief Set sfCounterpartySignature (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCounterpartySignature(STObject const& value) + { + object_.setFieldObject(sfCounterpartySignature, value); + return *this; + } + + /** + * @brief Set sfLoanOriginationFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanOriginationFee(std::decay_t const& value) + { + object_[sfLoanOriginationFee] = value; + return *this; + } + + /** + * @brief Set sfLoanServiceFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLoanServiceFee(std::decay_t const& value) + { + object_[sfLoanServiceFee] = value; + return *this; + } + + /** + * @brief Set sfLatePaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLatePaymentFee(std::decay_t const& value) + { + object_[sfLatePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfClosePaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setClosePaymentFee(std::decay_t const& value) + { + object_[sfClosePaymentFee] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setOverpaymentFee(std::decay_t const& value) + { + object_[sfOverpaymentFee] = value; + return *this; + } + + /** + * @brief Set sfInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setInterestRate(std::decay_t const& value) + { + object_[sfInterestRate] = value; + return *this; + } + + /** + * @brief Set sfLateInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setLateInterestRate(std::decay_t const& value) + { + object_[sfLateInterestRate] = value; + return *this; + } + + /** + * @brief Set sfCloseInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setCloseInterestRate(std::decay_t const& value) + { + object_[sfCloseInterestRate] = value; + return *this; + } + + /** + * @brief Set sfOverpaymentInterestRate (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setOverpaymentInterestRate(std::decay_t const& value) + { + object_[sfOverpaymentInterestRate] = value; + return *this; + } + + /** + * @brief Set sfPrincipalRequested (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPrincipalRequested(std::decay_t const& value) + { + object_[sfPrincipalRequested] = value; + return *this; + } + + /** + * @brief Set sfPaymentTotal (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPaymentTotal(std::decay_t const& value) + { + object_[sfPaymentTotal] = value; + return *this; + } + + /** + * @brief Set sfPaymentInterval (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setPaymentInterval(std::decay_t const& value) + { + object_[sfPaymentInterval] = value; + return *this; + } + + /** + * @brief Set sfGracePeriod (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + LoanSetBuilder& + setGracePeriod(std::decay_t const& value) + { + object_[sfGracePeriod] = value; + return *this; + } + + /** + * @brief Build and return the LoanSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + LoanSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return LoanSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h new file mode 100644 index 0000000000..a4641f5dc0 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenAuthorizeBuilder; + +/** + * @brief Transaction: MPTokenAuthorize + * + * Type: ttMPTOKEN_AUTHORIZE (57) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: mustAuthorizeMPT + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenAuthorizeBuilder to construct new transactions. + */ +class MPTokenAuthorize : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_AUTHORIZE; + + /** + * @brief Construct a MPTokenAuthorize transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenAuthorize(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenAuthorize"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } +}; + +/** + * @brief Builder for MPTokenAuthorize transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenAuthorizeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenAuthorizeBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenAuthorizeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_AUTHORIZE, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenAuthorizeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenAuthorizeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_AUTHORIZE) + { + throw std::runtime_error("Invalid transaction type for MPTokenAuthorizeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenAuthorizeBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenAuthorizeBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenAuthorize wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenAuthorize + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenAuthorize{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h new file mode 100644 index 0000000000..6f5c50fc4a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h @@ -0,0 +1,327 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceCreateBuilder; + +/** + * @brief Transaction: MPTokenIssuanceCreate + * + * Type: ttMPTOKEN_ISSUANCE_CREATE (54) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: createMPTIssuance + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceCreateBuilder to construct new transactions. + */ +class MPTokenIssuanceCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_CREATE; + + /** + * @brief Construct a MPTokenIssuanceCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAssetScale (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetScale() const + { + if (hasAssetScale()) + { + return this->tx_->at(sfAssetScale); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetScale() const + { + return this->tx_->isFieldPresent(sfAssetScale); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfMaximumAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaximumAmount() const + { + if (hasMaximumAmount()) + { + return this->tx_->at(sfMaximumAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMaximumAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaximumAmount() const + { + return this->tx_->isFieldPresent(sfMaximumAmount); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMutableFlags (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + { + return this->tx_->at(sfMutableFlags); + } + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->tx_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuanceCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceCreateBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_CREATE, account, sequence, fee) + { + } + + /** + * @brief Construct a MPTokenIssuanceCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_CREATE) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAssetScale (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setAssetScale(std::decay_t const& value) + { + object_[sfAssetScale] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfMaximumAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMaximumAmount(std::decay_t const& value) + { + object_[sfMaximumAmount] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceCreateBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h new file mode 100644 index 0000000000..28d2e49971 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceDestroyBuilder; + +/** + * @brief Transaction: MPTokenIssuanceDestroy + * + * Type: ttMPTOKEN_ISSUANCE_DESTROY (55) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: destroyMPTIssuance + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceDestroyBuilder to construct new transactions. + */ +class MPTokenIssuanceDestroy : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_DESTROY; + + /** + * @brief Construct a MPTokenIssuanceDestroy transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceDestroy(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceDestroy"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } +}; + +/** + * @brief Builder for MPTokenIssuanceDestroy transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceDestroyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceDestroyBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceDestroyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_DESTROY, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenIssuanceDestroyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceDestroyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_DESTROY) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceDestroyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceDestroyBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceDestroy wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceDestroy + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceDestroy{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h new file mode 100644 index 0000000000..429b252e95 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -0,0 +1,314 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class MPTokenIssuanceSetBuilder; + +/** + * @brief Transaction: MPTokenIssuanceSet + * + * Type: ttMPTOKEN_ISSUANCE_SET (56) + * Delegable: Delegation::delegable + * Amendment: featureMPTokensV1 + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use MPTokenIssuanceSetBuilder to construct new transactions. + */ +class MPTokenIssuanceSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttMPTOKEN_ISSUANCE_SET; + + /** + * @brief Construct a MPTokenIssuanceSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit MPTokenIssuanceSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfHolder (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolder() const + { + if (hasHolder()) + { + return this->tx_->at(sfHolder); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolder is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolder() const + { + return this->tx_->isFieldPresent(sfHolder); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfMutableFlags (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMutableFlags() const + { + if (hasMutableFlags()) + { + return this->tx_->at(sfMutableFlags); + } + return std::nullopt; + } + + /** + * @brief Check if sfMutableFlags is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMutableFlags() const + { + return this->tx_->isFieldPresent(sfMutableFlags); + } +}; + +/** + * @brief Builder for MPTokenIssuanceSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class MPTokenIssuanceSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new MPTokenIssuanceSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + MPTokenIssuanceSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_SET, account, sequence, fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a MPTokenIssuanceSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + MPTokenIssuanceSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttMPTOKEN_ISSUANCE_SET) + { + throw std::runtime_error("Invalid transaction type for MPTokenIssuanceSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfMutableFlags (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setMutableFlags(std::decay_t const& value) + { + object_[sfMutableFlags] = value; + return *this; + } + + /** + * @brief Build and return the MPTokenIssuanceSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + MPTokenIssuanceSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return MPTokenIssuanceSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h new file mode 100644 index 0000000000..53ae614ee4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenAcceptOfferBuilder; + +/** + * @brief Transaction: NFTokenAcceptOffer + * + * Type: ttNFTOKEN_ACCEPT_OFFER (29) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenAcceptOfferBuilder to construct new transactions. + */ +class NFTokenAcceptOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_ACCEPT_OFFER; + + /** + * @brief Construct a NFTokenAcceptOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenAcceptOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenAcceptOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenBuyOffer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenBuyOffer() const + { + if (hasNFTokenBuyOffer()) + { + return this->tx_->at(sfNFTokenBuyOffer); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenBuyOffer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenBuyOffer() const + { + return this->tx_->isFieldPresent(sfNFTokenBuyOffer); + } + + /** + * @brief Get sfNFTokenSellOffer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenSellOffer() const + { + if (hasNFTokenSellOffer()) + { + return this->tx_->at(sfNFTokenSellOffer); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenSellOffer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenSellOffer() const + { + return this->tx_->isFieldPresent(sfNFTokenSellOffer); + } + + /** + * @brief Get sfNFTokenBrokerFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getNFTokenBrokerFee() const + { + if (hasNFTokenBrokerFee()) + { + return this->tx_->at(sfNFTokenBrokerFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfNFTokenBrokerFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasNFTokenBrokerFee() const + { + return this->tx_->isFieldPresent(sfNFTokenBrokerFee); + } +}; + +/** + * @brief Builder for NFTokenAcceptOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenAcceptOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenAcceptOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenAcceptOfferBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_ACCEPT_OFFER, account, sequence, fee) + { + } + + /** + * @brief Construct a NFTokenAcceptOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenAcceptOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_ACCEPT_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenAcceptOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenBuyOffer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenBuyOffer(std::decay_t const& value) + { + object_[sfNFTokenBuyOffer] = value; + return *this; + } + + /** + * @brief Set sfNFTokenSellOffer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenSellOffer(std::decay_t const& value) + { + object_[sfNFTokenSellOffer] = value; + return *this; + } + + /** + * @brief Set sfNFTokenBrokerFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenAcceptOfferBuilder& + setNFTokenBrokerFee(std::decay_t const& value) + { + object_[sfNFTokenBrokerFee] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenAcceptOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenAcceptOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenAcceptOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h new file mode 100644 index 0000000000..123418d00a --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenBurnBuilder; + +/** + * @brief Transaction: NFTokenBurn + * + * Type: ttNFTOKEN_BURN (26) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: changeNFTCounts + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenBurnBuilder to construct new transactions. + */ +class NFTokenBurn : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_BURN; + + /** + * @brief Construct a NFTokenBurn transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenBurn(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenBurn"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } +}; + +/** + * @brief Builder for NFTokenBurn transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenBurnBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenBurnBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenBurnBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_BURN, account, sequence, fee) + { + setNFTokenID(nFTokenID); + } + + /** + * @brief Construct a NFTokenBurnBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenBurnBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_BURN) + { + throw std::runtime_error("Invalid transaction type for NFTokenBurnBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenBurnBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenBurnBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenBurn wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenBurn + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenBurn{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h new file mode 100644 index 0000000000..5fbbeca96c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenCancelOfferBuilder; + +/** + * @brief Transaction: NFTokenCancelOffer + * + * Type: ttNFTOKEN_CANCEL_OFFER (28) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenCancelOfferBuilder to construct new transactions. + */ +class NFTokenCancelOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_CANCEL_OFFER; + + /** + * @brief Construct a NFTokenCancelOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenCancelOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenCancelOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenOffers (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VECTOR256::type::value_type + getNFTokenOffers() const + { + return this->tx_->at(sfNFTokenOffers); + } +}; + +/** + * @brief Builder for NFTokenCancelOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenCancelOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenCancelOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenOffers The sfNFTokenOffers field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenCancelOfferBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenOffers, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_CANCEL_OFFER, account, sequence, fee) + { + setNFTokenOffers(nFTokenOffers); + } + + /** + * @brief Construct a NFTokenCancelOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenCancelOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_CANCEL_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenCancelOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenOffers (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCancelOfferBuilder& + setNFTokenOffers(std::decay_t const& value) + { + object_[sfNFTokenOffers] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenCancelOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenCancelOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenCancelOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h new file mode 100644 index 0000000000..e820272f83 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenCreateOfferBuilder; + +/** + * @brief Transaction: NFTokenCreateOffer + * + * Type: ttNFTOKEN_CREATE_OFFER (27) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenCreateOfferBuilder to construct new transactions. + */ +class NFTokenCreateOffer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_CREATE_OFFER; + + /** + * @brief Construct a NFTokenCreateOffer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenCreateOffer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenCreateOffer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for NFTokenCreateOffer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenCreateOfferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenCreateOfferBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenCreateOfferBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_CREATE_OFFER, account, sequence, fee) + { + setNFTokenID(nFTokenID); + setAmount(amount); + } + + /** + * @brief Construct a NFTokenCreateOfferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenCreateOfferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_CREATE_OFFER) + { + throw std::runtime_error("Invalid transaction type for NFTokenCreateOfferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenCreateOfferBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenCreateOffer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenCreateOffer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenCreateOffer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h new file mode 100644 index 0000000000..9854147570 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h @@ -0,0 +1,351 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenMintBuilder; + +/** + * @brief Transaction: NFTokenMint + * + * Type: ttNFTOKEN_MINT (25) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: changeNFTCounts + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenMintBuilder to construct new transactions. + */ +class NFTokenMint : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_MINT; + + /** + * @brief Construct a NFTokenMint transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenMint(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenMint"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenTaxon (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getNFTokenTaxon() const + { + return this->tx_->at(sfNFTokenTaxon); + } + + /** + * @brief Get sfTransferFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getTransferFee() const + { + if (hasTransferFee()) + { + return this->tx_->at(sfTransferFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfTransferFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasTransferFee() const + { + return this->tx_->isFieldPresent(sfTransferFee); + } + + /** + * @brief Get sfIssuer (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuer() const + { + if (hasIssuer()) + { + return this->tx_->at(sfIssuer); + } + return std::nullopt; + } + + /** + * @brief Check if sfIssuer is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuer() const + { + return this->tx_->isFieldPresent(sfIssuer); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for NFTokenMint transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenMintBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenMintBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenTaxon The sfNFTokenTaxon field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenMintBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenTaxon, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_MINT, account, sequence, fee) + { + setNFTokenTaxon(nFTokenTaxon); + } + + /** + * @brief Construct a NFTokenMintBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenMintBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_MINT) + { + throw std::runtime_error("Invalid transaction type for NFTokenMintBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenTaxon (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setNFTokenTaxon(std::decay_t const& value) + { + object_[sfNFTokenTaxon] = value; + return *this; + } + + /** + * @brief Set sfTransferFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setTransferFee(std::decay_t const& value) + { + object_[sfTransferFee] = value; + return *this; + } + + /** + * @brief Set sfIssuer (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setIssuer(std::decay_t const& value) + { + object_[sfIssuer] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenMintBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenMint wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenMint + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenMint{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h new file mode 100644 index 0000000000..6f9b75532c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class NFTokenModifyBuilder; + +/** + * @brief Transaction: NFTokenModify + * + * Type: ttNFTOKEN_MODIFY (61) + * Delegable: Delegation::delegable + * Amendment: featureDynamicNFT + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use NFTokenModifyBuilder to construct new transactions. + */ +class NFTokenModify : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttNFTOKEN_MODIFY; + + /** + * @brief Construct a NFTokenModify transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit NFTokenModify(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for NFTokenModify"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfNFTokenID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getNFTokenID() const + { + return this->tx_->at(sfNFTokenID); + } + + /** + * @brief Get sfOwner (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOwner() const + { + if (hasOwner()) + { + return this->tx_->at(sfOwner); + } + return std::nullopt; + } + + /** + * @brief Check if sfOwner is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOwner() const + { + return this->tx_->isFieldPresent(sfOwner); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } +}; + +/** + * @brief Builder for NFTokenModify transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class NFTokenModifyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new NFTokenModifyBuilder with required fields. + * @param account The account initiating the transaction. + * @param nFTokenID The sfNFTokenID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + NFTokenModifyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& nFTokenID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttNFTOKEN_MODIFY, account, sequence, fee) + { + setNFTokenID(nFTokenID); + } + + /** + * @brief Construct a NFTokenModifyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + NFTokenModifyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttNFTOKEN_MODIFY) + { + throw std::runtime_error("Invalid transaction type for NFTokenModifyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfNFTokenID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setNFTokenID(std::decay_t const& value) + { + object_[sfNFTokenID] = value; + return *this; + } + + /** + * @brief Set sfOwner (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + NFTokenModifyBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Build and return the NFTokenModify wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + NFTokenModify + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return NFTokenModify{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OfferCancel.h b/include/xrpl/protocol_autogen/transactions/OfferCancel.h new file mode 100644 index 0000000000..ed2c761c0e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OfferCancel.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OfferCancelBuilder; + +/** + * @brief Transaction: OfferCancel + * + * Type: ttOFFER_CANCEL (8) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OfferCancelBuilder to construct new transactions. + */ +class OfferCancel : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttOFFER_CANCEL; + + /** + * @brief Construct a OfferCancel transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OfferCancel(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OfferCancel"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOfferSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOfferSequence() const + { + return this->tx_->at(sfOfferSequence); + } +}; + +/** + * @brief Builder for OfferCancel transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OfferCancelBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OfferCancelBuilder with required fields. + * @param account The account initiating the transaction. + * @param offerSequence The sfOfferSequence field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OfferCancelBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& offerSequence, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttOFFER_CANCEL, account, sequence, fee) + { + setOfferSequence(offerSequence); + } + + /** + * @brief Construct a OfferCancelBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OfferCancelBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttOFFER_CANCEL) + { + throw std::runtime_error("Invalid transaction type for OfferCancelBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOfferSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCancelBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Build and return the OfferCancel wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OfferCancel + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OfferCancel{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OfferCreate.h b/include/xrpl/protocol_autogen/transactions/OfferCreate.h new file mode 100644 index 0000000000..a0eff385f6 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OfferCreate.h @@ -0,0 +1,264 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OfferCreateBuilder; + +/** + * @brief Transaction: OfferCreate + * + * Type: ttOFFER_CREATE (7) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OfferCreateBuilder to construct new transactions. + */ +class OfferCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttOFFER_CREATE; + + /** + * @brief Construct a OfferCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OfferCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OfferCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfTakerPays (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerPays() const + { + return this->tx_->at(sfTakerPays); + } + + /** + * @brief Get sfTakerGets (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getTakerGets() const + { + return this->tx_->at(sfTakerGets); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } + + /** + * @brief Get sfOfferSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOfferSequence() const + { + if (hasOfferSequence()) + { + return this->tx_->at(sfOfferSequence); + } + return std::nullopt; + } + + /** + * @brief Check if sfOfferSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOfferSequence() const + { + return this->tx_->isFieldPresent(sfOfferSequence); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for OfferCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OfferCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OfferCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param takerPays The sfTakerPays field value. + * @param takerGets The sfTakerGets field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OfferCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& takerPays, std::decay_t const& takerGets, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttOFFER_CREATE, account, sequence, fee) + { + setTakerPays(takerPays); + setTakerGets(takerGets); + } + + /** + * @brief Construct a OfferCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OfferCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttOFFER_CREATE) + { + throw std::runtime_error("Invalid transaction type for OfferCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfTakerPays (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setTakerPays(std::decay_t const& value) + { + object_[sfTakerPays] = value; + return *this; + } + + /** + * @brief Set sfTakerGets (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setTakerGets(std::decay_t const& value) + { + object_[sfTakerGets] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Set sfOfferSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setOfferSequence(std::decay_t const& value) + { + object_[sfOfferSequence] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OfferCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the OfferCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OfferCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OfferCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OracleDelete.h b/include/xrpl/protocol_autogen/transactions/OracleDelete.h new file mode 100644 index 0000000000..6cafd2b6de --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OracleDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OracleDeleteBuilder; + +/** + * @brief Transaction: OracleDelete + * + * Type: ttORACLE_DELETE (52) + * Delegable: Delegation::delegable + * Amendment: featurePriceOracle + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OracleDeleteBuilder to construct new transactions. + */ +class OracleDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttORACLE_DELETE; + + /** + * @brief Construct a OracleDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OracleDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OracleDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOracleDocumentID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOracleDocumentID() const + { + return this->tx_->at(sfOracleDocumentID); + } +}; + +/** + * @brief Builder for OracleDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OracleDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OracleDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param oracleDocumentID The sfOracleDocumentID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OracleDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& oracleDocumentID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttORACLE_DELETE, account, sequence, fee) + { + setOracleDocumentID(oracleDocumentID); + } + + /** + * @brief Construct a OracleDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OracleDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttORACLE_DELETE) + { + throw std::runtime_error("Invalid transaction type for OracleDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOracleDocumentID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleDeleteBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Build and return the OracleDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OracleDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OracleDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/OracleSet.h b/include/xrpl/protocol_autogen/transactions/OracleSet.h new file mode 100644 index 0000000000..8dd0d88865 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/OracleSet.h @@ -0,0 +1,288 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class OracleSetBuilder; + +/** + * @brief Transaction: OracleSet + * + * Type: ttORACLE_SET (51) + * Delegable: Delegation::delegable + * Amendment: featurePriceOracle + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use OracleSetBuilder to construct new transactions. + */ +class OracleSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttORACLE_SET; + + /** + * @brief Construct a OracleSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit OracleSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for OracleSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfOracleDocumentID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getOracleDocumentID() const + { + return this->tx_->at(sfOracleDocumentID); + } + + /** + * @brief Get sfProvider (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getProvider() const + { + if (hasProvider()) + { + return this->tx_->at(sfProvider); + } + return std::nullopt; + } + + /** + * @brief Check if sfProvider is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasProvider() const + { + return this->tx_->isFieldPresent(sfProvider); + } + + /** + * @brief Get sfURI (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getURI() const + { + if (hasURI()) + { + return this->tx_->at(sfURI); + } + return std::nullopt; + } + + /** + * @brief Check if sfURI is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasURI() const + { + return this->tx_->isFieldPresent(sfURI); + } + + /** + * @brief Get sfAssetClass (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetClass() const + { + if (hasAssetClass()) + { + return this->tx_->at(sfAssetClass); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetClass is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetClass() const + { + return this->tx_->isFieldPresent(sfAssetClass); + } + + /** + * @brief Get sfLastUpdateTime (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLastUpdateTime() const + { + return this->tx_->at(sfLastUpdateTime); + } + /** + * @brief Get sfPriceDataSeries (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getPriceDataSeries() const + { + return this->tx_->getFieldArray(sfPriceDataSeries); + } +}; + +/** + * @brief Builder for OracleSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class OracleSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new OracleSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param oracleDocumentID The sfOracleDocumentID field value. + * @param lastUpdateTime The sfLastUpdateTime field value. + * @param priceDataSeries The sfPriceDataSeries field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + OracleSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& oracleDocumentID, std::decay_t const& lastUpdateTime, STArray const& priceDataSeries, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttORACLE_SET, account, sequence, fee) + { + setOracleDocumentID(oracleDocumentID); + setLastUpdateTime(lastUpdateTime); + setPriceDataSeries(priceDataSeries); + } + + /** + * @brief Construct a OracleSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + OracleSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttORACLE_SET) + { + throw std::runtime_error("Invalid transaction type for OracleSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfOracleDocumentID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setOracleDocumentID(std::decay_t const& value) + { + object_[sfOracleDocumentID] = value; + return *this; + } + + /** + * @brief Set sfProvider (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setProvider(std::decay_t const& value) + { + object_[sfProvider] = value; + return *this; + } + + /** + * @brief Set sfURI (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setURI(std::decay_t const& value) + { + object_[sfURI] = value; + return *this; + } + + /** + * @brief Set sfAssetClass (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setAssetClass(std::decay_t const& value) + { + object_[sfAssetClass] = value; + return *this; + } + + /** + * @brief Set sfLastUpdateTime (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setLastUpdateTime(std::decay_t const& value) + { + object_[sfLastUpdateTime] = value; + return *this; + } + + /** + * @brief Set sfPriceDataSeries (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + OracleSetBuilder& + setPriceDataSeries(STArray const& value) + { + object_.setFieldArray(sfPriceDataSeries, value); + return *this; + } + + /** + * @brief Build and return the OracleSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + OracleSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return OracleSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/Payment.h b/include/xrpl/protocol_autogen/transactions/Payment.h new file mode 100644 index 0000000000..877286f90e --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/Payment.h @@ -0,0 +1,416 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentBuilder; + +/** + * @brief Transaction: Payment + * + * Type: ttPAYMENT (0) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentBuilder to construct new transactions. + */ +class Payment : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYMENT; + + /** + * @brief Construct a Payment transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit Payment(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for Payment"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSendMax (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSendMax() const + { + if (hasSendMax()) + { + return this->tx_->at(sfSendMax); + } + return std::nullopt; + } + + /** + * @brief Check if sfSendMax is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSendMax() const + { + return this->tx_->isFieldPresent(sfSendMax); + } + /** + * @brief Get sfPaths (soeDEFAULT) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getPaths() const + { + if (this->tx_->isFieldPresent(sfPaths)) + return this->tx_->getFieldPathSet(sfPaths); + return std::nullopt; + } + + /** + * @brief Check if sfPaths is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPaths() const + { + return this->tx_->isFieldPresent(sfPaths); + } + + /** + * @brief Get sfInvoiceID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getInvoiceID() const + { + if (hasInvoiceID()) + { + return this->tx_->at(sfInvoiceID); + } + return std::nullopt; + } + + /** + * @brief Check if sfInvoiceID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasInvoiceID() const + { + return this->tx_->isFieldPresent(sfInvoiceID); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 sfDeliverMin (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDeliverMin() const + { + if (hasDeliverMin()) + { + return this->tx_->at(sfDeliverMin); + } + return std::nullopt; + } + + /** + * @brief Check if sfDeliverMin is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDeliverMin() const + { + return this->tx_->isFieldPresent(sfDeliverMin); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } +}; + +/** + * @brief Builder for Payment transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYMENT, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a PaymentBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYMENT) + { + throw std::runtime_error("Invalid transaction type for PaymentBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSendMax (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setSendMax(std::decay_t const& value) + { + object_[sfSendMax] = value; + return *this; + } + + /** + * @brief Set sfPaths (soeDEFAULT) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setPaths(STPathSet const& value) + { + object_.setFieldPathSet(sfPaths, value); + return *this; + } + + /** + * @brief Set sfInvoiceID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setInvoiceID(std::decay_t const& value) + { + object_[sfInvoiceID] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfDeliverMin (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDeliverMin(std::decay_t const& value) + { + object_[sfDeliverMin] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the Payment wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + Payment + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return Payment{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h new file mode 100644 index 0000000000..1e90c74aa1 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h @@ -0,0 +1,314 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelClaimBuilder; + +/** + * @brief Transaction: PaymentChannelClaim + * + * Type: ttPAYCHAN_CLAIM (15) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelClaimBuilder to construct new transactions. + */ +class PaymentChannelClaim : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_CLAIM; + + /** + * @brief Construct a PaymentChannelClaim transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelClaim(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelClaim"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfChannel (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getChannel() const + { + return this->tx_->at(sfChannel); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } + + /** + * @brief Get sfBalance (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBalance() const + { + if (hasBalance()) + { + return this->tx_->at(sfBalance); + } + return std::nullopt; + } + + /** + * @brief Check if sfBalance is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBalance() const + { + return this->tx_->isFieldPresent(sfBalance); + } + + /** + * @brief Get sfSignature (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSignature() const + { + if (hasSignature()) + { + return this->tx_->at(sfSignature); + } + return std::nullopt; + } + + /** + * @brief Check if sfSignature is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignature() const + { + return this->tx_->isFieldPresent(sfSignature); + } + + /** + * @brief Get sfPublicKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getPublicKey() const + { + if (hasPublicKey()) + { + return this->tx_->at(sfPublicKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfPublicKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasPublicKey() const + { + return this->tx_->isFieldPresent(sfPublicKey); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for PaymentChannelClaim transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelClaimBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelClaimBuilder with required fields. + * @param account The account initiating the transaction. + * @param channel The sfChannel field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelClaimBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& channel, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_CLAIM, account, sequence, fee) + { + setChannel(channel); + } + + /** + * @brief Construct a PaymentChannelClaimBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelClaimBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_CLAIM) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelClaimBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfChannel (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setChannel(std::decay_t const& value) + { + object_[sfChannel] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfBalance (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setBalance(std::decay_t const& value) + { + object_[sfBalance] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelClaimBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelClaim wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelClaim + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelClaim{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h new file mode 100644 index 0000000000..4242046114 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h @@ -0,0 +1,275 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelCreateBuilder; + +/** + * @brief Transaction: PaymentChannelCreate + * + * Type: ttPAYCHAN_CREATE (13) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelCreateBuilder to construct new transactions. + */ +class PaymentChannelCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_CREATE; + + /** + * @brief Construct a PaymentChannelCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSettleDelay (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSettleDelay() const + { + return this->tx_->at(sfSettleDelay); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfCancelAfter (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCancelAfter() const + { + if (hasCancelAfter()) + { + return this->tx_->at(sfCancelAfter); + } + return std::nullopt; + } + + /** + * @brief Check if sfCancelAfter is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCancelAfter() const + { + return this->tx_->isFieldPresent(sfCancelAfter); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 Builder for PaymentChannelCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param settleDelay The sfSettleDelay field value. + * @param publicKey The sfPublicKey field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& destination, std::decay_t const& amount, std::decay_t const& settleDelay, std::decay_t const& publicKey, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_CREATE, account, sequence, fee) + { + setDestination(destination); + setAmount(amount); + setSettleDelay(settleDelay); + setPublicKey(publicKey); + } + + /** + * @brief Construct a PaymentChannelCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_CREATE) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSettleDelay (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setSettleDelay(std::decay_t const& value) + { + object_[sfSettleDelay] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfCancelAfter (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setCancelAfter(std::decay_t const& value) + { + object_[sfCancelAfter] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelCreateBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h new file mode 100644 index 0000000000..2fecf21154 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h @@ -0,0 +1,190 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PaymentChannelFundBuilder; + +/** + * @brief Transaction: PaymentChannelFund + * + * Type: ttPAYCHAN_FUND (14) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PaymentChannelFundBuilder to construct new transactions. + */ +class PaymentChannelFund : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPAYCHAN_FUND; + + /** + * @brief Construct a PaymentChannelFund transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PaymentChannelFund(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelFund"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfChannel (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getChannel() const + { + return this->tx_->at(sfChannel); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfExpiration (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getExpiration() const + { + if (hasExpiration()) + { + return this->tx_->at(sfExpiration); + } + return std::nullopt; + } + + /** + * @brief Check if sfExpiration is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasExpiration() const + { + return this->tx_->isFieldPresent(sfExpiration); + } +}; + +/** + * @brief Builder for PaymentChannelFund transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PaymentChannelFundBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PaymentChannelFundBuilder with required fields. + * @param account The account initiating the transaction. + * @param channel The sfChannel field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PaymentChannelFundBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& channel, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPAYCHAN_FUND, account, sequence, fee) + { + setChannel(channel); + setAmount(amount); + } + + /** + * @brief Construct a PaymentChannelFundBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PaymentChannelFundBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPAYCHAN_FUND) + { + throw std::runtime_error("Invalid transaction type for PaymentChannelFundBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfChannel (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setChannel(std::decay_t const& value) + { + object_[sfChannel] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfExpiration (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PaymentChannelFundBuilder& + setExpiration(std::decay_t const& value) + { + object_[sfExpiration] = value; + return *this; + } + + /** + * @brief Build and return the PaymentChannelFund wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PaymentChannelFund + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PaymentChannelFund{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h new file mode 100644 index 0000000000..c95e0f2e18 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PermissionedDomainDeleteBuilder; + +/** + * @brief Transaction: PermissionedDomainDelete + * + * Type: ttPERMISSIONED_DOMAIN_DELETE (63) + * Delegable: Delegation::delegable + * Amendment: featurePermissionedDomains + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PermissionedDomainDeleteBuilder to construct new transactions. + */ +class PermissionedDomainDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPERMISSIONED_DOMAIN_DELETE; + + /** + * @brief Construct a PermissionedDomainDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PermissionedDomainDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDomainID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getDomainID() const + { + return this->tx_->at(sfDomainID); + } +}; + +/** + * @brief Builder for PermissionedDomainDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PermissionedDomainDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param domainID The sfDomainID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PermissionedDomainDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& domainID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPERMISSIONED_DOMAIN_DELETE, account, sequence, fee) + { + setDomainID(domainID); + } + + /** + * @brief Construct a PermissionedDomainDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PermissionedDomainDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPERMISSIONED_DOMAIN_DELETE) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDomainID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainDeleteBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Build and return the PermissionedDomainDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PermissionedDomainDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PermissionedDomainDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h new file mode 100644 index 0000000000..38df7f4fc7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h @@ -0,0 +1,166 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class PermissionedDomainSetBuilder; + +/** + * @brief Transaction: PermissionedDomainSet + * + * Type: ttPERMISSIONED_DOMAIN_SET (62) + * Delegable: Delegation::delegable + * Amendment: featurePermissionedDomains + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use PermissionedDomainSetBuilder to construct new transactions. + */ +class PermissionedDomainSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttPERMISSIONED_DOMAIN_SET; + + /** + * @brief Construct a PermissionedDomainSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit PermissionedDomainSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + /** + * @brief Get sfAcceptedCredentials (soeREQUIRED) + * @note This is an untyped field. + * @return The field value. + */ + [[nodiscard]] + STArray const& + getAcceptedCredentials() const + { + return this->tx_->getFieldArray(sfAcceptedCredentials); + } +}; + +/** + * @brief Builder for PermissionedDomainSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class PermissionedDomainSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new PermissionedDomainSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param acceptedCredentials The sfAcceptedCredentials field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + PermissionedDomainSetBuilder(SF_ACCOUNT::type::value_type account, + STArray const& acceptedCredentials, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttPERMISSIONED_DOMAIN_SET, account, sequence, fee) + { + setAcceptedCredentials(acceptedCredentials); + } + + /** + * @brief Construct a PermissionedDomainSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + PermissionedDomainSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttPERMISSIONED_DOMAIN_SET) + { + throw std::runtime_error("Invalid transaction type for PermissionedDomainSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfAcceptedCredentials (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + PermissionedDomainSetBuilder& + setAcceptedCredentials(STArray const& value) + { + object_.setFieldArray(sfAcceptedCredentials, value); + return *this; + } + + /** + * @brief Build and return the PermissionedDomainSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + PermissionedDomainSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return PermissionedDomainSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SetFee.h b/include/xrpl/protocol_autogen/transactions/SetFee.h new file mode 100644 index 0000000000..7bf73201b9 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SetFee.h @@ -0,0 +1,401 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SetFeeBuilder; + +/** + * @brief Transaction: SetFee + * + * Type: ttFEE (101) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SetFeeBuilder to construct new transactions. + */ +class SetFee : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttFEE; + + /** + * @brief Construct a SetFee transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SetFee(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SetFee"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLedgerSequence (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLedgerSequence() const + { + if (hasLedgerSequence()) + { + return this->tx_->at(sfLedgerSequence); + } + return std::nullopt; + } + + /** + * @brief Check if sfLedgerSequence is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLedgerSequence() const + { + return this->tx_->isFieldPresent(sfLedgerSequence); + } + + /** + * @brief Get sfBaseFee (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFee() const + { + if (hasBaseFee()) + { + return this->tx_->at(sfBaseFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfBaseFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFee() const + { + return this->tx_->isFieldPresent(sfBaseFee); + } + + /** + * @brief Get sfReferenceFeeUnits (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReferenceFeeUnits() const + { + if (hasReferenceFeeUnits()) + { + return this->tx_->at(sfReferenceFeeUnits); + } + return std::nullopt; + } + + /** + * @brief Check if sfReferenceFeeUnits is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReferenceFeeUnits() const + { + return this->tx_->isFieldPresent(sfReferenceFeeUnits); + } + + /** + * @brief Get sfReserveBase (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBase() const + { + if (hasReserveBase()) + { + return this->tx_->at(sfReserveBase); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveBase is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBase() const + { + return this->tx_->isFieldPresent(sfReserveBase); + } + + /** + * @brief Get sfReserveIncrement (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrement() const + { + if (hasReserveIncrement()) + { + return this->tx_->at(sfReserveIncrement); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrement is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrement() const + { + return this->tx_->isFieldPresent(sfReserveIncrement); + } + + /** + * @brief Get sfBaseFeeDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBaseFeeDrops() const + { + if (hasBaseFeeDrops()) + { + return this->tx_->at(sfBaseFeeDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfBaseFeeDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBaseFeeDrops() const + { + return this->tx_->isFieldPresent(sfBaseFeeDrops); + } + + /** + * @brief Get sfReserveBaseDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveBaseDrops() const + { + if (hasReserveBaseDrops()) + { + return this->tx_->at(sfReserveBaseDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveBaseDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveBaseDrops() const + { + return this->tx_->isFieldPresent(sfReserveBaseDrops); + } + + /** + * @brief Get sfReserveIncrementDrops (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReserveIncrementDrops() const + { + if (hasReserveIncrementDrops()) + { + return this->tx_->at(sfReserveIncrementDrops); + } + return std::nullopt; + } + + /** + * @brief Check if sfReserveIncrementDrops is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReserveIncrementDrops() const + { + return this->tx_->isFieldPresent(sfReserveIncrementDrops); + } +}; + +/** + * @brief Builder for SetFee transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SetFeeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SetFeeBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SetFeeBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttFEE, account, sequence, fee) + { + } + + /** + * @brief Construct a SetFeeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SetFeeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttFEE) + { + throw std::runtime_error("Invalid transaction type for SetFeeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLedgerSequence (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfBaseFee (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setBaseFee(std::decay_t const& value) + { + object_[sfBaseFee] = value; + return *this; + } + + /** + * @brief Set sfReferenceFeeUnits (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReferenceFeeUnits(std::decay_t const& value) + { + object_[sfReferenceFeeUnits] = value; + return *this; + } + + /** + * @brief Set sfReserveBase (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveBase(std::decay_t const& value) + { + object_[sfReserveBase] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrement (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveIncrement(std::decay_t const& value) + { + object_[sfReserveIncrement] = value; + return *this; + } + + /** + * @brief Set sfBaseFeeDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setBaseFeeDrops(std::decay_t const& value) + { + object_[sfBaseFeeDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveBaseDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveBaseDrops(std::decay_t const& value) + { + object_[sfReserveBaseDrops] = value; + return *this; + } + + /** + * @brief Set sfReserveIncrementDrops (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetFeeBuilder& + setReserveIncrementDrops(std::decay_t const& value) + { + object_[sfReserveIncrementDrops] = value; + return *this; + } + + /** + * @brief Build and return the SetFee wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SetFee + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SetFee{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h new file mode 100644 index 0000000000..3d70d092c5 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h @@ -0,0 +1,142 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SetRegularKeyBuilder; + +/** + * @brief Transaction: SetRegularKey + * + * Type: ttREGULAR_KEY_SET (5) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SetRegularKeyBuilder to construct new transactions. + */ +class SetRegularKey : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttREGULAR_KEY_SET; + + /** + * @brief Construct a SetRegularKey transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SetRegularKey(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SetRegularKey"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfRegularKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRegularKey() const + { + if (hasRegularKey()) + { + return this->tx_->at(sfRegularKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfRegularKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRegularKey() const + { + return this->tx_->isFieldPresent(sfRegularKey); + } +}; + +/** + * @brief Builder for SetRegularKey transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SetRegularKeyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SetRegularKeyBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SetRegularKeyBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttREGULAR_KEY_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a SetRegularKeyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SetRegularKeyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttREGULAR_KEY_SET) + { + throw std::runtime_error("Invalid transaction type for SetRegularKeyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfRegularKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SetRegularKeyBuilder& + setRegularKey(std::decay_t const& value) + { + object_[sfRegularKey] = value; + return *this; + } + + /** + * @brief Build and return the SetRegularKey wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SetRegularKey + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SetRegularKey{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SignerListSet.h b/include/xrpl/protocol_autogen/transactions/SignerListSet.h new file mode 100644 index 0000000000..ec5330a9cb --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SignerListSet.h @@ -0,0 +1,164 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SignerListSetBuilder; + +/** + * @brief Transaction: SignerListSet + * + * Type: ttSIGNER_LIST_SET (12) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SignerListSetBuilder to construct new transactions. + */ +class SignerListSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttSIGNER_LIST_SET; + + /** + * @brief Construct a SignerListSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SignerListSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SignerListSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfSignerQuorum (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getSignerQuorum() const + { + return this->tx_->at(sfSignerQuorum); + } + /** + * @brief Get sfSignerEntries (soeOPTIONAL) + * @note This is an untyped field. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + std::optional> + getSignerEntries() const + { + if (this->tx_->isFieldPresent(sfSignerEntries)) + return this->tx_->getFieldArray(sfSignerEntries); + return std::nullopt; + } + + /** + * @brief Check if sfSignerEntries is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignerEntries() const + { + return this->tx_->isFieldPresent(sfSignerEntries); + } +}; + +/** + * @brief Builder for SignerListSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SignerListSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SignerListSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param signerQuorum The sfSignerQuorum field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SignerListSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& signerQuorum, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttSIGNER_LIST_SET, account, sequence, fee) + { + setSignerQuorum(signerQuorum); + } + + /** + * @brief Construct a SignerListSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SignerListSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttSIGNER_LIST_SET) + { + throw std::runtime_error("Invalid transaction type for SignerListSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfSignerQuorum (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + SignerListSetBuilder& + setSignerQuorum(std::decay_t const& value) + { + object_[sfSignerQuorum] = value; + return *this; + } + + /** + * @brief Set sfSignerEntries (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + SignerListSetBuilder& + setSignerEntries(STArray const& value) + { + object_.setFieldArray(sfSignerEntries, value); + return *this; + } + + /** + * @brief Build and return the SignerListSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SignerListSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SignerListSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/TicketCreate.h b/include/xrpl/protocol_autogen/transactions/TicketCreate.h new file mode 100644 index 0000000000..842ae93111 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/TicketCreate.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class TicketCreateBuilder; + +/** + * @brief Transaction: TicketCreate + * + * Type: ttTICKET_CREATE (10) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use TicketCreateBuilder to construct new transactions. + */ +class TicketCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttTICKET_CREATE; + + /** + * @brief Construct a TicketCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit TicketCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for TicketCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfTicketCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getTicketCount() const + { + return this->tx_->at(sfTicketCount); + } +}; + +/** + * @brief Builder for TicketCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class TicketCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new TicketCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param ticketCount The sfTicketCount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + TicketCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& ticketCount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttTICKET_CREATE, account, sequence, fee) + { + setTicketCount(ticketCount); + } + + /** + * @brief Construct a TicketCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + TicketCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttTICKET_CREATE) + { + throw std::runtime_error("Invalid transaction type for TicketCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfTicketCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + TicketCreateBuilder& + setTicketCount(std::decay_t const& value) + { + object_[sfTicketCount] = value; + return *this; + } + + /** + * @brief Build and return the TicketCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + TicketCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return TicketCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/TrustSet.h b/include/xrpl/protocol_autogen/transactions/TrustSet.h new file mode 100644 index 0000000000..5623311b92 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/TrustSet.h @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class TrustSetBuilder; + +/** + * @brief Transaction: TrustSet + * + * Type: ttTRUST_SET (20) + * Delegable: Delegation::delegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use TrustSetBuilder to construct new transactions. + */ +class TrustSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttTRUST_SET; + + /** + * @brief Construct a TrustSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit TrustSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for TrustSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfLimitAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLimitAmount() const + { + if (hasLimitAmount()) + { + return this->tx_->at(sfLimitAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfLimitAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLimitAmount() const + { + return this->tx_->isFieldPresent(sfLimitAmount); + } + + /** + * @brief Get sfQualityIn (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getQualityIn() const + { + if (hasQualityIn()) + { + return this->tx_->at(sfQualityIn); + } + return std::nullopt; + } + + /** + * @brief Check if sfQualityIn is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasQualityIn() const + { + return this->tx_->isFieldPresent(sfQualityIn); + } + + /** + * @brief Get sfQualityOut (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getQualityOut() const + { + if (hasQualityOut()) + { + return this->tx_->at(sfQualityOut); + } + return std::nullopt; + } + + /** + * @brief Check if sfQualityOut is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasQualityOut() const + { + return this->tx_->isFieldPresent(sfQualityOut); + } +}; + +/** + * @brief Builder for TrustSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class TrustSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new TrustSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + TrustSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttTRUST_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a TrustSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + TrustSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttTRUST_SET) + { + throw std::runtime_error("Invalid transaction type for TrustSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfLimitAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setLimitAmount(std::decay_t const& value) + { + object_[sfLimitAmount] = value; + return *this; + } + + /** + * @brief Set sfQualityIn (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setQualityIn(std::decay_t const& value) + { + object_[sfQualityIn] = value; + return *this; + } + + /** + * @brief Set sfQualityOut (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + TrustSetBuilder& + setQualityOut(std::decay_t const& value) + { + object_[sfQualityOut] = value; + return *this; + } + + /** + * @brief Build and return the TrustSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + TrustSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return TrustSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/UNLModify.h b/include/xrpl/protocol_autogen/transactions/UNLModify.h new file mode 100644 index 0000000000..b5e64f19a0 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/UNLModify.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class UNLModifyBuilder; + +/** + * @brief Transaction: UNLModify + * + * Type: ttUNL_MODIFY (102) + * Delegable: Delegation::notDelegable + * Amendment: uint256{} + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use UNLModifyBuilder to construct new transactions. + */ +class UNLModify : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttUNL_MODIFY; + + /** + * @brief Construct a UNLModify transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit UNLModify(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for UNLModify"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfUNLModifyDisabling (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getUNLModifyDisabling() const + { + return this->tx_->at(sfUNLModifyDisabling); + } + + /** + * @brief Get sfLedgerSequence (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getLedgerSequence() const + { + return this->tx_->at(sfLedgerSequence); + } + + /** + * @brief Get sfUNLModifyValidator (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getUNLModifyValidator() const + { + return this->tx_->at(sfUNLModifyValidator); + } +}; + +/** + * @brief Builder for UNLModify transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class UNLModifyBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new UNLModifyBuilder with required fields. + * @param account The account initiating the transaction. + * @param uNLModifyDisabling The sfUNLModifyDisabling field value. + * @param ledgerSequence The sfLedgerSequence field value. + * @param uNLModifyValidator The sfUNLModifyValidator field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + UNLModifyBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& uNLModifyDisabling, std::decay_t const& ledgerSequence, std::decay_t const& uNLModifyValidator, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttUNL_MODIFY, account, sequence, fee) + { + setUNLModifyDisabling(uNLModifyDisabling); + setLedgerSequence(ledgerSequence); + setUNLModifyValidator(uNLModifyValidator); + } + + /** + * @brief Construct a UNLModifyBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + UNLModifyBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttUNL_MODIFY) + { + throw std::runtime_error("Invalid transaction type for UNLModifyBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfUNLModifyDisabling (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setUNLModifyDisabling(std::decay_t const& value) + { + object_[sfUNLModifyDisabling] = value; + return *this; + } + + /** + * @brief Set sfLedgerSequence (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setLedgerSequence(std::decay_t const& value) + { + object_[sfLedgerSequence] = value; + return *this; + } + + /** + * @brief Set sfUNLModifyValidator (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + UNLModifyBuilder& + setUNLModifyValidator(std::decay_t const& value) + { + object_[sfUNLModifyValidator] = value; + return *this; + } + + /** + * @brief Build and return the UNLModify wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + UNLModify + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return UNLModify{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultClawback.h b/include/xrpl/protocol_autogen/transactions/VaultClawback.h new file mode 100644 index 0000000000..82b2b32e8d --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultClawback.h @@ -0,0 +1,192 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultClawbackBuilder; + +/** + * @brief Transaction: VaultClawback + * + * Type: ttVAULT_CLAWBACK (70) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayDeleteMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultClawbackBuilder to construct new transactions. + */ +class VaultClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_CLAWBACK; + + /** + * @brief Construct a VaultClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfHolder (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getHolder() const + { + return this->tx_->at(sfHolder); + } + + /** + * @brief Get sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAmount() const + { + if (hasAmount()) + { + return this->tx_->at(sfAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAmount() const + { + return this->tx_->isFieldPresent(sfAmount); + } +}; + +/** + * @brief Builder for VaultClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param holder The sfHolder field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultClawbackBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& holder, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_CLAWBACK, account, sequence, fee) + { + setVaultID(vaultID); + setHolder(holder); + } + + /** + * @brief Construct a VaultClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for VaultClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeOPTIONAL) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultClawbackBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the VaultClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultCreate.h b/include/xrpl/protocol_autogen/transactions/VaultCreate.h new file mode 100644 index 0000000000..03d4d4b88d --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultCreate.h @@ -0,0 +1,353 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultCreateBuilder; + +/** + * @brief Transaction: VaultCreate + * + * Type: ttVAULT_CREATE (65) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: createPseudoAcct | createMPTIssuance | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultCreateBuilder to construct new transactions. + */ +class VaultCreate : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_CREATE; + + /** + * @brief Construct a VaultCreate transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultCreate(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultCreate"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfAsset (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_ISSUE::type::value_type + getAsset() const + { + return this->tx_->at(sfAsset); + } + + /** + * @brief Get sfAssetsMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + { + return this->tx_->at(sfAssetsMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->tx_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfMPTokenMetadata (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMPTokenMetadata() const + { + if (hasMPTokenMetadata()) + { + return this->tx_->at(sfMPTokenMetadata); + } + return std::nullopt; + } + + /** + * @brief Check if sfMPTokenMetadata is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMPTokenMetadata() const + { + return this->tx_->isFieldPresent(sfMPTokenMetadata); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfWithdrawalPolicy (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getWithdrawalPolicy() const + { + if (hasWithdrawalPolicy()) + { + return this->tx_->at(sfWithdrawalPolicy); + } + return std::nullopt; + } + + /** + * @brief Check if sfWithdrawalPolicy is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasWithdrawalPolicy() const + { + return this->tx_->isFieldPresent(sfWithdrawalPolicy); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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); + } + + /** + * @brief Get sfScale (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getScale() const + { + if (hasScale()) + { + return this->tx_->at(sfScale); + } + return std::nullopt; + } + + /** + * @brief Check if sfScale is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasScale() const + { + return this->tx_->isFieldPresent(sfScale); + } +}; + +/** + * @brief Builder for VaultCreate transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultCreateBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultCreateBuilder with required fields. + * @param account The account initiating the transaction. + * @param asset The sfAsset field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultCreateBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& asset, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_CREATE, account, sequence, fee) + { + setAsset(asset); + } + + /** + * @brief Construct a VaultCreateBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultCreateBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_CREATE) + { + throw std::runtime_error("Invalid transaction type for VaultCreateBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfAsset (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setAsset(std::decay_t const& value) + { + object_[sfAsset] = STIssue(sfAsset, value); + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfMPTokenMetadata (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setMPTokenMetadata(std::decay_t const& value) + { + object_[sfMPTokenMetadata] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfWithdrawalPolicy (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setWithdrawalPolicy(std::decay_t const& value) + { + object_[sfWithdrawalPolicy] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Set sfScale (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultCreateBuilder& + setScale(std::decay_t const& value) + { + object_[sfScale] = value; + return *this; + } + + /** + * @brief Build and return the VaultCreate wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultCreate + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultCreate{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultDelete.h b/include/xrpl/protocol_autogen/transactions/VaultDelete.h new file mode 100644 index 0000000000..89a4ef2a2f --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultDelete.h @@ -0,0 +1,129 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultDeleteBuilder; + +/** + * @brief Transaction: VaultDelete + * + * Type: ttVAULT_DELETE (67) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mustDeleteAcct | destroyMPTIssuance | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultDeleteBuilder to construct new transactions. + */ +class VaultDelete : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_DELETE; + + /** + * @brief Construct a VaultDelete transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultDelete(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultDelete"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } +}; + +/** + * @brief Builder for VaultDelete transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultDeleteBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultDeleteBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultDeleteBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_DELETE, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a VaultDeleteBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultDeleteBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_DELETE) + { + throw std::runtime_error("Invalid transaction type for VaultDeleteBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultDeleteBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Build and return the VaultDelete wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultDelete + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultDelete{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h new file mode 100644 index 0000000000..4127638e57 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h @@ -0,0 +1,155 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultDepositBuilder; + +/** + * @brief Transaction: VaultDeposit + * + * Type: ttVAULT_DEPOSIT (68) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultDepositBuilder to construct new transactions. + */ +class VaultDeposit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_DEPOSIT; + + /** + * @brief Construct a VaultDeposit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultDeposit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultDeposit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for VaultDeposit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultDepositBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultDepositBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultDepositBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_DEPOSIT, account, sequence, fee) + { + setVaultID(vaultID); + setAmount(amount); + } + + /** + * @brief Construct a VaultDepositBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultDepositBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_DEPOSIT) + { + throw std::runtime_error("Invalid transaction type for VaultDepositBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultDepositBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultDepositBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the VaultDeposit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultDeposit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultDeposit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultSet.h b/include/xrpl/protocol_autogen/transactions/VaultSet.h new file mode 100644 index 0000000000..372697b5f7 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultSet.h @@ -0,0 +1,240 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultSetBuilder; + +/** + * @brief Transaction: VaultSet + * + * Type: ttVAULT_SET (66) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultSetBuilder to construct new transactions. + */ +class VaultSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_SET; + + /** + * @brief Construct a VaultSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAssetsMaximum (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAssetsMaximum() const + { + if (hasAssetsMaximum()) + { + return this->tx_->at(sfAssetsMaximum); + } + return std::nullopt; + } + + /** + * @brief Check if sfAssetsMaximum is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAssetsMaximum() const + { + return this->tx_->isFieldPresent(sfAssetsMaximum); + } + + /** + * @brief Get sfDomainID (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDomainID() const + { + if (hasDomainID()) + { + return this->tx_->at(sfDomainID); + } + return std::nullopt; + } + + /** + * @brief Check if sfDomainID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDomainID() const + { + return this->tx_->isFieldPresent(sfDomainID); + } + + /** + * @brief Get sfData (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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); + } +}; + +/** + * @brief Builder for VaultSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultSetBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_SET, account, sequence, fee) + { + setVaultID(vaultID); + } + + /** + * @brief Construct a VaultSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_SET) + { + throw std::runtime_error("Invalid transaction type for VaultSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAssetsMaximum (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setAssetsMaximum(std::decay_t const& value) + { + object_[sfAssetsMaximum] = value; + return *this; + } + + /** + * @brief Set sfDomainID (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setDomainID(std::decay_t const& value) + { + object_[sfDomainID] = value; + return *this; + } + + /** + * @brief Set sfData (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultSetBuilder& + setData(std::decay_t const& value) + { + object_[sfData] = value; + return *this; + } + + /** + * @brief Build and return the VaultSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h new file mode 100644 index 0000000000..8b2a6a9a3c --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h @@ -0,0 +1,229 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class VaultWithdrawBuilder; + +/** + * @brief Transaction: VaultWithdraw + * + * Type: ttVAULT_WITHDRAW (69) + * Delegable: Delegation::notDelegable + * Amendment: featureSingleAssetVault + * Privileges: mayDeleteMPT | mayAuthorizeMPT | mustModifyVault + * + * Immutable wrapper around STTx providing type-safe field access. + * Use VaultWithdrawBuilder to construct new transactions. + */ +class VaultWithdraw : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttVAULT_WITHDRAW; + + /** + * @brief Construct a VaultWithdraw transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit VaultWithdraw(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for VaultWithdraw"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfVaultID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getVaultID() const + { + return this->tx_->at(sfVaultID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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 Builder for VaultWithdraw transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class VaultWithdrawBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new VaultWithdrawBuilder with required fields. + * @param account The account initiating the transaction. + * @param vaultID The sfVaultID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + VaultWithdrawBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& vaultID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttVAULT_WITHDRAW, account, sequence, fee) + { + setVaultID(vaultID); + setAmount(amount); + } + + /** + * @brief Construct a VaultWithdrawBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + VaultWithdrawBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttVAULT_WITHDRAW) + { + throw std::runtime_error("Invalid transaction type for VaultWithdrawBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfVaultID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setVaultID(std::decay_t const& value) + { + object_[sfVaultID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @note This field supports MPT (Multi-Purpose Token) amounts. + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + VaultWithdrawBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Build and return the VaultWithdraw wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + VaultWithdraw + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return VaultWithdraw{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h new file mode 100644 index 0000000000..aebfbea5d3 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h @@ -0,0 +1,201 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAccountCreateCommitBuilder; + +/** + * @brief Transaction: XChainAccountCreateCommit + * + * Type: ttXCHAIN_ACCOUNT_CREATE_COMMIT (44) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAccountCreateCommitBuilder to construct new transactions. + */ +class XChainAccountCreateCommit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ACCOUNT_CREATE_COMMIT; + + /** + * @brief Construct a XChainAccountCreateCommit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAccountCreateCommit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } +}; + +/** + * @brief Builder for XChainAccountCreateCommit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAccountCreateCommitBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAccountCreateCommitBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAccountCreateCommitBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& destination, std::decay_t const& amount, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ACCOUNT_CREATE_COMMIT, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setDestination(destination); + setAmount(amount); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainAccountCreateCommitBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAccountCreateCommitBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ACCOUNT_CREATE_COMMIT) + { + throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommitBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAccountCreateCommitBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Build and return the XChainAccountCreateCommit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAccountCreateCommit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAccountCreateCommit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h new file mode 100644 index 0000000000..08dc646470 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h @@ -0,0 +1,369 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAddAccountCreateAttestationBuilder; + +/** + * @brief Transaction: XChainAddAccountCreateAttestation + * + * Type: ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION (46) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAddAccountCreateAttestationBuilder to construct new transactions. + */ +class XChainAddAccountCreateAttestation : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION; + + /** + * @brief Construct a XChainAddAccountCreateAttestation transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAddAccountCreateAttestation(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestation"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfAttestationSignerAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationSignerAccount() const + { + return this->tx_->at(sfAttestationSignerAccount); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfSignature (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getSignature() const + { + return this->tx_->at(sfSignature); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAttestationRewardAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationRewardAccount() const + { + return this->tx_->at(sfAttestationRewardAccount); + } + + /** + * @brief Get sfWasLockingChainSend (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWasLockingChainSend() const + { + return this->tx_->at(sfWasLockingChainSend); + } + + /** + * @brief Get sfXChainAccountCreateCount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainAccountCreateCount() const + { + return this->tx_->at(sfXChainAccountCreateCount); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } +}; + +/** + * @brief Builder for XChainAddAccountCreateAttestation transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAddAccountCreateAttestationBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAddAccountCreateAttestationBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param attestationSignerAccount The sfAttestationSignerAccount field value. + * @param publicKey The sfPublicKey field value. + * @param signature The sfSignature field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param amount The sfAmount field value. + * @param attestationRewardAccount The sfAttestationRewardAccount field value. + * @param wasLockingChainSend The sfWasLockingChainSend field value. + * @param xChainAccountCreateCount The sfXChainAccountCreateCount field value. + * @param destination The sfDestination field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAddAccountCreateAttestationBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& attestationSignerAccount, std::decay_t const& publicKey, std::decay_t const& signature, std::decay_t const& otherChainSource, std::decay_t const& amount, std::decay_t const& attestationRewardAccount, std::decay_t const& wasLockingChainSend, std::decay_t const& xChainAccountCreateCount, std::decay_t const& destination, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setAttestationSignerAccount(attestationSignerAccount); + setPublicKey(publicKey); + setSignature(signature); + setOtherChainSource(otherChainSource); + setAmount(amount); + setAttestationRewardAccount(attestationRewardAccount); + setWasLockingChainSend(wasLockingChainSend); + setXChainAccountCreateCount(xChainAccountCreateCount); + setDestination(destination); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainAddAccountCreateAttestationBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAddAccountCreateAttestationBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) + { + throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestationBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfAttestationSignerAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAttestationSignerAccount(std::decay_t const& value) + { + object_[sfAttestationSignerAccount] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAttestationRewardAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setAttestationRewardAccount(std::decay_t const& value) + { + object_[sfAttestationRewardAccount] = value; + return *this; + } + + /** + * @brief Set sfWasLockingChainSend (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setWasLockingChainSend(std::decay_t const& value) + { + object_[sfWasLockingChainSend] = value; + return *this; + } + + /** + * @brief Set sfXChainAccountCreateCount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setXChainAccountCreateCount(std::decay_t const& value) + { + object_[sfXChainAccountCreateCount] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddAccountCreateAttestationBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Build and return the XChainAddAccountCreateAttestation wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAddAccountCreateAttestation + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAddAccountCreateAttestation{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h new file mode 100644 index 0000000000..100fc855aa --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h @@ -0,0 +1,358 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainAddClaimAttestationBuilder; + +/** + * @brief Transaction: XChainAddClaimAttestation + * + * Type: ttXCHAIN_ADD_CLAIM_ATTESTATION (45) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: createAcct + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainAddClaimAttestationBuilder to construct new transactions. + */ +class XChainAddClaimAttestation : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_ADD_CLAIM_ATTESTATION; + + /** + * @brief Construct a XChainAddClaimAttestation transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainAddClaimAttestation(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestation"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfAttestationSignerAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationSignerAccount() const + { + return this->tx_->at(sfAttestationSignerAccount); + } + + /** + * @brief Get sfPublicKey (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getPublicKey() const + { + return this->tx_->at(sfPublicKey); + } + + /** + * @brief Get sfSignature (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getSignature() const + { + return this->tx_->at(sfSignature); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfAttestationRewardAccount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getAttestationRewardAccount() const + { + return this->tx_->at(sfAttestationRewardAccount); + } + + /** + * @brief Get sfWasLockingChainSend (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT8::type::value_type + getWasLockingChainSend() const + { + return this->tx_->at(sfWasLockingChainSend); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getDestination() const + { + if (hasDestination()) + { + return this->tx_->at(sfDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasDestination() const + { + return this->tx_->isFieldPresent(sfDestination); + } +}; + +/** + * @brief Builder for XChainAddClaimAttestation transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainAddClaimAttestationBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainAddClaimAttestationBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param attestationSignerAccount The sfAttestationSignerAccount field value. + * @param publicKey The sfPublicKey field value. + * @param signature The sfSignature field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param amount The sfAmount field value. + * @param attestationRewardAccount The sfAttestationRewardAccount field value. + * @param wasLockingChainSend The sfWasLockingChainSend field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainAddClaimAttestationBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& attestationSignerAccount, std::decay_t const& publicKey, std::decay_t const& signature, std::decay_t const& otherChainSource, std::decay_t const& amount, std::decay_t const& attestationRewardAccount, std::decay_t const& wasLockingChainSend, std::decay_t const& xChainClaimID, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_ADD_CLAIM_ATTESTATION, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setAttestationSignerAccount(attestationSignerAccount); + setPublicKey(publicKey); + setSignature(signature); + setOtherChainSource(otherChainSource); + setAmount(amount); + setAttestationRewardAccount(attestationRewardAccount); + setWasLockingChainSend(wasLockingChainSend); + setXChainClaimID(xChainClaimID); + } + + /** + * @brief Construct a XChainAddClaimAttestationBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainAddClaimAttestationBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_ADD_CLAIM_ATTESTATION) + { + throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestationBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfAttestationSignerAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAttestationSignerAccount(std::decay_t const& value) + { + object_[sfAttestationSignerAccount] = value; + return *this; + } + + /** + * @brief Set sfPublicKey (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setPublicKey(std::decay_t const& value) + { + object_[sfPublicKey] = value; + return *this; + } + + /** + * @brief Set sfSignature (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setSignature(std::decay_t const& value) + { + object_[sfSignature] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfAttestationRewardAccount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setAttestationRewardAccount(std::decay_t const& value) + { + object_[sfAttestationRewardAccount] = value; + return *this; + } + + /** + * @brief Set sfWasLockingChainSend (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setWasLockingChainSend(std::decay_t const& value) + { + object_[sfWasLockingChainSend] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainAddClaimAttestationBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Build and return the XChainAddClaimAttestation wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainAddClaimAttestation + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainAddClaimAttestation{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainClaim.h b/include/xrpl/protocol_autogen/transactions/XChainClaim.h new file mode 100644 index 0000000000..6b1b748677 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainClaim.h @@ -0,0 +1,238 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainClaimBuilder; + +/** + * @brief Transaction: XChainClaim + * + * Type: ttXCHAIN_CLAIM (43) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainClaimBuilder to construct new transactions. + */ +class XChainClaim : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CLAIM; + + /** + * @brief Construct a XChainClaim transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainClaim(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainClaim"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfDestinationTag (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + 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) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } +}; + +/** + * @brief Builder for XChainClaim transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainClaimBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainClaimBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param destination The sfDestination field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainClaimBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& xChainClaimID, std::decay_t const& destination, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CLAIM, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setDestination(destination); + setAmount(amount); + } + + /** + * @brief Construct a XChainClaimBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainClaimBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CLAIM) + { + throw std::runtime_error("Invalid transaction type for XChainClaimBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfDestinationTag (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setDestinationTag(std::decay_t const& value) + { + object_[sfDestinationTag] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainClaimBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainClaim wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainClaim + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainClaim{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCommit.h b/include/xrpl/protocol_autogen/transactions/XChainCommit.h new file mode 100644 index 0000000000..879c76f180 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCommit.h @@ -0,0 +1,214 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCommitBuilder; + +/** + * @brief Transaction: XChainCommit + * + * Type: ttXCHAIN_COMMIT (42) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCommitBuilder to construct new transactions. + */ +class XChainCommit : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_COMMIT; + + /** + * @brief Construct a XChainCommit transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCommit(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCommit"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfXChainClaimID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getXChainClaimID() const + { + return this->tx_->at(sfXChainClaimID); + } + + /** + * @brief Get sfAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getAmount() const + { + return this->tx_->at(sfAmount); + } + + /** + * @brief Get sfOtherChainDestination (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getOtherChainDestination() const + { + if (hasOtherChainDestination()) + { + return this->tx_->at(sfOtherChainDestination); + } + return std::nullopt; + } + + /** + * @brief Check if sfOtherChainDestination is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasOtherChainDestination() const + { + return this->tx_->isFieldPresent(sfOtherChainDestination); + } +}; + +/** + * @brief Builder for XChainCommit transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCommitBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCommitBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param xChainClaimID The sfXChainClaimID field value. + * @param amount The sfAmount field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCommitBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& xChainClaimID, std::decay_t const& amount, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_COMMIT, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setXChainClaimID(xChainClaimID); + setAmount(amount); + } + + /** + * @brief Construct a XChainCommitBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCommitBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_COMMIT) + { + throw std::runtime_error("Invalid transaction type for XChainCommitBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfXChainClaimID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setXChainClaimID(std::decay_t const& value) + { + object_[sfXChainClaimID] = value; + return *this; + } + + /** + * @brief Set sfAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setAmount(std::decay_t const& value) + { + object_[sfAmount] = value; + return *this; + } + + /** + * @brief Set sfOtherChainDestination (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainCommitBuilder& + setOtherChainDestination(std::decay_t const& value) + { + object_[sfOtherChainDestination] = value; + return *this; + } + + /** + * @brief Build and return the XChainCommit wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCommit + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCommit{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h new file mode 100644 index 0000000000..6ec22dd9fc --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h @@ -0,0 +1,190 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCreateBridgeBuilder; + +/** + * @brief Transaction: XChainCreateBridge + * + * Type: ttXCHAIN_CREATE_BRIDGE (48) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCreateBridgeBuilder to construct new transactions. + */ +class XChainCreateBridge : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_BRIDGE; + + /** + * @brief Construct a XChainCreateBridge transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCreateBridge(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCreateBridge"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + { + return this->tx_->at(sfMinAccountCreateAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->tx_->isFieldPresent(sfMinAccountCreateAmount); + } +}; + +/** + * @brief Builder for XChainCreateBridge transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCreateBridgeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCreateBridgeBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param signatureReward The sfSignatureReward field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCreateBridgeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& signatureReward, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CREATE_BRIDGE, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setSignatureReward(signatureReward); + } + + /** + * @brief Construct a XChainCreateBridgeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCreateBridgeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CREATE_BRIDGE) + { + throw std::runtime_error("Invalid transaction type for XChainCreateBridgeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainCreateBridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainCreateBridge wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCreateBridge + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCreateBridge{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h new file mode 100644 index 0000000000..ab76ee0e77 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h @@ -0,0 +1,177 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainCreateClaimIDBuilder; + +/** + * @brief Transaction: XChainCreateClaimID + * + * Type: ttXCHAIN_CREATE_CLAIM_ID (41) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainCreateClaimIDBuilder to construct new transactions. + */ +class XChainCreateClaimID : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_CLAIM_ID; + + /** + * @brief Construct a XChainCreateClaimID transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainCreateClaimID(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainCreateClaimID"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_AMOUNT::type::value_type + getSignatureReward() const + { + return this->tx_->at(sfSignatureReward); + } + + /** + * @brief Get sfOtherChainSource (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOtherChainSource() const + { + return this->tx_->at(sfOtherChainSource); + } +}; + +/** + * @brief Builder for XChainCreateClaimID transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainCreateClaimIDBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainCreateClaimIDBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param signatureReward The sfSignatureReward field value. + * @param otherChainSource The sfOtherChainSource field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainCreateClaimIDBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::decay_t const& signatureReward, std::decay_t const& otherChainSource, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_CREATE_CLAIM_ID, account, sequence, fee) + { + setXChainBridge(xChainBridge); + setSignatureReward(signatureReward); + setOtherChainSource(otherChainSource); + } + + /** + * @brief Construct a XChainCreateClaimIDBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainCreateClaimIDBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_CREATE_CLAIM_ID) + { + throw std::runtime_error("Invalid transaction type for XChainCreateClaimIDBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfOtherChainSource (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainCreateClaimIDBuilder& + setOtherChainSource(std::decay_t const& value) + { + object_[sfOtherChainSource] = value; + return *this; + } + + /** + * @brief Build and return the XChainCreateClaimID wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainCreateClaimID + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainCreateClaimID{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h new file mode 100644 index 0000000000..b57e0fdbdc --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h @@ -0,0 +1,203 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class XChainModifyBridgeBuilder; + +/** + * @brief Transaction: XChainModifyBridge + * + * Type: ttXCHAIN_MODIFY_BRIDGE (47) + * Delegable: Delegation::delegable + * Amendment: featureXChainBridge + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use XChainModifyBridgeBuilder to construct new transactions. + */ +class XChainModifyBridge : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttXCHAIN_MODIFY_BRIDGE; + + /** + * @brief Construct a XChainModifyBridge transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit XChainModifyBridge(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for XChainModifyBridge"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfXChainBridge (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_XCHAIN_BRIDGE::type::value_type + getXChainBridge() const + { + return this->tx_->at(sfXChainBridge); + } + + /** + * @brief Get sfSignatureReward (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSignatureReward() const + { + if (hasSignatureReward()) + { + return this->tx_->at(sfSignatureReward); + } + return std::nullopt; + } + + /** + * @brief Check if sfSignatureReward is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSignatureReward() const + { + return this->tx_->isFieldPresent(sfSignatureReward); + } + + /** + * @brief Get sfMinAccountCreateAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMinAccountCreateAmount() const + { + if (hasMinAccountCreateAmount()) + { + return this->tx_->at(sfMinAccountCreateAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfMinAccountCreateAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMinAccountCreateAmount() const + { + return this->tx_->isFieldPresent(sfMinAccountCreateAmount); + } +}; + +/** + * @brief Builder for XChainModifyBridge transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class XChainModifyBridgeBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new XChainModifyBridgeBuilder with required fields. + * @param account The account initiating the transaction. + * @param xChainBridge The sfXChainBridge field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + XChainModifyBridgeBuilder(SF_ACCOUNT::type::value_type account, + std::decay_t const& xChainBridge, std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttXCHAIN_MODIFY_BRIDGE, account, sequence, fee) + { + setXChainBridge(xChainBridge); + } + + /** + * @brief Construct a XChainModifyBridgeBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + XChainModifyBridgeBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttXCHAIN_MODIFY_BRIDGE) + { + throw std::runtime_error("Invalid transaction type for XChainModifyBridgeBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfXChainBridge (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setXChainBridge(std::decay_t const& value) + { + object_[sfXChainBridge] = value; + return *this; + } + + /** + * @brief Set sfSignatureReward (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setSignatureReward(std::decay_t const& value) + { + object_[sfSignatureReward] = value; + return *this; + } + + /** + * @brief Set sfMinAccountCreateAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + XChainModifyBridgeBuilder& + setMinAccountCreateAmount(std::decay_t const& value) + { + object_[sfMinAccountCreateAmount] = value; + return *this; + } + + /** + * @brief Build and return the XChainModifyBridge wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + XChainModifyBridge + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return XChainModifyBridge{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/src/xrpld/app/main/DBInit.h b/include/xrpl/rdb/DBInit.h similarity index 100% rename from src/xrpld/app/main/DBInit.h rename to include/xrpl/rdb/DBInit.h diff --git a/src/xrpld/core/DatabaseCon.h b/include/xrpl/rdb/DatabaseCon.h similarity index 88% rename from src/xrpld/core/DatabaseCon.h rename to include/xrpl/rdb/DatabaseCon.h index 89d582257b..d1fb7114e3 100644 --- a/src/xrpld/core/DatabaseCon.h +++ b/include/xrpl/rdb/DatabaseCon.h @@ -1,10 +1,9 @@ #pragma once -#include -#include -#include - #include +#include +#include +#include #include @@ -28,10 +27,12 @@ private: std::unique_lock lock_; public: - LockedSociSession(std::shared_ptr it, mutex& m) : session_(std::move(it)), lock_(m) + LockedSociSession(std::shared_ptr it, mutex& m) + : session_(std::move(it)), lock_(m) { } - LockedSociSession(LockedSociSession&& rhs) noexcept : session_(std::move(rhs.session_)), lock_(std::move(rhs.lock_)) + LockedSociSession(LockedSociSession&& rhs) noexcept + : session_(std::move(rhs.session_)), lock_(std::move(rhs.lock_)) { } LockedSociSession() = delete; @@ -68,7 +69,7 @@ public: { explicit Setup() = default; - Config::StartUpType startUp = Config::NORMAL; + StartUpType startUp = StartUpType::NORMAL; bool standAlone = false; boost::filesystem::path dataDir; // Indicates whether or not to return the `globalPragma` @@ -105,8 +106,9 @@ public: beast::Journal journal) // Use temporary files or regular DB files? : DatabaseCon( - setup.standAlone && setup.startUp != Config::LOAD && setup.startUp != Config::LOAD_FILE && - setup.startUp != Config::REPLAY + setup.standAlone && setup.startUp != StartUpType::LOAD && + setup.startUp != StartUpType::LOAD_FILE && + setup.startUp != StartUpType::REPLAY ? "" : (setup.dataDir / dbName), setup.commonPragma(), @@ -167,8 +169,8 @@ public: checkoutDb() { using namespace std::chrono_literals; - LockedSociSession session = - perf::measureDurationAndLog([&]() { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_); + LockedSociSession session = perf::measureDurationAndLog( + [&]() { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_); return session; } @@ -229,7 +231,4 @@ private: std::shared_ptr checkpointerFromId(std::uintptr_t id); -DatabaseCon::Setup -setup_DatabaseCon(Config const& c, std::optional j = std::nullopt); - } // namespace xrpl diff --git a/include/xrpl/rdb/RelationalDatabase.h b/include/xrpl/rdb/RelationalDatabase.h new file mode 100644 index 0000000000..e728e518aa --- /dev/null +++ b/include/xrpl/rdb/RelationalDatabase.h @@ -0,0 +1,479 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +class Transaction; +class Ledger; + +struct LedgerHashPair +{ + uint256 ledgerHash; + uint256 parentHash; +}; + +struct LedgerRange +{ + uint32_t min; + uint32_t max; +}; + +class RelationalDatabase +{ +public: + struct CountMinMax + { + std::size_t numberOfRows; + LedgerIndex minLedgerSequence; + LedgerIndex maxLedgerSequence; + }; + + struct AccountTxMarker + { + std::uint32_t ledgerSeq = 0; + std::uint32_t txnSeq = 0; + }; + + struct AccountTxOptions + { + AccountID const& account; + std::uint32_t minLedger; + std::uint32_t maxLedger; + std::uint32_t offset; + std::uint32_t limit; + bool bUnlimited; + }; + + struct AccountTxPageOptions + { + AccountID const& account; + std::uint32_t minLedger; + std::uint32_t maxLedger; + std::optional marker; + std::uint32_t limit; + bool bAdmin; + }; + + using AccountTx = std::pair, std::shared_ptr>; + using AccountTxs = std::vector; + using txnMetaLedgerType = std::tuple; + using MetaTxsList = std::vector; + + using LedgerSequence = uint32_t; + using LedgerHash = uint256; + using LedgerSpecifier = std::variant; + + struct AccountTxArgs + { + AccountID account; + std::optional ledger; + bool binary = false; + bool forward = false; + uint32_t limit = 0; + std::optional marker; + }; + + struct AccountTxResult + { + std::variant transactions; + LedgerRange ledgerRange; + uint32_t limit; + std::optional marker; + }; + + virtual ~RelationalDatabase() = default; + + /** + * @brief getMinLedgerSeq Returns the minimum ledger sequence in the Ledgers + * table. + * @return Ledger sequence or no value if no ledgers exist. + */ + virtual std::optional + getMinLedgerSeq() = 0; + + /** + * @brief getMaxLedgerSeq Returns the maximum ledger sequence in the Ledgers + * table. + * @return Ledger sequence or none if no ledgers exist. + */ + virtual std::optional + getMaxLedgerSeq() = 0; + + /** + * @brief getLedgerInfoByIndex Returns a ledger by its sequence. + * @param ledgerSeq Ledger sequence. + * @return The ledger if found, otherwise no value. + */ + virtual std::optional + getLedgerInfoByIndex(LedgerIndex ledgerSeq) = 0; + + /** + * @brief getNewestLedgerInfo Returns the info of the newest saved ledger. + * @return Ledger info if found, otherwise no value. + */ + virtual std::optional + getNewestLedgerInfo() = 0; + + /** + * @brief getLedgerInfoByHash Returns the info of the ledger with given + * hash. + * @param ledgerHash Hash of the ledger. + * @return Ledger if found, otherwise no value. + */ + virtual std::optional + getLedgerInfoByHash(uint256 const& ledgerHash) = 0; + + /** + * @brief getHashByIndex Returns the hash of the ledger with the given + * sequence. + * @param ledgerIndex Ledger sequence. + * @return Hash of the ledger. + */ + virtual uint256 + getHashByIndex(LedgerIndex ledgerIndex) = 0; + + /** + * @brief getHashesByIndex Returns the hashes of the ledger and its parent + * as specified by the ledgerIndex. + * @param ledgerIndex Ledger sequence. + * @return Struct LedgerHashPair which contains hashes of the ledger and + * its parent. + */ + virtual std::optional + getHashesByIndex(LedgerIndex ledgerIndex) = 0; + + /** + * @brief getHashesByIndex Returns hashes of each ledger and its parent for + * all ledgers within the provided range. + * @param minSeq Minimum ledger sequence. + * @param maxSeq Maximum ledger sequence. + * @return Container that maps the sequence number of a found ledger to the + * struct LedgerHashPair which contains the hashes of the ledger and + * its parent. + */ + virtual std::map + getHashesByIndex(LedgerIndex minSeq, LedgerIndex maxSeq) = 0; + + /** + * @brief getTxHistory Returns the 20 most recent transactions starting from + * the given number. + * @param startIndex First number of returned entry. + * @return Vector of shared pointers to transactions sorted in + * descending order by ledger sequence. + */ + virtual std::vector> + getTxHistory(LedgerIndex startIndex) = 0; + + /** + * @brief getTransactionsMinLedgerSeq Returns the minimum ledger sequence + * stored in the Transactions table. + * @return Ledger sequence or no value if no ledgers exist. + */ + virtual std::optional + getTransactionsMinLedgerSeq() = 0; + + /** + * @brief getAccountTransactionsMinLedgerSeq Returns the minimum ledger + * sequence stored in the AccountTransactions table. + * @return Ledger sequence or no value if no ledgers exist. + */ + virtual std::optional + getAccountTransactionsMinLedgerSeq() = 0; + + /** + * @brief deleteTransactionByLedgerSeq Deletes transactions from the ledger + * with the given sequence. + * @param ledgerSeq Ledger sequence. + */ + virtual void + deleteTransactionByLedgerSeq(LedgerIndex ledgerSeq) = 0; + + /** + * @brief deleteBeforeLedgerSeq Deletes all ledgers with a sequence number + * less than or equal to the given ledger sequence. + * @param ledgerSeq Ledger sequence. + */ + virtual void + deleteBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0; + + /** + * @brief deleteTransactionsBeforeLedgerSeq Deletes all transactions with + * a sequence number less than or equal to the given ledger + * sequence. + * @param ledgerSeq Ledger sequence. + */ + virtual void + deleteTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0; + + /** + * @brief deleteAccountTransactionsBeforeLedgerSeq Deletes all account + * transactions with a sequence number less than or equal to the + * given ledger sequence. + * @param ledgerSeq Ledger sequence. + */ + virtual void + deleteAccountTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0; + + /** + * @brief getTransactionCount Returns the number of transactions. + * @return Number of transactions. + */ + virtual std::size_t + getTransactionCount() = 0; + + /** + * @brief getAccountTransactionCount Returns the number of account + * transactions. + * @return Number of account transactions. + */ + virtual std::size_t + getAccountTransactionCount() = 0; + + /** + * @brief getLedgerCountMinMax Returns the minimum ledger sequence, + * maximum ledger sequence and total number of saved ledgers. + * @return Struct CountMinMax which contains the minimum sequence, + * maximum sequence and number of ledgers. + */ + virtual struct CountMinMax + getLedgerCountMinMax() = 0; + + /** + * @brief saveValidatedLedger Saves a ledger into the database. + * @param ledger The ledger. + * @param current True if the ledger is current. + * @return True if saving was successful. + */ + virtual bool + saveValidatedLedger(std::shared_ptr const& ledger, bool current) = 0; + + /** + * @brief getLimitedOldestLedgerInfo Returns the info of the oldest ledger + * whose sequence number is greater than or equal to the given + * sequence number. + * @param ledgerFirstIndex Minimum ledger sequence. + * @return Ledger info if found, otherwise no value. + */ + virtual std::optional + getLimitedOldestLedgerInfo(LedgerIndex ledgerFirstIndex) = 0; + + /** + * @brief getLimitedNewestLedgerInfo Returns the info of the newest ledger + * whose sequence number is greater than or equal to the given + * sequence number. + * @param ledgerFirstIndex Minimum ledger sequence. + * @return Ledger info if found, otherwise no value. + */ + virtual std::optional + getLimitedNewestLedgerInfo(LedgerIndex ledgerFirstIndex) = 0; + + /** + * @brief getOldestAccountTxs Returns the oldest transactions for the + * account that matches the given criteria starting from the provided + * offset. + * @param options Struct AccountTxOptions which contains the criteria to + * match: the account, ledger search range, the offset of the first + * entry to return, the number of transactions to return, a flag if + * this number is unlimited. + * @return Vector of pairs of found transactions and their metadata + * sorted in ascending order by account sequence. + */ + virtual AccountTxs + getOldestAccountTxs(AccountTxOptions const& options) = 0; + + /** + * @brief getNewestAccountTxs Returns the newest transactions for the + * account that matches the given criteria starting from the provided + * offset. + * @param options Struct AccountTxOptions which contains the criteria to + * match: the account, the ledger search range, the offset of the + * first entry to return, the number of transactions to return, a + * flag if this number unlimited. + * @return Vector of pairs of found transactions and their metadata + * sorted in descending order by account sequence. + */ + virtual AccountTxs + getNewestAccountTxs(AccountTxOptions const& options) = 0; + + /** + * @brief getOldestAccountTxsB Returns the oldest transactions in binary + * form for the account that matches the given criteria starting from + * the provided offset. + * @param options Struct AccountTxOptions which contains the criteria to + * match: the account, the ledger search range, the offset of the + * first entry to return, the number of transactions to return, a + * flag if this number unlimited. + * @return Vector of tuples of found transactions, their metadata and + * account sequences sorted in ascending order by account sequence. + */ + virtual MetaTxsList + getOldestAccountTxsB(AccountTxOptions const& options) = 0; + + /** + * @brief getNewestAccountTxsB Returns the newest transactions in binary + * form for the account that matches the given criteria starting from + * the provided offset. + * @param options Struct AccountTxOptions which contains the criteria to + * match: the account, the ledger search range, the offset of the + * first entry to return, the number of transactions to return, a + * flag if this number is unlimited. + * @return Vector of tuples of found transactions, their metadata and + * account sequences sorted in descending order by account + * sequence. + */ + virtual MetaTxsList + getNewestAccountTxsB(AccountTxOptions const& options) = 0; + + /** + * @brief oldestAccountTxPage Returns the oldest transactions for the + * account that matches the given criteria starting from the + * provided marker. + * @param options Struct AccountTxPageOptions which contains the criteria to + * match: the account, the ledger search range, the marker of first + * returned entry, the number of transactions to return, a flag if + * this number is unlimited. + * @return Vector of pairs of found transactions and their metadata + * sorted in ascending order by account sequence and a marker + * for the next search if the search was not finished. + */ + virtual std::pair> + oldestAccountTxPage(AccountTxPageOptions const& options) = 0; + + /** + * @brief newestAccountTxPage Returns the newest transactions for the + * account that matches the given criteria starting from the provided + * marker. + * @param options Struct AccountTxPageOptions which contains the criteria to + * match: the account, the ledger search range, the marker of the + * first returned entry, the number of transactions to return, a flag + * if this number unlimited. + * @return Vector of pairs of found transactions and their metadata + * sorted in descending order by account sequence and a marker + * for the next search if the search was not finished. + */ + virtual std::pair> + newestAccountTxPage(AccountTxPageOptions const& options) = 0; + + /** + * @brief oldestAccountTxPageB Returns the oldest transactions in binary + * form for the account that matches the given criteria starting from + * the provided marker. + * @param options Struct AccountTxPageOptions which contains criteria to + * match: the account, the ledger search range, the marker of the + * first returned entry, the number of transactions to return, a flag + * if this number unlimited. + * @return Vector of tuples of found transactions, their metadata and + * account sequences sorted in ascending order by account + * sequence and a marker for the next search if the search was not + * finished. + */ + virtual std::pair> + oldestAccountTxPageB(AccountTxPageOptions const& options) = 0; + + /** + * @brief newestAccountTxPageB Returns the newest transactions in binary + * form for the account that matches the given criteria starting from + * the provided marker. + * @param options Struct AccountTxPageOptions which contains the criteria to + * match: the account, the ledger search range, the marker of the + * first returned entry, the number of transactions to return, a flag + * if this number is unlimited. + * @return Vector of tuples of found transactions, their metadata and + * account sequences sorted in descending order by account + * sequence and a marker for the next search if the search was not + * finished. + */ + virtual std::pair> + newestAccountTxPageB(AccountTxPageOptions const& options) = 0; + + /** + * @brief getTransaction Returns the transaction with the given hash. If a + * range is provided but the transaction is not found, then check if + * all ledgers in the range are present in the database. + * @param id Hash of the transaction. + * @param range Range of ledgers to check, if present. + * @param ec Default error code value. + * @return Transaction and its metadata if found, otherwise TxSearched::all + * if a range is provided and all ledgers from the range are present + * in the database, TxSearched::some if a range is provided and not + * all ledgers are present, TxSearched::unknown if the range is not + * provided or a deserializing error occurred. In the last case the + * error code is returned via the ec parameter, in other cases the + * default error code is not changed. + */ + virtual std::variant + getTransaction( + uint256 const& id, + std::optional> const& range, + error_code_i& ec) = 0; + + /** + * @brief getKBUsedAll Returns the amount of space used by all databases. + * @return Space in kilobytes. + */ + virtual uint32_t + getKBUsedAll() = 0; + + /** + * @brief getKBUsedLedger Returns the amount of space space used by the + * ledger database. + * @return Space in kilobytes. + */ + virtual uint32_t + getKBUsedLedger() = 0; + + /** + * @brief getKBUsedTransaction Returns the amount of space used by the + * transaction database. + * @return Space in kilobytes. + */ + virtual uint32_t + getKBUsedTransaction() = 0; + + /** + * @brief Closes the ledger database + */ + virtual void + closeLedgerDB() = 0; + + /** + * @brief Closes the transaction database + */ + virtual void + closeTransactionDB() = 0; +}; + +template +T +rangeCheckedCast(C c) +{ + if ((c > std::numeric_limits::max()) || (!std::numeric_limits::is_signed && c < 0) || + (std::numeric_limits::is_signed && std::numeric_limits::is_signed && + c < std::numeric_limits::lowest())) + { + // This should never happen + // LCOV_EXCL_START + UNREACHABLE("xrpl::rangeCheckedCast : domain error"); + JLOG(debugLog().error()) << "rangeCheckedCast domain error:" + << " value = " << c + << " min = " << std::numeric_limits::lowest() + << " max: " << std::numeric_limits::max(); + // LCOV_EXCL_STOP + } + + return static_cast(c); +} + +} // namespace xrpl diff --git a/src/xrpld/core/SociDB.h b/include/xrpl/rdb/SociDB.h similarity index 100% rename from src/xrpld/core/SociDB.h rename to include/xrpl/rdb/SociDB.h diff --git a/include/xrpl/resource/ResourceManager.h b/include/xrpl/resource/ResourceManager.h index c04e7bb79b..4aab017691 100644 --- a/include/xrpl/resource/ResourceManager.h +++ b/include/xrpl/resource/ResourceManager.h @@ -27,7 +27,10 @@ public: virtual Consumer newInboundEndpoint(beast::IP::Endpoint const& address) = 0; virtual Consumer - newInboundEndpoint(beast::IP::Endpoint const& address, bool const proxy, std::string_view forwardedFor) = 0; + newInboundEndpoint( + beast::IP::Endpoint const& address, + bool const proxy, + std::string_view forwardedFor) = 0; /** Create a new endpoint keyed by outbound IP address and port. */ virtual Consumer diff --git a/include/xrpl/resource/detail/Logic.h b/include/xrpl/resource/detail/Logic.h index 066e416550..9a7959f481 100644 --- a/include/xrpl/resource/detail/Logic.h +++ b/include/xrpl/resource/detail/Logic.h @@ -68,7 +68,10 @@ private: //-------------------------------------------------------------------------- public: - Logic(beast::insight::Collector::ptr const& collector, clock_type& clock, beast::Journal journal) + Logic( + beast::insight::Collector::ptr const& collector, + clock_type& clock, + beast::Journal journal) : m_stats(collector), m_clock(clock), m_journal(journal) { } @@ -340,7 +343,8 @@ public: Import& import(iter->second); if (iter->second.whenExpires <= elapsed) { - for (auto item_iter(import.items.begin()); item_iter != import.items.end(); ++item_iter) + for (auto item_iter(import.items.begin()); item_iter != import.items.end(); + ++item_iter) { item_iter->consumer.entry().remote_balance -= item_iter->balance; } @@ -422,7 +426,8 @@ public: static constexpr Charge::value_type feeLogAsWarn = 3000; static constexpr Charge::value_type feeLogAsInfo = 1000; static constexpr Charge::value_type feeLogAsDebug = 100; - static_assert(feeLogAsWarn > feeLogAsInfo && feeLogAsInfo > feeLogAsDebug && feeLogAsDebug > 10); + static_assert( + feeLogAsWarn > feeLogAsInfo && feeLogAsInfo > feeLogAsDebug && feeLogAsDebug > 10); static auto getStream = [](Resource::Charge::value_type cost, beast::Journal& journal) { if (cost >= feeLogAsWarn) @@ -479,8 +484,8 @@ public: int const balance(entry.balance(now)); if (balance >= dropThreshold) { - JLOG(m_journal.warn()) << "Consumer entry " << entry << " dropped with balance " << balance - << " at or above drop threshold " << dropThreshold; + JLOG(m_journal.warn()) << "Consumer entry " << entry << " dropped with balance " + << balance << " at or above drop threshold " << dropThreshold; // Adding feeDrop at this point keeps the dropped connection // from re-connecting for at least a little while after it is @@ -502,7 +507,10 @@ public: //-------------------------------------------------------------------------- void - writeList(clock_type::time_point const now, beast::PropertyStream::Set& items, EntryIntrusiveList& list) + writeList( + clock_type::time_point const now, + beast::PropertyStream::Set& items, + EntryIntrusiveList& list) { for (auto& entry : list) { diff --git a/src/xrpld/rpc/InfoSub.h b/include/xrpl/server/InfoSub.h similarity index 94% rename from src/xrpld/rpc/InfoSub.h rename to include/xrpl/server/InfoSub.h index 7d4d4f06c8..d45f4d7740 100644 --- a/src/xrpld/rpc/InfoSub.h +++ b/include/xrpl/server/InfoSub.h @@ -1,12 +1,11 @@ #pragma once -#include - #include #include #include #include #include +#include namespace xrpl { @@ -63,7 +62,10 @@ public: // for use during InfoSub destruction // Removes only from the server virtual void - unsubAccountInternal(std::uint64_t uListener, hash_set const& vnaAccountIDs, bool realTime) = 0; + unsubAccountInternal( + std::uint64_t uListener, + hash_set const& vnaAccountIDs, + bool realTime) = 0; /** * subscribe an account's new transactions and retrieve the account's @@ -85,7 +87,10 @@ public: unsubAccountHistory(ref ispListener, AccountID const& account, bool historyOnly) = 0; virtual void - unsubAccountHistoryInternal(std::uint64_t uListener, AccountID const& account, bool historyOnly) = 0; + unsubAccountHistoryInternal( + std::uint64_t uListener, + AccountID const& account, + bool historyOnly) = 0; // VFALCO TODO Document the bool return value virtual bool @@ -132,6 +137,7 @@ public: virtual bool subPeerStatus(ref ispListener) = 0; + virtual bool unsubPeerStatus(std::uint64_t uListener) = 0; virtual void @@ -167,7 +173,7 @@ public: send(Json::Value const& jvObj, bool broadcast) = 0; std::uint64_t - getSeq(); + getSeq() const; void onSendEmpty(); diff --git a/src/xrpld/app/misc/LoadFeeTrack.h b/include/xrpl/server/LoadFeeTrack.h similarity index 94% rename from src/xrpld/app/misc/LoadFeeTrack.h rename to include/xrpl/server/LoadFeeTrack.h index 8eb53187ce..fc031a1833 100644 --- a/src/xrpld/app/misc/LoadFeeTrack.h +++ b/include/xrpl/server/LoadFeeTrack.h @@ -83,7 +83,8 @@ public: std::lock_guard sl(lock_); return std::make_pair( - std::max(localTxnLoadFee_, remoteTxnLoadFee_), std::max(remoteTxnLoadFee_, clusterTxnLoadFee_)); + std::max(localTxnLoadFee_, remoteTxnLoadFee_), + std::max(remoteTxnLoadFee_, clusterTxnLoadFee_)); } void @@ -110,7 +111,8 @@ public: isLoadedCluster() const { std::lock_guard sl(lock_); - return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee) || (clusterTxnLoadFee_ != lftNormalFee); + return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee) || + (clusterTxnLoadFee_ != lftNormalFee); } private: diff --git a/src/xrpld/app/misc/Manifest.h b/include/xrpl/server/Manifest.h similarity index 93% rename from src/xrpld/app/misc/Manifest.h rename to include/xrpl/server/Manifest.h index 79426a1a46..7f1bba921e 100644 --- a/src/xrpld/app/misc/Manifest.h +++ b/include/xrpl/server/Manifest.h @@ -85,7 +85,11 @@ struct Manifest std::optional const& signingKey_, std::uint32_t seq, std::string const& domain_) - : serialized(serialized_), masterKey(masterKey_), signingKey(signingKey_), sequence(seq), domain(domain_) + : serialized(serialized_) + , masterKey(masterKey_) + , signingKey(signingKey_) + , sequence(seq) + , domain(domain_) { } @@ -141,14 +145,20 @@ std::optional deserializeManifest(Slice s, beast::Journal journal); inline std::optional -deserializeManifest(std::string const& s, beast::Journal journal = beast::Journal(beast::Journal::getNullSink())) +deserializeManifest( + std::string const& s, + beast::Journal journal = beast::Journal(beast::Journal::getNullSink())) { return deserializeManifest(makeSlice(s), journal); } -template ::value || std::is_same::value>> +template < + class T, + class = std::enable_if_t::value || std::is_same::value>> std::optional -deserializeManifest(std::vector const& v, beast::Journal journal = beast::Journal(beast::Journal::getNullSink())) +deserializeManifest( + std::vector const& v, + beast::Journal journal = beast::Journal(beast::Journal::getNullSink())) { return deserializeManifest(makeSlice(v), journal); } @@ -159,8 +169,9 @@ operator==(Manifest const& lhs, Manifest const& rhs) { // In theory, comparing the two serialized strings should be // sufficient. - return lhs.sequence == rhs.sequence && lhs.masterKey == rhs.masterKey && lhs.signingKey == rhs.signingKey && - lhs.domain == rhs.domain && lhs.serialized == rhs.serialized; + return lhs.sequence == rhs.sequence && lhs.masterKey == rhs.masterKey && + lhs.signingKey == rhs.signingKey && lhs.domain == rhs.domain && + lhs.serialized == rhs.serialized; } inline bool @@ -368,7 +379,10 @@ public: May be called concurrently */ void - save(DatabaseCon& dbCon, std::string const& dbTable, std::function const& isTrusted); + save( + DatabaseCon& dbCon, + std::string const& dbTable, + std::function const& isTrusted); /** Invokes the callback once for every populated manifest. diff --git a/src/xrpld/app/misc/NetworkOPs.h b/include/xrpl/server/NetworkOPs.h similarity index 90% rename from src/xrpld/app/misc/NetworkOPs.h rename to include/xrpl/server/NetworkOPs.h index 66c915008a..75f1e0e1b2 100644 --- a/src/xrpld/app/misc/NetworkOPs.h +++ b/include/xrpl/server/NetworkOPs.h @@ -1,13 +1,12 @@ #pragma once -#include -#include -#include - #include -#include +#include #include +#include #include +#include +#include #include @@ -19,10 +18,13 @@ namespace xrpl { // Master operational handler, server sequencer, network tracker class Peer; +class STTx; +class ReadView; class LedgerMaster; class Transaction; class ValidatorKeys; class CanonicalTXSet; +class RCLCxPeerPos; // This is the primary interface into the "client" portion of the program. // Code that wants to do normal operations on the network such as @@ -114,7 +116,11 @@ public: * @param failType fail_hard setting from transaction submission. */ virtual void - processTransaction(std::shared_ptr& transaction, bool bUnlimited, bool bLocal, FailHard failType) = 0; + processTransaction( + std::shared_ptr& transaction, + bool bUnlimited, + bool bLocal, + FailHard failType) = 0; /** * Process a set of transactions synchronously, and ensuring that they are @@ -245,20 +251,4 @@ public: stateAccounting(Json::Value& obj) = 0; }; -//------------------------------------------------------------------------------ - -std::unique_ptr -make_NetworkOPs( - Application& app, - NetworkOPs::clock_type& clock, - bool standalone, - std::size_t minPeerCount, - bool start_valid, - JobQueue& job_queue, - LedgerMaster& ledgerMaster, - ValidatorKeys const& validatorKeys, - boost::asio::io_context& io_svc, - beast::Journal journal, - beast::insight::Collector::ptr const& collector); - } // namespace xrpl diff --git a/include/xrpl/server/Session.h b/include/xrpl/server/Session.h index 37cbf59775..f7146c3b00 100644 --- a/include/xrpl/server/Session.h +++ b/include/xrpl/server/Session.h @@ -63,7 +63,8 @@ public: void write(BufferSequence const& buffers) { - for (typename BufferSequence::const_iterator iter(buffers.begin()); iter != buffers.end(); ++iter) + for (typename BufferSequence::const_iterator iter(buffers.begin()); iter != buffers.end(); + ++iter) { typename BufferSequence::value_type const& buffer(*iter); write(buffer.data(), boost::asio::buffer_size(buffer)); diff --git a/src/xrpld/app/rdb/State.h b/include/xrpl/server/State.h similarity index 91% rename from src/xrpld/app/rdb/State.h rename to include/xrpl/server/State.h index 52118b3cf8..48e11869f4 100644 --- a/src/xrpld/app/rdb/State.h +++ b/include/xrpl/server/State.h @@ -1,10 +1,8 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include #include diff --git a/src/xrpld/app/rdb/Vacuum.h b/include/xrpl/server/Vacuum.h similarity index 90% rename from src/xrpld/app/rdb/Vacuum.h rename to include/xrpl/server/Vacuum.h index f592b4537e..5f80eced87 100644 --- a/src/xrpld/app/rdb/Vacuum.h +++ b/include/xrpl/server/Vacuum.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/rdb/Wallet.h b/include/xrpl/server/Wallet.h similarity index 90% rename from src/xrpld/app/rdb/Wallet.h rename to include/xrpl/server/Wallet.h index 141ef53f27..29b28c41a1 100644 --- a/src/xrpld/app/rdb/Wallet.h +++ b/include/xrpl/server/Wallet.h @@ -1,9 +1,8 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -36,7 +35,11 @@ makeTestWalletDB(DatabaseCon::Setup const& setup, std::string const& dbname, bea * @param j Journal. */ void -getManifests(soci::session& session, std::string const& dbTable, ManifestCache& mCache, beast::Journal j); +getManifests( + soci::session& session, + std::string const& dbTable, + ManifestCache& mCache, + beast::Journal j); /** * @brief saveManifests Saves all given manifests to the database. @@ -96,7 +99,10 @@ getPeerReservationTable(soci::session& session, beast::Journal j); * @param description Description of the node. */ void -insertPeerReservation(soci::session& session, PublicKey const& nodeId, std::string const& description); +insertPeerReservation( + soci::session& session, + PublicKey const& nodeId, + std::string const& description); /** * @brief deletePeerReservation Deletes an entry from the peer reservation @@ -141,6 +147,10 @@ readAmendments( * @param vote Whether to vote in favor of this amendment. */ void -voteAmendment(soci::session& session, uint256 const& amendment, std::string const& name, AmendmentVote vote); +voteAmendment( + soci::session& session, + uint256 const& amendment, + std::string const& name, + AmendmentVote vote); } // namespace xrpl diff --git a/include/xrpl/server/detail/BaseHTTPPeer.h b/include/xrpl/server/detail/BaseHTTPPeer.h index ab85cb7c89..f900261b84 100644 --- a/include/xrpl/server/detail/BaseHTTPPeer.h +++ b/include/xrpl/server/detail/BaseHTTPPeer.h @@ -198,7 +198,8 @@ BaseHTTPPeer::BaseHTTPPeer( , remote_address_(remote_address) , journal_(journal) { - read_buf_.commit(boost::asio::buffer_copy(read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers)); + read_buf_.commit( + boost::asio::buffer_copy(read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers)); static std::atomic sid; nid_ = ++sid; id_ = std::string("#") + std::to_string(nid_) + " "; @@ -218,7 +219,10 @@ void BaseHTTPPeer::close() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind((void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close, impl().shared_from_this())); + return post( + strand_, + std::bind( + (void (BaseHTTPPeer::*)(void))&BaseHTTPPeer::close, impl().shared_from_this())); boost::beast::get_lowest_layer(impl().stream_).close(); } @@ -242,7 +246,8 @@ BaseHTTPPeer::start_timer() { boost::beast::get_lowest_layer(impl().stream_) .expires_after( - std::chrono::seconds(remote_address_.address().is_loopback() ? timeoutSecondsLocal : timeoutSeconds)); + std::chrono::seconds( + remote_address_.address().is_loopback() ? timeoutSecondsLocal : timeoutSeconds)); } // Convenience for discarding the error code @@ -313,19 +318,29 @@ BaseHTTPPeer::on_write(error_code const& ec, std::size_t bytes_tr bind_executor( strand_, std::bind( - &BaseHTTPPeer::on_write, impl().shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + &BaseHTTPPeer::on_write, + impl().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } if (!complete_) return; if (graceful_) return do_close(); util::spawn( - strand_, std::bind(&BaseHTTPPeer::do_read, impl().shared_from_this(), std::placeholders::_1)); + strand_, + std::bind( + &BaseHTTPPeer::do_read, + impl().shared_from_this(), + std::placeholders::_1)); } template void -BaseHTTPPeer::do_writer(std::shared_ptr const& writer, bool keep_alive, yield_context do_yield) +BaseHTTPPeer::do_writer( + std::shared_ptr const& writer, + bool keep_alive, + yield_context do_yield) { std::function resume; { @@ -333,7 +348,12 @@ BaseHTTPPeer::do_writer(std::shared_ptr const& writer, bo resume = std::function([this, p, writer, keep_alive]() { util::spawn( strand_, - std::bind(&BaseHTTPPeer::do_writer, p, writer, keep_alive, std::placeholders::_1)); + std::bind( + &BaseHTTPPeer::do_writer, + p, + writer, + keep_alive, + std::placeholders::_1)); }); } @@ -342,8 +362,8 @@ BaseHTTPPeer::do_writer(std::shared_ptr const& writer, bo if (!writer->prepare(bufferSize, resume)) return; error_code ec; - auto const bytes_transferred = - boost::asio::async_write(impl().stream_, writer->data(), boost::asio::transfer_at_least(1), do_yield[ec]); + auto const bytes_transferred = boost::asio::async_write( + impl().stream_, writer->data(), boost::asio::transfer_at_least(1), do_yield[ec]); if (ec) return fail(ec, "writer"); writer->consume(bytes_transferred); @@ -355,7 +375,11 @@ BaseHTTPPeer::do_writer(std::shared_ptr const& writer, bo return do_close(); util::spawn( - strand_, std::bind(&BaseHTTPPeer::do_read, impl().shared_from_this(), std::placeholders::_1)); + strand_, + std::bind( + &BaseHTTPPeer::do_read, + impl().shared_from_this(), + std::placeholders::_1)); } //------------------------------------------------------------------------------ @@ -374,7 +398,9 @@ BaseHTTPPeer::write(void const* buf, std::size_t bytes) }()) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&BaseHTTPPeer::on_write, impl().shared_from_this(), error_code{}, 0)); + return post( + strand_, + std::bind(&BaseHTTPPeer::on_write, impl().shared_from_this(), error_code{}, 0)); else return on_write(error_code{}, 0); } @@ -410,7 +436,8 @@ void BaseHTTPPeer::complete() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&BaseHTTPPeer::complete, impl().shared_from_this())); + return post( + strand_, std::bind(&BaseHTTPPeer::complete, impl().shared_from_this())); message_ = {}; complete_ = true; @@ -423,7 +450,11 @@ BaseHTTPPeer::complete() // keep-alive util::spawn( - strand_, std::bind(&BaseHTTPPeer::do_read, impl().shared_from_this(), std::placeholders::_1)); + strand_, + std::bind( + &BaseHTTPPeer::do_read, + impl().shared_from_this(), + std::placeholders::_1)); } // DEPRECATED @@ -436,7 +467,7 @@ BaseHTTPPeer::close(bool graceful) return post( strand_, std::bind( - (void(BaseHTTPPeer::*)(bool)) & BaseHTTPPeer::close, + (void (BaseHTTPPeer::*)(bool))&BaseHTTPPeer::close, impl().shared_from_this(), graceful)); diff --git a/include/xrpl/server/detail/BaseWSPeer.h b/include/xrpl/server/detail/BaseWSPeer.h index ab985dfac5..f415520e59 100644 --- a/include/xrpl/server/detail/BaseWSPeer.h +++ b/include/xrpl/server/detail/BaseWSPeer.h @@ -48,7 +48,8 @@ private: bool ping_active_ = false; boost::beast::websocket::ping_data payload_; error_code ec_; - std::function control_callback_; + std::function + control_callback_; public: template @@ -174,16 +175,20 @@ BaseWSPeer::run() impl().ws_.set_option(port().pmd_options); // Must manage the control callback memory outside of the `control_callback` // function - control_callback_ = std::bind(&BaseWSPeer::on_ping_pong, this, std::placeholders::_1, std::placeholders::_2); + control_callback_ = + std::bind(&BaseWSPeer::on_ping_pong, this, std::placeholders::_1, std::placeholders::_2); impl().ws_.control_callback(control_callback_); start_timer(); close_on_timer_ = true; - impl().ws_.set_option(boost::beast::websocket::stream_base::decorator( - [](auto& res) { res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString()); })); + impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) { + res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString()); + })); impl().ws_.async_accept( request_, bind_executor( - strand_, std::bind(&BaseWSPeer::on_ws_handshake, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &BaseWSPeer::on_ws_handshake, impl().shared_from_this(), std::placeholders::_1))); } template @@ -227,9 +232,11 @@ BaseWSPeer::close(boost::beast::websocket::close_reason const& re if (wq_.empty()) { impl().ws_.async_close( - reason, bind_executor(strand_, [self = impl().shared_from_this()](boost::beast::error_code const& ec) { - self->on_close(ec); - })); + reason, + bind_executor( + strand_, [self = impl().shared_from_this()](boost::beast::error_code const& ec) { + self->on_close(ec); + })); } else { @@ -272,7 +279,8 @@ BaseWSPeer::on_write(error_code const& ec) if (ec) return fail(ec, "write"); auto& w = *wq_.front(); - auto const result = w.prepare(65536, std::bind(&BaseWSPeer::do_write, impl().shared_from_this())); + auto const result = + w.prepare(65536, std::bind(&BaseWSPeer::do_write, impl().shared_from_this())); if (boost::indeterminate(result.first)) return; start_timer(); @@ -280,13 +288,18 @@ BaseWSPeer::on_write(error_code const& ec) impl().ws_.async_write_some( static_cast(result.first), result.second, - bind_executor(strand_, std::bind(&BaseWSPeer::on_write, impl().shared_from_this(), std::placeholders::_1))); + bind_executor( + strand_, + std::bind( + &BaseWSPeer::on_write, impl().shared_from_this(), std::placeholders::_1))); else impl().ws_.async_write_some( static_cast(result.first), result.second, bind_executor( - strand_, std::bind(&BaseWSPeer::on_write_fin, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &BaseWSPeer::on_write_fin, impl().shared_from_this(), std::placeholders::_1))); } template @@ -300,7 +313,10 @@ BaseWSPeer::on_write_fin(error_code const& ec) { impl().ws_.async_close( cr_, - bind_executor(strand_, std::bind(&BaseWSPeer::on_close, impl().shared_from_this(), std::placeholders::_1))); + bind_executor( + strand_, + std::bind( + &BaseWSPeer::on_close, impl().shared_from_this(), std::placeholders::_1))); } else if (!wq_.empty()) on_write({}); @@ -313,7 +329,10 @@ BaseWSPeer::do_read() if (!strand_.running_in_this_thread()) return post(strand_, std::bind(&BaseWSPeer::do_read, impl().shared_from_this())); impl().ws_.async_read( - rb_, bind_executor(strand_, std::bind(&BaseWSPeer::on_read, impl().shared_from_this(), std::placeholders::_1))); + rb_, + bind_executor( + strand_, + std::bind(&BaseWSPeer::on_read, impl().shared_from_this(), std::placeholders::_1))); } template @@ -357,7 +376,11 @@ BaseWSPeer::start_timer() } timer_.async_wait(bind_executor( - strand_, std::bind(&BaseWSPeer::on_timer, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &BaseWSPeer::on_timer, + impl().shared_from_this(), + std::placeholders::_1))); } // Convenience for discarding the error code @@ -389,7 +412,9 @@ BaseWSPeer::on_ping(error_code const& ec) template void -BaseWSPeer::on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload) +BaseWSPeer::on_ping_pong( + boost::beast::websocket::frame_type kind, + boost::beast::string_view payload) { if (kind == boost::beast::websocket::frame_type::pong) { @@ -424,7 +449,9 @@ BaseWSPeer::on_timer(error_code ec) impl().ws_.async_ping( payload_, bind_executor( - strand_, std::bind(&BaseWSPeer::on_ping, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &BaseWSPeer::on_ping, impl().shared_from_this(), std::placeholders::_1))); JLOG(this->j_.trace()) << "sent ping"; return; } diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index e41049f203..f977dc6002 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -136,7 +136,11 @@ public: private: template void - create(bool ssl, ConstBufferSequence const& buffers, stream_type&& stream, endpoint_type remote_address); + create( + bool ssl, + ConstBufferSequence const& buffers, + stream_type&& stream, + endpoint_type remote_address); void do_accept(yield_context yield); @@ -165,7 +169,8 @@ template void Door::Detector::run() { - util::spawn(strand_, std::bind(&Detector::do_detect, this->shared_from_this(), std::placeholders::_1)); + util::spawn( + strand_, std::bind(&Detector::do_detect, this->shared_from_this(), std::placeholders::_1)); } template @@ -258,7 +263,11 @@ Door::reOpen() } template -Door::Door(Handler& handler, boost::asio::io_context& io_context, Port const& port, beast::Journal j) +Door::Door( + Handler& handler, + boost::asio::io_context& io_context, + Port const& port, + beast::Journal j) : j_(j) , port_(port) , handler_(handler) @@ -266,9 +275,11 @@ Door::Door(Handler& handler, boost::asio::io_context& io_context, Port , acceptor_(io_context) , strand_(boost::asio::make_strand(io_context)) , ssl_( - port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || port_.protocol.count("wss2") > 0 || - port_.protocol.count("peer") > 0) - , plain_(port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || port_.protocol.count("ws2")) + port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || + port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0) + , plain_( + port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || + port_.protocol.count("ws2")) , backoff_timer_(io_context) { reOpen(); @@ -278,7 +289,9 @@ template void Door::run() { - util::spawn(strand_, std::bind(&Door::do_accept, this->shared_from_this(), std::placeholders::_1)); + util::spawn( + strand_, + std::bind(&Door::do_accept, this->shared_from_this(), std::placeholders::_1)); } template @@ -286,7 +299,8 @@ void Door::close() { if (!strand_.running_in_this_thread()) - return boost::asio::post(strand_, std::bind(&Door::close, this->shared_from_this())); + return boost::asio::post( + strand_, std::bind(&Door::close, this->shared_from_this())); backoff_timer_.cancel(); error_code ec; acceptor_.close(ec); @@ -297,7 +311,11 @@ Door::close() template template void -Door::create(bool ssl, ConstBufferSequence const& buffers, stream_type&& stream, endpoint_type remote_address) +Door::create( + bool ssl, + ConstBufferSequence const& buffers, + stream_type&& stream, + endpoint_type remote_address) { if (ssl) { @@ -337,9 +355,11 @@ Door::do_accept(boost::asio::yield_context do_yield) if (ec == boost::asio::error::operation_aborted) break; - if (ec == boost::asio::error::no_descriptors || ec == boost::asio::error::no_buffer_space) + if (ec == boost::asio::error::no_descriptors || + ec == boost::asio::error::no_buffer_space) { - JLOG(j_.warn()) << "accept: Too many open files. Pausing for " << accept_delay_.count() << "ms."; + JLOG(j_.warn()) << "accept: Too many open files. Pausing for " + << accept_delay_.count() << "ms."; backoff_timer_.expires_after(accept_delay_); boost::system::error_code tec; @@ -358,8 +378,8 @@ Door::do_accept(boost::asio::yield_context do_yield) if (ssl_ && plain_) { - if (auto sp = - ios().template emplace(port_, handler_, ioc_, std::move(stream), remote_address, j_)) + if (auto sp = ios().template emplace( + port_, handler_, ioc_, std::move(stream), remote_address, j_)) sp->run(); } else if (ssl_ || plain_) diff --git a/include/xrpl/server/detail/PlainHTTPPeer.h b/include/xrpl/server/detail/PlainHTTPPeer.h index 94c84336f9..ef6128560a 100644 --- a/include/xrpl/server/detail/PlainHTTPPeer.h +++ b/include/xrpl/server/detail/PlainHTTPPeer.h @@ -60,7 +60,13 @@ PlainHTTPPeer::PlainHTTPPeer( endpoint_type remote_endpoint, ConstBufferSequence const& buffers, stream_type&& stream) - : BaseHTTPPeer(port, handler, ioc.get_executor(), journal, remote_endpoint, buffers) + : BaseHTTPPeer( + port, + handler, + ioc.get_executor(), + journal, + remote_endpoint, + buffers) , stream_(std::move(stream)) , socket_(stream_.socket()) { @@ -85,7 +91,9 @@ PlainHTTPPeer::run() if (!socket_.is_open()) return; - util::spawn(this->strand_, std::bind(&PlainHTTPPeer::do_read, this->shared_from_this(), std::placeholders::_1)); + util::spawn( + this->strand_, + std::bind(&PlainHTTPPeer::do_read, this->shared_from_this(), std::placeholders::_1)); } template @@ -107,7 +115,8 @@ void PlainHTTPPeer::do_request() { ++this->request_count_; - auto const what = this->handler_.onHandoff(this->session(), std::move(this->message_), this->remote_address_); + auto const what = + this->handler_.onHandoff(this->session(), std::move(this->message_), this->remote_address_); if (what.moved) return; boost::system::error_code ec; diff --git a/include/xrpl/server/detail/SSLHTTPPeer.h b/include/xrpl/server/detail/SSLHTTPPeer.h index 138145544e..240261fce6 100644 --- a/include/xrpl/server/detail/SSLHTTPPeer.h +++ b/include/xrpl/server/detail/SSLHTTPPeer.h @@ -73,7 +73,13 @@ SSLHTTPPeer::SSLHTTPPeer( endpoint_type remote_address, ConstBufferSequence const& buffers, middle_type&& stream) - : BaseHTTPPeer(port, handler, ioc.get_executor(), journal, remote_address, buffers) + : BaseHTTPPeer( + port, + handler, + ioc.get_executor(), + journal, + remote_address, + buffers) , stream_ptr_(std::make_unique(middle_type(std::move(stream)), *port.context)) , stream_(*stream_ptr_) , socket_(stream_.next_layer().socket()) @@ -92,7 +98,9 @@ SSLHTTPPeer::run() } if (!socket_.is_open()) return; - util::spawn(this->strand_, std::bind(&SSLHTTPPeer::do_handshake, this->shared_from_this(), std::placeholders::_1)); + util::spawn( + this->strand_, + std::bind(&SSLHTTPPeer::do_handshake, this->shared_from_this(), std::placeholders::_1)); } template @@ -116,17 +124,21 @@ SSLHTTPPeer::do_handshake(yield_context do_yield) boost::system::error_code ec; stream_.set_verify_mode(boost::asio::ssl::verify_none); this->start_timer(); - this->read_buf_.consume(stream_.async_handshake(stream_type::server, this->read_buf_.data(), do_yield[ec])); + this->read_buf_.consume( + stream_.async_handshake(stream_type::server, this->read_buf_.data(), do_yield[ec])); this->cancel_timer(); if (ec == boost::beast::error::timeout) return this->on_timer(); if (ec) return this->fail(ec, "handshake"); - bool const http = this->port().protocol.count("peer") > 0 || this->port().protocol.count("wss") > 0 || - this->port().protocol.count("wss2") > 0 || this->port().protocol.count("https") > 0; + bool const http = this->port().protocol.count("peer") > 0 || + this->port().protocol.count("wss") > 0 || this->port().protocol.count("wss2") > 0 || + this->port().protocol.count("https") > 0; if (http) { - util::spawn(this->strand_, std::bind(&SSLHTTPPeer::do_read, this->shared_from_this(), std::placeholders::_1)); + util::spawn( + this->strand_, + std::bind(&SSLHTTPPeer::do_read, this->shared_from_this(), std::placeholders::_1)); return; } // `this` will be destroyed @@ -153,7 +165,8 @@ SSLHTTPPeer::do_close() { this->start_timer(); stream_.async_shutdown(bind_executor( - this->strand_, std::bind(&SSLHTTPPeer::on_shutdown, this->shared_from_this(), std::placeholders::_1))); + this->strand_, + std::bind(&SSLHTTPPeer::on_shutdown, this->shared_from_this(), std::placeholders::_1))); } template diff --git a/include/xrpl/server/detail/ServerImpl.h b/include/xrpl/server/detail/ServerImpl.h index e836b964d2..1f91413521 100644 --- a/include/xrpl/server/detail/ServerImpl.h +++ b/include/xrpl/server/detail/ServerImpl.h @@ -116,7 +116,10 @@ private: }; template -ServerImpl::ServerImpl(Handler& handler, boost::asio::io_context& io_context, beast::Journal journal) +ServerImpl::ServerImpl( + Handler& handler, + boost::asio::io_context& io_context, + beast::Journal journal) : handler_(handler) , j_(journal) , io_context_(io_context) diff --git a/include/xrpl/server/detail/Spawn.h b/include/xrpl/server/detail/Spawn.h index d94fb4fa05..50760be761 100644 --- a/include/xrpl/server/detail/Spawn.h +++ b/include/xrpl/server/detail/Spawn.h @@ -12,7 +12,8 @@ namespace xrpl::util { namespace impl { template -concept IsStrand = std::same_as, boost::asio::strand::inner_executor_type>>; +concept IsStrand = std:: + same_as, boost::asio::strand::inner_executor_type>>; /** * @brief A completion handler that restores `boost::asio::spawn`'s behaviour @@ -66,7 +67,8 @@ spawn(Ctx&& ctx, F&& func) { if constexpr (impl::IsStrand) { - boost::asio::spawn(std::forward(ctx), std::forward(func), impl::kPROPAGATE_EXCEPTIONS); + boost::asio::spawn( + std::forward(ctx), std::forward(func), impl::kPROPAGATE_EXCEPTIONS); } else { diff --git a/include/xrpl/shamap/SHAMap.h b/include/xrpl/shamap/SHAMap.h index cd084ab48b..2c0910a830 100644 --- a/include/xrpl/shamap/SHAMap.h +++ b/include/xrpl/shamap/SHAMap.h @@ -99,7 +99,8 @@ public: /** The depth of the hash map: data is only present in the leaves */ static inline constexpr unsigned int leafDepth = 64; - using DeltaItem = std::pair, boost::intrusive_ptr>; + using DeltaItem = + std::pair, boost::intrusive_ptr>; using Delta = std::map; SHAMap() = delete; @@ -325,8 +326,10 @@ public: invariants() const; private: - using SharedPtrNodeStack = std::stack, SHAMapNodeID>>; - using DeltaRef = std::pair, boost::intrusive_ptr>; + using SharedPtrNodeStack = + std::stack, SHAMapNodeID>>; + using DeltaRef = + std::pair, boost::intrusive_ptr>; // tree node cache operations intr_ptr::SharedPtr @@ -349,7 +352,10 @@ private: /** Update hashes up to the root */ void - dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, intr_ptr::SharedPtr terminal); + dirtyUp( + SharedPtrNodeStack& stack, + uint256 const& target, + intr_ptr::SharedPtr terminal); /** Walk towards the specified id, returning the node. Caller must check if the return is nullptr, and if not, if the node->peekItem()->key() == @@ -376,11 +382,15 @@ private: // returns the first item at or below this node SHAMapLeafNode* - firstBelow(intr_ptr::SharedPtr, SharedPtrNodeStack& stack, int branch = 0) const; + firstBelow(intr_ptr::SharedPtr, SharedPtrNodeStack& stack, int branch = 0) + const; // returns the last item at or below this node SHAMapLeafNode* - lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch = branchFactor) const; + lastBelow( + intr_ptr::SharedPtr node, + SharedPtrNodeStack& stack, + int branch = branchFactor) const; // helper function for firstBelow and lastBelow SHAMapLeafNode* @@ -388,7 +398,8 @@ private: intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch, - std::tuple, std::function> const& loopParams) const; + std::tuple, std::function> const& loopParams) + const; // Simple descent // Get a child of the specified node @@ -403,12 +414,22 @@ private: // Descend with filter // If pending, callback is called as if it called fetchNodeNT - using descendCallback = std::function, SHAMapHash const&)>; + using descendCallback = + std::function, SHAMapHash const&)>; SHAMapTreeNode* - descendAsync(SHAMapInnerNode* parent, int branch, SHAMapSyncFilter* filter, bool& pending, descendCallback&&) const; + descendAsync( + SHAMapInnerNode* parent, + int branch, + SHAMapSyncFilter* filter, + bool& pending, + descendCallback&&) const; std::pair - descend(SHAMapInnerNode* parent, SHAMapNodeID const& parentID, int branch, SHAMapSyncFilter* filter) const; + descend( + SHAMapInnerNode* parent, + SHAMapNodeID const& parentID, + int branch, + SHAMapSyncFilter* filter) const; // Non-storing // Does not hook the returned node to its parent @@ -613,7 +634,10 @@ inline SHAMap::const_iterator::const_iterator(SHAMap const* map, std::nullptr_t) { } -inline SHAMap::const_iterator::const_iterator(SHAMap const* map, pointer item, SharedPtrNodeStack&& stack) +inline SHAMap::const_iterator::const_iterator( + SHAMap const* map, + pointer item, + SharedPtrNodeStack&& stack) : stack_(std::move(stack)), map_(map), item_(item) { } diff --git a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h index 552d2b4692..98ac9776c3 100644 --- a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h +++ b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h @@ -9,7 +9,8 @@ namespace xrpl { /** A leaf node for a state object. */ -class SHAMapAccountStateLeafNode final : public SHAMapLeafNode, public CountedObject +class SHAMapAccountStateLeafNode final : public SHAMapLeafNode, + public CountedObject { public: SHAMapAccountStateLeafNode(boost::intrusive_ptr item, std::uint32_t cowid) @@ -18,7 +19,10 @@ public: updateHash(); } - SHAMapAccountStateLeafNode(boost::intrusive_ptr item, std::uint32_t cowid, SHAMapHash const& hash) + SHAMapAccountStateLeafNode( + boost::intrusive_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) : SHAMapLeafNode(std::move(item), cowid, hash) { } diff --git a/include/xrpl/shamap/SHAMapAddNode.h b/include/xrpl/shamap/SHAMapAddNode.h index 7d0aaf3be4..e38884e050 100644 --- a/include/xrpl/shamap/SHAMapAddNode.h +++ b/include/xrpl/shamap/SHAMapAddNode.h @@ -51,7 +51,8 @@ inline SHAMapAddNode::SHAMapAddNode() : mGood(0), mBad(0), mDuplicate(0) { } -inline SHAMapAddNode::SHAMapAddNode(int good, int bad, int duplicate) : mGood(good), mBad(bad), mDuplicate(duplicate) +inline SHAMapAddNode::SHAMapAddNode(int good, int bad, int duplicate) + : mGood(good), mBad(bad), mDuplicate(duplicate) { } diff --git a/include/xrpl/shamap/SHAMapItem.h b/include/xrpl/shamap/SHAMapItem.h index ab9cf74dc8..dd098d88aa 100644 --- a/include/xrpl/shamap/SHAMapItem.h +++ b/include/xrpl/shamap/SHAMapItem.h @@ -42,9 +42,11 @@ private: // the only way to properly create one is to first allocate enough memory // so we limit this constructor to codepaths that do this right and limit // arbitrary construction. - SHAMapItem(uint256 const& tag, Slice data) : tag_(tag), size_(static_cast(data.size())) + SHAMapItem(uint256 const& tag, Slice data) + : tag_(tag), size_(static_cast(data.size())) { - std::memcpy(reinterpret_cast(this) + sizeof(*this), data.data(), data.size()); + std::memcpy( + reinterpret_cast(this) + sizeof(*this), data.data(), data.size()); } public: @@ -136,7 +138,8 @@ intrusive_ptr_release(SHAMapItem const* x) inline boost::intrusive_ptr make_shamapitem(uint256 const& tag, Slice data) { - XRPL_ASSERT(data.size() <= megabytes(16), "xrpl::make_shamapitem : maximum input size"); + XRPL_ASSERT( + data.size() <= megabytes(16), "xrpl::make_shamapitem : maximum input size"); std::uint8_t* raw = detail::slabber.allocate(data.size()); diff --git a/include/xrpl/shamap/SHAMapLeafNode.h b/include/xrpl/shamap/SHAMapLeafNode.h index aedcb3b79c..08f83b463a 100644 --- a/include/xrpl/shamap/SHAMapLeafNode.h +++ b/include/xrpl/shamap/SHAMapLeafNode.h @@ -14,7 +14,10 @@ protected: SHAMapLeafNode(boost::intrusive_ptr item, std::uint32_t cowid); - SHAMapLeafNode(boost::intrusive_ptr item, std::uint32_t cowid, SHAMapHash const& hash); + SHAMapLeafNode( + boost::intrusive_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash); public: SHAMapLeafNode(SHAMapLeafNode const&) = delete; diff --git a/include/xrpl/shamap/SHAMapSyncFilter.h b/include/xrpl/shamap/SHAMapSyncFilter.h index 219f9ac558..b34d160c25 100644 --- a/include/xrpl/shamap/SHAMapSyncFilter.h +++ b/include/xrpl/shamap/SHAMapSyncFilter.h @@ -18,8 +18,12 @@ public: // Note that the nodeData is overwritten by this call virtual void - gotNode(bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, SHAMapNodeType type) - const = 0; + gotNode( + bool fromFilter, + SHAMapHash const& nodeHash, + std::uint32_t ledgerSeq, + Blob&& nodeData, + SHAMapNodeType type) const = 0; virtual std::optional getNode(SHAMapHash const& nodeHash) const = 0; diff --git a/include/xrpl/shamap/SHAMapTreeNode.h b/include/xrpl/shamap/SHAMapTreeNode.h index 668f658754..f837a8643b 100644 --- a/include/xrpl/shamap/SHAMapTreeNode.h +++ b/include/xrpl/shamap/SHAMapTreeNode.h @@ -55,7 +55,8 @@ protected: { } - explicit SHAMapTreeNode(std::uint32_t cowid, SHAMapHash const& hash) noexcept : hash_(hash), cowid_(cowid) + explicit SHAMapTreeNode(std::uint32_t cowid, SHAMapHash const& hash) noexcept + : hash_(hash), cowid_(cowid) { } /** @} */ diff --git a/include/xrpl/shamap/SHAMapTxLeafNode.h b/include/xrpl/shamap/SHAMapTxLeafNode.h index 0a44380b9b..dcedb26881 100644 --- a/include/xrpl/shamap/SHAMapTxLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxLeafNode.h @@ -18,7 +18,10 @@ public: updateHash(); } - SHAMapTxLeafNode(boost::intrusive_ptr item, std::uint32_t cowid, SHAMapHash const& hash) + SHAMapTxLeafNode( + boost::intrusive_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) : SHAMapLeafNode(std::move(item), cowid, hash) { } diff --git a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h index d11cc87ff7..60e645fccc 100644 --- a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h @@ -9,7 +9,8 @@ namespace xrpl { /** A leaf node for a transaction and its associated metadata. */ -class SHAMapTxPlusMetaLeafNode final : public SHAMapLeafNode, public CountedObject +class SHAMapTxPlusMetaLeafNode final : public SHAMapLeafNode, + public CountedObject { public: SHAMapTxPlusMetaLeafNode(boost::intrusive_ptr item, std::uint32_t cowid) @@ -18,7 +19,10 @@ public: updateHash(); } - SHAMapTxPlusMetaLeafNode(boost::intrusive_ptr item, std::uint32_t cowid, SHAMapHash const& hash) + SHAMapTxPlusMetaLeafNode( + boost::intrusive_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) : SHAMapLeafNode(std::move(item), cowid, hash) { } diff --git a/include/xrpl/shamap/detail/TaggedPointer.h b/include/xrpl/shamap/detail/TaggedPointer.h index 217cd93d0a..d7adde4b05 100644 --- a/include/xrpl/shamap/detail/TaggedPointer.h +++ b/include/xrpl/shamap/detail/TaggedPointer.h @@ -39,7 +39,9 @@ namespace xrpl { class TaggedPointer { private: - static_assert(alignof(SHAMapHash) >= 4, "Bad alignment: Tag pointer requires low two bits to be zero."); + static_assert( + alignof(SHAMapHash) >= 4, + "Bad alignment: Tag pointer requires low two bits to be zero."); /** Upper bits are the pointer, lowest two bits are the tag A moved-from object will have a tp_ of zero. */ diff --git a/include/xrpl/shamap/detail/TaggedPointer.ipp b/include/xrpl/shamap/detail/TaggedPointer.ipp index bedb33a733..903108ac66 100644 --- a/include/xrpl/shamap/detail/TaggedPointer.ipp +++ b/include/xrpl/shamap/detail/TaggedPointer.ipp @@ -25,7 +25,8 @@ static_assert( // Terminology: A chunk is the memory being allocated from a block. A block // contains multiple chunks. This is the terminology the boost documentation // uses. Pools use "Simple Segregated Storage" as their storage format. -constexpr size_t elementSizeBytes = (sizeof(SHAMapHash) + sizeof(intr_ptr::SharedPtr)); +constexpr size_t elementSizeBytes = + (sizeof(SHAMapHash) + sizeof(intr_ptr::SharedPtr)); constexpr size_t blockSizeBytes = kilobytes(512); @@ -37,7 +38,8 @@ initArrayChunkSizeBytes(std::index_sequence) boundaries[I] * elementSizeBytes..., }; } -constexpr auto arrayChunkSizeBytes = initArrayChunkSizeBytes(std::make_index_sequence{}); +constexpr auto arrayChunkSizeBytes = + initArrayChunkSizeBytes(std::make_index_sequence{}); template constexpr std::array @@ -47,7 +49,8 @@ initArrayChunksPerBlock(std::index_sequence) blockSizeBytes / arrayChunkSizeBytes[I]..., }; } -constexpr auto chunksPerBlock = initArrayChunksPerBlock(std::make_index_sequence{}); +constexpr auto chunksPerBlock = + initArrayChunksPerBlock(std::make_index_sequence{}); [[nodiscard]] inline std::uint8_t numAllocatedChildren(std::uint8_t n) @@ -59,8 +62,10 @@ numAllocatedChildren(std::uint8_t n) [[nodiscard]] inline std::size_t boundariesIndex(std::uint8_t numChildren) { - XRPL_ASSERT(numChildren <= SHAMapInnerNode::branchFactor, "xrpl::boundariesIndex : valid input"); - return std::distance(boundaries.begin(), std::lower_bound(boundaries.begin(), boundaries.end(), numChildren)); + XRPL_ASSERT( + numChildren <= SHAMapInnerNode::branchFactor, "xrpl::boundariesIndex : valid input"); + return std::distance( + boundaries.begin(), std::lower_bound(boundaries.begin(), boundaries.end(), numChildren)); } template @@ -358,7 +363,8 @@ inline TaggedPointer::TaggedPointer( // keep new (&dstHashes[dstIndex]) SHAMapHash{srcHashes[srcIndex]}; - new (&dstChildren[dstIndex]) intr_ptr::SharedPtr{std::move(srcChildren[srcIndex])}; + new (&dstChildren[dstIndex]) + intr_ptr::SharedPtr{std::move(srcChildren[srcIndex])}; ++dstIndex; ++srcIndex; } @@ -413,7 +419,10 @@ inline TaggedPointer::TaggedPointer( } } -inline TaggedPointer::TaggedPointer(TaggedPointer&& other, std::uint16_t isBranch, std::uint8_t toAllocate) +inline TaggedPointer::TaggedPointer( + TaggedPointer&& other, + std::uint16_t isBranch, + std::uint8_t toAllocate) : TaggedPointer(std::move(other)) { auto const oldNumAllocated = capacity(); @@ -435,7 +444,8 @@ inline TaggedPointer::TaggedPointer(TaggedPointer&& other, std::uint16_t isBranc // new arrays are dense, old arrays are sparse iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) { new (&newHashes[branchNum]) SHAMapHash{oldHashes[indexNum]}; - new (&newChildren[branchNum]) intr_ptr::SharedPtr{std::move(oldChildren[indexNum])}; + new (&newChildren[branchNum]) + intr_ptr::SharedPtr{std::move(oldChildren[indexNum])}; }); // Run the constructors for the remaining elements for (int i = 0; i < SHAMapInnerNode::branchFactor; ++i) @@ -518,7 +528,8 @@ TaggedPointer::getHashesAndChildren() const auto const [tag, ptr] = decode(); auto const hashes = reinterpret_cast(ptr); std::uint8_t numAllocated = boundaries[tag]; - auto const children = reinterpret_cast*>(hashes + numAllocated); + auto const children = + reinterpret_cast*>(hashes + numAllocated); return {numAllocated, hashes, children}; }; diff --git a/src/xrpld/app/tx/detail/ApplyContext.h b/include/xrpl/tx/ApplyContext.h similarity index 86% rename from src/xrpld/app/tx/detail/ApplyContext.h rename to include/xrpl/tx/ApplyContext.h index 473feffcd3..909e84e80d 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -1,9 +1,7 @@ #pragma once -#include -#include - #include +#include #include #include #include @@ -17,7 +15,7 @@ class ApplyContext { public: explicit ApplyContext( - Application& app, + ServiceRegistry& registry, OpenView& base, std::optional const& parentBatchId, STTx const& tx, @@ -27,19 +25,19 @@ public: beast::Journal journal = beast::Journal{beast::Journal::getNullSink()}); explicit ApplyContext( - Application& app, + ServiceRegistry& registry, OpenView& base, STTx const& tx, TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, beast::Journal journal = beast::Journal{beast::Journal::getNullSink()}) - : ApplyContext(app, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal) + : ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal) { XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set"); } - Application& app; + ServiceRegistry& registry; STTx const& tx; TER const preclaimResult; XRPAmount const baseFee; @@ -104,11 +102,12 @@ public: /** Visit unapplied changes. */ void - visit(std::function const& before, - std::shared_ptr const& after)> const& func); + visit( + std::function const& before, + std::shared_ptr const& after)> const& func); void destroyXRP(XRPAmount const& fee) diff --git a/src/xrpld/app/tx/detail/SignerEntries.h b/include/xrpl/tx/SignerEntries.h similarity index 90% rename from src/xrpld/app/tx/detail/SignerEntries.h rename to include/xrpl/tx/SignerEntries.h index fe01d6b1ee..0b997b3e4d 100644 --- a/src/xrpld/app/tx/detail/SignerEntries.h +++ b/include/xrpl/tx/SignerEntries.h @@ -1,11 +1,10 @@ #pragma once -#include // NotTEC - #include // #include // beast::Journal #include // temMALFORMED #include // AccountID +#include // NotTEC #include #include @@ -34,7 +33,10 @@ public: std::uint16_t weight; std::optional tag; - SignerEntry(AccountID const& inAccount, std::uint16_t inWeight, std::optional inTag) + SignerEntry( + AccountID const& inAccount, + std::uint16_t inWeight, + std::optional inTag) : account(inAccount), weight(inWeight), tag(inTag) { } diff --git a/src/xrpld/app/tx/detail/Transactor.h b/include/xrpl/tx/Transactor.h similarity index 89% rename from src/xrpld/app/tx/detail/Transactor.h rename to include/xrpl/tx/Transactor.h index e06086a55d..8d816f60f8 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -1,12 +1,11 @@ #pragma once -#include -#include - #include #include #include #include +#include +#include namespace xrpl { @@ -14,7 +13,7 @@ namespace xrpl { struct PreflightContext { public: - Application& app; + ServiceRegistry& registry; STTx const& tx; Rules const rules; ApplyFlags flags; @@ -22,24 +21,29 @@ public: beast::Journal const j; PreflightContext( - Application& app_, + ServiceRegistry& registry_, STTx const& tx_, uint256 parentBatchId_, Rules const& rules_, ApplyFlags flags_, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) - : app(app_), tx(tx_), rules(rules_), flags(flags_), parentBatchId(parentBatchId_), j(j_) + : registry(registry_) + , tx(tx_) + , rules(rules_) + , flags(flags_) + , parentBatchId(parentBatchId_) + , j(j_) { XRPL_ASSERT((flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set"); } PreflightContext( - Application& app_, + ServiceRegistry& registry_, STTx const& tx_, Rules const& rules_, ApplyFlags flags_, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) - : app(app_), tx(tx_), rules(rules_), flags(flags_), j(j_) + : registry(registry_), tx(tx_), rules(rules_), flags(flags_), j(j_) { XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set"); } @@ -52,7 +56,7 @@ public: struct PreclaimContext { public: - Application& app; + ServiceRegistry& registry; ReadView const& view; TER preflightResult; ApplyFlags flags; @@ -61,14 +65,14 @@ public: beast::Journal const j; PreclaimContext( - Application& app_, + ServiceRegistry& registry_, ReadView const& view_, TER preflightResult_, STTx const& tx_, ApplyFlags flags_, std::optional parentBatchId_, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) - : app(app_) + : registry(registry_) , view(view_) , preflightResult(preflightResult_) , flags(flags_) @@ -82,13 +86,13 @@ public: } PreclaimContext( - Application& app_, + ServiceRegistry& registry_, ReadView const& view_, TER preflightResult_, STTx const& tx_, ApplyFlags flags_, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) - : PreclaimContext(app_, view_, preflightResult_, tx_, flags_, std::nullopt, j_) + : PreclaimContext(registry_, view_, preflightResult_, tx_, flags_, std::nullopt, j_) { XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set"); } @@ -110,8 +114,7 @@ protected: beast::Journal const j_; AccountID const account_; - XRPAmount mPriorBalance; // Balance before fees. - XRPAmount mSourceBalance; // Balance after fees. + XRPAmount preFeeBalance_{}; // Balance before fees. virtual ~Transactor() = default; Transactor(Transactor const&) = delete; @@ -206,9 +209,13 @@ public: checkPermission(ReadView const& view, STTx const& tx); ///////////////////////////////////////////////////// - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER - ticketDelete(ApplyView& view, AccountID const& account, uint256 const& ticketIndex, beast::Journal j); + ticketDelete( + ApplyView& view, + AccountID const& account, + uint256 const& ticketIndex, + beast::Journal j); protected: TER @@ -225,14 +232,14 @@ protected: /** Compute the minimum fee required to process a transaction with a given baseFee based on the current server load. - @param app The application hosting the server + @param registry The service registry. @param baseFee The base fee of a candidate transaction @see xrpl::calculateBaseFee @param fees Fee settings from the current ledger @param flags Transaction processing fees */ static XRPAmount - minimumFee(Application& app, XRPAmount baseFee, Fees const& fees, ApplyFlags flags); + minimumFee(ServiceRegistry& registry, XRPAmount baseFee, Fees const& fees, ApplyFlags flags); // Returns the fee in fee units, not scaled for load. static XRPAmount @@ -281,7 +288,9 @@ protected: /// Minimum will usually be zero. template static bool - validNumericMinimum(std::optional value, unit::ValueUnit min = unit::ValueUnit{}); + validNumericMinimum( + std::optional value, + unit::ValueUnit min = unit::ValueUnit{}); private: std::pair @@ -396,7 +405,10 @@ Transactor::validNumericRange(std::optional value, T max, T min) template bool -Transactor::validNumericRange(std::optional value, unit::ValueUnit max, unit::ValueUnit min) +Transactor::validNumericRange( + std::optional value, + unit::ValueUnit max, + unit::ValueUnit min) { return validNumericRange(value, max.value(), min.value()); } diff --git a/src/xrpld/app/tx/apply.h b/include/xrpl/tx/apply.h similarity index 93% rename from src/xrpld/app/tx/apply.h rename to include/xrpl/tx/apply.h index c3ff4f905e..49b30fea02 100644 --- a/src/xrpld/app/tx/apply.h +++ b/include/xrpl/tx/apply.h @@ -1,18 +1,16 @@ #pragma once -#include -#include - #include #include #include +#include #include namespace xrpl { -class Application; class HashRouter; +class ServiceRegistry; /** Describes the pre-processing validity of a transaction. @@ -41,7 +39,7 @@ enum class Validity { @see Validity */ std::pair -checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config const& config); +checkValidity(HashRouter& router, STTx const& tx, Rules const& rules); /** Sets the validity of a given transaction in the cache. @@ -97,7 +95,12 @@ forceValidity(HashRouter& router, uint256 const& txid, Validity validity); whether or not the transaction was applied. */ ApplyResult -apply(Application& app, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal journal); +apply( + ServiceRegistry& registry, + OpenView& view, + STTx const& tx, + ApplyFlags flags, + beast::Journal journal); /** Enum class for return value from `applyTransaction` @@ -121,7 +124,7 @@ enum class ApplyTransactionResult { */ ApplyTransactionResult applyTransaction( - Application& app, + ServiceRegistry& registry, OpenView& view, STTx const& tx, bool retryAssured, diff --git a/src/xrpld/app/tx/applySteps.h b/include/xrpl/tx/applySteps.h similarity index 94% rename from src/xrpld/app/tx/applySteps.h rename to include/xrpl/tx/applySteps.h index ef87d352f3..9ce6dfb1e2 100644 --- a/src/xrpld/app/tx/applySteps.h +++ b/include/xrpl/tx/applySteps.h @@ -5,7 +5,7 @@ namespace xrpl { -class Application; +class ServiceRegistry; class STTx; class TxQ; @@ -15,7 +15,8 @@ struct ApplyResult bool applied; std::optional metadata; - ApplyResult(TER t, bool a, std::optional m = std::nullopt) : ter(t), applied(a), metadata(std::move(m)) + ApplyResult(TER t, bool a, std::optional m = std::nullopt) + : ter(t), applied(a), metadata(std::move(m)) { } }; @@ -212,7 +213,7 @@ public: , flags(ctx_.flags) , j(ctx_.j) , ter(ter_) - , likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags)) + , likelyToClaimFee(isTesSuccess(ter) || isTecClaimHardFail(ter, flags)) { } @@ -240,11 +241,16 @@ public: */ /** @{ */ PreflightResult -preflight(Application& app, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j); +preflight( + ServiceRegistry& registry, + Rules const& rules, + STTx const& tx, + ApplyFlags flags, + beast::Journal j); PreflightResult preflight( - Application& app, + ServiceRegistry& registry, Rules const& rules, uint256 const& parentBatchId, STTx const& tx, @@ -281,7 +287,7 @@ preflight( this transaction. */ PreclaimResult -preclaim(PreflightResult const& preflightResult, Application& app, OpenView const& view); +preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view); /** Compute only the expected base fee for a transaction. @@ -323,7 +329,7 @@ calculateDefaultBaseFee(ReadView const& view, STTx const& tx); @param preclaimResult The result of a previous call to `preclaim` for the transaction. - @param app The current running `Application`. + @param registry The service registry. @param view The open ledger that the transaction will attempt to be applied to. @@ -333,6 +339,6 @@ calculateDefaultBaseFee(ReadView const& view, STTx const& tx); whether or not the transaction was applied. */ ApplyResult -doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view); +doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view); } // namespace xrpl diff --git a/include/xrpl/tx/invariants/AMMInvariant.h b/include/xrpl/tx/invariants/AMMInvariant.h new file mode 100644 index 0000000000..63ebb804ae --- /dev/null +++ b/include/xrpl/tx/invariants/AMMInvariant.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +class ValidAMM +{ + std::optional ammAccount_; + std::optional lptAMMBalanceAfter_; + std::optional lptAMMBalanceBefore_; + bool ammPoolChanged_; + +public: + enum class ZeroAllowed : bool { No = false, Yes = true }; + + ValidAMM() : ammPoolChanged_{false} + { + } + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + +private: + bool + finalizeBid(bool enforce, beast::Journal const&) const; + bool + finalizeVote(bool enforce, beast::Journal const&) const; + bool + finalizeCreate(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const; + bool + finalizeDelete(bool enforce, TER res, beast::Journal const&) const; + bool + finalizeDeposit(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const; + // Includes clawback + bool + finalizeWithdraw(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const; + bool + finalizeDEX(bool enforce, beast::Journal const&) const; + bool + generalInvariant(STTx const&, ReadView const&, ZeroAllowed zeroAllowed, beast::Journal const&) + const; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/FreezeInvariant.h b/include/xrpl/tx/invariants/FreezeInvariant.h new file mode 100644 index 0000000000..ac9d83166e --- /dev/null +++ b/include/xrpl/tx/invariants/FreezeInvariant.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +/** + * @brief Invariant: frozen trust line balance change is not allowed. + * + * We iterate all affected trust lines and ensure that they don't have + * unexpected change of balance if they're frozen. + */ +class TransfersNotFrozen +{ + struct BalanceChange + { + std::shared_ptr const line; + int const balanceChangeSign; + }; + + struct IssuerChanges + { + std::vector senders; + std::vector receivers; + }; + + using ByIssuer = std::map; + ByIssuer balanceChanges_; + + std::map const> possibleIssuers_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + +private: + bool + isValidEntry(std::shared_ptr const& before, std::shared_ptr const& after); + + STAmount + calculateBalanceChange( + std::shared_ptr const& before, + std::shared_ptr const& after, + bool isDelete); + + void + recordBalance(Issue const& issue, BalanceChange change); + + void + recordBalanceChanges(std::shared_ptr const& after, STAmount const& balanceChange); + + std::shared_ptr + findIssuer(AccountID const& issuerID, ReadView const& view); + + bool + validateIssuerChanges( + std::shared_ptr const& issuer, + IssuerChanges const& changes, + STTx const& tx, + beast::Journal const& j, + bool enforce); + + bool + validateFrozenState( + BalanceChange const& change, + bool high, + STTx const& tx, + beast::Journal const& j, + bool enforce, + bool globalFreeze); +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h new file mode 100644 index 0000000000..e0ad65f14c --- /dev/null +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -0,0 +1,416 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +#if GENERATING_DOCS +/** + * @brief Prototype for invariant check implementations. + * + * __THIS CLASS DOES NOT EXIST__ - or rather it exists in documentation only to + * communicate the interface required of any invariant checker. Any invariant + * check implementation should implement the public methods documented here. + * + * ## Rules for implementing `finalize` + * + * ### Invariants must run regardless of transaction result + * + * An invariant's `finalize` method MUST perform meaningful checks even when + * the transaction has failed (i.e., `!isTesSuccess(tec)`). The following + * pattern is almost certainly wrong and must never be used: + * + * @code + * // WRONG: skipping all checks on failure defeats the purpose of invariants + * if (!isTesSuccess(tec)) + * return true; + * @endcode + * + * The entire purpose of invariants is to detect and prevent the impossible. + * A bug or exploit could cause a failed transaction to mutate ledger state in + * unexpected ways. Invariants are the last line of defense against such + * scenarios. + * + * In general: an invariant that expects a domain-specific state change to + * occur (e.g., a new object being created) should only expect that change + * when the transaction succeeded. A failed VaultCreate must not have created + * a Vault. A failed LoanSet must not have created a Loan. + * + * Also be aware that failed transactions, regardless of type, carry no + * Privileges. Any privilege-gated checks must therefore also be applied to + * failed transactions. + */ +class InvariantChecker_PROTOTYPE +{ +public: + explicit InvariantChecker_PROTOTYPE() = default; + + /** + * @brief called for each ledger entry in the current transaction. + * + * @param isDelete true if the SLE is being deleted + * @param before ledger entry before modification by the transaction + * @param after ledger entry after modification by the transaction + */ + void + visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after); + + /** + * @brief called after all ledger entries have been visited to determine + * the final status of the check. + * + * This method MUST perform meaningful checks even when `tec` indicates a + * failed transaction. See the class-level documentation for the rules + * governing how failed transactions must be handled. + * + * @param tx the transaction being applied + * @param tec the current TER result of the transaction + * @param fee the fee actually charged for this transaction + * @param view a ReadView of the ledger being modified + * @param j journal for logging + * + * @return true if check passes, false if it fails + */ + bool + finalize( + STTx const& tx, + TER const tec, + XRPAmount const fee, + ReadView const& view, + beast::Journal const& j); +}; +#endif + +/** + * @brief Invariant: We should never charge a transaction a negative fee or a + * fee that is larger than what the transaction itself specifies. + * + * We can, in some circumstances, charge less. + */ +class TransactionFeeCheck +{ +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +/** + * @brief Invariant: A transaction must not create XRP and should only destroy + * the XRP fee. + * + * We iterate through all account roots, payment channels and escrow entries + * that were modified and calculate the net change in XRP caused by the + * transactions. + */ +class XRPNotCreated +{ + std::int64_t drops_ = 0; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: we cannot remove an account ledger entry + * + * We iterate all account roots that were modified, and ensure that any that + * were present before the transaction was applied continue to be present + * afterwards unless they were explicitly deleted by a successful + * AccountDelete transaction. + */ +class AccountRootsNotDeleted +{ + std::uint32_t accountsDeleted_ = 0; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: a deleted account must not have any objects left + * + * We iterate all deleted account roots, and ensure that there are no + * objects left that are directly accessible with that account's ID. + * + * There should only be one deleted account, but that's checked by + * AccountRootsNotDeleted. This invariant will handle multiple deleted account + * roots without a problem. + */ +class AccountRootsDeletedClean +{ + // Pair is . Before is used for most of the checks, so that + // if, for example, an object ID field is cleared, but the object is not + // deleted, it can still be found. After is used specifically for any checks + // that are expected as part of the deletion, such as zeroing out the + // balance. + std::vector, std::shared_ptr>> accountsDeleted_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +/** + * @brief Invariant: An account XRP balance must be in XRP and take a value + * between 0 and INITIAL_XRP drops, inclusive. + * + * We iterate all account roots modified by the transaction and ensure that + * their XRP balances are reasonable. + */ +class XRPBalanceChecks +{ + bool bad_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: corresponding modified ledger entries should match in type + * and added entries should be a valid type. + */ +class LedgerEntryTypesMatch +{ + bool typeMismatch_ = false; + bool invalidTypeAdded_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: Trust lines using XRP are not allowed. + * + * We iterate all the trust lines created by this transaction and ensure + * that they are against a valid issuer. + */ +class NoXRPTrustLines +{ + bool xrpTrustLine_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: Trust lines with deep freeze flag are not allowed if normal + * freeze flag is not set. + * + * We iterate all the trust lines created by this transaction and ensure + * that they don't have deep freeze flag set without normal freeze flag set. + */ +class NoDeepFreezeTrustLinesWithoutFreeze +{ + bool deepFreezeWithoutFreeze_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: offers should be for non-negative amounts and must not + * be XRP to XRP. + * + * Examine all offers modified by the transaction and ensure that there are + * no offers which contain negative amounts or which exchange XRP for XRP. + */ +class NoBadOffers +{ + bool bad_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: an escrow entry must take a value between 0 and + * INITIAL_XRP drops exclusive. + */ +class NoZeroEscrow +{ + bool bad_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: a new account root must be the consequence of a payment, + * must have the right starting sequence, and the payment + * may not create more than one new account root. + */ +class ValidNewAccountRoot +{ + std::uint32_t accountsCreated_ = 0; + std::uint32_t accountSeq_ = 0; + bool pseudoAccount_ = false; + std::uint32_t flags_ = 0; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: Token holder's trustline balance cannot be negative after + * Clawback. + * + * We iterate all the trust lines affected by this transaction and ensure + * that no more than one trustline is modified, and also holder's balance is + * non-negative. + */ +class ValidClawback +{ + std::uint32_t trustlinesChanged = 0; + std::uint32_t mptokensChanged = 0; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariants: Pseudo-accounts have valid and consistent properties + * + * Pseudo-accounts have certain properties, and some of those properties are + * unique to pseudo-accounts. Check that all pseudo-accounts are following the + * rules, and that only pseudo-accounts look like pseudo-accounts. + * + */ +class ValidPseudoAccounts +{ + std::vector errors_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +/** + * @brief Invariants: Some fields are unmodifiable + * + * Check that any fields specified as unmodifiable are not modified when the + * object is modified. Creation and deletion are ignored. + * + */ +class NoModifiedUnmodifiableFields +{ + // Pair is . + std::set> changedEntries_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +// additional invariant checks can be declared above and then added to this +// tuple +using InvariantChecks = std::tuple< + TransactionFeeCheck, + AccountRootsNotDeleted, + AccountRootsDeletedClean, + LedgerEntryTypesMatch, + XRPBalanceChecks, + XRPNotCreated, + NoXRPTrustLines, + NoDeepFreezeTrustLinesWithoutFreeze, + TransfersNotFrozen, + NoBadOffers, + NoZeroEscrow, + ValidNewAccountRoot, + ValidNFTokenPage, + NFTokenCountTracking, + ValidClawback, + ValidMPTIssuance, + ValidPermissionedDomain, + ValidPermissionedDEX, + ValidAMM, + NoModifiedUnmodifiableFields, + ValidPseudoAccounts, + ValidLoanBroker, + ValidLoan, + ValidVault>; + +/** + * @brief get a tuple of all invariant checks + * + * @return std::tuple of instances that implement the required invariant check + * methods + * + * @see xrpl::InvariantChecker_PROTOTYPE + */ +inline InvariantChecks +getInvariantChecks() +{ + return InvariantChecks{}; +} + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/InvariantCheckPrivilege.h b/include/xrpl/tx/invariants/InvariantCheckPrivilege.h new file mode 100644 index 0000000000..161b3572db --- /dev/null +++ b/include/xrpl/tx/invariants/InvariantCheckPrivilege.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include + +namespace xrpl { + +/* +assert(enforce) + +There are several asserts (or XRPL_ASSERTs) in invariant check files that check +a variable named `enforce` when an invariant fails. At first glance, those +asserts may look incorrect, but they are not. + +Those asserts take advantage of two facts: +1. `asserts` are not (normally) executed in release builds. +2. Invariants should *never* fail, except in tests that specifically modify + the open ledger to break them. + +This makes `assert(enforce)` sort of a second-layer of invariant enforcement +aimed at _developers_. It's designed to fire if a developer writes code that +violates an invariant, and runs it in unit tests or a develop build that _does +not have the relevant amendments enabled_. It's intentionally a pain in the neck +so that bad code gets caught and fixed as early as possible. +*/ + +enum Privilege { + noPriv = 0x0000, // The transaction can not do any of the enumerated operations + createAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object. + createPseudoAcct = 0x0002, // The transaction can create a pseudo account, + // which implies createAcct + mustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object + mayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT + // object, but does not have to + overrideFreeze = 0x0010, // The transaction can override some freeze rules + changeNFTCounts = 0x0020, // The transaction can mint or burn an NFT + createMPTIssuance = 0x0040, // The transaction can create a new MPT issuance + destroyMPTIssuance = 0x0080, // The transaction can destroy an MPT issuance + mustAuthorizeMPT = 0x0100, // The transaction MUST create or delete an MPT + // object (except by issuer) + mayAuthorizeMPT = 0x0200, // The transaction MAY create or delete an MPT + // object (except by issuer) + mayDeleteMPT = 0x0400, // The transaction MAY delete an MPT object. May not create. + mustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault + mayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault +}; + +constexpr Privilege +operator|(Privilege lhs, Privilege rhs) +{ + return safe_cast( + safe_cast>(lhs) | + safe_cast>(rhs)); +} + +bool +hasPrivilege(STTx const& tx, Privilege priv); + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/LoanInvariant.h b/include/xrpl/tx/invariants/LoanInvariant.h new file mode 100644 index 0000000000..be771cd582 --- /dev/null +++ b/include/xrpl/tx/invariants/LoanInvariant.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +/** + * @brief Invariants: Loan brokers are internally consistent + * + * 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one + * node (the root), which will only hold entries for `RippleState` or + * `MPToken` objects. + * + */ +class ValidLoanBroker +{ + // Not all of these elements will necessarily be populated. Remaining items + // will be looked up as needed. + struct BrokerInfo + { + SLE::const_pointer brokerBefore = nullptr; + // After is used for most of the checks, except + // those that check changed values. + SLE::const_pointer brokerAfter = nullptr; + }; + // Collect all the LoanBrokers found directly or indirectly through + // pseudo-accounts. Key is the brokerID / index. It will be used to find the + // LoanBroker object if brokerBefore and brokerAfter are nullptr + std::map brokers_; + // Collect all the modified trust lines. Their high and low accounts will be + // loaded to look for LoanBroker pseudo-accounts. + std::vector lines_; + // Collect all the modified MPTokens. Their accounts will be loaded to look + // for LoanBroker pseudo-accounts. + std::vector mpts_; + + bool + goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +/** + * @brief Invariants: Loans are internally consistent + * + * 1. If `Loan.PaymentRemaining = 0` then `Loan.PrincipalOutstanding = 0` + * + */ +class ValidLoan +{ + // Pair is . After is used for most of the checks, except + // those that check changed values. + std::vector> loans_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h new file mode 100644 index 0000000000..68f866d362 --- /dev/null +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace xrpl { + +class ValidMPTIssuance +{ + std::uint32_t mptIssuancesCreated_ = 0; + std::uint32_t mptIssuancesDeleted_ = 0; + + std::uint32_t mptokensCreated_ = 0; + std::uint32_t mptokensDeleted_ = 0; + // non-MPT transactions may attempt to create + // MPToken by an issuer + bool mptCreatedByIssuer_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/NFTInvariant.h b/include/xrpl/tx/invariants/NFTInvariant.h new file mode 100644 index 0000000000..5bb5f90437 --- /dev/null +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +/** + * @brief Invariant: Validates several invariants for NFToken pages. + * + * The following checks are made: + * - The page is correctly associated with the owner. + * - The page is correctly ordered between the next and previous links. + * - The page contains at least one and no more than 32 NFTokens. + * - The NFTokens on this page do not belong on a lower or higher page. + * - The NFTokens are correctly sorted on the page. + * - Each URI, if present, is not empty. + */ +class ValidNFTokenPage +{ + bool badEntry_ = false; + bool badLink_ = false; + bool badSort_ = false; + bool badURI_ = false; + bool invalidSize_ = false; + bool deletedFinalPage_ = false; + bool deletedLink_ = false; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: Validates counts of NFTokens after all transaction types. + * + * The following checks are made: + * - The number of minted or burned NFTokens can only be changed by + * NFTokenMint or NFTokenBurn transactions. + * - A successful NFTokenMint must increase the number of NFTokens. + * - A failed NFTokenMint must not change the number of minted NFTokens. + * - An NFTokenMint transaction cannot change the number of burned NFTokens. + * - A successful NFTokenBurn must increase the number of burned NFTokens. + * - A failed NFTokenBurn must not change the number of burned NFTokens. + * - An NFTokenBurn transaction cannot change the number of minted NFTokens. + */ +class NFTokenCountTracking +{ + std::uint32_t beforeMintedTotal = 0; + std::uint32_t beforeBurnedTotal = 0; + std::uint32_t afterMintedTotal = 0; + std::uint32_t afterBurnedTotal = 0; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h new file mode 100644 index 0000000000..b4e06cd212 --- /dev/null +++ b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace xrpl { + +class ValidPermissionedDEX +{ + bool regularOffers_ = false; + bool badHybrids_ = false; + hash_set domains_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h new file mode 100644 index 0000000000..f6c902ecb2 --- /dev/null +++ b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace xrpl { + +/** + * @brief Invariants: Permissioned Domains must have some rules and + * AcceptedCredentials must have length between 1 and 10 inclusive. + * + * Since only permissions constitute rules, an empty credentials list + * means that there are no rules and the invariant is violated. + * + * Credentials must be sorted and no duplicates allowed + * + */ +class ValidPermissionedDomain +{ + struct SleStatus + { + std::size_t credentialsSize_{0}; + bool isSorted_ = false; + bool isUnique_ = false; + bool isDelete_ = false; + }; + std::vector sleStatus_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h new file mode 100644 index 0000000000..ded9e4618b --- /dev/null +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +/* + * @brief Invariants: Vault object and MPTokenIssuance for vault shares + * + * - vault deleted and vault created is empty + * - vault created must be linked to pseudo-account for shares and assets + * - vault must have MPTokenIssuance for shares + * - vault without shares outstanding must have no shares + * - loss unrealized does not exceed the difference between assets total and + * assets available + * - assets available do not exceed assets total + * - vault deposit increases assets and share issuance, and adds to: + * total assets, assets available, shares outstanding + * - vault withdrawal and clawback reduce assets and share issuance, and + * subtracts from: total assets, assets available, shares outstanding + * - vault set must not alter the vault assets or shares balance + * - no vault transaction can change loss unrealized (it's updated by loan + * transactions) + * + */ +class ValidVault +{ + Number static constexpr zero{}; + + struct Vault final + { + uint256 key = beast::zero; + Asset asset = {}; + AccountID pseudoId = {}; + AccountID owner = {}; + uint192 shareMPTID = beast::zero; + Number assetsTotal = 0; + Number assetsAvailable = 0; + Number assetsMaximum = 0; + Number lossUnrealized = 0; + + Vault static make(SLE const&); + }; + + struct Shares final + { + MPTIssue share = {}; + std::uint64_t sharesTotal = 0; + std::uint64_t sharesMaximum = 0; + + Shares static make(SLE const&); + }; + + std::vector afterVault_ = {}; + std::vector afterMPTs_ = {}; + std::vector beforeVault_ = {}; + std::vector beforeMPTs_ = {}; + std::unordered_map deltas_ = {}; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/BookTip.h b/include/xrpl/tx/paths/BookTip.h similarity index 97% rename from src/xrpld/app/tx/detail/BookTip.h rename to include/xrpl/tx/paths/BookTip.h index 69d3ec70d8..98036a86eb 100644 --- a/src/xrpld/app/tx/detail/BookTip.h +++ b/include/xrpl/tx/paths/BookTip.h @@ -22,7 +22,7 @@ private: uint256 m_dir; uint256 m_index; std::shared_ptr m_entry; - Quality m_quality; + Quality m_quality{}; public: /** Create the iterator. */ diff --git a/src/xrpld/app/paths/Flow.h b/include/xrpl/tx/paths/Flow.h similarity index 95% rename from src/xrpld/app/paths/Flow.h rename to include/xrpl/tx/paths/Flow.h index 1a46ce221a..32e8c3611b 100644 --- a/src/xrpld/app/paths/Flow.h +++ b/include/xrpl/tx/paths/Flow.h @@ -1,9 +1,8 @@ #pragma once -#include -#include - #include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/Offer.h b/include/xrpl/tx/paths/Offer.h similarity index 95% rename from src/xrpld/app/tx/detail/Offer.h rename to include/xrpl/tx/paths/Offer.h index 7c783233f1..4b161c5f2d 100644 --- a/src/xrpld/app/tx/detail/Offer.h +++ b/include/xrpl/tx/paths/Offer.h @@ -154,8 +154,9 @@ public: if (consumed.in > m_amounts.in || consumed.out > m_amounts.out) { // LCOV_EXCL_START - JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " << to_string(consumed.in) << " " - << to_string(consumed.out) << " amounts " << to_string(m_amounts.in) << " " + JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " + << to_string(consumed.in) << " " << to_string(consumed.out) + << " amounts " << to_string(m_amounts.in) << " " << to_string(m_amounts.out); return false; @@ -204,7 +205,8 @@ TOffer::setFieldAmounts() template TAmounts -TOffer::limitOut(TAmounts const& offerAmount, TOut const& limit, bool roundUp) const +TOffer::limitOut(TAmounts const& offerAmount, TOut const& limit, bool roundUp) + const { // It turns out that the ceil_out implementation has some slop in // it, which ceil_out_strict removes. @@ -213,9 +215,11 @@ TOffer::limitOut(TAmounts const& offerAmount, TOut const& template TAmounts -TOffer::limitIn(TAmounts const& offerAmount, TIn const& limit, bool roundUp) const +TOffer::limitIn(TAmounts const& offerAmount, TIn const& limit, bool roundUp) + const { - if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixReducedOffersV2)) + if (auto const& rules = getCurrentTransactionRules(); + rules && rules->enabled(fixReducedOffersV2)) // It turns out that the ceil_in implementation has some slop in // it. ceil_in_strict removes that slop. But removing that slop // affects transaction outcomes, so the change must be made using diff --git a/src/xrpld/app/tx/detail/OfferStream.h b/include/xrpl/tx/paths/OfferStream.h similarity index 98% rename from src/xrpld/app/tx/detail/OfferStream.h rename to include/xrpl/tx/paths/OfferStream.h index 2f6bda3fab..df96a1b6da 100644 --- a/src/xrpld/app/tx/detail/OfferStream.h +++ b/include/xrpl/tx/paths/OfferStream.h @@ -1,12 +1,11 @@ #pragma once -#include -#include - #include #include #include #include +#include +#include #include diff --git a/src/xrpld/app/paths/RippleCalc.h b/include/xrpl/tx/paths/RippleCalc.h similarity index 100% rename from src/xrpld/app/paths/RippleCalc.h rename to include/xrpl/tx/paths/RippleCalc.h diff --git a/src/xrpld/app/paths/detail/AmountSpec.h b/include/xrpl/tx/paths/detail/AmountSpec.h similarity index 100% rename from src/xrpld/app/paths/detail/AmountSpec.h rename to include/xrpl/tx/paths/detail/AmountSpec.h diff --git a/src/xrpld/app/paths/detail/FlatSets.h b/include/xrpl/tx/paths/detail/FlatSets.h similarity index 100% rename from src/xrpld/app/paths/detail/FlatSets.h rename to include/xrpl/tx/paths/detail/FlatSets.h diff --git a/src/xrpld/app/paths/detail/FlowDebugInfo.h b/include/xrpl/tx/paths/detail/FlowDebugInfo.h similarity index 81% rename from src/xrpld/app/paths/detail/FlowDebugInfo.h rename to include/xrpl/tx/paths/detail/FlowDebugInfo.h index d7b97a49d4..d2d9dc8466 100644 --- a/src/xrpld/app/paths/detail/FlowDebugInfo.h +++ b/include/xrpl/tx/paths/detail/FlowDebugInfo.h @@ -1,10 +1,9 @@ #pragma once -#include - #include #include #include +#include #include @@ -206,44 +205,52 @@ struct FlowDebugInfo } ostr << ']'; }; - auto writeXrpAmtList = [&write_list](std::vector const& amts, char delim = ';') { - auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.xrp); }; + auto writeXrpAmtList = [&write_list]( + std::vector const& amts, char delim = ';') { + auto get_val = [](EitherAmount const& a) -> std::string { + return xrpl::to_string(a.xrp); + }; write_list(amts, get_val, delim); }; - auto writeIouAmtList = [&write_list](std::vector const& amts, char delim = ';') { - auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.iou); }; + auto writeIouAmtList = [&write_list]( + std::vector const& amts, char delim = ';') { + auto get_val = [](EitherAmount const& a) -> std::string { + return xrpl::to_string(a.iou); + }; write_list(amts, get_val, delim); }; auto writeIntList = [&write_list](std::vector const& vals, char delim = ';') { auto get_val = [](size_t const& v) -> size_t const& { return v; }; write_list(vals, get_val); }; - auto writeNestedIouAmtList = [&ostr, &writeIouAmtList](std::vector> const& amts) { - ostr << '['; - if (!amts.empty()) - { - writeIouAmtList(amts[0], '|'); - for (size_t i = 1, e = amts.size(); i < e; ++i) + auto writeNestedIouAmtList = + [&ostr, &writeIouAmtList](std::vector> const& amts) { + ostr << '['; + if (!amts.empty()) { - ostr << ';'; - writeIouAmtList(amts[i], '|'); + writeIouAmtList(amts[0], '|'); + for (size_t i = 1, e = amts.size(); i < e; ++i) + { + ostr << ';'; + writeIouAmtList(amts[i], '|'); + } } - } - ostr << ']'; - }; - auto writeNestedXrpAmtList = [&ostr, &writeXrpAmtList](std::vector> const& amts) { - ostr << '['; - if (!amts.empty()) - { - writeXrpAmtList(amts[0], '|'); - for (size_t i = 1, e = amts.size(); i < e; ++i) + ostr << ']'; + }; + auto writeNestedXrpAmtList = + [&ostr, &writeXrpAmtList](std::vector> const& amts) { + ostr << '['; + if (!amts.empty()) { - ostr << ';'; - writeXrpAmtList(amts[i], '|'); + writeXrpAmtList(amts[0], '|'); + for (size_t i = 1, e = amts.size(); i < e; ++i) + { + ostr << ';'; + writeXrpAmtList(amts[i], '|'); + } } - } - ostr << ']'; - }; + ostr << ']'; + }; ostr << ", in_pass: "; if (passInfo.nativeIn) @@ -277,7 +284,9 @@ struct FlowDebugInfo }; inline void -writeDiffElement(std::ostringstream& ostr, std::pair, STAmount> const& elem) +writeDiffElement( + std::ostringstream& ostr, + std::pair, STAmount> const& elem) { using namespace std; auto const k = elem.first; @@ -303,7 +312,8 @@ writeDiffs(std::ostringstream& ostr, Iter begin, Iter end) ostr << ']'; }; -using BalanceDiffs = std::pair, STAmount>, XRPAmount>; +using BalanceDiffs = + std::pair, STAmount>, XRPAmount>; inline BalanceDiffs balanceDiffs(PaymentSandbox const& sb, ReadView const& rv) diff --git a/src/xrpld/app/paths/detail/Steps.h b/include/xrpl/tx/paths/detail/Steps.h similarity index 97% rename from src/xrpld/app/paths/detail/Steps.h rename to include/xrpl/tx/paths/detail/Steps.h index 580b8c487e..53c9294155 100644 --- a/src/xrpld/app/paths/detail/Steps.h +++ b/include/xrpl/tx/paths/detail/Steps.h @@ -1,13 +1,12 @@ #pragma once -#include - #include #include #include #include #include #include +#include #include @@ -549,8 +548,9 @@ struct StrandContext bool ownerPaysTransferFee_, OfferCrossing offerCrossing_, bool isDefaultPath_, - std::array, 2>& seenDirectIssues_, ///< For detecting currency loops - boost::container::flat_set& seenBookOuts_, ///< For detecting book loops + std::array, 2>& + seenDirectIssues_, ///< For detecting currency loops + boost::container::flat_set& seenBookOuts_, ///< For detecting book loops AMMContext& ammContext_, std::optional const& domainID, beast::Journal j_); ///< Journal for logging @@ -560,7 +560,11 @@ struct StrandContext namespace test { // Needed for testing bool -directStepEqual(Step const& step, AccountID const& src, AccountID const& dst, Currency const& currency); +directStepEqual( + Step const& step, + AccountID const& src, + AccountID const& dst, + Currency const& currency); bool xrpEndpointStepEqual(Step const& step, AccountID const& acc); @@ -570,7 +574,11 @@ bookStepEqual(Step const& step, xrpl::Book const& book); } // namespace test std::pair> -make_DirectStepI(StrandContext const& ctx, AccountID const& src, AccountID const& dst, Currency const& c); +make_DirectStepI( + StrandContext const& ctx, + AccountID const& src, + AccountID const& dst, + Currency const& c); std::pair> make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out); diff --git a/src/xrpld/app/paths/detail/StrandFlow.h b/include/xrpl/tx/paths/detail/StrandFlow.h similarity index 91% rename from src/xrpld/app/paths/detail/StrandFlow.h rename to include/xrpl/tx/paths/detail/StrandFlow.h index aa3d00a822..2a94b9b968 100644 --- a/src/xrpld/app/paths/detail/StrandFlow.h +++ b/include/xrpl/tx/paths/detail/StrandFlow.h @@ -1,18 +1,19 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - #include -#include +#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -143,8 +144,9 @@ flow( // throwing out the sandbox can only increase liquidity // yet the limiting is still limiting // LCOV_EXCL_START - JLOG(j.fatal()) << "Re-executed limiting step failed. r.first: " - << to_string(get(r.first)) << " maxIn: " << to_string(*maxIn); + JLOG(j.fatal()) + << "Re-executed limiting step failed. r.first: " + << to_string(get(r.first)) << " maxIn: " << to_string(*maxIn); UNREACHABLE( "xrpl::flow : first step re-executing the " "limiting step failed"); @@ -179,8 +181,9 @@ flow( // yet the limiting is still limiting // LCOV_EXCL_START #ifndef NDEBUG - JLOG(j.fatal()) << "Re-executed limiting step failed. r.second: " << r.second - << " stepOut: " << stepOut; + JLOG(j.fatal()) + << "Re-executed limiting step failed. r.second: " << r.second + << " stepOut: " << stepOut; #else JLOG(j.fatal()) << "Re-executed limiting step failed"; #endif @@ -216,7 +219,8 @@ flow( // new limit // LCOV_EXCL_START #ifndef NDEBUG - JLOG(j.fatal()) << "Re-executed forward pass failed. r.first: " << r.first << " stepIn: " << stepIn; + JLOG(j.fatal()) << "Re-executed forward pass failed. r.first: " << r.first + << " stepIn: " << stepIn; #else JLOG(j.fatal()) << "Re-executed forward pass failed"; #endif @@ -253,11 +257,18 @@ flow( } #endif - bool const inactive = std::any_of( - strand.begin(), strand.end(), [](std::unique_ptr const& step) { return step->inactive(); }); + bool const inactive = + std::any_of(strand.begin(), strand.end(), [](std::unique_ptr const& step) { + return step->inactive(); + }); return Result( - strand, get(strandIn), get(strandOut), std::move(*sb), std::move(ofrsToRm), inactive); + strand, + get(strandIn), + get(strandOut), + std::move(*sb), + std::move(ofrsToRm), + inactive); } catch (FlowException const&) { @@ -282,15 +293,24 @@ struct FlowResult TOutAmt const& out_, PaymentSandbox&& sandbox_, boost::container::flat_set ofrsToRm) - : in(in_), out(out_), sandbox(std::move(sandbox_)), removableOffers(std::move(ofrsToRm)), ter(tesSUCCESS) + : in(in_) + , out(out_) + , sandbox(std::move(sandbox_)) + , removableOffers(std::move(ofrsToRm)) + , ter(tesSUCCESS) { } - FlowResult(TER ter_, boost::container::flat_set ofrsToRm) : removableOffers(std::move(ofrsToRm)), ter(ter_) + FlowResult(TER ter_, boost::container::flat_set ofrsToRm) + : removableOffers(std::move(ofrsToRm)), ter(ter_) { } - FlowResult(TER ter_, TInAmt const& in_, TOutAmt const& out_, boost::container::flat_set ofrsToRm) + FlowResult( + TER ter_, + TInAmt const& in_, + TOutAmt const& out_, + boost::container::flat_set ofrsToRm) : in(in_), out(out_), removableOffers(std::move(ofrsToRm)), ter(ter_) { } @@ -326,7 +346,11 @@ qualityUpperBound(ReadView const& v, Strand const& strand) */ template inline TOutAmt -limitOut(ReadView const& v, Strand const& strand, TOutAmt const& remainingOut, Quality const& limitQuality) +limitOut( + ReadView const& v, + Strand const& strand, + TOutAmt const& remainingOut, + Quality const& limitQuality) { std::optional stepQualityFunc; std::optional qf; @@ -428,10 +452,13 @@ public: } // must stable sort for deterministic order across different c++ // standard library implementations - std::stable_sort(strandQualities.begin(), strandQualities.end(), [](auto const& lhs, auto const& rhs) { - // higher qualities first - return std::get(lhs) > std::get(rhs); - }); + std::stable_sort( + strandQualities.begin(), + strandQualities.end(), + [](auto const& lhs, auto const& rhs) { + // higher qualities first + return std::get(lhs) > std::get(rhs); + }); next_.clear(); next_.reserve(strandQualities.size()); for (auto const& sq : strandQualities) @@ -650,8 +677,8 @@ flow( Quality const q(f.out, f.in); - JLOG(j.trace()) << "New flow iter (iter, in, out): " << curTry - 1 << " " << to_string(f.in) << " " - << to_string(f.out); + JLOG(j.trace()) << "New flow iter (iter, in, out): " << curTry - 1 << " " + << to_string(f.in) << " " << to_string(f.out); // limitOut() finds output to generate exact requested // limitQuality. But the actual limit quality might be slightly @@ -688,9 +715,11 @@ flow( remainingIn = *sendMax - sum(savedIns); if (flowDebugInfo) - flowDebugInfo->pushPass(EitherAmount(best->in), EitherAmount(best->out), activeStrands.size()); + flowDebugInfo->pushPass( + EitherAmount(best->in), EitherAmount(best->out), activeStrands.size()); - JLOG(j.trace()) << "Best path: in: " << to_string(best->in) << " out: " << to_string(best->out) + JLOG(j.trace()) << "Best path: in: " << to_string(best->in) + << " out: " << to_string(best->out) << " remainingOut: " << to_string(remainingOut); best->sb.apply(sb); @@ -720,7 +749,8 @@ flow( auto const actualOut = sum(savedOuts); auto const actualIn = sum(savedIns); - JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn) << " out: " << to_string(actualOut); + JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn) + << " out: " << to_string(actualOut); /* flowCross doesn't handle offer crossing with tfFillOrKill flag correctly. * 1. If tfFillOrKill is set then the owner must receive the full @@ -765,7 +795,8 @@ flow( return {tecPATH_DRY, std::move(ofrsToRmOnFail)}; } } - if (offerCrossing && (!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::sell))) + if (offerCrossing && + (!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::sell))) { // If we're offer crossing and partialPayment is *not* true, then // we're handling a FillOrKill offer. In this case remainingIn must diff --git a/src/xrpld/app/tx/detail/DeleteAccount.h b/include/xrpl/tx/transactors/account/AccountDelete.h similarity index 71% rename from src/xrpld/app/tx/detail/DeleteAccount.h rename to include/xrpl/tx/transactors/account/AccountDelete.h index 742d1f4257..81a11152ed 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.h +++ b/include/xrpl/tx/transactors/account/AccountDelete.h @@ -1,15 +1,15 @@ #pragma once -#include +#include namespace xrpl { -class DeleteAccount : public Transactor +class AccountDelete : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker}; - explicit DeleteAccount(ApplyContext& ctx) : Transactor(ctx) + explicit AccountDelete(ApplyContext& ctx) : Transactor(ctx) { } @@ -29,6 +29,4 @@ public: doApply() override; }; -using AccountDelete = DeleteAccount; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/SetAccount.h b/include/xrpl/tx/transactors/account/AccountSet.h similarity index 76% rename from src/xrpld/app/tx/detail/SetAccount.h rename to include/xrpl/tx/transactors/account/AccountSet.h index db59826347..0940ab0739 100644 --- a/src/xrpld/app/tx/detail/SetAccount.h +++ b/include/xrpl/tx/transactors/account/AccountSet.h @@ -1,17 +1,16 @@ #pragma once -#include - #include +#include namespace xrpl { -class SetAccount : public Transactor +class AccountSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - explicit SetAccount(ApplyContext& ctx) : Transactor(ctx) + explicit AccountSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -34,6 +33,4 @@ public: doApply() override; }; -using AccountSet = SetAccount; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/SetRegularKey.h b/include/xrpl/tx/transactors/account/SetRegularKey.h similarity index 90% rename from src/xrpld/app/tx/detail/SetRegularKey.h rename to include/xrpl/tx/transactors/account/SetRegularKey.h index bc712b319a..bb1dd48a68 100644 --- a/src/xrpld/app/tx/detail/SetRegularKey.h +++ b/include/xrpl/tx/transactors/account/SetRegularKey.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/SetSignerList.h b/include/xrpl/tx/transactors/account/SignerListSet.h similarity index 75% rename from src/xrpld/app/tx/detail/SetSignerList.h rename to include/xrpl/tx/transactors/account/SignerListSet.h index efd8e508f9..2529f0d36c 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -1,10 +1,9 @@ #pragma once -#include -#include - #include #include +#include +#include #include #include @@ -12,10 +11,10 @@ namespace xrpl { /** -See the README.md for an overview of the SetSignerList transaction that +See the README.md for an overview of the SignerListSet transaction that this class implements. */ -class SetSignerList : public Transactor +class SignerListSet : public Transactor { private: // Values determined during preCompute for use later. @@ -27,7 +26,7 @@ private: public: static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker}; - explicit SetSignerList(ApplyContext& ctx) : Transactor(ctx) + explicit SignerListSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -42,9 +41,13 @@ public: void preCompute() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER - removeFromLedger(Application& app, ApplyView& view, AccountID const& account, beast::Journal j); + removeFromLedger( + ServiceRegistry& registry, + ApplyView& view, + AccountID const& account, + beast::Journal j); private: static std::tuple, Operation> @@ -67,6 +70,4 @@ private: writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const; }; -using SignerListSet = SetSignerList; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/XChainBridge.h b/include/xrpl/tx/transactors/bridge/XChainBridge.h similarity index 99% rename from src/xrpld/app/tx/detail/XChainBridge.h rename to include/xrpl/tx/transactors/bridge/XChainBridge.h index 3d0c250f24..0a2fccc18b 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.h +++ b/include/xrpl/tx/transactors/bridge/XChainBridge.h @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include namespace xrpl { diff --git a/include/xrpl/tx/transactors/check/CheckCancel.h b/include/xrpl/tx/transactors/check/CheckCancel.h new file mode 100644 index 0000000000..f16c6d2827 --- /dev/null +++ b/include/xrpl/tx/transactors/check/CheckCancel.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class CheckCancel : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CheckCancel(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/check/CheckCash.h b/include/xrpl/tx/transactors/check/CheckCash.h new file mode 100644 index 0000000000..c64d45061d --- /dev/null +++ b/include/xrpl/tx/transactors/check/CheckCash.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class CheckCash : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CheckCash(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/check/CheckCreate.h b/include/xrpl/tx/transactors/check/CheckCreate.h new file mode 100644 index 0000000000..2b5aa9123d --- /dev/null +++ b/include/xrpl/tx/transactors/check/CheckCreate.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class CheckCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CheckCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialAccept.h b/include/xrpl/tx/transactors/credentials/CredentialAccept.h new file mode 100644 index 0000000000..1895e54589 --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialAccept.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialAccept : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialCreate.h b/include/xrpl/tx/transactors/credentials/CredentialCreate.h new file mode 100644 index 0000000000..500b034a9c --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/credentials/CredentialDelete.h b/include/xrpl/tx/transactors/credentials/CredentialDelete.h new file mode 100644 index 0000000000..26305232ce --- /dev/null +++ b/include/xrpl/tx/transactors/credentials/CredentialDelete.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class CredentialDelete : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/DelegateSet.h b/include/xrpl/tx/transactors/delegate/DelegateSet.h similarity index 66% rename from src/xrpld/app/tx/detail/DelegateSet.h rename to include/xrpl/tx/transactors/delegate/DelegateSet.h index 2120674557..cc3a8fe4cb 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.h +++ b/include/xrpl/tx/transactors/delegate/DelegateSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -22,9 +22,13 @@ public: TER doApply() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER - deleteDelegate(ApplyView& view, std::shared_ptr const& sle, AccountID const& account, beast::Journal j); + deleteDelegate( + ApplyView& view, + std::shared_ptr const& sle, + AccountID const& account, + beast::Journal j); }; } // namespace xrpl diff --git a/src/xrpld/app/misc/DelegateUtils.h b/include/xrpl/tx/transactors/delegate/DelegateUtils.h similarity index 100% rename from src/xrpld/app/misc/DelegateUtils.h rename to include/xrpl/tx/transactors/delegate/DelegateUtils.h diff --git a/src/xrpld/app/tx/detail/AMMBid.h b/include/xrpl/tx/transactors/dex/AMMBid.h similarity index 98% rename from src/xrpld/app/tx/detail/AMMBid.h rename to include/xrpl/tx/transactors/dex/AMMBid.h index 83ea6e0729..b80bfe3bef 100644 --- a/src/xrpld/app/tx/detail/AMMBid.h +++ b/include/xrpl/tx/transactors/dex/AMMBid.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/AMMClawback.h b/include/xrpl/tx/transactors/dex/AMMClawback.h similarity index 97% rename from src/xrpld/app/tx/detail/AMMClawback.h rename to include/xrpl/tx/transactors/dex/AMMClawback.h index 3da3c44605..2bfccfa202 100644 --- a/src/xrpld/app/tx/detail/AMMClawback.h +++ b/include/xrpl/tx/transactors/dex/AMMClawback.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { class Sandbox; diff --git a/src/xrpld/app/paths/AMMContext.h b/include/xrpl/tx/transactors/dex/AMMContext.h similarity index 100% rename from src/xrpld/app/paths/AMMContext.h rename to include/xrpl/tx/transactors/dex/AMMContext.h diff --git a/src/xrpld/app/tx/detail/AMMCreate.h b/include/xrpl/tx/transactors/dex/AMMCreate.h similarity index 98% rename from src/xrpld/app/tx/detail/AMMCreate.h rename to include/xrpl/tx/transactors/dex/AMMCreate.h index 6f9fd77a2f..5deaa129ed 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.h +++ b/include/xrpl/tx/transactors/dex/AMMCreate.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/AMMDelete.h b/include/xrpl/tx/transactors/dex/AMMDelete.h similarity index 94% rename from src/xrpld/app/tx/detail/AMMDelete.h rename to include/xrpl/tx/transactors/dex/AMMDelete.h index 20c8f87262..1c0996f8a2 100644 --- a/src/xrpld/app/tx/detail/AMMDelete.h +++ b/include/xrpl/tx/transactors/dex/AMMDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/AMMDeposit.h b/include/xrpl/tx/transactors/dex/AMMDeposit.h similarity index 99% rename from src/xrpld/app/tx/detail/AMMDeposit.h rename to include/xrpl/tx/transactors/dex/AMMDeposit.h index 45c7995438..287d46ff07 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.h +++ b/include/xrpl/tx/transactors/dex/AMMDeposit.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/misc/AMMHelpers.h b/include/xrpl/tx/transactors/dex/AMMHelpers.h similarity index 90% rename from src/xrpld/app/misc/AMMHelpers.h rename to include/xrpl/tx/transactors/dex/AMMHelpers.h index cde485f5dc..e2735ea80f 100644 --- a/src/xrpld/app/misc/AMMHelpers.h +++ b/include/xrpl/tx/transactors/dex/AMMHelpers.h @@ -60,7 +60,11 @@ lpTokensOut( * @return */ STAmount -ammAssetIn(STAmount const& asset1Balance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee); +ammAssetIn( + STAmount const& asset1Balance, + STAmount const& lptAMMBalance, + STAmount const& lpTokens, + std::uint16_t tfee); /** Calculate LP Tokens given asset's withdraw amount. Return 0 * if can't calculate. @@ -85,7 +89,11 @@ lpTokensIn( * @return calculated asset amount */ STAmount -ammAssetOut(STAmount const& assetBalance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee); +ammAssetOut( + STAmount const& assetBalance, + STAmount const& lptAMMBalance, + STAmount const& lpTokens, + std::uint16_t tfee); /** Check if the relative distance between the qualities * is within the requested distance. @@ -159,7 +167,10 @@ solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c); */ template std::optional> -getAMMOfferStartWithTakerGets(TAmounts const& pool, Quality const& targetQuality, std::uint16_t const& tfee) +getAMMOfferStartWithTakerGets( + TAmounts const& pool, + Quality const& targetQuality, + std::uint16_t const& tfee) { if (targetQuality.rate() == beast::zero) return std::nullopt; @@ -185,7 +196,8 @@ getAMMOfferStartWithTakerGets(TAmounts const& pool, Quality const& ta auto getAmounts = [&pool, &tfee](Number const& nTakerGetsProposed) { // Round downward to minimize the offer and to maximize the quality. // This has the most impact when takerGets is XRP. - auto const takerGets = toAmount(getIssue(pool.out), nTakerGetsProposed, Number::downward); + auto const takerGets = + toAmount(getIssue(pool.out), nTakerGetsProposed, Number::downward); return TAmounts{swapAssetOut(pool, takerGets, tfee), takerGets}; }; @@ -222,7 +234,10 @@ getAMMOfferStartWithTakerGets(TAmounts const& pool, Quality const& ta */ template std::optional> -getAMMOfferStartWithTakerPays(TAmounts const& pool, Quality const& targetQuality, std::uint16_t tfee) +getAMMOfferStartWithTakerPays( + TAmounts const& pool, + Quality const& targetQuality, + std::uint16_t tfee) { if (targetQuality.rate() == beast::zero) return std::nullopt; @@ -248,7 +263,8 @@ getAMMOfferStartWithTakerPays(TAmounts const& pool, Quality const& ta auto getAmounts = [&pool, &tfee](Number const& nTakerPaysProposed) { // Round downward to minimize the offer and to maximize the quality. // This has the most impact when takerPays is XRP. - auto const takerPays = toAmount(getIssue(pool.in), nTakerPaysProposed, Number::downward); + auto const takerPays = + toAmount(getIssue(pool.in), nTakerPaysProposed, Number::downward); return TAmounts{takerPays, swapAssetIn(pool, takerPays, tfee)}; }; @@ -313,32 +329,34 @@ changeSpotPriceQuality( }(); if (nTakerPays <= 0) { - JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " " - << to_string(pool.out) << " " << quality << " " << tfee; + JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) + << " " << to_string(pool.out) << " " << quality << " " << tfee; return std::nullopt; } auto const takerPays = toAmount(getIssue(pool.in), nTakerPays, Number::upward); // should not fail - if (auto const amounts = TAmounts{takerPays, swapAssetIn(pool, takerPays, tfee)}; - Quality{amounts} < quality && !withinRelativeDistance(Quality{amounts}, quality, Number(1, -7))) + if (auto const amounts = + TAmounts{takerPays, swapAssetIn(pool, takerPays, tfee)}; + Quality{amounts} < quality && + !withinRelativeDistance(Quality{amounts}, quality, Number(1, -7))) { - JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out) - << " " - << " " << quality << " " << tfee << " " << to_string(amounts.in) << " " - << to_string(amounts.out); + JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " + << to_string(pool.out) << " " + << " " << quality << " " << tfee << " " << to_string(amounts.in) + << " " << to_string(amounts.out); Throw("changeSpotPriceQuality failed"); } else { JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " " << to_string(pool.out) << " " - << " " << quality << " " << tfee << " " << to_string(amounts.in) << " " - << to_string(amounts.out); + << " " << quality << " " << tfee << " " << to_string(amounts.in) + << " " << to_string(amounts.out); return amounts; } } - JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " " << to_string(pool.out) - << " " << quality << " " << tfee; + JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " " + << to_string(pool.out) << " " << quality << " " << tfee; return std::nullopt; } @@ -351,20 +369,23 @@ changeSpotPriceQuality( }(); if (!amounts) { - JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " << to_string(pool.out) << " " - << quality << " " << tfee << std::endl; + JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " + << to_string(pool.out) << " " << quality << " " << tfee; return std::nullopt; } if (Quality{*amounts} < quality) { - JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out) << " " - << quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out); + JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " + << to_string(pool.out) << " " << quality << " " << tfee << " " + << to_string(amounts->in) << " " << to_string(amounts->out); return std::nullopt; } - JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " " << to_string(pool.out) << " " - << " " << quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out); + JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " " + << to_string(pool.out) << " " + << " " << quality << " " << tfee << " " << to_string(amounts->in) << " " + << to_string(amounts->out); return amounts; } @@ -618,9 +639,9 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe STAmount getRoundedAsset( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& balance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit); /** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas @@ -631,7 +652,11 @@ getRoundedAsset( * digits) when adding the lptokens to the balance. */ STAmount -getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& frac, IsDeposit isDeposit); +getRoundedLPTokens( + Rules const& rules, + STAmount const& balance, + Number const& frac, + IsDeposit isDeposit); /** Round AMM single deposit/withdrawal LPToken amount. * The lambda's are used to delay evaluation until the function is executed @@ -647,9 +672,9 @@ getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& fr STAmount getRoundedLPTokens( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& lptAMMBalance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit); /* Next two functions adjust asset in/out amount to factor in the adjusted @@ -684,6 +709,10 @@ adjustAssetOutByTokens( * is used to adjust equal deposit/withdraw amount. */ Number -adjustFracByTokens(Rules const& rules, STAmount const& lptAMMBalance, STAmount const& tokens, Number const& frac); +adjustFracByTokens( + Rules const& rules, + STAmount const& lptAMMBalance, + STAmount const& tokens, + Number const& frac); } // namespace xrpl diff --git a/src/xrpld/app/misc/AMMUtils.h b/include/xrpl/tx/transactors/dex/AMMUtils.h similarity index 94% rename from src/xrpld/app/misc/AMMUtils.h rename to include/xrpl/tx/transactors/dex/AMMUtils.h index c97397a141..77ad18106e 100644 --- a/src/xrpld/app/misc/AMMUtils.h +++ b/include/xrpl/tx/transactors/dex/AMMUtils.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +51,11 @@ ammLPHolds( beast::Journal const j); STAmount -ammLPHolds(ReadView const& view, SLE const& ammSle, AccountID const& lpAccount, beast::Journal const j); +ammLPHolds( + ReadView const& view, + SLE const& ammSle, + AccountID const& lpAccount, + beast::Journal const j); /** Get AMM trading fee for the given account. The fee is discounted * if the account is the auction slot owner or one of the slot's authorized diff --git a/src/xrpld/app/tx/detail/AMMVote.h b/include/xrpl/tx/transactors/dex/AMMVote.h similarity index 97% rename from src/xrpld/app/tx/detail/AMMVote.h rename to include/xrpl/tx/transactors/dex/AMMVote.h index 2bc3da2301..ab04b30993 100644 --- a/src/xrpld/app/tx/detail/AMMVote.h +++ b/include/xrpl/tx/transactors/dex/AMMVote.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h similarity index 99% rename from src/xrpld/app/tx/detail/AMMWithdraw.h rename to include/xrpl/tx/transactors/dex/AMMWithdraw.h index 246c66100c..5328b03abb 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -1,8 +1,8 @@ #pragma once -#include - #include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/CancelOffer.h b/include/xrpl/tx/transactors/dex/OfferCancel.h similarity index 65% rename from src/xrpld/app/tx/detail/CancelOffer.h rename to include/xrpl/tx/transactors/dex/OfferCancel.h index 30a7129fb4..c4c57ff3b4 100644 --- a/src/xrpld/app/tx/detail/CancelOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCancel.h @@ -1,17 +1,16 @@ #pragma once -#include - #include +#include namespace xrpl { -class CancelOffer : public Transactor +class OfferCancel : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CancelOffer(ApplyContext& ctx) : Transactor(ctx) + explicit OfferCancel(ApplyContext& ctx) : Transactor(ctx) { } @@ -25,6 +24,4 @@ public: doApply() override; }; -using OfferCancel = CancelOffer; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/CreateOffer.h b/include/xrpl/tx/transactors/dex/OfferCreate.h similarity index 91% rename from src/xrpld/app/tx/detail/CreateOffer.h rename to include/xrpl/tx/transactors/dex/OfferCreate.h index 14f82c501e..2179180054 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include namespace xrpl { @@ -10,13 +9,13 @@ class PaymentSandbox; class Sandbox; /** Transactor specialized for creating offers in the ledger. */ -class CreateOffer : public Transactor +class OfferCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; /** Construct a Transactor subclass that creates an offer in the ledger. */ - explicit CreateOffer(ApplyContext& ctx) : Transactor(ctx) + explicit OfferCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -75,6 +74,4 @@ private: std::function)> const& setDir); }; -using OfferCreate = CreateOffer; - } // namespace xrpl diff --git a/src/xrpld/app/misc/PermissionedDEXHelpers.h b/include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h similarity index 75% rename from src/xrpld/app/misc/PermissionedDEXHelpers.h rename to include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h index 832129db2f..3992f8885e 100644 --- a/src/xrpld/app/misc/PermissionedDEXHelpers.h +++ b/include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h @@ -10,7 +10,11 @@ accountInDomain(ReadView const& view, AccountID const& account, Domain const& do // Check if an offer is in the permissioned domain [[nodiscard]] bool -offerInDomain(ReadView const& view, uint256 const& offerID, Domain const& domainID, beast::Journal j); +offerInDomain( + ReadView const& view, + uint256 const& offerID, + Domain const& domainID, + beast::Journal j); } // namespace permissioned_dex diff --git a/src/xrpld/app/tx/detail/DID.h b/include/xrpl/tx/transactors/did/DIDDelete.h similarity index 57% rename from src/xrpld/app/tx/detail/DID.h rename to include/xrpl/tx/transactors/did/DIDDelete.h index a00039a999..037a57bb02 100644 --- a/src/xrpld/app/tx/detail/DID.h +++ b/include/xrpl/tx/transactors/did/DIDDelete.h @@ -1,27 +1,9 @@ #pragma once -#include +#include namespace xrpl { -class DIDSet : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit DIDSet(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - class DIDDelete : public Transactor { public: diff --git a/include/xrpl/tx/transactors/did/DIDSet.h b/include/xrpl/tx/transactors/did/DIDSet.h new file mode 100644 index 0000000000..fe1c476b47 --- /dev/null +++ b/include/xrpl/tx/transactors/did/DIDSet.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace xrpl { + +class DIDSet : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit DIDSet(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowCancel.h b/include/xrpl/tx/transactors/escrow/EscrowCancel.h new file mode 100644 index 0000000000..c8b97203f0 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowCancel.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowCancel : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowCreate.h b/include/xrpl/tx/transactors/escrow/EscrowCreate.h new file mode 100644 index 0000000000..e3f07f8c4d --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit EscrowCreate(ApplyContext& ctx) : Transactor(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); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/escrow/EscrowFinish.h b/include/xrpl/tx/transactors/escrow/EscrowFinish.h new file mode 100644 index 0000000000..0917c35d93 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowFinish.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowFinish : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx) + { + } + + static bool + checkExtraFeatures(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static NotTEC + preflightSigValidated(PreflightContext const& ctx); + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/src/xrpld/app/misc/LendingHelpers.h b/include/xrpl/tx/transactors/lending/LendingHelpers.h similarity index 97% rename from src/xrpld/app/misc/LendingHelpers.h rename to include/xrpl/tx/transactors/lending/LendingHelpers.h index f8297dfdf7..4057c9c173 100644 --- a/src/xrpld/app/misc/LendingHelpers.h +++ b/include/xrpl/tx/transactors/lending/LendingHelpers.h @@ -154,7 +154,11 @@ struct LoanProperties // accumulated rounding errors and leftover dust amounts. template void -adjustImpreciseNumber(NumberProxy value, Number const& adjustment, Asset const& asset, int vaultScale) +adjustImpreciseNumber( + NumberProxy value, + Number const& adjustment, + Asset const& asset, + int vaultScale) { value = roundToAsset(asset, value + adjustment, vaultScale); @@ -199,7 +203,11 @@ LoanState constructRoundedLoanState(SLE::const_ref loan); Number -computeManagementFee(Asset const& asset, Number const& interest, TenthBips32 managementFeeRate, std::int32_t scale); +computeManagementFee( + Asset const& asset, + Number const& interest, + TenthBips32 managementFeeRate, + std::int32_t scale); Number computeFullPaymentInterest( @@ -370,7 +378,10 @@ computeInterestAndFeeParts( std::int32_t loanScale); Number -loanPeriodicPayment(Number const& principalOutstanding, Number const& periodicRate, std::uint32_t paymentsRemaining); +loanPeriodicPayment( + Number const& principalOutstanding, + Number const& periodicRate, + std::uint32_t paymentsRemaining); Number loanPrincipalFromPeriodicPayment( diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h similarity index 93% rename from src/xrpld/app/tx/detail/LoanBrokerCoverClawback.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h index 139d50696a..b1e539392f 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h similarity index 92% rename from src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h index 3f683c6a62..8dda417443 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h similarity index 93% rename from src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h index 50d0b98fa5..52b14bfb67 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanBrokerDelete.h b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h similarity index 92% rename from src/xrpld/app/tx/detail/LoanBrokerDelete.h rename to include/xrpl/tx/transactors/lending/LoanBrokerDelete.h index cb44277f55..b9c9851c41 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.h b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h similarity index 93% rename from src/xrpld/app/tx/detail/LoanBrokerSet.h rename to include/xrpl/tx/transactors/lending/LoanBrokerSet.h index cda452bebe..ce1e069791 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanDelete.h b/include/xrpl/tx/transactors/lending/LoanDelete.h similarity index 92% rename from src/xrpld/app/tx/detail/LoanDelete.h rename to include/xrpl/tx/transactors/lending/LoanDelete.h index 37889d31fb..ff78d7db60 100644 --- a/src/xrpld/app/tx/detail/LoanDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanManage.h b/include/xrpl/tx/transactors/lending/LoanManage.h similarity index 76% rename from src/xrpld/app/tx/detail/LoanManage.h rename to include/xrpl/tx/transactors/lending/LoanManage.h index 44b2b62b3d..810e9bbf65 100644 --- a/src/xrpld/app/tx/detail/LoanManage.h +++ b/include/xrpl/tx/transactors/lending/LoanManage.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -39,12 +39,22 @@ public: /** Helper function that might be needed by other transactors */ static TER - impairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j); + impairLoan( + ApplyView& view, + SLE::ref loanSle, + SLE::ref vaultSle, + Asset const& vaultAsset, + beast::Journal j); /** Helper function that might be needed by other transactors */ [[nodiscard]] static TER - unimpairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j); + unimpairLoan( + ApplyView& view, + SLE::ref loanSle, + SLE::ref vaultSle, + Asset const& vaultAsset, + beast::Journal j); TER doApply() override; diff --git a/src/xrpld/app/tx/detail/LoanPay.h b/include/xrpl/tx/transactors/lending/LoanPay.h similarity index 94% rename from src/xrpld/app/tx/detail/LoanPay.h rename to include/xrpl/tx/transactors/lending/LoanPay.h index c947b1b6f8..2e3cce75ed 100644 --- a/src/xrpld/app/tx/detail/LoanPay.h +++ b/include/xrpl/tx/transactors/lending/LoanPay.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanSet.h b/include/xrpl/tx/transactors/lending/LoanSet.h similarity index 93% rename from src/xrpld/app/tx/detail/LoanSet.h rename to include/xrpl/tx/transactors/lending/LoanSet.h index e4bb79a36d..86101d0735 100644 --- a/src/xrpld/app/tx/detail/LoanSet.h +++ b/include/xrpl/tx/transactors/lending/LoanSet.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h similarity index 86% rename from src/xrpld/app/tx/detail/NFTokenAcceptOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h index 549c38d33b..60962fc9ca 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -26,9 +26,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/NFTokenBurn.h b/include/xrpl/tx/transactors/nft/NFTokenBurn.h similarity index 90% rename from src/xrpld/app/tx/detail/NFTokenBurn.h rename to include/xrpl/tx/transactors/nft/NFTokenBurn.h index c2bc300ab8..8737997f03 100644 --- a/src/xrpld/app/tx/detail/NFTokenBurn.h +++ b/include/xrpl/tx/transactors/nft/NFTokenBurn.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h similarity index 78% rename from src/xrpld/app/tx/detail/NFTokenCancelOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h index b1801ede25..6d60a0bebd 100644 --- a/src/xrpld/app/tx/detail/NFTokenCancelOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h similarity index 91% rename from src/xrpld/app/tx/detail/NFTokenCreateOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h index ed54338f49..a48e53589d 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/NFTokenMint.h b/include/xrpl/tx/transactors/nft/NFTokenMint.h similarity index 89% rename from src/xrpld/app/tx/detail/NFTokenMint.h rename to include/xrpl/tx/transactors/nft/NFTokenMint.h index 52981ef467..d4eeba2bf0 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.h +++ b/include/xrpl/tx/transactors/nft/NFTokenMint.h @@ -1,9 +1,8 @@ #pragma once -#include -#include - #include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/NFTokenModify.h b/include/xrpl/tx/transactors/nft/NFTokenModify.h similarity index 90% rename from src/xrpld/app/tx/detail/NFTokenModify.h rename to include/xrpl/tx/transactors/nft/NFTokenModify.h index f755746f1f..a64df65783 100644 --- a/src/xrpld/app/tx/detail/NFTokenModify.h +++ b/include/xrpl/tx/transactors/nft/NFTokenModify.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.h b/include/xrpl/tx/transactors/nft/NFTokenUtils.h similarity index 81% rename from src/xrpld/app/tx/detail/NFTokenUtils.h rename to include/xrpl/tx/transactors/nft/NFTokenUtils.h index 44d3cfb956..33aab068c6 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.h +++ b/include/xrpl/tx/transactors/nft/NFTokenUtils.h @@ -1,12 +1,12 @@ #pragma once -#include - #include #include #include #include +#include #include +#include namespace xrpl { @@ -15,7 +15,10 @@ namespace nft { /** Delete up to a specified number of offers from the specified token offer * directory. */ std::size_t -removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t maxDeletableOffers); +removeTokenOffersWithLimit( + ApplyView& view, + Keylet const& directory, + std::size_t maxDeletableOffers); /** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */ TER @@ -31,7 +34,8 @@ struct TokenAndPage STObject token; std::shared_ptr page; - TokenAndPage(STObject const& token_, std::shared_ptr page_) : token(token_), page(std::move(page_)) + TokenAndPage(STObject const& token_, std::shared_ptr page_) + : token(token_), page(std::move(page_)) { } }; @@ -47,7 +51,11 @@ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER -removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, std::shared_ptr&& page); +removeToken( + ApplyView& view, + AccountID const& owner, + uint256 const& nftokenID, + std::shared_ptr const& page); /** Deletes the given token offer. @@ -88,7 +96,7 @@ tokenOfferCreatePreflight( std::uint16_t nftFlags, Rules const& rules, std::optional const& owner = std::nullopt, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); /** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */ TER @@ -102,7 +110,7 @@ tokenOfferCreatePreclaim( std::uint16_t xferFee, beast::Journal j, std::optional const& owner = std::nullopt, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); /** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */ TER @@ -116,13 +124,21 @@ tokenOfferCreateApply( uint256 const& nftokenID, XRPAmount const& priorBalance, beast::Journal j, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); TER -checkTrustlineAuthorized(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue); +checkTrustlineAuthorized( + ReadView const& view, + AccountID const id, + beast::Journal const j, + Issue const& issue); TER -checkTrustlineDeepFrozen(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue); +checkTrustlineDeepFrozen( + ReadView const& view, + AccountID const id, + beast::Journal const j, + Issue const& issue); } // namespace nft diff --git a/src/xrpld/app/tx/detail/DeleteOracle.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h similarity index 63% rename from src/xrpld/app/tx/detail/DeleteOracle.h rename to include/xrpl/tx/transactors/oracle/OracleDelete.h index 7d7cc340cb..9d1e6d3a44 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -10,15 +10,15 @@ namespace xrpl { to decentralized applications (dApps) on the blockchain. This implementation conforms to the requirements specified in the XLS-47d. - The DeleteOracle transactor implements the deletion of Oracle objects. + The OracleDelete transactor implements the deletion of Oracle objects. */ -class DeleteOracle : public Transactor +class OracleDelete : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx) + explicit OracleDelete(ApplyContext& ctx) : Transactor(ctx) { } @@ -32,9 +32,11 @@ public: doApply() override; static TER - deleteOracle(ApplyView& view, std::shared_ptr const& sle, AccountID const& account, beast::Journal j); + deleteOracle( + ApplyView& view, + std::shared_ptr const& sle, + AccountID const& account, + beast::Journal j); }; -using OracleDelete = DeleteOracle; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/SetOracle.h b/include/xrpl/tx/transactors/oracle/OracleSet.h similarity index 72% rename from src/xrpld/app/tx/detail/SetOracle.h rename to include/xrpl/tx/transactors/oracle/OracleSet.h index 6b47a5397e..d879d14a84 100644 --- a/src/xrpld/app/tx/detail/SetOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -10,15 +10,15 @@ namespace xrpl { to decentralized applications (dApps) on the blockchain. This implementation conforms to the requirements specified in the XLS-47d. - The SetOracle transactor implements creating or updating Oracle objects. + The OracleSet transactor implements creating or updating Oracle objects. */ -class SetOracle : public Transactor +class OracleSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SetOracle(ApplyContext& ctx) : Transactor(ctx) + explicit OracleSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -32,6 +32,4 @@ public: doApply() override; }; -using OracleSet = SetOracle; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/DepositPreauth.h b/include/xrpl/tx/transactors/payment/DepositPreauth.h similarity index 87% rename from src/xrpld/app/tx/detail/DepositPreauth.h rename to include/xrpl/tx/transactors/payment/DepositPreauth.h index f1afac3b18..b397a81748 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.h +++ b/include/xrpl/tx/transactors/payment/DepositPreauth.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -25,7 +25,7 @@ public: TER doApply() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER removeFromLedger(ApplyView& view, uint256 const& delIndex, beast::Journal j); }; diff --git a/src/xrpld/app/tx/detail/Payment.h b/include/xrpl/tx/transactors/payment/Payment.h similarity index 95% rename from src/xrpld/app/tx/detail/Payment.h rename to include/xrpl/tx/transactors/payment/Payment.h index 192f2b0edb..bc5bc4fee3 100644 --- a/src/xrpld/app/tx/detail/Payment.h +++ b/include/xrpl/tx/transactors/payment/Payment.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h new file mode 100644 index 0000000000..449e503bc4 --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelClaim : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit PaymentChannelClaim(ApplyContext& ctx) : Transactor(ctx) + { + } + + static bool + checkExtraFeatures(PreflightContext const& ctx); + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/CancelCheck.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h similarity index 55% rename from src/xrpld/app/tx/detail/CancelCheck.h rename to include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h index f125b9af5b..7fdacf9b3a 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h @@ -1,18 +1,21 @@ #pragma once -#include +#include namespace xrpl { -class CancelCheck : public Transactor +class PaymentChannelCreate : public Transactor { public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - explicit CancelCheck(ApplyContext& ctx) : Transactor(ctx) + explicit PaymentChannelCreate(ApplyContext& ctx) : Transactor(ctx) { } + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + static NotTEC preflight(PreflightContext const& ctx); @@ -23,6 +26,4 @@ public: doApply() override; }; -using CheckCancel = CancelCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h new file mode 100644 index 0000000000..69f74a4c53 --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelFund : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit PaymentChannelFund(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/PermissionedDomainDelete.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h similarity index 91% rename from src/xrpld/app/tx/detail/PermissionedDomainDelete.h rename to include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h index 294fb794ae..b5c72413a2 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainDelete.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/PermissionedDomainSet.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h similarity index 92% rename from src/xrpld/app/tx/detail/PermissionedDomainSet.h rename to include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h index 824104e50d..acf9194ee2 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainSet.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/Batch.h b/include/xrpl/tx/transactors/system/Batch.h similarity index 93% rename from src/xrpld/app/tx/detail/Batch.h rename to include/xrpl/tx/transactors/system/Batch.h index 8af8b2e020..0861deb094 100644 --- a/src/xrpld/app/tx/detail/Batch.h +++ b/include/xrpl/tx/transactors/system/Batch.h @@ -1,10 +1,8 @@ #pragma once -#include -#include - #include #include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/Change.h b/include/xrpl/tx/transactors/system/Change.h similarity index 93% rename from src/xrpld/app/tx/detail/Change.h rename to include/xrpl/tx/transactors/system/Change.h index 683b054ccb..1bf63ff0db 100644 --- a/src/xrpld/app/tx/detail/Change.h +++ b/include/xrpl/tx/transactors/system/Change.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LedgerStateFix.h b/include/xrpl/tx/transactors/system/LedgerStateFix.h similarity index 92% rename from src/xrpld/app/tx/detail/LedgerStateFix.h rename to include/xrpl/tx/transactors/system/LedgerStateFix.h index 66fe124cff..728f8c651d 100644 --- a/src/xrpld/app/tx/detail/LedgerStateFix.h +++ b/include/xrpl/tx/transactors/system/LedgerStateFix.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/CreateTicket.h b/include/xrpl/tx/transactors/system/TicketCreate.h similarity index 87% rename from src/xrpld/app/tx/detail/CreateTicket.h rename to include/xrpl/tx/transactors/system/TicketCreate.h index 2a6e1bb8cc..88d83c37a7 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -1,11 +1,10 @@ #pragma once -#include -#include +#include namespace xrpl { -class CreateTicket : public Transactor +class TicketCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; @@ -26,7 +25,7 @@ public: // average, 1.25 ms. // // Using that same test set up creating 250 Tickets in a single - // CreateTicket::doApply() in a unit test took, on average, 1.21 ms. + // TicketCreate::doApply() in a unit test took, on average, 1.21 ms. // // So, for the moment, a single transaction creating 250 Tickets takes // about the same compute time as a single compute-intensive payment. @@ -42,7 +41,7 @@ public: // ledger-stuffing with Tickets. constexpr static std::uint32_t maxTicketThreshold = 250; - explicit CreateTicket(ApplyContext& ctx) : Transactor(ctx) + explicit TicketCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -62,6 +61,4 @@ public: doApply() override; }; -using TicketCreate = CreateTicket; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/Clawback.h b/include/xrpl/tx/transactors/token/Clawback.h similarity index 77% rename from src/xrpld/app/tx/detail/Clawback.h rename to include/xrpl/tx/transactors/token/Clawback.h index 427edb25b4..a795115f7a 100644 --- a/src/xrpld/app/tx/detail/Clawback.h +++ b/include/xrpl/tx/transactors/token/Clawback.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.h b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h similarity index 79% rename from src/xrpld/app/tx/detail/MPTokenAuthorize.h rename to include/xrpl/tx/transactors/token/MPTokenAuthorize.h index c887b70fa8..b6a34d4f14 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.h +++ b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -32,7 +32,11 @@ public: preclaim(PreclaimContext const& ctx); static TER - createMPToken(ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, std::uint32_t const flags); + createMPToken( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + std::uint32_t const flags); TER doApply() override; diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h similarity index 96% rename from src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 56c20ed551..0ebde22a37 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -1,9 +1,8 @@ #pragma once -#include - #include #include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h similarity index 78% rename from src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h index 89243944d0..416708565a 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -13,9 +13,6 @@ public: { } - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h similarity index 93% rename from src/xrpld/app/tx/detail/MPTokenIssuanceSet.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h index 68794ca48c..dccd4e4cee 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/SetTrust.h b/include/xrpl/tx/transactors/token/TrustSet.h similarity index 74% rename from src/xrpld/app/tx/detail/SetTrust.h rename to include/xrpl/tx/transactors/token/TrustSet.h index 1081567a66..2e67aaeded 100644 --- a/src/xrpld/app/tx/detail/SetTrust.h +++ b/include/xrpl/tx/transactors/token/TrustSet.h @@ -1,17 +1,16 @@ #pragma once -#include - #include +#include namespace xrpl { -class SetTrust : public Transactor +class TrustSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SetTrust(ApplyContext& ctx) : Transactor(ctx) + explicit TrustSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -31,6 +30,4 @@ public: doApply() override; }; -using TrustSet = SetTrust; - } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/VaultClawback.h b/include/xrpl/tx/transactors/vault/VaultClawback.h similarity index 93% rename from src/xrpld/app/tx/detail/VaultClawback.h rename to include/xrpl/tx/transactors/vault/VaultClawback.h index c93289e641..131a1d87e7 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.h +++ b/include/xrpl/tx/transactors/vault/VaultClawback.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/VaultCreate.h b/include/xrpl/tx/transactors/vault/VaultCreate.h similarity index 92% rename from src/xrpld/app/tx/detail/VaultCreate.h rename to include/xrpl/tx/transactors/vault/VaultCreate.h index 20b18f5cc2..cc35cd765b 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.h +++ b/include/xrpl/tx/transactors/vault/VaultCreate.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/VaultDelete.h b/include/xrpl/tx/transactors/vault/VaultDelete.h similarity index 90% rename from src/xrpld/app/tx/detail/VaultDelete.h rename to include/xrpl/tx/transactors/vault/VaultDelete.h index 8d1f214885..f881a692fd 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.h +++ b/include/xrpl/tx/transactors/vault/VaultDelete.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/VaultDeposit.h b/include/xrpl/tx/transactors/vault/VaultDeposit.h similarity index 90% rename from src/xrpld/app/tx/detail/VaultDeposit.h rename to include/xrpl/tx/transactors/vault/VaultDeposit.h index 6c63308407..0943596f20 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.h +++ b/include/xrpl/tx/transactors/vault/VaultDeposit.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/VaultSet.h b/include/xrpl/tx/transactors/vault/VaultSet.h similarity index 91% rename from src/xrpld/app/tx/detail/VaultSet.h rename to include/xrpl/tx/transactors/vault/VaultSet.h index 1e8a15291e..fb69f132b1 100644 --- a/src/xrpld/app/tx/detail/VaultSet.h +++ b/include/xrpl/tx/transactors/vault/VaultSet.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.h b/include/xrpl/tx/transactors/vault/VaultWithdraw.h similarity index 90% rename from src/xrpld/app/tx/detail/VaultWithdraw.h rename to include/xrpl/tx/transactors/vault/VaultWithdraw.h index f832c206f8..ffe14a7141 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.h +++ b/include/xrpl/tx/transactors/vault/VaultWithdraw.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/wasm/HostFunc.h b/include/xrpl/tx/wasm/HostFunc.h similarity index 78% rename from src/xrpld/app/wasm/HostFunc.h rename to include/xrpl/tx/wasm/HostFunc.h index f3ed5ad6f9..a3a26cfbaf 100644 --- a/src/xrpld/app/wasm/HostFunc.h +++ b/include/xrpl/tx/wasm/HostFunc.h @@ -1,7 +1,5 @@ #pragma once -#include - #include #include #include @@ -10,6 +8,7 @@ #include #include #include +#include namespace xrpl { @@ -104,56 +103,50 @@ struct HostFunctions return nullptr; } - std::int64_t - getGas() - { - return -1; - } - - void - setGas(std::int64_t) - { - return; - } - beast::Journal - getJournal() + getJournal() const { return j_; } + virtual bool + checkSelf() const + { + return true; + } + virtual Expected - getLedgerSqn() + getLedgerSqn() const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getParentLedgerTime() + getParentLedgerTime() const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getParentLedgerHash() + getParentLedgerHash() const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getBaseFee() + getBaseFee() const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - isAmendmentEnabled(uint256 const& amendmentId) + isAmendmentEnabled(uint256 const& amendmentId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - isAmendmentEnabled(std::string_view const& amendmentName) + isAmendmentEnabled(std::string_view const& amendmentName) const { return Unexpected(HostFunctionError::INTERNAL); } @@ -165,73 +158,73 @@ struct HostFunctions } virtual Expected - getTxField(SField const& fname) + getTxField(SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getCurrentLedgerObjField(SField const& fname) + getCurrentLedgerObjField(SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) + getLedgerObjField(int32_t cacheIdx, SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getTxNestedField(Slice const& locator) + getTxNestedField(Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getCurrentLedgerObjNestedField(Slice const& locator) + getCurrentLedgerObjNestedField(Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) + getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getTxArrayLen(SField const& fname) + getTxArrayLen(SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getCurrentLedgerObjArrayLen(SField const& fname) + getCurrentLedgerObjArrayLen(SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) + getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getTxNestedArrayLen(Slice const& locator) + getTxNestedArrayLen(Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getCurrentLedgerObjNestedArrayLen(Slice const& locator) + getCurrentLedgerObjNestedArrayLen(Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) + getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const { return Unexpected(HostFunctionError::INTERNAL); } @@ -243,259 +236,260 @@ struct HostFunctions } virtual Expected - checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) + checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - computeSha512HalfHash(Slice const& data) + computeSha512HalfHash(Slice const& data) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - accountKeylet(AccountID const& account) + accountKeylet(AccountID const& account) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - ammKeylet(Asset const& issue1, Asset const& issue2) + ammKeylet(Asset const& issue1, Asset const& issue2) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - checkKeylet(AccountID const& account, std::uint32_t seq) + checkKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) + const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - didKeylet(AccountID const& account) + didKeylet(AccountID const& account) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - delegateKeylet(AccountID const& account, AccountID const& authorize) + delegateKeylet(AccountID const& account, AccountID const& authorize) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - depositPreauthKeylet(AccountID const& account, AccountID const& authorize) + depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - escrowKeylet(AccountID const& account, std::uint32_t seq) + escrowKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) + lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) + mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - mptokenKeylet(MPTID const& mptid, AccountID const& holder) + mptokenKeylet(MPTID const& mptid, AccountID const& holder) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - nftOfferKeylet(AccountID const& account, std::uint32_t seq) + nftOfferKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - offerKeylet(AccountID const& account, std::uint32_t seq) + offerKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - oracleKeylet(AccountID const& account, std::uint32_t docId) + oracleKeylet(AccountID const& account, std::uint32_t docId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) + paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) + permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - signersKeylet(AccountID const& account) + signersKeylet(AccountID const& account) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - ticketKeylet(AccountID const& account, std::uint32_t seq) + ticketKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - vaultKeylet(AccountID const& account, std::uint32_t seq) + vaultKeylet(AccountID const& account, std::uint32_t seq) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFT(AccountID const& account, uint256 const& nftId) + getNFT(AccountID const& account, uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFTIssuer(uint256 const& nftId) + getNFTIssuer(uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFTTaxon(uint256 const& nftId) + getNFTTaxon(uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFTFlags(uint256 const& nftId) + getNFTFlags(uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFTTransferFee(uint256 const& nftId) + getNFTTransferFee(uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - getNFTSerial(uint256 const& nftId) + getNFTSerial(uint256 const& nftId) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - trace(std::string_view const& msg, Slice const& data, bool asHex) + trace(std::string_view const& msg, Slice const& data, bool asHex) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - traceNum(std::string_view const& msg, int64_t data) + traceNum(std::string_view const& msg, int64_t data) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - traceAccount(std::string_view const& msg, AccountID const& account) + traceAccount(std::string_view const& msg, AccountID const& account) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - traceFloat(std::string_view const& msg, Slice const& data) + traceFloat(std::string_view const& msg, Slice const& data) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - traceAmount(std::string_view const& msg, STAmount const& amount) + traceAmount(std::string_view const& msg, STAmount const& amount) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatFromInt(int64_t x, int32_t mode) + floatFromInt(int64_t x, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatFromUint(uint64_t x, int32_t mode) + floatFromUint(uint64_t x, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatSet(int64_t mantissa, int32_t exponent, int32_t mode) + floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatCompare(Slice const& x, Slice const& y) + floatCompare(Slice const& x, Slice const& y) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatAdd(Slice const& x, Slice const& y, int32_t mode) + floatAdd(Slice const& x, Slice const& y, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatSubtract(Slice const& x, Slice const& y, int32_t mode) + floatSubtract(Slice const& x, Slice const& y, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatMultiply(Slice const& x, Slice const& y, int32_t mode) + floatMultiply(Slice const& x, Slice const& y, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatDivide(Slice const& x, Slice const& y, int32_t mode) + floatDivide(Slice const& x, Slice const& y, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatRoot(Slice const& x, int32_t n, int32_t mode) + floatRoot(Slice const& x, int32_t n, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatPower(Slice const& x, int32_t n, int32_t mode) + floatPower(Slice const& x, int32_t n, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } virtual Expected - floatLog(Slice const& x, int32_t mode) + floatLog(Slice const& x, int32_t mode) const { return Unexpected(HostFunctionError::INTERNAL); } diff --git a/src/xrpld/app/wasm/HostFuncImpl.h b/include/xrpl/tx/wasm/HostFuncImpl.h similarity index 56% rename from src/xrpld/app/wasm/HostFuncImpl.h rename to include/xrpl/tx/wasm/HostFuncImpl.h index d8e080a8ef..6992e1f523 100644 --- a/src/xrpld/app/wasm/HostFuncImpl.h +++ b/include/xrpl/tx/wasm/HostFuncImpl.h @@ -1,49 +1,47 @@ #pragma once -#include -#include +#include +#include namespace xrpl { class WasmHostFunctionsImpl : public HostFunctions { - ApplyContext& ctx; - Keylet leKey; - std::shared_ptr currentLedgerObj = nullptr; - bool isLedgerObjCached = false; + ApplyContext& ctx_; + + Keylet leKey_; + mutable std::optional> currentLedgerObj_; static int constexpr MAX_CACHE = 256; - std::array, MAX_CACHE> cache; + std::array, MAX_CACHE> cache_; + std::optional data_; void const* rt_ = nullptr; Expected, HostFunctionError> - getCurrentLedgerObj() + getCurrentLedgerObj() const { - if (!isLedgerObjCached) - { - isLedgerObjCached = true; - currentLedgerObj = ctx.view().read(leKey); - } - if (currentLedgerObj) - return currentLedgerObj; + if (!currentLedgerObj_) + currentLedgerObj_ = ctx_.view().read(leKey_); + if (*currentLedgerObj_) + return *currentLedgerObj_; return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); } Expected - normalizeCacheIndex(int32_t cacheIdx) + normalizeCacheIndex(int32_t cacheIdx) const { --cacheIdx; if (cacheIdx < 0 || cacheIdx >= MAX_CACHE) return Unexpected(HostFunctionError::SLOT_OUT_RANGE); - if (!cache[cacheIdx]) + if (!cache_[cacheIdx]) return Unexpected(HostFunctionError::EMPTY_SLOT); return cacheIdx; } template void - log(std::string_view const& msg, F&& dataFn) + log(std::string_view const& msg, F&& dataFn) const { #ifdef DEBUG_OUTPUT auto& j = std::cerr; @@ -52,7 +50,7 @@ class WasmHostFunctionsImpl : public HostFunctions return; auto j = getJournal().trace(); #endif - j << "WasmTrace[" << to_short_string(leKey.key) << "]: " << msg << " " << dataFn(); + j << "WasmTrace[" << to_short_string(leKey_.key) << "]: " << msg << " " << dataFn(); #ifdef DEBUG_OUTPUT j << std::endl; @@ -60,7 +58,8 @@ class WasmHostFunctionsImpl : public HostFunctions } public: - WasmHostFunctionsImpl(ApplyContext& ct, Keylet const& leKey) : HostFunctions(ct.journal), ctx(ct), leKey(leKey) + WasmHostFunctionsImpl(ApplyContext& ct, Keylet const& leKey) + : HostFunctions(ct.journal), ctx_(ct), leKey_(leKey) { } @@ -76,6 +75,13 @@ public: return rt_; } + virtual bool + checkSelf() const override + { + return !currentLedgerObj_ && !data_ && + std::ranges::find_if(cache_, [](auto& p) { return !!p; }) == cache_.end(); + } + std::optional const& getData() const { @@ -83,193 +89,197 @@ public: } Expected - getLedgerSqn() override; + getLedgerSqn() const override; Expected - getParentLedgerTime() override; + getParentLedgerTime() const override; Expected - getParentLedgerHash() override; + getParentLedgerHash() const override; Expected - getBaseFee() override; + getBaseFee() const override; Expected - isAmendmentEnabled(uint256 const& amendmentId) override; + isAmendmentEnabled(uint256 const& amendmentId) const override; Expected - isAmendmentEnabled(std::string_view const& amendmentName) override; + isAmendmentEnabled(std::string_view const& amendmentName) const override; Expected cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override; Expected - getTxField(SField const& fname) override; + getTxField(SField const& fname) const override; Expected - getCurrentLedgerObjField(SField const& fname) override; + getCurrentLedgerObjField(SField const& fname) const override; Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) override; + getLedgerObjField(int32_t cacheIdx, SField const& fname) const override; Expected - getTxNestedField(Slice const& locator) override; + getTxNestedField(Slice const& locator) const override; Expected - getCurrentLedgerObjNestedField(Slice const& locator) override; + getCurrentLedgerObjNestedField(Slice const& locator) const override; Expected - getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override; + getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const override; Expected - getTxArrayLen(SField const& fname) override; + getTxArrayLen(SField const& fname) const override; Expected - getCurrentLedgerObjArrayLen(SField const& fname) override; + getCurrentLedgerObjArrayLen(SField const& fname) const override; Expected - getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override; + getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const override; Expected - getTxNestedArrayLen(Slice const& locator) override; + getTxNestedArrayLen(Slice const& locator) const override; Expected - getCurrentLedgerObjNestedArrayLen(Slice const& locator) override; + getCurrentLedgerObjNestedArrayLen(Slice const& locator) const override; Expected - getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override; + getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const override; Expected updateData(Slice const& data) override; Expected - checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) override; + checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) + const override; Expected - computeSha512HalfHash(Slice const& data) override; + computeSha512HalfHash(Slice const& data) const override; Expected - accountKeylet(AccountID const& account) override; + accountKeylet(AccountID const& account) const override; Expected - ammKeylet(Asset const& issue1, Asset const& issue2) override; + ammKeylet(Asset const& issue1, Asset const& issue2) const override; Expected - checkKeylet(AccountID const& account, std::uint32_t seq) override; + checkKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) override; + credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) + const override; Expected - didKeylet(AccountID const& account) override; + didKeylet(AccountID const& account) const override; Expected - delegateKeylet(AccountID const& account, AccountID const& authorize) override; + delegateKeylet(AccountID const& account, AccountID const& authorize) const override; Expected - depositPreauthKeylet(AccountID const& account, AccountID const& authorize) override; + depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const override; Expected - escrowKeylet(AccountID const& account, std::uint32_t seq) override; + escrowKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) override; + lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) + const override; Expected - mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) override; + mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const override; Expected - mptokenKeylet(MPTID const& mptid, AccountID const& holder) override; + mptokenKeylet(MPTID const& mptid, AccountID const& holder) const override; Expected - nftOfferKeylet(AccountID const& account, std::uint32_t seq) override; + nftOfferKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - offerKeylet(AccountID const& account, std::uint32_t seq) override; + offerKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - oracleKeylet(AccountID const& account, std::uint32_t docId) override; + oracleKeylet(AccountID const& account, std::uint32_t docId) const override; Expected - paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) override; + paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) + const override; Expected - permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) override; + permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - signersKeylet(AccountID const& account) override; + signersKeylet(AccountID const& account) const override; Expected - ticketKeylet(AccountID const& account, std::uint32_t seq) override; + ticketKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - vaultKeylet(AccountID const& account, std::uint32_t seq) override; + vaultKeylet(AccountID const& account, std::uint32_t seq) const override; Expected - getNFT(AccountID const& account, uint256 const& nftId) override; + getNFT(AccountID const& account, uint256 const& nftId) const override; Expected - getNFTIssuer(uint256 const& nftId) override; + getNFTIssuer(uint256 const& nftId) const override; Expected - getNFTTaxon(uint256 const& nftId) override; + getNFTTaxon(uint256 const& nftId) const override; Expected - getNFTFlags(uint256 const& nftId) override; + getNFTFlags(uint256 const& nftId) const override; Expected - getNFTTransferFee(uint256 const& nftId) override; + getNFTTransferFee(uint256 const& nftId) const override; Expected - getNFTSerial(uint256 const& nftId) override; + getNFTSerial(uint256 const& nftId) const override; Expected - trace(std::string_view const& msg, Slice const& data, bool asHex) override; + trace(std::string_view const& msg, Slice const& data, bool asHex) const override; Expected - traceNum(std::string_view const& msg, int64_t data) override; + traceNum(std::string_view const& msg, int64_t data) const override; Expected - traceAccount(std::string_view const& msg, AccountID const& account) override; + traceAccount(std::string_view const& msg, AccountID const& account) const override; Expected - traceFloat(std::string_view const& msg, Slice const& data) override; + traceFloat(std::string_view const& msg, Slice const& data) const override; Expected - traceAmount(std::string_view const& msg, STAmount const& amount) override; + traceAmount(std::string_view const& msg, STAmount const& amount) const override; Expected - floatFromInt(int64_t x, int32_t mode) override; + floatFromInt(int64_t x, int32_t mode) const override; Expected - floatFromUint(uint64_t x, int32_t mode) override; + floatFromUint(uint64_t x, int32_t mode) const override; Expected - floatSet(int64_t mantissa, int32_t exponent, int32_t mode) override; + floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override; Expected - floatCompare(Slice const& x, Slice const& y) override; + floatCompare(Slice const& x, Slice const& y) const override; Expected - floatAdd(Slice const& x, Slice const& y, int32_t mode) override; + floatAdd(Slice const& x, Slice const& y, int32_t mode) const override; Expected - floatSubtract(Slice const& x, Slice const& y, int32_t mode) override; + floatSubtract(Slice const& x, Slice const& y, int32_t mode) const override; Expected - floatMultiply(Slice const& x, Slice const& y, int32_t mode) override; + floatMultiply(Slice const& x, Slice const& y, int32_t mode) const override; Expected - floatDivide(Slice const& x, Slice const& y, int32_t mode) override; + floatDivide(Slice const& x, Slice const& y, int32_t mode) const override; Expected - floatRoot(Slice const& x, int32_t n, int32_t mode) override; + floatRoot(Slice const& x, int32_t n, int32_t mode) const override; Expected - floatPower(Slice const& x, int32_t n, int32_t mode) override; + floatPower(Slice const& x, int32_t n, int32_t mode) const override; Expected - floatLog(Slice const& x, int32_t mode) override; + floatLog(Slice const& x, int32_t mode) const override; }; namespace wasm_float { diff --git a/src/xrpld/app/wasm/HostFuncWrapper.h b/include/xrpl/tx/wasm/HostFuncWrapper.h similarity index 78% rename from src/xrpld/app/wasm/HostFuncWrapper.h rename to include/xrpl/tx/wasm/HostFuncWrapper.h index 601e9372dd..444c3be97c 100644 --- a/src/xrpld/app/wasm/HostFuncWrapper.h +++ b/include/xrpl/tx/wasm/HostFuncWrapper.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { @@ -46,7 +46,10 @@ getTxNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* r using getCurrentLedgerObjNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* -getCurrentLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); +getCurrentLedgerObjNestedField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); using getLedgerObjNestedField_proto = int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* @@ -70,7 +73,10 @@ getTxNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t using getCurrentLedgerObjNestedArrayLen_proto = int32_t(uint8_t const*, int32_t); wasm_trap_t* -getCurrentLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); +getCurrentLedgerObjNestedArrayLen_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); using getLedgerObjNestedArrayLen_proto = int32_t(int32_t, uint8_t const*, int32_t); wasm_trap_t* @@ -80,7 +86,8 @@ using updateData_proto = int32_t(uint8_t const*, int32_t); wasm_trap_t* updateData_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using checkSignature_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t); +using checkSignature_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t); wasm_trap_t* checkSignature_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); @@ -92,24 +99,35 @@ using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* accountKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using ammKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using ammKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* ammKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using checkKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using checkKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* checkKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using credentialKeylet_proto = - int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using credentialKeylet_proto = int32_t( + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t*, + int32_t); wasm_trap_t* credentialKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using delegateKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using delegateKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* delegateKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using depositPreauthKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using depositPreauthKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* depositPreauthKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); @@ -117,41 +135,62 @@ using didKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* didKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using escrowKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using escrowKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* escrowKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using lineKeylet_proto = - int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using lineKeylet_proto = int32_t( + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t*, + int32_t); wasm_trap_t* lineKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using mptIssuanceKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using mptIssuanceKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* mptIssuanceKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using mptokenKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using mptokenKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* mptokenKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using nftOfferKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using nftOfferKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* nftOfferKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using offerKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using offerKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* offerKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using oracleKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using oracleKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* oracleKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using paychanKeylet_proto = - int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using paychanKeylet_proto = int32_t( + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t const*, + int32_t, + uint8_t*, + int32_t); wasm_trap_t* paychanKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using permissionedDomainKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using permissionedDomainKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* permissionedDomainKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); @@ -159,11 +198,13 @@ using signersKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* signersKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using ticketKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using ticketKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* ticketKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using vaultKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +using vaultKeylet_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); wasm_trap_t* vaultKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); @@ -227,19 +268,23 @@ using floatCompare_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int3 wasm_trap_t* floatCompare_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using floatAdd_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +using floatAdd_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); wasm_trap_t* floatAdd_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using floatSubtract_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +using floatSubtract_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); wasm_trap_t* floatSubtract_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using floatMultiply_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +using floatMultiply_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); wasm_trap_t* floatMultiply_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); -using floatDivide_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +using floatDivide_proto = + int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); wasm_trap_t* floatDivide_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); diff --git a/src/xrpld/app/wasm/ParamsHelper.h b/include/xrpl/tx/wasm/ParamsHelper.h similarity index 72% rename from src/xrpld/app/wasm/ParamsHelper.h rename to include/xrpl/tx/wasm/ParamsHelper.h index 90ecb4caa5..5490411226 100644 --- a/src/xrpld/app/wasm/ParamsHelper.h +++ b/include/xrpl/tx/wasm/ParamsHelper.h @@ -50,9 +50,11 @@ struct WasmImportFunc typedef std::pair WasmUserData; typedef std::vector ImportVec; -#define WASM_IMPORT_FUNC(v, f, ...) WasmImpFunc(v, #f, reinterpret_cast(&f##_wrap), ##__VA_ARGS__) +#define WASM_IMPORT_FUNC(v, f, ...) \ + WasmImpFunc(v, #f, reinterpret_cast(&f##_wrap), ##__VA_ARGS__) -#define WASM_IMPORT_FUNC2(v, f, n, ...) WasmImpFunc(v, n, reinterpret_cast(&f##_wrap), ##__VA_ARGS__) +#define WASM_IMPORT_FUNC2(v, f, n, ...) \ + WasmImpFunc(v, n, reinterpret_cast(&f##_wrap), ##__VA_ARGS__) template void @@ -87,7 +89,8 @@ WasmImpRet(WasmImportFunc& e) e.result = WT_I64; else if constexpr (std::is_void_v) e.result.reset(); -#if (defined(__GNUC__) && (__GNUC__ >= 14)) || ((defined(__clang_major__)) && (__clang_major__ >= 18)) +#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \ + ((defined(__clang_major__)) && (__clang_major__ >= 18)) else static_assert(false, "Unsupported return type"); #endif @@ -108,7 +111,12 @@ WasmImpFuncHelper(WasmImportFunc& e) template void -WasmImpFunc(ImportVec& v, std::string_view imp_name, void* f_wrap, void* data = nullptr, uint32_t gas = 0) +WasmImpFunc( + ImportVec& v, + std::string_view imp_name, + void* f_wrap, + void* data = nullptr, + uint32_t gas = 0) { WasmImportFunc e; e.name = imp_name; @@ -173,52 +181,6 @@ wasmParamsHlp(std::vector& v, std::int64_t p, Types&&... args) // wasmParamsHlp(v, std::forward(args)...); // } -template -inline void -wasmParamsHlp(std::vector& v, std::uint8_t const* dt, std::int32_t sz, Types&&... args) -{ - v.push_back({.type = WT_U8V, .of = {.u8v = {.d = dt, .sz = sz}}}); - wasmParamsHlp(v, std::forward(args)...); -} - -template -inline void -wasmParamsHlp(std::vector& v, Bytes const& p, Types&&... args) -{ - if (p.size() > std::numeric_limits::max()) - throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size())); // LCOV_EXCL_LINE - - wasmParamsHlp(v, p.data(), static_cast(p.size()), std::forward(args)...); -} - -template -inline void -wasmParamsHlp(std::vector& v, std::string_view const& p, Types&&... args) -{ - if (p.size() > std::numeric_limits::max()) - throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size())); - - wasmParamsHlp( - v, - reinterpret_cast(p.data()), - static_cast(p.size()), - std::forward(args)...); -} - -template -inline void -wasmParamsHlp(std::vector& v, std::string const& p, Types&&... args) -{ - if (p.size() > std::numeric_limits::max()) - throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size())); // LCOV_EXCL_LINE - - wasmParamsHlp( - v, - reinterpret_cast(p.c_str()), - static_cast(p.size()), - std::forward(args)...); -} - inline void wasmParamsHlp(std::vector& v) { diff --git a/src/xrpld/app/wasm/README.md b/include/xrpl/tx/wasm/README.md similarity index 100% rename from src/xrpld/app/wasm/README.md rename to include/xrpl/tx/wasm/README.md diff --git a/src/xrpld/app/wasm/WasmVM.h b/include/xrpl/tx/wasm/WasmVM.h similarity index 79% rename from src/xrpld/app/wasm/WasmVM.h rename to include/xrpl/tx/wasm/WasmVM.h index f14c475dc5..b872adfc95 100644 --- a/src/xrpld/app/wasm/WasmVM.h +++ b/include/xrpl/tx/wasm/WasmVM.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -24,7 +24,7 @@ class WasmiEngine; class WasmEngine { - std::unique_ptr const impl; + std::unique_ptr const impl_; WasmEngine(); @@ -43,20 +43,20 @@ public: Expected, TER> run(Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gasLimit, std::string_view funcName = {}, std::vector const& params = {}, - std::shared_ptr const& imports = {}, - std::shared_ptr const& hfs = {}, - int64_t gasLimit = -1, + ImportVec const& imports = {}, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); NotTEC check( Bytes const& wasmCode, + HostFunctions& hfs, std::string_view funcName, std::vector const& params = {}, - std::shared_ptr const& imports = {}, - std::shared_ptr const& hfs = {}, + ImportVec const& imports = {}, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); // Host functions helper functionality @@ -69,21 +69,21 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::shared_ptr +ImportVec createWasmImport(HostFunctions& hfs); Expected runEscrowWasm( Bytes const& wasmCode, - std::shared_ptr const& hfs, + HostFunctions& hfs, + int64_t gasLimit, std::string_view funcName = ESCROW_FUNCTION_NAME, - std::vector const& params = {}, - int64_t gasLimit = -1); + std::vector const& params = {}); NotTEC preflightEscrowWasm( Bytes const& wasmCode, - std::shared_ptr const& hfs, + HostFunctions& hfs, std::string_view funcName = ESCROW_FUNCTION_NAME, std::vector const& params = {}); diff --git a/src/xrpld/app/wasm/WasmiVM.h b/include/xrpl/tx/wasm/WasmiVM.h similarity index 77% rename from src/xrpld/app/wasm/WasmiVM.h rename to include/xrpl/tx/wasm/WasmiVM.h index d4ea0bfaa9..bd37b4e42e 100644 --- a/src/xrpld/app/wasm/WasmiVM.h +++ b/include/xrpl/tx/wasm/WasmiVM.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -60,13 +60,19 @@ struct WasmVec } }; -using WasmValtypeVec = WasmVec; +using WasmValtypeVec = + WasmVec; using WasmValVec = WasmVec; -using WasmExternVec = WasmVec; -using WasmExporttypeVec = - WasmVec; -using WasmImporttypeVec = - WasmVec; +using WasmExternVec = + WasmVec; +using WasmExporttypeVec = WasmVec< + wasm_exporttype_vec_t, + &wasm_exporttype_vec_new_uninitialized, + &wasm_exporttype_vec_delete>; +using WasmImporttypeVec = WasmVec< + wasm_importtype_vec_t, + &wasm_importtype_vec_new_uninitialized, + &wasm_importtype_vec_delete>; struct WasmiResult { @@ -100,7 +106,12 @@ struct InstanceWrapper private: static InstancePtr - init(StorePtr& s, ModulePtr& m, WasmExternVec& expt, WasmExternVec const& imports, beast::Journal j); + init( + StorePtr& s, + ModulePtr& m, + WasmExternVec& expt, + WasmExternVec const& imports, + beast::Journal j); public: InstanceWrapper(); @@ -125,7 +136,8 @@ public: std::int64_t getGas() const; - std::int64_t setGas(std::int64_t) const; + std::int64_t + setGas(std::int64_t) const; }; struct ModuleWrapper @@ -148,7 +160,7 @@ public: StorePtr& s, Bytes const& wasmBin, bool instantiate, - std::shared_ptr const& imports, + ImportVec const& imports, beast::Journal j); ~ModuleWrapper() = default; @@ -174,7 +186,7 @@ public: private: WasmExternVec - buildImports(StorePtr& s, std::shared_ptr const& imports); + buildImports(StorePtr& s, ImportVec const& imports); }; class WasmiEngine @@ -186,10 +198,6 @@ class WasmiEngine std::mutex m_; // 1 instance mutex - // to ensure lifetime during next executions with the same module - std::shared_ptr imports_; - std::shared_ptr hfs_; - public: WasmiEngine(); ~WasmiEngine() = default; @@ -199,24 +207,24 @@ public: Expected, TER> run(Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gas, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, - int64_t gas, + ImportVec const& imports, beast::Journal j); NotTEC check( Bytes const& wasmCode, + HostFunctions& hfs, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, + ImportVec const& imports, beast::Journal j); std::int64_t - getGas(); + getGas() const; // Host functions helper functionality wasm_trap_t* @@ -227,22 +235,30 @@ public: private: InstanceWrapper const& - getRT(int m = 0, int i = 0); + getRT(int m = 0, int i = 0) const; wmem getMem() const; - int32_t - allocate(int32_t size); - Expected, TER> - runHlp(Bytes const& wasmCode, std::string_view funcName, std::vector const& params, int64_t gas); + runHlp( + Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gas, + std::string_view funcName, + std::vector const& params, + ImportVec const& imports); NotTEC - checkHlp(Bytes const& wasmCode, std::string_view funcName, std::vector const& params); + checkHlp( + Bytes const& wasmCode, + HostFunctions& hfs, + std::string_view funcName, + std::vector const& params, + ImportVec const& imports); int - addModule(Bytes const& wasmCode, bool instantiate, int64_t gas); + addModule(Bytes const& wasmCode, bool instantiate, ImportVec const& imports, int64_t gas); void clearModules(); @@ -255,7 +271,7 @@ private: makeModule(Bytes const& wasmCode, WasmExternVec const& imports = {}); FuncInfo - getFunc(std::string_view funcName); + getFunc(std::string_view funcName) const; std::vector convertParams(std::vector const& params); @@ -290,7 +306,12 @@ private: template inline WasmiResult - call(FuncInfo const& f, std::vector& in, uint8_t const* d, int32_t sz, Types&&... args); + call( + FuncInfo const& f, + std::vector& in, + uint8_t const* d, + int32_t sz, + Types&&... args); template inline WasmiResult diff --git a/nix/devshell.nix b/nix/devshell.nix new file mode 100644 index 0000000000..1d907f4d87 --- /dev/null +++ b/nix/devshell.nix @@ -0,0 +1,140 @@ +{ pkgs, ... }: +let + commonPackages = with pkgs; [ + ccache + cmake + conan + gcovr + git + gnumake + llvmPackages_21.clang-tools + ninja + perl # needed for openssl + pkg-config + pre-commit + python314 + ]; + + # Supported compiler versions + gccVersion = pkgs.lib.range 13 15; + clangVersions = pkgs.lib.range 18 21; + + defaultCompiler = if pkgs.stdenv.isDarwin then "apple-clang" else "gcc"; + defaultGccVersion = pkgs.lib.last gccVersion; + defaultClangVersion = pkgs.lib.last clangVersions; + + strToCompilerEnv = + compiler: version: + ( + if compiler == "gcc" then + let + gccPkg = pkgs."gcc${toString version}Stdenv" or null; + in + if gccPkg != null && builtins.elem version gccVersion then + gccPkg + else + throw "Invalid GCC version: ${toString version}. Must be one of: ${toString gccVersion}" + else if compiler == "clang" then + let + clangPkg = pkgs."llvmPackages_${toString version}".stdenv or null; + in + if clangPkg != null && builtins.elem version clangVersions then + clangPkg + else + throw "Invalid Clang version: ${toString version}. Must be one of: ${toString clangVersions}" + else if compiler == "apple-clang" || compiler == "none" then + pkgs.stdenvNoCC + else + throw "Invalid compiler: ${compiler}. Must be one of: gcc, clang, apple-clang, none" + ); + + # Helper function to create a shell with a specific compiler + makeShell = + { + compiler ? defaultCompiler, + version ? ( + if compiler == "gcc" then + defaultGccVersion + else if compiler == "clang" then + defaultClangVersion + else + null + ), + }: + let + compilerStdEnv = strToCompilerEnv compiler version; + + compilerName = + if compiler == "apple-clang" then + "clang" + else if compiler == "none" then + null + else + compiler; + + gccOnMacWarning = + if pkgs.stdenv.isDarwin && compiler == "gcc" then + '' + echo "WARNING: Using GCC on macOS with Conan may not work." + echo " Consider using 'nix develop .#clang' or the default shell instead." + echo "" + '' + else + ""; + + compilerVersion = + if compilerName != null then + '' + echo "Compiler: " + ${compilerName} --version + '' + else + '' + echo "No compiler specified - using system compiler" + ''; + + shellAttrs = { + packages = commonPackages; + + shellHook = '' + echo "Welcome to xrpld development shell"; + ${gccOnMacWarning}${compilerVersion} + ''; + }; + in + pkgs.mkShell.override { stdenv = compilerStdEnv; } shellAttrs; + + # Generate shells for each compiler version + gccShells = builtins.listToAttrs ( + map (version: { + name = "gcc${toString version}"; + value = makeShell { + compiler = "gcc"; + version = version; + }; + }) gccVersion + ); + + clangShells = builtins.listToAttrs ( + map (version: { + name = "clang${toString version}"; + value = makeShell { + compiler = "clang"; + version = version; + }; + }) clangVersions + ); + +in +gccShells +// clangShells +// { + # Default shells + default = makeShell { }; + gcc = makeShell { compiler = "gcc"; }; + clang = makeShell { compiler = "clang"; }; + + # No compiler + no-compiler = makeShell { compiler = "none"; }; + apple-clang = makeShell { compiler = "apple-clang"; }; +} diff --git a/nix/utils.nix b/nix/utils.nix new file mode 100644 index 0000000000..821d60a6f6 --- /dev/null +++ b/nix/utils.nix @@ -0,0 +1,19 @@ +{ nixpkgs }: +{ + forEachSystem = + function: + nixpkgs.lib.genAttrs + [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ] + ( + system: + function { + inherit system; + pkgs = import nixpkgs { inherit system; }; + } + ); +} diff --git a/sanitizers/suppressions/asan.supp b/sanitizers/suppressions/asan.supp index 7ba98766bd..e02d580aa6 100644 --- a/sanitizers/suppressions/asan.supp +++ b/sanitizers/suppressions/asan.supp @@ -1,24 +1,6 @@ # The idea is to empty this file gradually by fixing the underlying issues and removing suppressions. # # ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0" -# -# The detect_container_overflow=0 option disables false positives from: -# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h) -# - Boost context/coroutine stack switching (Workers.cpp, thread.h) -# -# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow - -# Boost -interceptor_name:boost/asio - -# Leaks in Doctest tests: xrpl.test.* -interceptor_name:src/libxrpl/net/HTTPClient.cpp -interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp -interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp -interceptor_name:xrpl/net/AutoSocket.h -interceptor_name:xrpl/net/HTTPClient.h -interceptor_name:xrpl/net/HTTPClientSSLContext.h -interceptor_name:xrpl/net/RegisterSSLCerts.h # Suppress false positive stack-buffer errors in thread stack allocation # Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189) diff --git a/sanitizers/suppressions/lsan.supp b/sanitizers/suppressions/lsan.supp index a81d7d89fa..30d01a2bfa 100644 --- a/sanitizers/suppressions/lsan.supp +++ b/sanitizers/suppressions/lsan.supp @@ -1,16 +1,5 @@ # The idea is to empty this file gradually by fixing the underlying issues and removing suppresions. -# Suppress leaks detected by asan in rippled code. -leak:src/libxrpl/net/HTTPClient.cpp -leak:src/libxrpl/net/RegisterSSLCerts.cpp -leak:src/tests/libxrpl/net/HTTPClient.cpp -leak:xrpl/net/AutoSocket.h -leak:xrpl/net/HTTPClient.h -leak:xrpl/net/HTTPClientSSLContext.h -leak:xrpl/net/RegisterSSLCerts.h -leak:ripple::HTTPClient -leak:ripple::HTTPClientImp - # Suppress leaks detected by asan in boost code. leak:boost::asio leak:boost/asio diff --git a/sanitizers/suppressions/runtime-asan-options.txt b/sanitizers/suppressions/runtime-asan-options.txt new file mode 100644 index 0000000000..fb2727f868 --- /dev/null +++ b/sanitizers/suppressions/runtime-asan-options.txt @@ -0,0 +1,8 @@ +detect_container_overflow=false +detect_stack_use_after_return=false +debug=true +halt_on_error=false +print_stats=true +print_cmdline=true +use_sigaltstack=0 +print_stacktrace=1 diff --git a/sanitizers/suppressions/runtime-lsan-options.txt b/sanitizers/suppressions/runtime-lsan-options.txt new file mode 100644 index 0000000000..fcfccf7bae --- /dev/null +++ b/sanitizers/suppressions/runtime-lsan-options.txt @@ -0,0 +1 @@ +halt_on_error=false diff --git a/sanitizers/suppressions/runtime-tsan-options.txt b/sanitizers/suppressions/runtime-tsan-options.txt new file mode 100644 index 0000000000..aafe997ea4 --- /dev/null +++ b/sanitizers/suppressions/runtime-tsan-options.txt @@ -0,0 +1,3 @@ +halt_on_error=false +verbosity=1 +second_deadlock_stack=1 diff --git a/sanitizers/suppressions/runtime-ubsan-options.txt b/sanitizers/suppressions/runtime-ubsan-options.txt new file mode 100644 index 0000000000..fcfccf7bae --- /dev/null +++ b/sanitizers/suppressions/runtime-ubsan-options.txt @@ -0,0 +1 @@ +halt_on_error=false diff --git a/sanitizers/suppressions/sanitizer-ignorelist.txt b/sanitizers/suppressions/sanitizer-ignorelist.txt index 5dbead48a2..dc9a31f8e4 100644 --- a/sanitizers/suppressions/sanitizer-ignorelist.txt +++ b/sanitizers/suppressions/sanitizer-ignorelist.txt @@ -27,3 +27,11 @@ src:core/JobQueue.cpp src:libxrpl/beast/utility/beast_Journal.cpp src:test/beast/beast_PropertyStream_test.cpp src:src/test/app/Invariants_test.cpp + +# ASan false positive: stack-use-after-scope in ErrorCodes.h inline functions. +# When Clang inlines the StaticString overloads (e.g. invalid_field_error(StaticString)), +# ASan scope-poisons the temporary std::string before the inlined callee finishes reading +# through the const ref. This corrupts the coroutine stack and crashes the Simulate test. +# See asan.supp comments for full explanation and planned fix. +[address] +src:*ErrorCodes.h diff --git a/sanitizers/suppressions/ubsan.supp b/sanitizers/suppressions/ubsan.supp index 1504ef685f..a02cbb17de 100644 --- a/sanitizers/suppressions/ubsan.supp +++ b/sanitizers/suppressions/ubsan.supp @@ -104,7 +104,7 @@ undefined:src/xrpld/app/misc/NetworkOPs.cpp undefined:src/libxrpl/json/json_value.cpp undefined:src/xrpld/app/paths/detail/StrandFlow.h undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp -undefined:src/xrpld/app/tx/detail/SetOracle.cpp +undefined:src/xrpld/app/tx/detail/OracleSet.cpp undefined:src/xrpld/core/detail/JobQueue.cpp undefined:src/xrpld/core/detail/Workers.cpp undefined:src/xrpld/rpc/detail/Role.cpp @@ -154,7 +154,7 @@ unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp -unsigned-integer-overflow:src/xrpld/app/tx/detail/SetOracle.cpp +unsigned-integer-overflow:src/xrpld/app/tx/detail/OracleSet.cpp unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp unsigned-integer-overflow:xrpl/basics/base_uint.h @@ -182,6 +182,17 @@ signed-integer-overflow:src/test/beast/LexicalCast_test.cpp # External library suppressions unsigned-integer-overflow:nudb/detail/xxhash.hpp +# Loan_test.cpp intentional underflow in test arithmetic +unsigned-integer-overflow:src/test/app/Loan_test.cpp +undefined:src/test/app/Loan_test.cpp + +# Source tree restructured paths (libxrpl/tx/transactors/) +# These duplicate the xrpld/app/tx/detail entries above for the new layout +unsigned-integer-overflow:src/libxrpl/tx/transactors/oracle/OracleSet.cpp +undefined:src/libxrpl/tx/transactors/oracle/OracleSet.cpp +unsigned-integer-overflow:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +undefined:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp + # Protobuf intentional overflows in hash functions # Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393) unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h diff --git a/scripts/generate_ledger_classes.py b/scripts/generate_ledger_classes.py new file mode 100644 index 0000000000..ad773ab9af --- /dev/null +++ b/scripts/generate_ledger_classes.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +""" +Generate C++ wrapper classes for XRP Ledger entry types from ledger_entries.macro. + +This script parses the ledger_entries.macro file and generates type-safe wrapper +classes for each ledger entry type, similar to the transaction wrapper classes. + +Uses pcpp to preprocess the macro file and pyparsing to parse the DSL. +""" + +import io +import argparse +from pathlib import Path +import pyparsing as pp + +# Import common utilities +from macro_parser_common import ( + CppCleaner, + parse_sfields_macro, + parse_field_list, + generate_cpp_class, + generate_from_template, + clear_output_directory, +) + + +def create_ledger_entry_parser(): + """Create a pyparsing parser for LEDGER_ENTRY macros. + + This parser extracts the full LEDGER_ENTRY macro call and parses its arguments + using pyparsing's nesting-aware delimited list parsing. + """ + # Match the exact words + ledger_entry = pp.Keyword("LEDGER_ENTRY") | pp.Keyword("LEDGER_ENTRY_DUPLICATE") + + # Define nested structures so pyparsing protects them + nested_braces = pp.original_text_for(pp.nested_expr("{", "}")) + nested_parens = pp.original_text_for(pp.nested_expr("(", ")")) + + # Define standard text (anything that isn't a comma, parens, or braces) + plain_text = pp.Word(pp.printables + " \t\n", exclude_chars=",{}()") + + # A single argument is any combination of the above + single_arg = pp.Combine(pp.OneOrMore(nested_braces | nested_parens | plain_text)) + single_arg.set_parse_action(lambda t: t[0].strip()) + + # The arguments are a delimited list + args_list = pp.DelimitedList(single_arg) + + # The full macro: LEDGER_ENTRY(args) or LEDGER_ENTRY_DUPLICATE(args) + macro_parser = ( + ledger_entry + pp.Suppress("(") + pp.Group(args_list)("args") + pp.Suppress(")") + ) + + return macro_parser + + +def parse_ledger_entry_args(args_list): + """Parse the arguments of a LEDGER_ENTRY macro call. + + Args: + args_list: A list of parsed arguments from pyparsing, e.g., + ['ltACCOUNT_ROOT', '0x0061', 'AccountRoot', 'account', '({...})'] + + Returns: + A dict with parsed ledger entry information. + """ + if len(args_list) < 5: + raise ValueError( + f"Expected at least 5 parts in LEDGER_ENTRY, got {len(args_list)}: {args_list}" + ) + + tag = args_list[0] + value = args_list[1] + name = args_list[2] + rpc_name = args_list[3] + fields_str = args_list[-1] + + # Parse fields: ({field1, field2, ...}) + fields = parse_field_list(fields_str) + + return { + "tag": tag, + "value": value, + "name": name, + "rpc_name": rpc_name, + "fields": fields, + } + + +def parse_macro_file(file_path): + """Parse the ledger_entries.macro file and return a list of ledger entry definitions. + + Uses pcpp to preprocess the file and pyparsing to parse the LEDGER_ENTRY macros. + """ + with open(file_path, "r") as f: + c_code = f.read() + + # Step 1: Clean the C++ code using pcpp + cleaner = CppCleaner("LEDGER_ENTRY_INCLUDE", "LEDGER_ENTRY") + cleaner.parse(c_code) + + out = io.StringIO() + cleaner.write(out) + clean_text = out.getvalue() + + # Step 2: Parse the clean text using pyparsing + parser = create_ledger_entry_parser() + entries = [] + + for match, _, _ in parser.scan_string(clean_text): + # Extract the macro name and arguments + raw_args = match.args + + # Parse the arguments + entry_data = parse_ledger_entry_args(raw_args) + entries.append(entry_data) + + return entries + + +def main(): + parser = argparse.ArgumentParser( + description="Generate C++ ledger entry classes from ledger_entries.macro" + ) + parser.add_argument("macro_path", help="Path to ledger_entries.macro") + parser.add_argument( + "--header-dir", + help="Output directory for header files", + default="include/xrpl/protocol_autogen/ledger_entries", + ) + parser.add_argument( + "--test-dir", + help="Output directory for test files (optional)", + default=None, + ) + parser.add_argument( + "--sfields-macro", + help="Path to sfields.macro (default: auto-detect from macro_path)", + ) + parser.add_argument( + "--list-outputs", + action="store_true", + help="List output files without generating (one per line)", + ) + + args = parser.parse_args() + + # Parse the macro file to get ledger entry names + entries = parse_macro_file(args.macro_path) + + # If --list-outputs, just print the output file paths and exit + if args.list_outputs: + header_dir = Path(args.header_dir) + for entry in entries: + print(header_dir / f"{entry['name']}.h") + if args.test_dir: + test_dir = Path(args.test_dir) + for entry in entries: + print(test_dir / f"{entry['name']}Tests.cpp") + return + + # Auto-detect sfields.macro path if not provided + if args.sfields_macro: + sfields_path = Path(args.sfields_macro) + else: + # Assume sfields.macro is in the same directory as ledger_entries.macro + macro_path = Path(args.macro_path) + sfields_path = macro_path.parent / "sfields.macro" + + # Parse sfields.macro to get field type information + print(f"Parsing {sfields_path}...") + field_types = parse_sfields_macro(sfields_path) + print( + f"Found {len(field_types)} field definitions ({sum(1 for f in field_types.values() if f['typed'])} typed, {sum(1 for f in field_types.values() if not f['typed'])} untyped)\n" + ) + + print(f"Found {len(entries)} ledger entries\n") + + for entry in entries: + print(f"Ledger Entry: {entry['name']}") + print(f" Tag: {entry['tag']}") + print(f" Value: {entry['value']}") + print(f" RPC Name: {entry['rpc_name']}") + print(f" Fields: {len(entry['fields'])}") + for field in entry["fields"]: + mpt_info = f" ({field['mpt_support']})" if "mpt_support" in field else "" + print(f" - {field['name']}: {field['requirement']}{mpt_info}") + print() + + # Set up template directory + script_dir = Path(__file__).parent + template_dir = script_dir / "templates" + + # Generate C++ classes + header_dir = Path(args.header_dir) + header_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated files before regenerating + clear_output_directory(header_dir) + + for entry in entries: + generate_cpp_class( + entry, header_dir, template_dir, field_types, "LedgerEntry.h.mako" + ) + + print(f"\nGenerated {len(entries)} ledger entry classes") + + # Generate unit tests if --test-dir is provided + if args.test_dir: + test_dir = Path(args.test_dir) + test_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated test files before regenerating + clear_output_directory(test_dir) + + for entry in entries: + # Fields are already enriched from generate_cpp_class above + generate_from_template( + entry, test_dir, template_dir, "LedgerEntryTests.cpp.mako", "Tests.cpp" + ) + + print(f"\nGenerated {len(entries)} ledger entry test files") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_tx_classes.py b/scripts/generate_tx_classes.py new file mode 100644 index 0000000000..f21b7101df --- /dev/null +++ b/scripts/generate_tx_classes.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +""" +Parse transactions.macro file to extract transaction information +and generate C++ classes for each transaction type. + +Uses pcpp to preprocess the macro file and pyparsing to parse the DSL. +""" + +import io +import argparse +from pathlib import Path +import pyparsing as pp + +# Import common utilities +from macro_parser_common import ( + CppCleaner, + parse_sfields_macro, + parse_field_list, + generate_cpp_class, + generate_from_template, + clear_output_directory, +) + + +def create_transaction_parser(): + """Create a pyparsing parser for TRANSACTION macros. + + This parser extracts the full TRANSACTION macro call and parses its arguments + using pyparsing's nesting-aware delimited list parsing. + """ + # Define nested structures so pyparsing protects them + nested_braces = pp.original_text_for(pp.nested_expr("{", "}")) + nested_parens = pp.original_text_for(pp.nested_expr("(", ")")) + + # Define standard text (anything that isn't a comma, parens, or braces) + plain_text = pp.Word(pp.printables + " \t\n", exclude_chars=",{}()") + + # A single argument is any combination of the above + single_arg = pp.Combine(pp.OneOrMore(nested_braces | nested_parens | plain_text)) + single_arg.set_parse_action(lambda t: t[0].strip()) + + # The arguments are a delimited list + args_list = pp.DelimitedList(single_arg) + + # The full macro: TRANSACTION(args) + macro_parser = ( + pp.Keyword("TRANSACTION") + + pp.Suppress("(") + + pp.Group(args_list)("args") + + pp.Suppress(")") + ) + + return macro_parser + + +def parse_transaction_args(args_list): + """Parse the arguments of a TRANSACTION macro call. + + Args: + args_list: A list of parsed arguments from pyparsing, e.g., + ['ttPAYMENT', '0', 'Payment', 'Delegation::delegable', + 'uint256{}', 'createAcct', '({...})'] + + Returns: + A dict with parsed transaction information. + """ + if len(args_list) < 7: + raise ValueError( + f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}" + ) + + tag = args_list[0] + value = args_list[1] + name = args_list[2] + delegable = args_list[3] + amendments = args_list[4] + privileges = args_list[5] + fields_str = args_list[-1] + + # Parse fields: ({field1, field2, ...}) + fields = parse_field_list(fields_str) + + return { + "tag": tag, + "value": value, + "name": name, + "delegable": delegable, + "amendments": amendments, + "privileges": privileges, + "fields": fields, + } + + +def parse_macro_file(filepath): + """Parse the transactions.macro file. + + Uses pcpp to preprocess the file and pyparsing to parse the TRANSACTION macros. + """ + with open(filepath, "r") as f: + c_code = f.read() + + # Step 1: Clean the C++ code using pcpp + cleaner = CppCleaner("TRANSACTION_INCLUDE", "TRANSACTION") + cleaner.parse(c_code) + + out = io.StringIO() + cleaner.write(out) + clean_text = out.getvalue() + + # Step 2: Parse the clean text using pyparsing + parser = create_transaction_parser() + transactions = [] + + for match, _, _ in parser.scan_string(clean_text): + # Extract the macro name and arguments + raw_args = match.args + + # Parse the arguments + tx_data = parse_transaction_args(raw_args) + transactions.append(tx_data) + + return transactions + + +# TransactionBase is a static file in the repository at: +# - include/xrpl/protocol/TransactionBase.h +# - src/libxrpl/protocol/TransactionBase.cpp +# It is NOT generated by this script. + + +def main(): + parser = argparse.ArgumentParser( + description="Generate C++ transaction classes from transactions.macro" + ) + parser.add_argument("macro_path", help="Path to transactions.macro") + parser.add_argument( + "--header-dir", + help="Output directory for header files", + default="include/xrpl/protocol_autogen/transactions", + ) + parser.add_argument( + "--test-dir", + help="Output directory for test files (optional)", + default=None, + ) + parser.add_argument( + "--sfields-macro", + help="Path to sfields.macro (default: auto-detect from macro_path)", + ) + parser.add_argument( + "--list-outputs", + action="store_true", + help="List output files without generating (one per line)", + ) + + args = parser.parse_args() + + # Parse the macro file to get transaction names + transactions = parse_macro_file(args.macro_path) + + # If --list-outputs, just print the output file paths and exit + if args.list_outputs: + header_dir = Path(args.header_dir) + for tx in transactions: + print(header_dir / f"{tx['name']}.h") + if args.test_dir: + test_dir = Path(args.test_dir) + for tx in transactions: + print(test_dir / f"{tx['name']}Tests.cpp") + return + + # Auto-detect sfields.macro path if not provided + if args.sfields_macro: + sfields_path = Path(args.sfields_macro) + else: + # Assume sfields.macro is in the same directory as transactions.macro + macro_path = Path(args.macro_path) + sfields_path = macro_path.parent / "sfields.macro" + + # Parse sfields.macro to get field type information + print(f"Parsing {sfields_path}...") + field_types = parse_sfields_macro(sfields_path) + print( + f"Found {len(field_types)} field definitions ({sum(1 for f in field_types.values() if f['typed'])} typed, {sum(1 for f in field_types.values() if not f['typed'])} untyped)\n" + ) + + print(f"Found {len(transactions)} transactions\n") + + for tx in transactions: + print(f"Transaction: {tx['name']}") + print(f" Tag: {tx['tag']}") + print(f" Value: {tx['value']}") + print(f" Fields: {len(tx['fields'])}") + for field in tx["fields"]: + print(f" - {field['name']}: {field['requirement']}") + print() + + # Set up output directory + header_dir = Path(args.header_dir) + header_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated files before regenerating + clear_output_directory(header_dir) + + print(f"\nGenerating header-only template classes...") + print(f" Headers: {header_dir}\n") + + # Set up template directory + script_dir = Path(__file__).parent + template_dir = script_dir / "templates" + + generated_files = [] + for tx_info in transactions: + header_path = generate_cpp_class( + tx_info, header_dir, template_dir, field_types, "Transaction.h.mako" + ) + generated_files.append(header_path) + print(f" Generated: {tx_info['name']}.h") + + print( + f"\nGenerated {len(transactions)} transaction classes ({len(generated_files)} header files)" + ) + print(f" Headers: {header_dir.absolute()}") + + # Generate unit tests if --test-dir is provided + if args.test_dir: + test_dir = Path(args.test_dir) + test_dir.mkdir(parents=True, exist_ok=True) + + # Clear existing generated test files before regenerating + clear_output_directory(test_dir) + + for tx_info in transactions: + # Fields are already enriched from generate_cpp_class above + generate_from_template( + tx_info, + test_dir, + template_dir, + "TransactionTests.cpp.mako", + "Tests.cpp", + ) + + print(f"\nGenerated {len(transactions)} transaction test files") + + +if __name__ == "__main__": + main() diff --git a/scripts/macro_parser_common.py b/scripts/macro_parser_common.py new file mode 100644 index 0000000000..eae4df39db --- /dev/null +++ b/scripts/macro_parser_common.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +""" +Common utilities for parsing XRP Ledger macro files. + +This module provides shared functionality for parsing transactions.macro +and ledger_entries.macro files using pcpp and pyparsing. +""" + +import re +import shutil +from pathlib import Path +import pyparsing as pp +from pcpp import Preprocessor + + +def clear_output_directory(directory): + """Clear all generated files from an output directory. + + Removes all .h and .cpp files from the directory, but preserves + the directory itself and any subdirectories. + + Args: + directory: Path to the directory to clear + """ + dir_path = Path(directory) + if not dir_path.exists(): + return + + # Remove generated files (headers and source files) + for pattern in ["*.h", "*.cpp"]: + for file_path in dir_path.glob(pattern): + file_path.unlink() + + print(f"Cleared output directory: {dir_path}") + + +class CppCleaner(Preprocessor): + """C preprocessor that removes C++ noise while preserving macro calls.""" + + def __init__(self, macro_include_name, macro_name): + """ + Initialize the preprocessor. + + Args: + macro_include_name: The name of the include flag to set to 0 + (e.g., "TRANSACTION_INCLUDE" or "LEDGER_ENTRY_INCLUDE") + macro_name: The name of the macro to define so #if !defined() checks pass + (e.g., "TRANSACTION" or "LEDGER_ENTRY") + """ + super(CppCleaner, self).__init__() + # Define flags so #if blocks evaluate correctly + # We set the include flag to 0 so includes are skipped + self.define(f"{macro_include_name} 0") + # Define the macro so #if !defined(MACRO) / #error checks pass + # We define it to expand to itself so the macro calls remain in the output + # for pyparsing to find and parse + self.define(f"{macro_name}(...) {macro_name}(__VA_ARGS__)") + # Suppress line directives + self.line_directive = None + + def on_error(self, file, line, msg): + # Ignore #error directives + pass + + def on_include_not_found( + self, is_malformed, is_system_include, curdir, includepath + ): + # Ignore missing headers + pass + + +def parse_sfields_macro(sfields_path): + """ + Parse sfields.macro to determine which fields are typed vs untyped. + + Returns a dict mapping field names to their type information: + { + 'sfMemos': {'typed': False, 'stiSuffix': 'ARRAY', 'typeData': {...}}, + 'sfAmount': {'typed': True, 'stiSuffix': 'AMOUNT', 'typeData': {...}}, + ... + } + """ + # Mapping from STI suffix to C++ type for untyped fields + UNTYPED_TYPE_MAP = { + "ARRAY": { + "getter_method": "getFieldArray", + "setter_method": "setFieldArray", + "setter_use_brackets": False, + "setter_type": "STArray const&", + "return_type": "STArray const&", + "return_type_optional": "std::optional>", + }, + "OBJECT": { + "getter_method": "getFieldObject", + "setter_method": "setFieldObject", + "setter_use_brackets": False, + "setter_type": "STObject const&", + "return_type": "STObject", + "return_type_optional": "std::optional", + }, + "PATHSET": { + "getter_method": "getFieldPathSet", + "setter_method": "setFieldPathSet", + "setter_use_brackets": False, + "setter_type": "STPathSet const&", + "return_type": "STPathSet const&", + "return_type_optional": "std::optional>", + }, + } + + field_info = {} + + with open(sfields_path, "r") as f: + content = f.read() + + # Parse TYPED_SFIELD entries + # Format: TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) + typed_pattern = r"TYPED_SFIELD\s*\(\s*(\w+)\s*,\s*(\w+)\s*," + for match in re.finditer(typed_pattern, content): + field_name = match.group(1) + sti_suffix = match.group(2) + field_info[field_name] = { + "typed": True, + "stiSuffix": sti_suffix, + "typeData": { + "getter_method": "at", + "setter_method": "", + "setter_use_brackets": True, + "setter_type": f"std::decay_t const&", + "return_type": f"SF_{sti_suffix}::type::value_type", + "return_type_optional": f"protocol_autogen::Optional", + }, + } + + # Parse UNTYPED_SFIELD entries + # Format: UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) + untyped_pattern = r"UNTYPED_SFIELD\s*\(\s*(\w+)\s*,\s*(\w+)\s*," + for match in re.finditer(untyped_pattern, content): + field_name = match.group(1) + sti_suffix = match.group(2) + type_data = UNTYPED_TYPE_MAP.get( + sti_suffix, UNTYPED_TYPE_MAP.get("OBJECT") + ) # Default to OBJECT + field_info[field_name] = { + "typed": False, + "stiSuffix": sti_suffix, + "typeData": type_data, + } + + return field_info + + +def create_field_list_parser(): + """Create a pyparsing parser for field lists like '({...})'.""" + # A field identifier (e.g., sfDestination, soeREQUIRED, soeMPTSupported) + field_identifier = pp.Word(pp.alphas + "_", pp.alphanums + "_") + + # A single field definition: {sfName, soeREQUIRED, ...} + # Allow optional trailing comma inside the braces + field_def = ( + pp.Suppress("{") + + pp.Group(pp.DelimitedList(field_identifier) + pp.Optional(pp.Suppress(",")))( + "parts" + ) + + pp.Suppress("}") + ) + + # The field list: ({field1, field2, ...}) or ({}) for empty lists + # Allow optional trailing comma after the last field definition + field_list = ( + pp.Suppress("(") + + pp.Suppress("{") + + pp.Group( + pp.Optional(pp.DelimitedList(field_def) + pp.Optional(pp.Suppress(","))) + )("fields") + + pp.Suppress("}") + + pp.Suppress(")") + ) + + return field_list + + +def parse_field_list(fields_str): + """Parse a field list string like '({...})' using pyparsing. + + Args: + fields_str: A string like '({ + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported} + })' + + Returns: + A list of field dicts with 'name', 'requirement', 'flags', and 'supports_mpt'. + """ + parser = create_field_list_parser() + + try: + result = parser.parse_string(fields_str, parse_all=True) + fields = [] + + for field_parts in result.fields: + if len(field_parts) < 2: + continue + + field_name = field_parts[0] + requirement = field_parts[1] + flags = list(field_parts[2:]) if len(field_parts) > 2 else [] + supports_mpt = "soeMPTSupported" in flags + + fields.append( + { + "name": field_name, + "requirement": requirement, + "flags": flags, + "supports_mpt": supports_mpt, + } + ) + + return fields + except pp.ParseException as e: + raise ValueError(f"Failed to parse field list: {e}") + + +def enrich_fields_with_type_data(entry_info, field_types): + """Enrich field information with type data from sfields.macro. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + field_types: Dict mapping field names to type information + + Modifies entry_info["fields"] in place. + """ + for field in entry_info["fields"]: + field_name = field["name"] + if field_name in field_types: + field["typed"] = field_types[field_name]["typed"] + field["paramName"] = field_name[2].lower() + field_name[3:] + field["stiSuffix"] = field_types[field_name]["stiSuffix"] + field["typeData"] = field_types[field_name]["typeData"] + else: + # Unknown field - assume typed for safety + field["typed"] = True + field["paramName"] = "" + field["stiSuffix"] = None + field["typeData"] = None + + +def generate_from_template( + entry_info, output_dir, template_dir, template_name, output_suffix +): + """Generate a file from a Mako template. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + Fields should already be enriched with type data. + output_dir: Output directory for generated files + template_dir: Directory containing Mako templates + template_name: Name of the Mako template file to use + output_suffix: Suffix for the output file (e.g., ".h" or "Tests.cpp") + + Returns: + Path to the generated file + """ + from mako.template import Template + + template_path = Path(template_dir) / template_name + template = Template(filename=str(template_path)) + + # Render the template - pass entry_info directly so templates can access any field + content = template.render(**entry_info) + + # Write output file in binary mode to avoid any line ending conversion + output_path = Path(output_dir) / f"{entry_info['name']}{output_suffix}" + with open(output_path, "wb") as f: + f.write(content.encode("utf-8")) + + print(f"Generated {output_path}") + return output_path + + +def generate_cpp_class( + entry_info, header_dir, template_dir, field_types, template_name +): + """Generate C++ header file from a Mako template. + + Args: + entry_info: Dict containing entry information (name, fields, etc.) + header_dir: Output directory for generated header files + template_dir: Directory containing Mako templates + field_types: Dict mapping field names to type information + template_name: Name of the Mako template file to use + """ + # Enrich field information with type data + enrich_fields_with_type_data(entry_info, field_types) + + # Generate the header file + generate_from_template(entry_info, header_dir, template_dir, template_name, ".h") diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000000..40b472078d --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,13 @@ +# Python dependencies for XRP Ledger code generation scripts +# +# These packages are required to run the code generation scripts that +# parse macro files and generate C++ wrapper classes. + +# C preprocessor for Python - used to preprocess macro files +pcpp>=1.30 + +# Parser combinator library - used to parse the macro DSL +pyparsing>=3.0.0 + +# Template engine - used to generate C++ code from templates +Mako>=1.2.0 diff --git a/scripts/templates/LedgerEntry.h.mako b/scripts/templates/LedgerEntry.h.mako new file mode 100644 index 0000000000..fdb55a973a --- /dev/null +++ b/scripts/templates/LedgerEntry.h.mako @@ -0,0 +1,216 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class ${name}Builder; + +/** + * @brief Ledger Entry: ${name} + * + * Type: ${tag} (${value}) + * RPC Name: ${rpc_name} + * + * Immutable wrapper around SLE providing type-safe field access. + * Use ${name}Builder to construct new ledger entries. + */ +class ${name} : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ${tag}; + + /** + * @brief Construct a ${name} ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit ${name}(std::shared_ptr sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for ${name}"); + } + } + + // Ledger entry-specific field getters +% for field in fields: +% if field['typed']: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (has${field['name'][2:]}()) + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->sle_->isFieldPresent(${field['name']}); + } +% endif +% else: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif + * @note This is an untyped field (${field.get('cppType', 'unknown')}). +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (this->sle_->isFieldPresent(${field['name']})) + return this->sle_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->sle_->isFieldPresent(${field['name']}); + } +% endif +% endif +% endfor +}; + +<% + required_fields = [f for f in fields if f['requirement'] == 'soeREQUIRED'] +%>\ +/** + * @brief Builder for ${name} ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses Json::Value internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class ${name}Builder : public LedgerEntryBuilderBase<${name}Builder> +{ +public: + /** + * @brief Construct a new ${name}Builder with required fields. +% for field in required_fields: + * @param ${field['paramName']} The ${field['name']} field value. +% endfor + */ + ${name}Builder(\ +% for i, field in enumerate(required_fields): +${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(required_fields) - 1 else ''}\ +% endfor +) + : LedgerEntryBuilderBase<${name}Builder>(${tag}) + { +% for field in required_fields: + set${field['name'][2:]}(${field['paramName']}); +% endfor + } + + /** + * @brief Construct a ${name}Builder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + ${name}Builder(std::shared_ptr sle) + { + if (sle->at(sfLedgerEntryType) != ${tag}) + { + throw std::runtime_error("Invalid ledger entry type for ${name}"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ +% for field in fields: + + /** + * @brief Set ${field['name']} (${field['requirement']}) +% if field.get('mpt_support'): + * MPT Support: ${field['mpt_support']} +% endif + * @return Reference to this builder for method chaining. + */ + ${name}Builder& + set${field['name'][2:]}(${field['typeData']['setter_type']} value) + { +% if field.get('stiSuffix') == 'ISSUE': + object_[${field['name']}] = STIssue(${field['name']}, value); +% elif field['typeData'].get('setter_use_brackets'): + object_[${field['name']}] = value; +% else: + object_.${field['typeData']['setter_method']}(${field['name']}, value); +% endif + return *this; + } +% endfor + + /** + * @brief Build and return the completed ${name} wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + ${name} + build(uint256 const& index) + { + return ${name}{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/scripts/templates/LedgerEntryTests.cpp.mako b/scripts/templates/LedgerEntryTests.cpp.mako new file mode 100644 index 0000000000..35ce57f17b --- /dev/null +++ b/scripts/templates/LedgerEntryTests.cpp.mako @@ -0,0 +1,231 @@ +// Auto-generated unit tests for ledger entry ${name} +<% + required_fields = [f for f in fields if f["requirement"] == "soeREQUIRED"] + optional_fields = [f for f in fields if f["requirement"] != "soeREQUIRED"] + + def canonical_expr(field): + return f"canonical_{field['stiSuffix']}()" + + # Pick a wrong ledger entry to test type mismatch + # Use Ticket as it has minimal required fields (just Account) + if name != "Ticket": + wrong_le_include = "Ticket" + else: + wrong_le_include = "Check" +%> + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(${name}Tests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ +% for i, field in enumerate(required_fields): + ${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""} +% endfor + }; + +% for field in optional_fields: + builder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = entry.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = entry.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "${field["name"]}"); + EXPECT_TRUE(entry.has${field["name"][2:]}()); + } + +% endfor + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(${name}Tests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + auto sle = std::make_shared(${name}::entryType, index); + +% for field in fields: +% if field.get("stiSuffix") == "ISSUE": + sle->at(${field["name"]}) = STIssue(${field["name"]}, ${field["paramName"]}Value); +% elif field["typeData"].get("setter_use_brackets"): + sle->at(${field["name"]}) = ${field["paramName"]}Value; +% else: + sle->${field["typeData"]["setter_method"]}(${field["name"]}, ${field["paramName"]}Value); +% endif +% endfor + + ${name}Builder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + ${name} entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + + auto const fromSle = entryFromSle.get${field["name"][2:]}(); + auto const fromBuilder = entryFromBuilder.get${field["name"][2:]}(); + + expectEqualField(expected, fromSle, "${field["name"]}"); + expectEqualField(expected, fromBuilder, "${field["name"]}"); + } + +% endfor +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + + auto const fromSleOpt = entryFromSle.get${field["name"][2:]}(); + auto const fromBuilderOpt = entryFromBuilder.get${field["name"][2:]}(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "${field["name"]}"); + expectEqualField(expected, *fromBuilderOpt, "${field["name"]}"); + } + +% endfor + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(${name}Tests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq +% if wrong_le_include == "Ticket": + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; +% else: + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; +% endif + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(${name}{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(${name}Tests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type +% if wrong_le_include == "Ticket": + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; +% else: + ${wrong_le_include}Builder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; +% endif + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(${name}Builder{wrongEntry.getSle()}, std::runtime_error); +} + +% if optional_fields: +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(${name}Tests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + +% for field in required_fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ +% for i, field in enumerate(required_fields): + ${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""} +% endfor + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present +% for field in optional_fields: + EXPECT_FALSE(entry.has${field["name"][2:]}()); + EXPECT_FALSE(entry.get${field["name"][2:]}().has_value()); +% endfor +} +% endif +} diff --git a/scripts/templates/Transaction.h.mako b/scripts/templates/Transaction.h.mako new file mode 100644 index 0000000000..62c51a5c97 --- /dev/null +++ b/scripts/templates/Transaction.h.mako @@ -0,0 +1,226 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ${name}Builder; + +/** + * @brief Transaction: ${name} + * + * Type: ${tag} (${value}) + * Delegable: ${delegable} + * Amendment: ${amendments} + * Privileges: ${privileges} + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ${name}Builder to construct new transactions. + */ +class ${name} : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ${tag}; + + /** + * @brief Construct a ${name} transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ${name}(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ${name}"); + } + } + + // Transaction-specific field getters +% for field in fields: +% if field['typed']: + + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (has${field['name'][2:]}()) + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->tx_->isFieldPresent(${field['name']}); + } +% endif +% else: + /** + * @brief Get ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif + * @note This is an untyped field. +% if field['requirement'] == 'soeREQUIRED': + * @return The field value. +% else: + * @return The field value, or std::nullopt if not present. +% endif + */ +% if field['requirement'] == 'soeREQUIRED': + [[nodiscard]] + ${field['typeData']['return_type']} + get${field['name'][2:]}() const + { + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + } +% else: + [[nodiscard]] + ${field['typeData']['return_type_optional']} + get${field['name'][2:]}() const + { + if (this->tx_->isFieldPresent(${field['name']})) + return this->tx_->${field['typeData']['getter_method']}(${field['name']}); + return std::nullopt; + } + + /** + * @brief Check if ${field['name']} is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + has${field['name'][2:]}() const + { + return this->tx_->isFieldPresent(${field['name']}); + } +% endif +% endif +% endfor +}; + +<% + required_fields = [f for f in fields if f['requirement'] == 'soeREQUIRED'] +%>\ +/** + * @brief Builder for ${name} transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ${name}Builder : public TransactionBuilderBase<${name}Builder> +{ +public: + /** + * @brief Construct a new ${name}Builder with required fields. + * @param account The account initiating the transaction. +% for field in required_fields: + * @param ${field['paramName']} The ${field['name']} field value. +% endfor + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ${name}Builder(SF_ACCOUNT::type::value_type account, +% for i, field in enumerate(required_fields): + ${field['typeData']['setter_type']} ${field['paramName']},\ +% endfor + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase<${name}Builder>(${tag}, account, sequence, fee) + { +% for field in required_fields: + set${field['name'][2:]}(${field['paramName']}); +% endfor + } + + /** + * @brief Construct a ${name}Builder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ${name}Builder(std::shared_ptr tx) + { + if (tx->getTxnType() != ${tag}) + { + throw std::runtime_error("Invalid transaction type for ${name}Builder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ +% for field in fields: + + /** + * @brief Set ${field['name']} (${field['requirement']}) +% if field.get('supports_mpt'): + * @note This field supports MPT (Multi-Purpose Token) amounts. +% endif + * @return Reference to this builder for method chaining. + */ + ${name}Builder& + set${field['name'][2:]}(${field['typeData']['setter_type']} value) + { +% if field.get('stiSuffix') == 'ISSUE': + object_[${field['name']}] = STIssue(${field['name']}, value); +% elif field['typeData'].get('setter_use_brackets'): + object_[${field['name']}] = value; +% else: + object_.${field['typeData']['setter_method']}(${field['name']}, value); +% endif + return *this; + } +% endfor + + /** + * @brief Build and return the ${name} wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ${name} + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ${name}{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/scripts/templates/TransactionTests.cpp.mako b/scripts/templates/TransactionTests.cpp.mako new file mode 100644 index 0000000000..b6bd5cd831 --- /dev/null +++ b/scripts/templates/TransactionTests.cpp.mako @@ -0,0 +1,241 @@ +// Auto-generated unit tests for transaction ${name} +<% + required_fields = [f for f in fields if f["requirement"] == "soeREQUIRED"] + optional_fields = [f for f in fields if f["requirement"] != "soeREQUIRED"] + + def canonical_expr(field): + return f"canonical_{field['stiSuffix']}()" + + # Pick a wrong transaction to test type mismatch + if name != "AccountSet": + wrong_tx_include = "AccountSet" + else: + wrong_tx_include = "OfferCancel" +%> + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(Transactions${name}Tests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + + // Set optional fields +% for field in optional_fields: + builder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = tx.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor + // Verify optional fields +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = tx.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field ${field["name"]} should be present"; + expectEqualField(expected, *actualOpt, "${field["name"]}"); + EXPECT_TRUE(tx.has${field["name"][2:]}()); + } + +% endfor +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(Transactions${name}Tests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}FromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values +% for field in fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + // Build an initial transaction + ${name}Builder initialBuilder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + +% for field in optional_fields: + initialBuilder.set${field["name"][2:]}(${field["paramName"]}Value); +% endfor + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ${name}Builder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields +% for field in required_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actual = rebuiltTx.get${field["name"][2:]}(); + expectEqualField(expected, actual, "${field["name"]}"); + } + +% endfor + // Verify optional fields +% for field in optional_fields: + { + auto const& expected = ${field["paramName"]}Value; + auto const actualOpt = rebuiltTx.get${field["name"][2:]}(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field ${field["name"]} should be present"; + expectEqualField(expected, *actualOpt, "${field["name"]}"); + } + +% endfor +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(Transactions${name}Tests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + +% if wrong_tx_include == "AccountSet": + ${wrong_tx_include}Builder wrongBuilder{account, 1, canonical_AMOUNT()}; +% else: + ${wrong_tx_include}Builder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; +% endif + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(${name}{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(Transactions${name}Tests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + +% if wrong_tx_include == "AccountSet": + ${wrong_tx_include}Builder wrongBuilder{account, 1, canonical_AMOUNT()}; +% else: + ${wrong_tx_include}Builder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; +% endif + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(${name}Builder{wrongTx.getSTTx()}, std::runtime_error); +} + +% if optional_fields: +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(Transactions${name}Tests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("test${name}Nullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values +% for field in required_fields: + auto const ${field["paramName"]}Value = ${canonical_expr(field)}; +% endfor + + ${name}Builder builder{ + accountValue, +% for field in required_fields: + ${field["paramName"]}Value, +% endfor + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present +% for field in optional_fields: + EXPECT_FALSE(tx.has${field["name"][2:]}()); + EXPECT_FALSE(tx.get${field["name"][2:]}().has_value()); +% endfor +} +% endif + +} diff --git a/src/libxrpl/basics/Archive.cpp b/src/libxrpl/basics/Archive.cpp index a4100e8196..a09d77d794 100644 --- a/src/libxrpl/basics/Archive.cpp +++ b/src/libxrpl/basics/Archive.cpp @@ -41,8 +41,9 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& Throw("Failed to allocate archive"); if (archive_write_disk_set_options( - aw.get(), ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS) < - ARCHIVE_OK) + aw.get(), + ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | + ARCHIVE_EXTRACT_FFLAGS) < ARCHIVE_OK) { Throw(archive_error_string(aw.get())); } @@ -50,8 +51,8 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& if (archive_write_disk_set_standard_lookup(aw.get()) < ARCHIVE_OK) Throw(archive_error_string(aw.get())); - int result; - struct archive_entry* entry; + int result = 0; + struct archive_entry* entry = nullptr; while (true) { result = archive_read_next_header(ar.get(), &entry); @@ -66,9 +67,9 @@ extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& if (archive_entry_size(entry) > 0) { - void const* buf; - size_t sz; - la_int64_t offset; + void const* buf = nullptr; + size_t sz = 0; + la_int64_t offset = 0; while (true) { result = archive_read_data_block(ar.get(), &buf, &sz, &offset); diff --git a/src/libxrpl/basics/BasicConfig.cpp b/src/libxrpl/basics/BasicConfig.cpp index 64e80c63ec..edc8bea7d9 100644 --- a/src/libxrpl/basics/BasicConfig.cpp +++ b/src/libxrpl/basics/BasicConfig.cpp @@ -55,7 +55,7 @@ Section::append(std::vector const& lines) val = ""; break; } - else if (val.at(comment - 1) == '\\') + if (val.at(comment - 1) == '\\') { // we have an escaped comment char. Erase the escape char // and keep looking @@ -83,9 +83,13 @@ Section::append(std::vector const& lines) boost::smatch match; if (boost::regex_match(line, match, re1)) + { set(match[1], match[2]); + } else + { values_.push_back(line); + } lines_.push_back(std::move(line)); } @@ -132,7 +136,8 @@ BasicConfig::section(std::string const& name) const void BasicConfig::overwrite(std::string const& section, std::string const& key, std::string const& value) { - auto const result = map_.emplace(std::piecewise_construct, std::make_tuple(section), std::make_tuple(section)); + auto const result = + map_.emplace(std::piecewise_construct, std::make_tuple(section), std::make_tuple(section)); result.first->second.set(key, value); } @@ -161,8 +166,8 @@ BasicConfig::build(IniFileSections const& ifs) { for (auto const& entry : ifs) { - auto const result = - map_.emplace(std::piecewise_construct, std::make_tuple(entry.first), std::make_tuple(entry.first)); + auto const result = map_.emplace( + std::piecewise_construct, std::make_tuple(entry.first), std::make_tuple(entry.first)); result.first->second.append(entry.second); } } diff --git a/src/libxrpl/basics/FileUtilities.cpp b/src/libxrpl/basics/FileUtilities.cpp index 35cc4350ef..0f636276da 100644 --- a/src/libxrpl/basics/FileUtilities.cpp +++ b/src/libxrpl/basics/FileUtilities.cpp @@ -45,7 +45,8 @@ getFileContents( return {}; } - std::string const result{std::istreambuf_iterator{fileStream}, std::istreambuf_iterator{}}; + std::string const result{ + std::istreambuf_iterator{fileStream}, std::istreambuf_iterator{}}; if (fileStream.bad()) { @@ -57,7 +58,10 @@ getFileContents( } void -writeFileContents(boost::system::error_code& ec, boost::filesystem::path const& destPath, std::string const& contents) +writeFileContents( + boost::system::error_code& ec, + boost::filesystem::path const& destPath, + std::string const& contents) { using namespace boost::filesystem; using namespace boost::system::errc; diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index fc5100a152..9cebd1f04a 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -163,7 +163,11 @@ Logs::partition_severities() const } void -Logs::write(beast::severities::Severity level, std::string const& partition, std::string const& text, bool console) +Logs::write( + beast::severities::Severity level, + std::string const& partition, + std::string const& text, + bool console) { std::string s; format(s, text, level, partition); @@ -413,9 +417,13 @@ public: swap(holder_, sink); if (holder_) + { sink_ = *holder_; + } else + { sink_ = beast::Journal::getNullSink(); + } return sink; } diff --git a/src/libxrpl/basics/MallocTrim.cpp b/src/libxrpl/basics/MallocTrim.cpp new file mode 100644 index 0000000000..ed20ac6c94 --- /dev/null +++ b/src/libxrpl/basics/MallocTrim.cpp @@ -0,0 +1,157 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if defined(__GLIBC__) && BOOST_OS_LINUX +#include + +#include +#include + +// Require RUSAGE_THREAD for thread-scoped page fault tracking +#ifndef RUSAGE_THREAD +#error "MallocTrim rusage instrumentation requires RUSAGE_THREAD on Linux/glibc" +#endif + +namespace { + +bool +getRusageThread(struct rusage& ru) +{ + return ::getrusage(RUSAGE_THREAD, &ru) == 0; // LCOV_EXCL_LINE +} + +} // namespace +#endif + +namespace xrpl { + +namespace detail { + +// cSpell:ignore statm + +#if defined(__GLIBC__) && BOOST_OS_LINUX + +inline int +mallocTrimWithPad(std::size_t padBytes) +{ + return ::malloc_trim(padBytes); +} + +long +parseStatmRSSkB(std::string const& statm) +{ + // /proc/self/statm format: size resident shared text lib data dt + // We want the second field (resident) which is in pages + std::istringstream iss(statm); + long size = 0, resident = 0; + if (!(iss >> size >> resident)) + return -1; + + // Convert pages to KB + long const pageSize = ::sysconf(_SC_PAGESIZE); + if (pageSize <= 0) + return -1; + + return (resident * pageSize) / 1024; +} + +#endif // __GLIBC__ && BOOST_OS_LINUX + +} // namespace detail + +MallocTrimReport +mallocTrim(std::string_view tag, beast::Journal journal) +{ + // LCOV_EXCL_START + + MallocTrimReport report; + +#if !(defined(__GLIBC__) && BOOST_OS_LINUX) + JLOG(journal.debug()) << "malloc_trim not supported on this platform (tag=" << tag << ")"; +#else + // Keep glibc malloc_trim padding at 0 (default): 12h Mainnet tests across 0/256KB/1MB/16MB + // showed no clear, consistent benefit from custom padding—0 provided the best overall balance + // of RSS reduction and trim-latency stability without adding a tuning surface. + constexpr std::size_t TRIM_PAD = 0; + + report.supported = true; + + if (journal.debug()) + { + auto readFile = [](std::string const& path) -> std::string { + std::ifstream ifs(path, std::ios::in | std::ios::binary); + if (!ifs.is_open()) + return {}; + + // /proc files are often not seekable; read as a stream. + std::ostringstream oss; + oss << ifs.rdbuf(); + return oss.str(); + }; + + std::string const tagStr{tag}; + std::string const statmPath = "/proc/self/statm"; + + auto const statmBefore = readFile(statmPath); + long const rssBeforeKB = detail::parseStatmRSSkB(statmBefore); + + struct rusage ru0{}; + bool const have_ru0 = getRusageThread(ru0); + + auto const t0 = std::chrono::steady_clock::now(); + + report.trimResult = detail::mallocTrimWithPad(TRIM_PAD); + + auto const t1 = std::chrono::steady_clock::now(); + + struct rusage ru1{}; + bool const have_ru1 = getRusageThread(ru1); + + auto const statmAfter = readFile(statmPath); + long const rssAfterKB = detail::parseStatmRSSkB(statmAfter); + + // Populate report fields + report.rssBeforeKB = rssBeforeKB; + report.rssAfterKB = rssAfterKB; + report.durationUs = std::chrono::duration_cast(t1 - t0); + + if (have_ru0 && have_ru1) + { + report.minfltDelta = ru1.ru_minflt - ru0.ru_minflt; + report.majfltDelta = ru1.ru_majflt - ru0.ru_majflt; + } + + std::int64_t const deltaKB = (rssBeforeKB < 0 || rssAfterKB < 0) + ? 0 + : (static_cast(rssAfterKB) - static_cast(rssBeforeKB)); + + JLOG(journal.debug()) << "malloc_trim tag=" << tagStr << " result=" << report.trimResult + << " pad=" << TRIM_PAD << " bytes" + << " rss_before=" << rssBeforeKB << "kB" + << " rss_after=" << rssAfterKB << "kB" + << " delta=" << deltaKB << "kB" + << " duration_us=" << report.durationUs.count() + << " minflt_delta=" << report.minfltDelta + << " majflt_delta=" << report.majfltDelta; + } + else + { + report.trimResult = detail::mallocTrimWithPad(TRIM_PAD); + } + +#endif + + return report; + + // LCOV_EXCL_STOP +} + +} // namespace xrpl diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 6096e6afcc..b64c2f5707 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -66,14 +66,12 @@ concept UnsignedMantissa = std::is_unsigned_v || std::is_same_v class Number::Guard { - std::uint64_t digits_; // 16 decimal guard digits - std::uint8_t xbit_ : 1; // has a non-zero digit been shifted off the end - std::uint8_t sbit_ : 1; // the sign of the guard digits + std::uint64_t digits_{0}; // 16 decimal guard digits + std::uint8_t xbit_ : 1 {0}; // has a non-zero digit been shifted off the end + std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits public: - explicit Guard() : digits_{0}, xbit_{0}, sbit_{0} - { - } + explicit Guard() = default; // set & test the sign bit void @@ -96,7 +94,7 @@ public: // This enables the client to round towards nearest, and on // tie, round towards even. int - round() noexcept; + round() const noexcept; // Modify the result to the correctly rounded value template @@ -116,7 +114,7 @@ public: // Modify the result to the correctly rounded value void - doRound(rep& drops, std::string location); + doRound(rep& drops, std::string location) const; private: void @@ -173,7 +171,7 @@ Number::Guard::pop() noexcept // 0 if Guard is exactly half // 1 if Guard is greater than half int -Number::Guard::round() noexcept +Number::Guard::round() const noexcept { auto mode = Number::getround(); @@ -211,7 +209,11 @@ Number::Guard::round() noexcept template void -Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa) +Number::Guard::bringIntoRange( + bool& negative, + T& mantissa, + int& exponent, + internalrep const& minMantissa) { // Bring mantissa back into the minMantissa / maxMantissa range AFTER // rounding @@ -254,12 +256,16 @@ Number::Guard::doRoundUp( } bringIntoRange(negative, mantissa, exponent, minMantissa); if (exponent > maxExponent) - throw std::overflow_error(location); + Throw(std::string(location)); } template void -Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa) +Number::Guard::doRoundDown( + bool& negative, + T& mantissa, + int& exponent, + internalrep const& minMantissa) { auto r = round(); if (r == 1 || (r == 0 && (mantissa & 1) == 1)) @@ -276,7 +282,7 @@ Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent, internalr // Modify the result to the correctly rounded value void -Number::Guard::doRound(rep& drops, std::string location) +Number::Guard::doRound(rep& drops, std::string location) const { auto r = round(); if (r == 1 || (r == 0 && (drops & 1) == 1)) @@ -290,7 +296,7 @@ Number::Guard::doRound(rep& drops, std::string location) // or "(maxRep + 1) / 10", neither of which will round up when // converting to rep, though the latter might overflow _before_ // rounding. - throw std::overflow_error(location); // LCOV_EXCL_LINE + Throw(std::string(location)); // LCOV_EXCL_LINE } ++drops; } @@ -428,7 +434,9 @@ doNormalize( g.doRoundUp(negative, mantissa_, exponent_, minMantissa, maxMantissa, "Number::normalize 2"); XRPL_ASSERT_PARTS( - mantissa_ >= minMantissa && mantissa_ <= maxMantissa, "xrpl::doNormalize", "final mantissa fits in range"); + mantissa_ >= minMantissa && mantissa_ <= maxMantissa, + "xrpl::doNormalize", + "final mantissa fits in range"); } template <> @@ -669,7 +677,12 @@ Number::operator*=(Number const& y) xm = static_cast(zm); xe = ze; g.doRoundUp( - zn, xm, xe, minMantissa, maxMantissa, "Number::multiplication overflow : exponent is " + std::to_string(xe)); + zn, + xm, + xe, + minMantissa, + maxMantissa, + "Number::multiplication overflow : exponent is " + std::to_string(xe)); negative_ = zn; mantissa_ = xm; exponent_ = xe; @@ -773,7 +786,8 @@ Number::operator/=(Number const& y) return *this; } -Number::operator rep() const +Number:: +operator rep() const { rep drops = mantissa(); int offset = exponent(); @@ -884,9 +898,11 @@ to_string(Number const& amount) XRPL_ASSERT(post_to >= post_from, "xrpl::to_string(Number) : second distance check"); - post_to = std::find_if(std::make_reverse_iterator(post_to), std::make_reverse_iterator(post_from), [](char c) { - return c != '0'; - }).base(); + post_to = std::find_if( + std::make_reverse_iterator(post_to), + std::make_reverse_iterator(post_from), + [](char c) { return c != '0'; }) + .base(); std::string ret; @@ -895,9 +911,13 @@ to_string(Number const& amount) // Assemble the output: if (pre_from == pre_to) + { ret.append(1, '0'); + } else + { ret.append(pre_from, pre_to); + } if (post_to != post_from) { diff --git a/src/libxrpl/basics/ResolverAsio.cpp b/src/libxrpl/basics/ResolverAsio.cpp index 78346870a7..b746092645 100644 --- a/src/libxrpl/basics/ResolverAsio.cpp +++ b/src/libxrpl/basics/ResolverAsio.cpp @@ -35,7 +35,6 @@ namespace xrpl { template class AsyncObject { -protected: AsyncObject() : m_pending(0) { } @@ -93,6 +92,8 @@ public: private: // The number of handlers pending. std::atomic m_pending; + + friend Derived; }; class ResolverAsioImpl : public ResolverAsio, public AsyncObject @@ -108,7 +109,7 @@ public: std::condition_variable m_cv; std::mutex m_mut; - bool m_asyncHandlersCompleted; + bool m_asyncHandlersCompleted{true}; std::atomic m_stop_called; std::atomic m_stopped; @@ -135,7 +136,6 @@ public: , m_io_context(io_context) , m_strand(boost::asio::make_strand(io_context)) , m_resolver(io_context) - , m_asyncHandlersCompleted(true) , m_stop_called(false) , m_stopped(true) { @@ -187,7 +187,8 @@ public: boost::asio::dispatch( m_io_context, boost::asio::bind_executor( - m_strand, std::bind(&ResolverAsioImpl::do_stop, this, CompletionCounter(this)))); + m_strand, + std::bind(&ResolverAsioImpl::do_stop, this, CompletionCounter(this)))); JLOG(m_journal.debug()) << "Queued a stop request"; } @@ -216,7 +217,9 @@ public: boost::asio::dispatch( m_io_context, boost::asio::bind_executor( - m_strand, std::bind(&ResolverAsioImpl::do_resolve, this, names, handler, CompletionCounter(this)))); + m_strand, + std::bind( + &ResolverAsioImpl::do_resolve, this, names, handler, CompletionCounter(this)))); } //------------------------------------------------------------------------- @@ -264,7 +267,8 @@ public: boost::asio::post( m_io_context, - boost::asio::bind_executor(m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); + boost::asio::bind_executor( + m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); } HostAndPort @@ -339,7 +343,8 @@ public: boost::asio::post( m_io_context, boost::asio::bind_executor( - m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); + m_strand, + std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); return; } @@ -366,15 +371,16 @@ public: { m_work.emplace_back(names, handler); - JLOG(m_journal.debug()) << "Queued new job with " << names.size() << " tasks. " << m_work.size() - << " jobs outstanding."; + JLOG(m_journal.debug()) << "Queued new job with " << names.size() << " tasks. " + << m_work.size() << " jobs outstanding."; - if (m_work.size() > 0) + if (!m_work.empty()) { boost::asio::post( m_io_context, boost::asio::bind_executor( - m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); + m_strand, + std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this)))); } } } diff --git a/src/libxrpl/basics/StringUtilities.cpp b/src/libxrpl/basics/StringUtilities.cpp index 96b5cfdb9b..0dd466c9b4 100644 --- a/src/libxrpl/basics/StringUtilities.cpp +++ b/src/libxrpl/basics/StringUtilities.cpp @@ -103,7 +103,7 @@ trim_whitespace(std::string str) std::optional to_uint64(std::string const& s) { - std::uint64_t result; + std::uint64_t result = 0; if (beast::lexicalCastChecked(result, s)) return result; return std::nullopt; diff --git a/src/libxrpl/basics/base64.cpp b/src/libxrpl/basics/base64.cpp index 3b692bc743..31888ec99c 100644 --- a/src/libxrpl/basics/base64.cpp +++ b/src/libxrpl/basics/base64.cpp @@ -47,7 +47,8 @@ namespace base64 { inline char const* get_alphabet() { - static char constexpr tab[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; + static char constexpr tab[] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; return &tab[0]; } @@ -76,13 +77,13 @@ get_inverse() } /// Returns max chars needed to encode a base64 string -inline std::size_t constexpr encoded_size(std::size_t n) +std::size_t constexpr encoded_size(std::size_t n) { return 4 * ((n + 2) / 3); } /// Returns max bytes needed to decode a base64 string -inline std::size_t constexpr decoded_size(std::size_t n) +std::size_t constexpr decoded_size(std::size_t n) { return ((n / 4) * 3) + 2; } @@ -115,6 +116,7 @@ encode(void* dest, void const* src, std::size_t len) in += 3; } + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (len % 3) { case 2: @@ -167,7 +169,7 @@ decode(void* dest, char const* src, std::size_t len) break; ++in; c4[i] = v; - if (++i == 4) + if (++i; i == 4) { c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); diff --git a/src/libxrpl/basics/make_SSLContext.cpp b/src/libxrpl/basics/make_SSLContext.cpp index 57cbf7a24a..74aab709d8 100644 --- a/src/libxrpl/basics/make_SSLContext.cpp +++ b/src/libxrpl/basics/make_SSLContext.cpp @@ -174,19 +174,22 @@ initAnonymous(boost::asio::ssl::context& context) X509V3_set_ctx_nodb(&ctx); X509V3_set_ctx(&ctx, x509, x509, nullptr, nullptr, 0); - if (auto ext = X509V3_EXT_conf_nid(nullptr, &ctx, NID_basic_constraints, "critical,CA:FALSE")) + if (auto ext = + X509V3_EXT_conf_nid(nullptr, &ctx, NID_basic_constraints, "critical,CA:FALSE")) { X509_add_ext(x509, ext, -1); X509_EXTENSION_free(ext); } - if (auto ext = X509V3_EXT_conf_nid(nullptr, &ctx, NID_ext_key_usage, "critical,serverAuth,clientAuth")) + if (auto ext = X509V3_EXT_conf_nid( + nullptr, &ctx, NID_ext_key_usage, "critical,serverAuth,clientAuth")) { X509_add_ext(x509, ext, -1); X509_EXTENSION_free(ext); } - if (auto ext = X509V3_EXT_conf_nid(nullptr, &ctx, NID_key_usage, "critical,digitalSignature")) + if (auto ext = + X509V3_EXT_conf_nid(nullptr, &ctx, NID_key_usage, "critical,digitalSignature")) { X509_add_ext(x509, ext, -1); X509_EXTENSION_free(ext); @@ -236,6 +239,7 @@ initAuthenticated( { boost::system::error_code ec; + // NOLINTNEXTLINE(bugprone-unused-return-value) context.use_certificate_file(cert_file, boost::asio::ssl::context::pem, ec); if (ec) @@ -268,9 +272,11 @@ initAuthenticated( if (!cert_set) { if (SSL_CTX_use_certificate(ssl, x) != 1) + { LogicError( "Problem retrieving SSL certificate from chain " "file."); + } cert_set = true; } @@ -286,7 +292,8 @@ initAuthenticated( catch (std::exception const& ex) { fclose(f); - LogicError(std::string("Reading the SSL chain file generated an exception: ") + ex.what()); + LogicError( + std::string("Reading the SSL chain file generated an exception: ") + ex.what()); } } @@ -294,6 +301,7 @@ initAuthenticated( { boost::system::error_code ec; + // NOLINTNEXTLINE(bugprone-unused-return-value) context.use_private_key_file(key_file, boost::asio::ssl::context::pem, ec); if (ec) diff --git a/src/libxrpl/beast/clock/basic_seconds_clock.cpp b/src/libxrpl/beast/clock/basic_seconds_clock.cpp index 27fb7b78c9..4727599740 100644 --- a/src/libxrpl/beast/clock/basic_seconds_clock.cpp +++ b/src/libxrpl/beast/clock/basic_seconds_clock.cpp @@ -16,7 +16,7 @@ class seconds_clock_thread { using Clock = basic_seconds_clock::Clock; - bool stop_; + bool stop_{false}; std::mutex mut_; std::condition_variable cv_; std::thread thread_; @@ -38,7 +38,8 @@ static_assert(std::atomic::is_always_lock_free); seconds_clock_thread::~seconds_clock_thread() { - XRPL_ASSERT(thread_.joinable(), "beast::seconds_clock_thread::~seconds_clock_thread : thread joinable"); + XRPL_ASSERT( + thread_.joinable(), "beast::seconds_clock_thread::~seconds_clock_thread : thread joinable"); { std::lock_guard lock(mut_); stop_ = true; @@ -47,7 +48,7 @@ seconds_clock_thread::~seconds_clock_thread() thread_.join(); } -seconds_clock_thread::seconds_clock_thread() : stop_{false}, tp_{Clock::now().time_since_epoch().count()} +seconds_clock_thread::seconds_clock_thread() : tp_{Clock::now().time_since_epoch().count()} { thread_ = std::thread(&seconds_clock_thread::run, this); } diff --git a/src/libxrpl/beast/core/CurrentThreadName.cpp b/src/libxrpl/beast/core/CurrentThreadName.cpp index 9d8d4e4bf2..8e54a74e4d 100644 --- a/src/libxrpl/beast/core/CurrentThreadName.cpp +++ b/src/libxrpl/beast/core/CurrentThreadName.cpp @@ -62,7 +62,8 @@ namespace beast::detail { inline void setCurrentThreadNameImpl(std::string_view name) { - pthread_setname_np(name.data()); + // The string is assumed to be null terminated + pthread_setname_np(name.data()); // NOLINT(bugprone-suspicious-stringview-data-usage) } } // namespace beast::detail @@ -80,15 +81,20 @@ setCurrentThreadNameImpl(std::string_view name) { // truncate and set the thread name. char boundedName[maxThreadNameLength + 1]; - std::snprintf(boundedName, sizeof(boundedName), "%.*s", static_cast(maxThreadNameLength), name.data()); + std::snprintf( + boundedName, + sizeof(boundedName), + "%.*s", + static_cast(maxThreadNameLength), + name.data()); // NOLINT(bugprone-suspicious-stringview-data-usage) pthread_setname_np(pthread_self(), boundedName); #ifdef TRUNCATED_THREAD_NAME_LOGS if (name.size() > maxThreadNameLength) { - std::cerr << "WARNING: Thread name \"" << name << "\" (length " << name.size() << ") exceeds maximum of " - << maxThreadNameLength << " characters on Linux.\n"; + std::cerr << "WARNING: Thread name \"" << name << "\" (length " << name.size() + << ") exceeds maximum of " << maxThreadNameLength << " characters on Linux.\n"; XRPL_ASSERT( false, diff --git a/src/libxrpl/beast/core/SemanticVersion.cpp b/src/libxrpl/beast/core/SemanticVersion.cpp index d22211cc7d..34bb5e3df0 100644 --- a/src/libxrpl/beast/core/SemanticVersion.cpp +++ b/src/libxrpl/beast/core/SemanticVersion.cpp @@ -29,7 +29,7 @@ print_identifiers(SemanticVersion::identifier_list const& list) bool isNumeric(std::string const& s) { - int n; + int n = 0; // Must be convertible to an integer if (!lexicalCastChecked(n, s)) @@ -58,8 +58,9 @@ chopUInt(int& value, int limit, std::string& input) if (input.empty()) return false; - auto left_iter = std::find_if_not( - input.begin(), input.end(), [](std::string::value_type c) { return std::isdigit(c, std::locale::classic()); }); + auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) { + return std::isdigit(c, std::locale::classic()); + }); std::string item(input.begin(), left_iter); @@ -67,7 +68,7 @@ chopUInt(int& value, int limit, std::string& input) if (item.empty()) return false; - int n; + int n = 0; // Must be convertible to an integer if (!lexicalCastChecked(n, item)) @@ -98,7 +99,8 @@ extract_identifier(std::string& value, bool allowLeadingZeroes, std::string& inp if (!allowLeadingZeroes && input[0] == '0') return false; - auto last = input.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"); + auto last = + input.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"); // Must not be empty if (last == 0) @@ -110,7 +112,10 @@ extract_identifier(std::string& value, bool allowLeadingZeroes, std::string& inp } bool -extract_identifiers(SemanticVersion::identifier_list& identifiers, bool allowLeadingZeroes, std::string& input) +extract_identifiers( + SemanticVersion::identifier_list& identifiers, + bool allowLeadingZeroes, + std::string& input) { if (input.empty()) return false; @@ -133,18 +138,19 @@ SemanticVersion::SemanticVersion() : majorVersion(0), minorVersion(0), patchVers { } -SemanticVersion::SemanticVersion(std::string const& version) : SemanticVersion() +SemanticVersion::SemanticVersion(std::string_view version) : SemanticVersion() { if (!parse(version)) throw std::invalid_argument("invalid version string"); } bool -SemanticVersion::parse(std::string const& input) +SemanticVersion::parse(std::string_view input) { // May not have leading or trailing whitespace - auto left_iter = std::find_if_not( - input.begin(), input.end(), [](std::string::value_type c) { return std::isspace(c, std::locale::classic()); }); + auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) { + return std::isspace(c, std::locale::classic()); + }); auto right_iter = std::find_if_not(input.rbegin(), input.rend(), [](std::string::value_type c) { return std::isspace(c, std::locale::classic()); @@ -206,7 +212,8 @@ SemanticVersion::print() const { std::string s; - s = std::to_string(majorVersion) + "." + std::to_string(minorVersion) + "." + std::to_string(patchVersion); + s = std::to_string(majorVersion) + "." + std::to_string(minorVersion) + "." + + std::to_string(patchVersion); if (!preReleaseIdentifiers.empty()) { @@ -227,45 +234,71 @@ int compare(SemanticVersion const& lhs, SemanticVersion const& rhs) { if (lhs.majorVersion > rhs.majorVersion) + { return 1; - else if (lhs.majorVersion < rhs.majorVersion) + } + if (lhs.majorVersion < rhs.majorVersion) + { return -1; + } if (lhs.minorVersion > rhs.minorVersion) + { return 1; - else if (lhs.minorVersion < rhs.minorVersion) + } + if (lhs.minorVersion < rhs.minorVersion) + { return -1; + } if (lhs.patchVersion > rhs.patchVersion) + { return 1; - else if (lhs.patchVersion < rhs.patchVersion) + } + if (lhs.patchVersion < rhs.patchVersion) + { return -1; + } if (lhs.isPreRelease() || rhs.isPreRelease()) { // Pre-releases have a lower precedence if (lhs.isRelease() && rhs.isPreRelease()) + { return 1; - else if (lhs.isPreRelease() && rhs.isRelease()) + } + if (lhs.isPreRelease() && rhs.isRelease()) + { return -1; + } // Compare pre-release identifiers - for (int i = 0; i < std::max(lhs.preReleaseIdentifiers.size(), rhs.preReleaseIdentifiers.size()); ++i) + for (int i = 0; + i < std::max(lhs.preReleaseIdentifiers.size(), rhs.preReleaseIdentifiers.size()); + ++i) { // A larger list of identifiers has a higher precedence if (i >= rhs.preReleaseIdentifiers.size()) + { return 1; - else if (i >= lhs.preReleaseIdentifiers.size()) + } + if (i >= lhs.preReleaseIdentifiers.size()) + { return -1; + } std::string const& left(lhs.preReleaseIdentifiers[i]); std::string const& right(rhs.preReleaseIdentifiers[i]); // Numeric identifiers have lower precedence if (!isNumeric(left) && isNumeric(right)) + { return 1; - else if (isNumeric(left) && !isNumeric(right)) + } + if (isNumeric(left) && !isNumeric(right)) + { return -1; + } if (isNumeric(left)) { @@ -275,9 +308,13 @@ compare(SemanticVersion const& lhs, SemanticVersion const& rhs) int const iRight(lexicalCastThrow(right)); if (iLeft > iRight) + { return 1; - else if (iLeft < iRight) + } + if (iLeft < iRight) + { return -1; + } } else { diff --git a/src/libxrpl/beast/insight/Groups.cpp b/src/libxrpl/beast/insight/Groups.cpp index 1ff8dbc4c1..2b4178a9ca 100644 --- a/src/libxrpl/beast/insight/Groups.cpp +++ b/src/libxrpl/beast/insight/Groups.cpp @@ -25,7 +25,8 @@ public: std::string const m_name; Collector::ptr m_collector; - GroupImp(std::string const& name_, Collector::ptr const& collector) : m_name(name_), m_collector(collector) + GroupImp(std::string const& name_, Collector::ptr const& collector) + : m_name(name_), m_collector(collector) { } diff --git a/src/libxrpl/beast/insight/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index e005a0b177..cc3bbf2861 100644 --- a/src/libxrpl/beast/insight/StatsDCollector.cpp +++ b/src/libxrpl/beast/insight/StatsDCollector.cpp @@ -104,8 +104,8 @@ private: std::shared_ptr m_impl; std::string m_name; - CounterImpl::value_type m_value; - bool m_dirty; + CounterImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ @@ -162,9 +162,9 @@ private: std::shared_ptr m_impl; std::string m_name; - GaugeImpl::value_type m_last_value; - GaugeImpl::value_type m_value; - bool m_dirty; + GaugeImpl::value_type m_last_value{0}; + GaugeImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ @@ -172,7 +172,9 @@ private: class StatsDMeterImpl : public MeterImpl, public StatsDMetricBase { public: - explicit StatsDMeterImpl(std::string const& name, std::shared_ptr const& impl); + explicit StatsDMeterImpl( + std::string const& name, + std::shared_ptr const& impl); ~StatsDMeterImpl() override; @@ -192,13 +194,14 @@ private: std::shared_ptr m_impl; std::string m_name; - MeterImpl::value_type m_value; - bool m_dirty; + MeterImpl::value_type m_value{0}; + bool m_dirty{false}; }; //------------------------------------------------------------------------------ -class StatsDCollectorImp : public StatsDCollector, public std::enable_shared_from_this +class StatsDCollectorImp : public StatsDCollector, + public std::enable_shared_from_this { private: enum { @@ -246,7 +249,7 @@ public: { m_timer.cancel(); } - catch (boost::system::system_error const&) + catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) { // ignored } @@ -333,7 +336,10 @@ public: // The keepAlive parameter makes sure the buffers sent to // boost::asio::async_send do not go away until the call is finished void - on_send(std::shared_ptr> /*keepAlive*/, boost::system::error_code ec, std::size_t) + on_send( + std::shared_ptr> /*keepAlive*/, + boost::system::error_code ec, + std::size_t) { if (ec == boost::asio::error::operation_aborted) return; @@ -390,7 +396,11 @@ public: m_socket.async_send( buffers, std::bind( - &StatsDCollectorImp::on_send, this, keepAlive, std::placeholders::_1, std::placeholders::_2)); + &StatsDCollectorImp::on_send, + this, + keepAlive, + std::placeholders::_1, + std::placeholders::_2)); buffers.clear(); size = 0; } @@ -404,7 +414,12 @@ public: log(buffers); m_socket.async_send( buffers, - std::bind(&StatsDCollectorImp::on_send, this, keepAlive, std::placeholders::_1, std::placeholders::_2)); + std::bind( + &StatsDCollectorImp::on_send, + this, + keepAlive, + std::placeholders::_1, + std::placeholders::_2)); } } @@ -455,6 +470,7 @@ public: m_io_context.run(); + // NOLINTNEXTLINE(bugprone-unused-return-value) m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec); m_socket.close(); @@ -465,7 +481,9 @@ public: //------------------------------------------------------------------------------ -StatsDHookImpl::StatsDHookImpl(HandlerType const& handler, std::shared_ptr const& impl) +StatsDHookImpl::StatsDHookImpl( + HandlerType const& handler, + std::shared_ptr const& impl) : m_impl(impl), m_handler(handler) { m_impl->add(*this); @@ -484,8 +502,10 @@ StatsDHookImpl::do_process() //------------------------------------------------------------------------------ -StatsDCounterImpl::StatsDCounterImpl(std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_value(0), m_dirty(false) +StatsDCounterImpl::StatsDCounterImpl( + std::string const& name, + std::shared_ptr const& impl) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -501,7 +521,9 @@ StatsDCounterImpl::increment(CounterImpl::value_type amount) boost::asio::dispatch( m_impl->get_io_context(), std::bind( - &StatsDCounterImpl::do_increment, std::static_pointer_cast(shared_from_this()), amount)); + &StatsDCounterImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void @@ -533,7 +555,9 @@ StatsDCounterImpl::do_process() //------------------------------------------------------------------------------ -StatsDEventImpl::StatsDEventImpl(std::string const& name, std::shared_ptr const& impl) +StatsDEventImpl::StatsDEventImpl( + std::string const& name, + std::shared_ptr const& impl) : m_impl(impl), m_name(name) { } @@ -543,7 +567,10 @@ StatsDEventImpl::notify(EventImpl::value_type const& value) { boost::asio::dispatch( m_impl->get_io_context(), - std::bind(&StatsDEventImpl::do_notify, std::static_pointer_cast(shared_from_this()), value)); + std::bind( + &StatsDEventImpl::do_notify, + std::static_pointer_cast(shared_from_this()), + value)); } void @@ -557,8 +584,10 @@ StatsDEventImpl::do_notify(EventImpl::value_type const& value) //------------------------------------------------------------------------------ -StatsDGaugeImpl::StatsDGaugeImpl(std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_last_value(0), m_value(0), m_dirty(false) +StatsDGaugeImpl::StatsDGaugeImpl( + std::string const& name, + std::shared_ptr const& impl) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -573,7 +602,10 @@ StatsDGaugeImpl::set(GaugeImpl::value_type value) { boost::asio::dispatch( m_impl->get_io_context(), - std::bind(&StatsDGaugeImpl::do_set, std::static_pointer_cast(shared_from_this()), value)); + std::bind( + &StatsDGaugeImpl::do_set, + std::static_pointer_cast(shared_from_this()), + value)); } void @@ -582,7 +614,9 @@ StatsDGaugeImpl::increment(GaugeImpl::difference_type amount) boost::asio::dispatch( m_impl->get_io_context(), std::bind( - &StatsDGaugeImpl::do_increment, std::static_pointer_cast(shared_from_this()), amount)); + &StatsDGaugeImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void @@ -639,8 +673,10 @@ StatsDGaugeImpl::do_process() //------------------------------------------------------------------------------ -StatsDMeterImpl::StatsDMeterImpl(std::string const& name, std::shared_ptr const& impl) - : m_impl(impl), m_name(name), m_value(0), m_dirty(false) +StatsDMeterImpl::StatsDMeterImpl( + std::string const& name, + std::shared_ptr const& impl) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -656,7 +692,9 @@ StatsDMeterImpl::increment(MeterImpl::value_type amount) boost::asio::dispatch( m_impl->get_io_context(), std::bind( - &StatsDMeterImpl::do_increment, std::static_pointer_cast(shared_from_this()), amount)); + &StatsDMeterImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void diff --git a/src/libxrpl/beast/net/IPAddressV4.cpp b/src/libxrpl/beast/net/IPAddressV4.cpp index 021b7588d9..c65e6fcf89 100644 --- a/src/libxrpl/beast/net/IPAddressV4.cpp +++ b/src/libxrpl/beast/net/IPAddressV4.cpp @@ -7,8 +7,8 @@ bool is_private(AddressV4 const& addr) { return ((addr.to_uint() & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.# - ((addr.to_uint() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# - ((addr.to_uint() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# + ((addr.to_uint() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# + ((addr.to_uint() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# addr.is_loopback(); } diff --git a/src/libxrpl/beast/net/IPAddressV6.cpp b/src/libxrpl/beast/net/IPAddressV6.cpp index b1db5ae09c..a261002ed3 100644 --- a/src/libxrpl/beast/net/IPAddressV6.cpp +++ b/src/libxrpl/beast/net/IPAddressV6.cpp @@ -11,7 +11,8 @@ is_private(AddressV6 const& addr) { return ( (addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ? - (addr.is_v4_mapped() && is_private(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr)))); + (addr.is_v4_mapped() && + is_private(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr)))); } bool diff --git a/src/libxrpl/beast/net/IPEndpoint.cpp b/src/libxrpl/beast/net/IPEndpoint.cpp index a79b0d750c..f8352f4318 100644 --- a/src/libxrpl/beast/net/IPEndpoint.cpp +++ b/src/libxrpl/beast/net/IPEndpoint.cpp @@ -50,7 +50,9 @@ std::string Endpoint::to_string() const { std::string s; - s.reserve((address().is_v6() ? INET6_ADDRSTRLEN - 1 : 15) + (port() == 0 ? 0 : 6 + (address().is_v6() ? 2 : 0))); + s.reserve( + (address().is_v6() ? INET6_ADDRSTRLEN - 1 : 15) + + (port() == 0 ? 0 : 6 + (address().is_v6() ? 2 : 0))); if (port() != 0 && address().is_v6()) s += '['; @@ -93,10 +95,14 @@ operator>>(std::istream& is, Endpoint& endpoint) char i{0}; char readTo{0}; is.get(i); - if (i == '[') // we are an IPv6 endpoint + if (i == '[') + { // we are an IPv6 endpoint readTo = ']'; + } else + { addrStr += i; + } while (is && is.rdbuf()->in_avail() > 0 && is.get(i)) { @@ -108,12 +114,14 @@ operator>>(std::istream& is, Endpoint& endpoint) if (isspace(static_cast(i)) || (readTo && i == readTo)) break; - if ((i == '.') || (i >= '0' && i <= ':') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) + if ((i == '.') || (i >= '0' && i <= ':') || (i >= 'a' && i <= 'f') || + (i >= 'A' && i <= 'F')) { addrStr += i; // don't exceed a reasonable length... - if (addrStr.size() == INET6_ADDRSTRLEN || (readTo && readTo == ':' && addrStr.size() > 15)) + if (addrStr.size() == INET6_ADDRSTRLEN || + (readTo && readTo == ':' && addrStr.size() > 15)) { is.setstate(std::ios_base::failbit); return is; @@ -155,14 +163,16 @@ operator>>(std::istream& is, Endpoint& endpoint) if (is.rdbuf()->in_avail() > 0) { - Port port; + Port port = 0; is >> port; if (is.fail()) return is; endpoint = Endpoint(addr, port); } else + { endpoint = Endpoint(addr); + } return is; } diff --git a/src/libxrpl/beast/utility/beast_Journal.cpp b/src/libxrpl/beast/utility/beast_Journal.cpp index f9ee0cdb73..7164fcfb06 100644 --- a/src/libxrpl/beast/utility/beast_Journal.cpp +++ b/src/libxrpl/beast/utility/beast_Journal.cpp @@ -124,9 +124,13 @@ Journal::ScopedStream::~ScopedStream() if (!s.empty()) { if (s == "\n") + { m_sink.write(m_level, ""); + } else + { m_sink.write(m_level, s); + } } } diff --git a/src/libxrpl/beast/utility/beast_PropertyStream.cpp b/src/libxrpl/beast/utility/beast_PropertyStream.cpp index daf6bc4526..454b9d1323 100644 --- a/src/libxrpl/beast/utility/beast_PropertyStream.cpp +++ b/src/libxrpl/beast/utility/beast_PropertyStream.cpp @@ -15,7 +15,7 @@ namespace beast { // //------------------------------------------------------------------------------ -PropertyStream::Item::Item(Source* source) : m_source(source) +PropertyStream::Item::Item(Source* source) : ListNode(), m_source(source) { } @@ -151,7 +151,8 @@ PropertyStream::Set::stream() const // //------------------------------------------------------------------------------ -PropertyStream::Source::Source(std::string const& name) : m_name(name), item_(this), parent_(nullptr) +PropertyStream::Source::Source(std::string const& name) + : m_name(name), item_(this), parent_(nullptr) { } @@ -176,7 +177,8 @@ PropertyStream::Source::add(Source& source) std::lock_guard lk1(lock_, std::adopt_lock); std::lock_guard lk2(source.lock_, std::adopt_lock); - XRPL_ASSERT(source.parent_ == nullptr, "beast::PropertyStream::Source::add : null source parent"); + XRPL_ASSERT( + source.parent_ == nullptr, "beast::PropertyStream::Source::add : null source parent"); children_.push_back(source.item_); source.parent_ = this; } @@ -188,7 +190,8 @@ PropertyStream::Source::remove(Source& child) std::lock_guard lk1(lock_, std::adopt_lock); std::lock_guard lk2(child.lock_, std::adopt_lock); - XRPL_ASSERT(child.parent_ == this, "beast::PropertyStream::Source::remove : child parent match"); + XRPL_ASSERT( + child.parent_ == this, "beast::PropertyStream::Source::remove : child parent match"); children_.erase(children_.iterator_to(child.item_)); child.parent_ = nullptr; } @@ -234,9 +237,13 @@ PropertyStream::Source::write(PropertyStream& stream, std::string const& path) return; if (result.second) + { result.first->write(stream); + } else + { result.first->write_one(stream); + } } std::pair @@ -298,9 +305,13 @@ PropertyStream::Source::peel_name(std::string* path) std::string s(first, pos); if (pos != last) + { *path = std::string(pos + 1, last); + } else + { *path = std::string(); + } return s; } @@ -368,9 +379,13 @@ void PropertyStream::add(std::string const& key, bool value) { if (value) + { add(key, "true"); + } else + { add(key, "false"); + } } void @@ -461,9 +476,13 @@ void PropertyStream::add(bool value) { if (value) + { add("true"); + } else + { add("false"); + } } void diff --git a/src/xrpld/conditions/detail/Condition.cpp b/src/libxrpl/conditions/Condition.cpp similarity index 97% rename from src/xrpld/conditions/detail/Condition.cpp rename to src/libxrpl/conditions/Condition.cpp index 9cac75121d..30beba3402 100644 --- a/src/xrpld/conditions/detail/Condition.cpp +++ b/src/libxrpl/conditions/Condition.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include namespace xrpl { namespace cryptoconditions { diff --git a/src/xrpld/conditions/detail/Fulfillment.cpp b/src/libxrpl/conditions/Fulfillment.cpp similarity index 94% rename from src/xrpld/conditions/detail/Fulfillment.cpp rename to src/libxrpl/conditions/Fulfillment.cpp index 9ecaa44ab8..11581a8705 100644 --- a/src/xrpld/conditions/detail/Fulfillment.cpp +++ b/src/libxrpl/conditions/Fulfillment.cpp @@ -1,9 +1,8 @@ -#include -#include -#include -#include - #include +#include +#include +#include +#include namespace xrpl { namespace cryptoconditions { diff --git a/src/xrpld/conditions/detail/error.cpp b/src/libxrpl/conditions/error.cpp similarity index 95% rename from src/xrpld/conditions/detail/error.cpp rename to src/libxrpl/conditions/error.cpp index 04c8d03fbb..9c9d4658e4 100644 --- a/src/xrpld/conditions/detail/error.cpp +++ b/src/libxrpl/conditions/error.cpp @@ -1,6 +1,5 @@ -#include - #include +#include #include @@ -110,7 +109,8 @@ std::error_code make_error_code(error ev) { return std::error_code{ - safe_cast::type>(ev), detail::get_cryptoconditions_error_category()}; + safe_cast::type>(ev), + detail::get_cryptoconditions_error_category()}; } } // namespace cryptoconditions diff --git a/src/xrpld/app/misc/HashRouter.cpp b/src/libxrpl/core/HashRouter.cpp similarity index 69% rename from src/xrpld/app/misc/HashRouter.cpp rename to src/libxrpl/core/HashRouter.cpp index eca46c9872..dff1394f77 100644 --- a/src/xrpld/app/misc/HashRouter.cpp +++ b/src/libxrpl/core/HashRouter.cpp @@ -1,5 +1,4 @@ -#include -#include +#include namespace xrpl { @@ -108,38 +107,4 @@ HashRouter::shouldRelay(uint256 const& key) -> std::optional( - "HashRouter hold time must be at least 12 seconds (the " - "approximate validation time for three ledgers)."); - setup.holdTime = seconds(tmp); - } - if (set(tmp, "relay_time", section)) - { - if (tmp < 8) - Throw( - "HashRouter relay time must be at least 8 seconds (the " - "approximate validation time for two ledgers)."); - setup.relayTime = seconds(tmp); - } - if (setup.relayTime > setup.holdTime) - { - Throw("HashRouter relay time must be less than or equal to hold time"); - } - - return setup; -} - } // namespace xrpl diff --git a/src/libxrpl/core/detail/Job.cpp b/src/libxrpl/core/detail/Job.cpp index 4e957b9619..7e5ca274b3 100644 --- a/src/libxrpl/core/detail/Job.cpp +++ b/src/libxrpl/core/detail/Job.cpp @@ -11,7 +11,12 @@ Job::Job(JobType type, std::uint64_t index) : mType(type), mJobIndex(index) { } -Job::Job(JobType type, std::string const& name, std::uint64_t index, LoadMonitor& lm, std::function const& job) +Job::Job( + JobType type, + std::string const& name, + std::uint64_t index, + LoadMonitor& lm, + std::function const& job) : mType(type), mJobIndex(index), mJob(job), mName(name), m_queue_time(clock_type::now()) { m_loadEvent = std::make_shared(std::ref(lm), name, false); diff --git a/src/libxrpl/core/detail/JobQueue.cpp b/src/libxrpl/core/detail/JobQueue.cpp index 37e5402946..9adbf6312b 100644 --- a/src/libxrpl/core/detail/JobQueue.cpp +++ b/src/libxrpl/core/detail/JobQueue.cpp @@ -62,7 +62,8 @@ JobQueue::addRefCountedJob(JobType type, std::string const& name, JobFunction co XRPL_ASSERT(type != jtINVALID, "xrpl::JobQueue::addRefCountedJob : valid input job type"); auto iter(m_jobData.find(type)); - XRPL_ASSERT(iter != m_jobData.end(), "xrpl::JobQueue::addRefCountedJob : job type found in jobs"); + XRPL_ASSERT( + iter != m_jobData.end(), "xrpl::JobQueue::addRefCountedJob : job type found in jobs"); if (iter == m_jobData.end()) return false; @@ -83,7 +84,8 @@ JobQueue::addRefCountedJob(JobType type, std::string const& name, JobFunction co JobType const type(job.getType()); XRPL_ASSERT(type != jtINVALID, "xrpl::JobQueue::addRefCountedJob : has valid job type"); - XRPL_ASSERT(m_jobSet.find(job) != m_jobSet.end(), "xrpl::JobQueue::addRefCountedJob : job found"); + XRPL_ASSERT( + m_jobSet.find(job) != m_jobSet.end(), "xrpl::JobQueue::addRefCountedJob : job found"); perfLog_.jobQueue(type); JobTypeData& data(getJobTypeData(type)); @@ -165,7 +167,9 @@ JobQueue::addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed) bool JobQueue::isOverloaded() { - return std::any_of(m_jobData.begin(), m_jobData.end(), [](auto& entry) { return entry.second.load().isOver(); }); + return std::any_of(m_jobData.begin(), m_jobData.end(), [](auto& entry) { + return entry.second.load().isOver(); + }); } Json::Value @@ -285,7 +289,8 @@ JobQueue::getNextJob(Job& job) XRPL_ASSERT(type != jtINVALID, "xrpl::JobQueue::getNextJob : valid job type"); JobTypeData& data(getJobTypeData(type)); - XRPL_ASSERT(data.running <= getJobLimit(type), "xrpl::JobQueue::getNextJob : maximum jobs running"); + XRPL_ASSERT( + data.running <= getJobLimit(type), "xrpl::JobQueue::getNextJob : maximum jobs running"); // Run this job if we're running below the limit. if (data.running < getJobLimit(data.type())) @@ -312,7 +317,9 @@ JobQueue::finishJob(JobType type) // Queue a deferred task if possible if (data.deferred > 0) { - XRPL_ASSERT(data.running + data.waiting >= getJobLimit(type), "xrpl::JobQueue::finishJob : job limit"); + XRPL_ASSERT( + data.running + data.waiting >= getJobLimit(type), + "xrpl::JobQueue::finishJob : job limit"); --data.deferred; m_workers.addTask(); @@ -324,7 +331,7 @@ JobQueue::finishJob(JobType type) void JobQueue::processTask(int instance) { - JobType type; + JobType type = jtINVALID; { using namespace std::chrono; diff --git a/src/libxrpl/core/detail/LoadMonitor.cpp b/src/libxrpl/core/detail/LoadMonitor.cpp index 376b25c178..89a364ca30 100644 --- a/src/libxrpl/core/detail/LoadMonitor.cpp +++ b/src/libxrpl/core/detail/LoadMonitor.cpp @@ -89,7 +89,8 @@ LoadMonitor::addLoadSample(LoadEvent const& s) if (latency > 500ms) { auto mj = (latency > 1s) ? j_.warn() : j_.info(); - JLOG(mj) << "Job: " << s.name() << " run: " << round(s.runTime()).count() << "ms" + JLOG(mj) << "Job: " << s.name() << " run: " << round(s.runTime()).count() + << "ms" << " wait: " << round(s.waitTime()).count() << "ms"; } @@ -142,7 +143,8 @@ LoadMonitor::isOver() if (mLatencyEvents == 0) return 0; - return isOverTarget(mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4)); + return isOverTarget( + mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4)); } LoadMonitor::Stats diff --git a/src/libxrpl/core/detail/Workers.cpp b/src/libxrpl/core/detail/Workers.cpp index 27103d765d..c037169a29 100644 --- a/src/libxrpl/core/detail/Workers.cpp +++ b/src/libxrpl/core/detail/Workers.cpp @@ -5,7 +5,11 @@ namespace xrpl { -Workers::Workers(Callback& callback, perf::PerfLog* perfLog, std::string const& threadNames, int numberOfThreads) +Workers::Workers( + Callback& callback, + perf::PerfLog* perfLog, + std::string const& threadNames, + int numberOfThreads) : m_callback(callback) , perfLog_(perfLog) , m_threadNames(threadNames) @@ -133,7 +137,11 @@ Workers::deleteWorkers(beast::LockFreeStack& stack) //------------------------------------------------------------------------------ Workers::Worker::Worker(Workers& workers, std::string const& threadName, int const instance) - : m_workers{workers}, threadName_{threadName}, instance_{instance}, wakeCount_{0}, shouldExit_{false} + : m_workers{workers} + , threadName_{threadName} + , instance_{instance} + , wakeCount_{0} + , shouldExit_{false} { thread_ = std::thread{&Workers::Worker::run, this}; } @@ -197,11 +205,9 @@ Workers::Worker::run() // We got paused break; } - else - { - // Undo our decrement - ++m_workers.m_pauseCount; - } + + // Undo our decrement + ++m_workers.m_pauseCount; } // We couldn't pause so we must have gotten diff --git a/src/libxrpl/crypto/RFC1751.cpp b/src/libxrpl/crypto/RFC1751.cpp index dc841fca83..f7c1e675cb 100644 --- a/src/libxrpl/crypto/RFC1751.cpp +++ b/src/libxrpl/crypto/RFC1751.cpp @@ -21,163 +21,187 @@ namespace xrpl { // char const* RFC1751::s_dictionary[2048] = { - "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", - "AMY", "AN", "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", "ARM", - "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", "AWE", "AWK", "AWL", "AWN", "AX", - "AYE", "BAD", "BAG", "BAH", "BAM", "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", - "BET", "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", "BOP", "BOW", "BOY", - "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", - "CAP", "CAR", "CAT", "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", "CRY", - "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", "DAR", "DAY", "DEE", "DEL", "DEN", - "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", - "DUB", "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", "ELI", "ELK", "ELM", - "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", - "FED", "FEE", "FEW", "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", "FRY", - "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", "GAS", "GAY", "GEE", "GEL", "GEM", - "GET", "GIG", "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", - "HAD", "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", "HEN", "HER", "HEW", - "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", - "HOW", "HUB", "HUE", "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", "INK", - "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", "ITS", "IVY", "JAB", "JAG", "JAM", - "JAN", "JAR", "JAW", "JAY", "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", - "JUT", "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", "LAC", "LAD", "LAG", - "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", - "LIN", "LIP", "LIT", "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", "LYE", - "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME", "MEG", "MEL", "MEN", - "MET", "MEW", "MID", "MIN", "MIT", "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", - "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", "NEE", "NET", "NEW", - "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", - "NUT", "O", "OAF", "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", "OK", - "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW", "OWE", - "OWL", "OWN", "OX", "PA", "PAD", "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", - "PEG", "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", "PLY", "PO", "POD", - "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", - "RAN", "RAP", "RAT", "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", "RIO", - "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", - "SAC", "SAD", "SAG", "SAL", "SAM", "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", - "SET", "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", "SLY", "SO", "SOB", - "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", - "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", "TIM", - "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", "TOW", "TOY", "TRY", "TUB", "TUG", - "TUM", "TUN", "TWO", "UN", "UP", "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", - "WAS", "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON", "WOO", - "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", - "ABLE", "ABUT", "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", "ADEN", "AFAR", "AFRO", "AGEE", - "AHEM", "AHOY", "AIDA", "AIDE", "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", "ALLY", "ALMA", - "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", - "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", - "ASKS", "ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", "AWAY", "AWRY", "BABE", "BABY", - "BACH", "BACK", "BADE", "BAIL", "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", "BAND", "BANE", - "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", - "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", "BEAU", "BECK", "BEEF", "BEEN", "BEER", "BEET", - "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", - "BIDE", "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", "BITS", "BLAB", "BLAT", "BLED", "BLEW", - "BLOB", "BLOC", "BLOT", "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY", "BOGY", - "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", - "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", - "BRAN", "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", "BULB", "BULK", "BULL", "BUNK", - "BUNT", "BUOY", "BURG", "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", "BUSY", "BYTE", "CADY", - "CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", - "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", - "CHEF", "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", "CHUM", "CITE", "CITY", "CLAD", "CLAM", - "CLAN", "CLAW", "CLAY", "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", "COCO", "CODA", - "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", "COOL", - "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", - "CRIB", "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", "CURB", "CURD", "CURE", "CURL", "CURT", - "CUTS", "DADE", "DALE", "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", "DART", "DASH", "DATA", - "DATE", "DAVE", "DAVY", "DAWN", "DAYS", "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", - "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", - "DINT", "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL", "DOLT", "DOME", "DONE", - "DOOM", "DOOR", "DORA", "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", "DRAM", "DRAW", "DREW", - "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", - "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", - "EDIT", "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS", "ERIC", "EROS", "EVEN", - "EVER", "EVIL", "EYED", "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG", "FARM", - "FAST", "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", "FEUD", - "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", - "FIVE", "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", - "FLUE", "FOAL", "FOAM", "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD", "FORE", - "FORK", "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", - "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", - "GALA", "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", "GATE", "GAUL", "GAUR", "GAVE", "GAWK", - "GEAR", "GELD", "GENE", "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", "GINA", "GIRD", "GIRL", - "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", - "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", - "GRAB", "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW", "GRUB", "GULF", "GULL", - "GUNK", "GURU", "GUSH", "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", "HALE", "HALF", "HALL", - "HALO", "HALT", "HAND", "HANG", "HANK", "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", "HATH", - "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", - "HELL", "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", "HICK", "HIDE", "HIGH", "HIKE", "HILL", - "HILT", "HIND", "HINT", "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", "HOME", - "HONE", "HONK", "HOOD", "HOOF", "HOOK", "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", - "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", - "HYDE", "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", "IONS", "IOTA", "IOWA", "IRIS", - "IRMA", "IRON", "ISLE", "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", "JAVA", "JEAN", "JEFF", - "JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", - "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", - "JUST", "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", "KEEN", "KENO", "KENT", "KERN", "KERR", - "KEYS", "KICK", "KILL", "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", "KNOB", "KNOT", - "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", - "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", - "LAWS", "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", "LEFT", "LEND", "LENS", "LENT", "LEON", - "LESK", "LESS", "LEST", "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", "LIFE", "LIFT", "LIKE", - "LILA", "LILT", "LILY", "LIMA", "LIMB", "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", - "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", - "LORD", "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE", "LUKE", "LULU", "LUND", - "LUNG", "LURA", "LURE", "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", "MADE", "MAGI", "MAID", - "MAIL", "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", - "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", - "MEEK", "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE", "MIKE", "MILD", "MILE", - "MILK", "MILL", "MILT", "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE", "MITT", - "MOAN", "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", "MOOR", - "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", - "MURK", "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", "NAIR", "NAME", "NARY", "NASH", "NAVE", - "NAVY", "NEAL", "NEAR", "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS", "NEWT", - "NIBS", "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", - "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", - "OKAY", "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", "ONES", "ONLY", "ONTO", "ONUS", "ORAL", - "ORGY", "OSLO", "OTIS", "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", "OWNS", "QUAD", "QUIT", - "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", - "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", - "RENA", "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME", "RING", "RINK", "RISE", - "RISK", "RITE", "ROAD", "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", "ROOD", "ROOF", "ROOK", - "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", "RUDE", - "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", - "SAGE", "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", "SANG", "SANK", "SARA", "SAUL", "SAVE", - "SAYS", "SCAN", "SCAR", "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN", "SEES", - "SELF", "SELL", "SEND", "SENT", "SETS", "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", - "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", - "SINE", "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", "SKIM", "SKIN", "SKIT", "SLAB", - "SLAM", "SLAT", "SLAY", "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", "SLOW", "SLUG", "SLUM", - "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", - "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", - "STAR", "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", "SUDS", "SUIT", "SULK", "SUMS", "SUNG", - "SUNK", "SURE", "SURF", "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", "TACT", "TAIL", - "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", - "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", - "THIN", "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", "TILE", "TILL", "TILT", "TIME", "TINA", - "TINE", "TINT", "TINY", "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", "TONY", "TOOK", "TOOL", - "TOOT", "TORE", "TORN", "TOTE", "TOUR", "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", - "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", - "TUSK", "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH", "VAIL", "VAIN", "VALE", - "VARY", "VASE", "VAST", "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", "VETO", "VICE", "VIEW", - "VINE", "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", - "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", - "WAVY", "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL", "WELT", "WENT", "WERE", - "WERT", "WEST", "WHAM", "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL", "WIND", - "WINE", "WING", "WINK", "WINO", "WIRE", "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", "WORE", - "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", - "YEAR", "YELL", "YOGA", "YOKE"}; + "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", "AGO", "AID", "AIM", "AIR", "ALL", + "ALP", "AM", "AMY", "AN", "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", + "ARC", "ARE", "ARK", "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", + "AVE", "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", "BAN", + "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", "BEY", "BIB", "BID", + "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", "BOP", "BOW", "BOY", "BUB", "BUD", + "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", + "CAP", "CAR", "CAT", "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", + "COY", "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", "DAR", + "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP", "DO", + "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", "DUD", "DUE", "DUG", "DUN", "EAR", + "EAT", "ED", "EEL", "EGG", "EGO", "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", + "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", + "FEW", "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", "FRY", + "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", "GAS", "GAY", "GEE", + "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS", "GUT", + "GUY", "GYM", "GYP", "HA", "HAD", "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", + "HAY", "HE", "HEM", "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", + "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", "HUG", + "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", "INK", "INN", "IO", + "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", "ITS", "IVY", "JAB", "JAG", "JAM", + "JAN", "JAR", "JAW", "JAY", "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", + "JOY", "JUG", "JUT", "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", + "LAB", "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", "LEG", + "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", "LO", "LOB", "LOG", + "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", "LYE", "MA", "MAC", "MAD", "MAE", + "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", + "MID", "MIN", "MIT", "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", + "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", "NEE", + "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR", "NOT", + "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", "OAK", "OAR", "OAT", "ODD", "ODE", + "OF", "OFF", "OFT", "OH", "OIL", "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", + "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", + "PAD", "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", "PEN", + "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", "PLY", "PO", "POD", + "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT", "QUO", + "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", + "RID", "RIG", "RIM", "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", + "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", "SAN", + "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", "SEW", "SHE", "SHY", + "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", "SLY", "SO", "SOB", "SOD", "SON", + "SOP", "SOW", "SOY", "SPA", "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", + "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", + "TIE", "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", "TOW", + "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", "US", "USE", "VAN", + "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", "WAY", "WE", "WEB", "WED", "WEE", + "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", + "YAP", "YAW", "YE", "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", + "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", "ADEN", "AFAR", "AFRO", "AGEE", + "AHEM", "AHOY", "AIDA", "AIDE", "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", + "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", "AMES", "AMID", "AMMO", "AMOK", + "AMOS", "AMRA", "ANDY", "ANEW", "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", + "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", "AUNT", "AURA", "AUTO", "AVER", + "AVID", "AVIS", "AVON", "AVOW", "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", + "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", "BAND", "BANE", "BANG", "BANK", + "BARB", "BARD", "BARE", "BARK", "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", + "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", "BEAU", "BECK", "BEEF", "BEEN", + "BEER", "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", "BESS", "BEST", + "BETA", "BETH", "BHOY", "BIAS", "BIDE", "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", + "BITE", "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", "BLOW", "BLUE", "BLUM", + "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", + "BOLT", "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", "BOON", "BOOT", + "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", + "BRAN", "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", "BULB", "BULK", + "BULL", "BUNK", "BUNT", "BUOY", "BURG", "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", + "BUST", "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", "CAME", + "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", + "CEIL", "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", "CHEN", "CHEW", "CHIC", + "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", + "CLAY", "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", "COCO", "CODA", + "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", + "COOK", "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL", "CRAB", + "CRAG", "CRAM", "CRAY", "CREW", "CRIB", "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", + "CUNY", "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", "DAME", "DANA", "DANE", + "DANG", "DANK", "DARE", "DARK", "DARN", "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", + "DAYS", "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", "DEER", "DEFT", + "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", + "DINT", "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL", "DOLT", + "DOME", "DONE", "DOOM", "DOOR", "DORA", "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", + "DRAG", "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL", "DUET", + "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", + "EAST", "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", "EDNA", "EGAN", "ELAN", + "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", + "EYED", "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG", "FARM", + "FAST", "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", + "FEST", "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK", "FIRE", "FIRM", + "FISH", "FISK", "FIST", "FITS", "FIVE", "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", + "FLEW", "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", "FOGY", "FOIL", "FOLD", + "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", + "FOUL", "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM", "FUEL", + "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", + "GALA", "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", "GATE", "GAUL", "GAUR", + "GAVE", "GAWK", "GEAR", "GELD", "GENE", "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", + "GILT", "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB", "GLOM", + "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", + "GONG", "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", "GRAD", "GRAY", "GREG", + "GREW", "GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", + "GUSH", "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", "HALE", "HALF", "HALL", + "HALO", "HALT", "HAND", "HANG", "HANK", "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", + "HATE", "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", "HEBE", "HECK", + "HEED", "HEEL", "HEFT", "HELD", "HELL", "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", + "HEWN", "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", "HIRE", "HISS", "HIVE", + "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", + "HOOK", "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", "HUCK", "HUED", + "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", + "HYDE", "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", "IONS", "IOTA", + "IOWA", "IRIS", "IRMA", "IRON", "ISLE", "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", + "JANE", "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", "JOAN", + "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", + "JUDY", "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", "JUTE", "KAHN", "KALE", + "KANE", "KANT", "KARL", "KATE", "KEEL", "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", + "KILL", "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", "KNOB", "KNOT", + "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", + "LAIN", "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS", "LAST", + "LATE", "LAUD", "LAVA", "LAWN", "LAWS", "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", + "LEER", "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", "LETS", "LIAR", "LICE", + "LICK", "LIED", "LIEN", "LIES", "LIEU", "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", + "LIMB", "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", "LOAD", "LOAF", + "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", + "LORD", "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE", "LUKE", + "LULU", "LUND", "LUNG", "LURA", "LURE", "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", + "MACE", "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT", "MANA", + "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", + "MATE", "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", "MEET", "MELD", "MELT", + "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", + "MILT", "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE", "MITT", + "MOAN", "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", + "MOON", "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE", "MUCH", "MUCK", + "MUDD", "MUFF", "MULE", "MULL", "MURK", "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", + "NAIL", "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", "NEAT", "NECK", "NEED", + "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", + "NINA", "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE", "NOTE", + "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", + "OKAY", "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", "ONES", "ONLY", "ONTO", + "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", + "OWLY", "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID", "RAIL", + "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", + "REAR", "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", "REND", "RENT", "REST", + "RICE", "RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", + "ROAD", "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", "ROOD", "ROOF", "ROOK", + "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", + "RUBY", "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", "RUSK", "RUSS", + "RUST", "RUTH", "SACK", "SAFE", "SAGE", "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", + "SANE", "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", "SCAT", "SCOT", "SEAL", + "SEAM", "SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", + "SETS", "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", "SHOE", "SHOT", + "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", + "SINE", "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", "SKIM", "SKIN", + "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", + "SLOT", "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", "SNUG", + "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", + "SORE", "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", "STAY", "STEM", "STEW", + "STIR", "STOW", "STUB", "STUN", "SUCH", "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", + "SURF", "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", "TACT", "TAIL", + "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", + "TEEM", "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN", "THAT", + "THEE", "THEM", "THEN", "THEY", "THIN", "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", + "TIER", "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", "TIRE", "TOAD", "TOGO", + "TOIL", "TOLD", "TOLL", "TONE", "TONG", "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", + "TOUR", "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", "TRIO", "TROD", + "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", + "TUSK", "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH", "VAIL", + "VAIN", "VALE", "VARY", "VASE", "VAST", "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", + "VERY", "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE", "WAGE", + "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", + "WARM", "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", "WAYS", "WEAK", "WEAL", + "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", + "WHAM", "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL", "WIND", + "WINE", "WING", "WINK", "WINO", "WIRE", "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", + "WORD", "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", "YARD", + "YARN", "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA", "YOKE"}; /* Extract 'length' bits from the char array 's' starting with bit 'start' */ unsigned long RFC1751::extract(char const* s, int start, int length) { - unsigned char cl; - unsigned char cc; - unsigned char cr; - unsigned long x; + unsigned char cl = 0; + unsigned char cc = 0; + unsigned char cr = 0; + unsigned long x = 0; XRPL_ASSERT(length <= 11, "xrpl::RFC1751::extract : maximum length"); XRPL_ASSERT(start >= 0, "xrpl::RFC1751::extract : minimum start"); @@ -202,7 +226,7 @@ void RFC1751::btoe(std::string& strHuman, std::string const& strData) { char caBuffer[9]; /* add in room for the parity 2 bits*/ - int p, i; + int p = 0, i = 0; memcpy(caBuffer, strData.c_str(), 8); @@ -212,19 +236,20 @@ RFC1751::btoe(std::string& strHuman, std::string const& strData) caBuffer[8] = char(p) << 6; - strHuman = std::string() + s_dictionary[extract(caBuffer, 0, 11)] + " " + s_dictionary[extract(caBuffer, 11, 11)] + - " " + s_dictionary[extract(caBuffer, 22, 11)] + " " + s_dictionary[extract(caBuffer, 33, 11)] + " " + + strHuman = std::string() + s_dictionary[extract(caBuffer, 0, 11)] + " " + + s_dictionary[extract(caBuffer, 11, 11)] + " " + s_dictionary[extract(caBuffer, 22, 11)] + + " " + s_dictionary[extract(caBuffer, 33, 11)] + " " + s_dictionary[extract(caBuffer, 44, 11)] + " " + s_dictionary[extract(caBuffer, 55, 11)]; } void RFC1751::insert(char* s, int x, int start, int length) { - unsigned char cl; - unsigned char cc; - unsigned char cr; - unsigned long y; - int shift; + unsigned char cl = 0; + unsigned char cc = 0; + unsigned char cr = 0; + unsigned long y = 0; + int shift = 0; XRPL_ASSERT(length <= 11, "xrpl::RFC1751::insert : maximum length"); XRPL_ASSERT(start >= 0, "xrpl::RFC1751::insert : minimum start"); @@ -260,13 +285,21 @@ RFC1751::standard(std::string& strWord) for (auto& letter : strWord) { if (islower(static_cast(letter))) + { letter = toupper(static_cast(letter)); + } else if (letter == '1') + { letter = 'L'; + } else if (letter == '0') + { letter = 'O'; + } else if (letter == '5') + { letter = 'S'; + } } } @@ -311,7 +344,7 @@ RFC1751::etob(std::string& strData, std::vector vsHuman) if (6 != vsHuman.size()) return -1; - int i, p = 0; + int i = 0, p = 0; char b[9] = {0}; for (auto& strWord : vsHuman) @@ -363,7 +396,8 @@ RFC1751::getKeyFromEnglish(std::string& strKey, std::string const& strHuman) boost::algorithm::trim(strTrimmed); - boost::algorithm::split(vWords, strTrimmed, boost::algorithm::is_space(), boost::algorithm::token_compress_on); + boost::algorithm::split( + vWords, strTrimmed, boost::algorithm::is_space(), boost::algorithm::token_compress_on); rc = 12 == vWords.size() ? 1 : -1; diff --git a/src/libxrpl/crypto/csprng.cpp b/src/libxrpl/crypto/csprng.cpp index 7858d4089c..25e24c0b08 100644 --- a/src/libxrpl/crypto/csprng.cpp +++ b/src/libxrpl/crypto/csprng.cpp @@ -30,7 +30,7 @@ csprng_engine::~csprng_engine() void csprng_engine::mix_entropy(void* buffer, std::size_t count) { - std::array entropy; + std::array entropy{}; { // On every platform we support, std::random_device @@ -71,7 +71,7 @@ csprng_engine::operator()(void* ptr, std::size_t count) csprng_engine::result_type csprng_engine::operator()() { - result_type ret; + result_type ret = 0; (*this)(&ret, sizeof(result_type)); return ret; } diff --git a/src/libxrpl/git/Git.cpp b/src/libxrpl/git/Git.cpp new file mode 100644 index 0000000000..2992852632 --- /dev/null +++ b/src/libxrpl/git/Git.cpp @@ -0,0 +1,31 @@ +#include "xrpl/git/Git.h" + +#include + +#ifndef GIT_COMMIT_HASH +#error "GIT_COMMIT_HASH must be defined" +#endif +#ifndef GIT_BUILD_BRANCH +#error "GIT_BUILD_BRANCH must be defined" +#endif + +namespace xrpl::git { + +static constexpr char kGIT_COMMIT_HASH[] = GIT_COMMIT_HASH; +static constexpr char kGIT_BUILD_BRANCH[] = GIT_BUILD_BRANCH; + +std::string const& +getCommitHash() +{ + static std::string const kVALUE = kGIT_COMMIT_HASH; + return kVALUE; +} + +std::string const& +getBuildBranch() +{ + static std::string const kVALUE = kGIT_BUILD_BRANCH; + return kVALUE; +} + +} // namespace xrpl::git diff --git a/src/libxrpl/json/Writer.cpp b/src/libxrpl/json/Writer.cpp index be9595b088..6ff5a130dd 100644 --- a/src/libxrpl/json/Writer.cpp +++ b/src/libxrpl/json/Writer.cpp @@ -25,7 +25,7 @@ std::map jsonSpecialCharacterEscape = { {'\r', "\\r"}, {'\t', "\\t"}}; -static size_t const jsonEscapeLength = 2; +size_t const jsonEscapeLength = 2; // All other JSON punctuation. char const closeBrace = '}'; @@ -36,7 +36,7 @@ char const openBrace = '{'; char const openBracket = '['; char const quote = '"'; -static auto const integralFloatsBecomeInts = false; +auto const integralFloatsBecomeInts = false; size_t lengthWithoutTrailingZeros(std::string const& s) @@ -137,9 +137,13 @@ public: check(false, "Not an " + ((type == array ? "array: " : "object: ") + message)); } if (stack_.top().isFirst) + { stack_.top().isFirst = false; + } else + { output_({&comma, 1}); + } } void @@ -196,7 +200,7 @@ private: explicit Collection() = default; /** What type of collection are we in? */ - Writer::CollectionType type; + Writer::CollectionType type = Writer::CollectionType::array; /** Is this the first entry in a collection? * If false, we have to emit a , before we write the next entry. */ diff --git a/src/libxrpl/json/json_reader.cpp b/src/libxrpl/json/json_reader.cpp index 22df530b7e..4c2a27400f 100644 --- a/src/libxrpl/json/json_reader.cpp +++ b/src/libxrpl/json/json_reader.cpp @@ -94,7 +94,7 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root) nodes_.push(&root); bool successful = readValue(0); - Token token; + Token token{}; skipCommentTokens(token); if (!root.isNull() && !root.isArray() && !root.isObject()) @@ -114,7 +114,7 @@ Reader::parse(char const* beginDoc, char const* endDoc, Value& root) bool Reader::readValue(unsigned depth) { - Token token; + Token token{}; skipCommentTokens(token); if (depth > nest_limit) return addError("Syntax error: maximum nesting depth exceeded", token); @@ -278,9 +278,13 @@ Reader::skipSpaces() Char c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { ++current_; + } else + { break; + } } } @@ -293,8 +297,10 @@ Reader::match(Location pattern, int patternLength) int index = patternLength; while (index--) + { if (current_[index] != pattern[index]) return false; + } current_ += patternLength; return true; @@ -358,7 +364,8 @@ Reader::readNumber() { if (!std::isdigit(static_cast(*current_))) { - auto ret = std::find(std::begin(extended_tokens), std::end(extended_tokens), *current_); + auto ret = + std::find(std::begin(extended_tokens), std::end(extended_tokens), *current_); if (ret == std::end(extended_tokens)) break; @@ -383,9 +390,13 @@ Reader::readString() c = getNextChar(); if (c == '\\') + { getNextChar(); + } else if (c == '"') + { break; + } } return c == '"'; @@ -394,7 +405,7 @@ Reader::readString() bool Reader::readObject(Token& tokenStart, unsigned depth) { - Token tokenName; + Token tokenName{}; std::string name; currentValue() = Value(objectValue); @@ -419,11 +430,12 @@ Reader::readObject(Token& tokenStart, unsigned depth) if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); - Token colon; + Token colon{}; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover("Missing ':' after object member name", colon, tokenObjectEnd); + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); } // Reject duplicate names @@ -438,12 +450,14 @@ Reader::readObject(Token& tokenStart, unsigned depth) if (!ok) // error already set return recoverFromError(tokenObjectEnd); - Token comma; + Token comma{}; if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { - return addErrorAndRecover("Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); } bool finalizeTokenOk = true; @@ -466,7 +480,7 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (*current_ == ']') // empty array { - Token endArray; + Token endArray{}; readToken(endArray); return true; } @@ -483,7 +497,7 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token token{}; // Accept Comment after last item in the array. ok = readToken(token); @@ -496,7 +510,8 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (!ok || badTokenType) { - return addErrorAndRecover("Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); } if (token.type_ == tokenArrayEnd) @@ -517,14 +532,17 @@ Reader::decodeNumber(Token& token) if (current == token.end_) { - return addError("'" + std::string(token.start_, token.end_) + "' is not a valid number.", token); + return addError( + "'" + std::string(token.start_, token.end_) + "' is not a valid number.", token); } // The existing Json integers are 32-bit so using a 64-bit value here avoids // overflows in the conversion code below. std::int64_t value = 0; - static_assert(sizeof(value) > sizeof(Value::maxUInt), "The JSON integer overflow logic will need to be reworked."); + static_assert( + sizeof(value) > sizeof(Value::maxUInt), + "The JSON integer overflow logic will need to be reworked."); while (current < token.end_ && (value <= Value::maxUInt)) { @@ -532,7 +550,8 @@ Reader::decodeNumber(Token& token) if (c < '0' || c > '9') { - return addError("'" + std::string(token.start_, token.end_) + "' is not a number.", token); + return addError( + "'" + std::string(token.start_, token.end_) + "' is not a number.", token); } value = (value * 10) + (c - '0'); @@ -541,7 +560,8 @@ Reader::decodeNumber(Token& token) // More tokens left -> input is larger than largest possible return value if (current != token.end_) { - return addError("'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", token); + return addError( + "'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", token); } if (isNegative) @@ -550,7 +570,9 @@ Reader::decodeNumber(Token& token) if (value < Value::minInt || value > Value::maxInt) { - return addError("'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", token); + return addError( + "'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", + token); } currentValue() = static_cast(value); @@ -559,14 +581,20 @@ Reader::decodeNumber(Token& token) { if (value > Value::maxUInt) { - return addError("'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", token); + return addError( + "'" + std::string(token.start_, token.end_) + "' exceeds the allowable range.", + token); } // If it's representable as a signed integer, construct it as one. if (value <= Value::maxInt) + { currentValue() = static_cast(value); + } else + { currentValue() = static_cast(value); + } } return true; @@ -577,7 +605,7 @@ Reader::decodeDouble(Token& token) { double value = 0; int const bufferSize = 32; - int count; + int count = 0; int length = int(token.end_ - token.start_); // Sanity check to avoid buffer overflow exploits. if (length < 0) @@ -632,8 +660,10 @@ Reader::decodeString(Token& token, std::string& decoded) Char c = *current++; if (c == '"') + { break; - else if (c == '\\') + } + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); @@ -675,7 +705,7 @@ Reader::decodeString(Token& token, std::string& decoded) break; case 'u': { - unsigned int unicode; + unsigned int unicode = 0; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; @@ -707,39 +737,47 @@ Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, un { // surrogate pairs if (end - current < 6) + { return addError( "additional six characters expected to parse unicode surrogate " "pair.", token, current); - - unsigned int surrogatePair; - - if (*(current++) == '\\' && *(current++) == 'u') - { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; } - else + + unsigned int surrogatePair = 0; + + if (*current != '\\' || *(current + 1) != 'u') + { return addError( - "expecting another \\u token to begin the second half of a " - "unicode surrogate pair", + "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current); + } + + current += 2; // skip two characters checked above + + if (!decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) + return false; + + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } return true; } bool -Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) +Reader::decodeUnicodeEscapeSequence( + Token& token, + Location& current, + Location end, + unsigned int& unicode) { if (end - current < 4) - return addError("Bad unicode escape sequence in string: four digits expected.", token, current); + { + return addError( + "Bad unicode escape sequence in string: four digits expected.", token, current); + } unicode = 0; @@ -749,17 +787,25 @@ Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location en unicode *= 16; if (c >= '0' && c <= '9') + { unicode += c - '0'; + } else if (c >= 'a' && c <= 'f') + { unicode += c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') + { unicode += c - 'A' + 10; + } else + { return addError( "Bad unicode escape sequence in string: hexadecimal digit " "expected.", token, current); + } } return true; @@ -780,7 +826,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); - Token skip; + Token skip{}; while (true) { @@ -851,7 +897,7 @@ Reader::getLocationLineAndColumn(Location location, int& line, int& column) cons std::string Reader::getLocationLineAndColumn(Location location) const { - int line, column; + int line = 0, column = 0; getLocationLineAndColumn(location, line, column); return "Line " + std::to_string(line) + ", Column " + std::to_string(column); } diff --git a/src/libxrpl/json/json_value.cpp b/src/libxrpl/json/json_value.cpp index edbbafc1eb..94b077d224 100644 --- a/src/libxrpl/json/json_value.cpp +++ b/src/libxrpl/json/json_value.cpp @@ -95,9 +95,14 @@ Value::CZString::CZString(char const* cstr, DuplicationPolicy allocate) Value::CZString::CZString(CZString const& other) : cstr_( - other.index_ != noDuplication && other.cstr_ != 0 ? valueAllocator()->makeMemberName(other.cstr_) - : other.cstr_) - , index_(other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_) + other.index_ != noDuplication && other.cstr_ != 0 + ? valueAllocator()->makeMemberName(other.cstr_) + : other.cstr_) + , index_([&]() -> int { + if (!other.cstr_) + return other.index_; + return other.index_ == noDuplication ? noDuplication : duplicate; + }()) { } @@ -155,7 +160,7 @@ Value::CZString::isStaticString() const * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType type) : type_(type), allocated_(0) +Value::Value(ValueType type) : type_(type) { switch (type) { @@ -219,10 +224,11 @@ Value::Value(xrpl::Number const& value) : type_(stringValue), allocated_(true) Value::Value(std::string const& value) : type_(stringValue), allocated_(true) { - value_.string_ = valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length()); + value_.string_ = + valueAllocator()->duplicateStringValue(value.c_str(), (unsigned int)value.length()); } -Value::Value(StaticString const& value) : type_(stringValue), allocated_(false) +Value::Value(StaticString const& value) : type_(stringValue) { value_.string_ = const_cast(value.c_str()); } @@ -251,7 +257,9 @@ Value::Value(Value const& other) : type_(other.type_) allocated_ = true; } else + { value_.string_ = 0; + } break; @@ -305,7 +313,8 @@ Value::operator=(Value const& other) return *this; } -Value::Value(Value&& other) noexcept : value_(other.value_), type_(other.type_), allocated_(other.allocated_) +Value::Value(Value&& other) noexcept + : value_(other.value_), type_(other.type_), allocated_(other.allocated_) { other.type_ = nullValue; other.allocated_ = 0; @@ -347,7 +356,9 @@ integerCmp(Int i, UInt ui) return -1; // Now we can safely compare. - return (i < ui) ? -1 : (i == ui) ? 0 : 1; + if (i < ui) + return -1; + return (i == ui) ? 0 : 1; } bool @@ -356,9 +367,13 @@ operator<(Value const& x, Value const& y) if (auto signum = x.type_ - y.type_) { if (x.type_ == intValue && y.type_ == uintValue) + { signum = integerCmp(x.value_.int_, y.value_.uint_); + } else if (x.type_ == uintValue && y.type_ == intValue) + { signum = -integerCmp(y.value_.int_, x.value_.uint_); + } return signum < 0; } @@ -381,7 +396,8 @@ operator<(Value const& x, Value const& y) case stringValue: return (x.value_.string_ == 0 && y.value_.string_) || - (y.value_.string_ && x.value_.string_ && strcmp(x.value_.string_, y.value_.string_) < 0); + (y.value_.string_ && x.value_.string_ && + strcmp(x.value_.string_, y.value_.string_) < 0); case arrayValue: case objectValue: { @@ -431,11 +447,13 @@ operator==(Value const& x, Value const& y) case stringValue: return x.value_.string_ == y.value_.string_ || - (y.value_.string_ && x.value_.string_ && !strcmp(x.value_.string_, y.value_.string_)); + (y.value_.string_ && x.value_.string_ && + !strcmp(x.value_.string_, y.value_.string_)); case arrayValue: case objectValue: - return x.value_.map_->size() == y.value_.map_->size() && *x.value_.map_ == *y.value_.map_; + return x.value_.map_->size() == y.value_.map_->size() && + *x.value_.map_ == *y.value_.map_; // LCOV_EXCL_START default: @@ -501,11 +519,14 @@ Value::asInt() const return value_.int_; case uintValue: - JSON_ASSERT_MESSAGE(value_.uint_ < (unsigned)maxInt, "integer out of signed integer range"); + JSON_ASSERT_MESSAGE( + value_.uint_ < (unsigned)maxInt, "integer out of signed integer range"); return value_.uint_; case realValue: - JSON_ASSERT_MESSAGE(value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range"); + JSON_ASSERT_MESSAGE( + value_.real_ >= minInt && value_.real_ <= maxInt, + "Real out of signed integer range"); return Int(value_.real_); case booleanValue: @@ -551,7 +572,8 @@ Value::asAbsUInt() const case realValue: { if (value_.real_ < 0) { - JSON_ASSERT_MESSAGE(-1 * value_.real_ <= maxUInt, "Real out of unsigned integer range"); + JSON_ASSERT_MESSAGE( + -1 * value_.real_ <= maxUInt, "Real out of unsigned integer range"); return UInt(-1 * value_.real_); } JSON_ASSERT_MESSAGE(value_.real_ <= maxUInt, "Real out of unsigned integer range"); @@ -595,14 +617,16 @@ Value::asUInt() const return 0; case intValue: - JSON_ASSERT_MESSAGE(value_.int_ >= 0, "Negative integer can not be converted to unsigned integer"); + JSON_ASSERT_MESSAGE( + value_.int_ >= 0, "Negative integer can not be converted to unsigned integer"); return value_.int_; case uintValue: return value_.uint_; case realValue: - JSON_ASSERT_MESSAGE(value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); + JSON_ASSERT_MESSAGE( + value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); return UInt(value_.real_); case booleanValue: @@ -683,7 +707,7 @@ Value::asBool() const case arrayValue: case objectValue: - return value_.map_->size() != 0; + return !value_.map_->empty(); // LCOV_EXCL_START default: @@ -704,33 +728,36 @@ Value::isConvertibleTo(ValueType other) const case intValue: return (other == nullValue && value_.int_ == 0) || other == intValue || - (other == uintValue && value_.int_ >= 0) || other == realValue || other == stringValue || - other == booleanValue; + (other == uintValue && value_.int_ >= 0) || other == realValue || + other == stringValue || other == booleanValue; case uintValue: return (other == nullValue && value_.uint_ == 0) || - (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue || other == realValue || - other == stringValue || other == booleanValue; + (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue || + other == realValue || other == stringValue || other == booleanValue; case realValue: return (other == nullValue && value_.real_ == 0.0) || (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) || (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt && - std::fabs(round(value_.real_) - value_.real_) < std::numeric_limits::epsilon()) || + std::fabs(round(value_.real_) - value_.real_) < + std::numeric_limits::epsilon()) || other == realValue || other == stringValue || other == booleanValue; case booleanValue: - return (other == nullValue && value_.bool_ == false) || other == intValue || other == uintValue || - other == realValue || other == stringValue || other == booleanValue; + return (other == nullValue && value_.bool_ == false) || other == intValue || + other == uintValue || other == realValue || other == stringValue || + other == booleanValue; case stringValue: - return other == stringValue || (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); + return other == stringValue || + (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); case arrayValue: - return other == arrayValue || (other == nullValue && value_.map_->size() == 0); + return other == arrayValue || (other == nullValue && value_.map_->empty()); case objectValue: - return other == objectValue || (other == nullValue && value_.map_->size() == 0); + return other == objectValue || (other == nullValue && value_.map_->empty()); // LCOV_EXCL_START default: @@ -777,7 +804,8 @@ Value::size() const return 0; // unreachable; } -Value::operator bool() const +Value:: +operator bool() const { if (isNull()) return false; @@ -794,7 +822,9 @@ Value::operator bool() const void Value::clear() { - XRPL_ASSERT(type_ == nullValue || type_ == arrayValue || type_ == objectValue, "Json::Value::clear : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == arrayValue || type_ == objectValue, + "Json::Value::clear : valid type"); switch (type_) { @@ -811,7 +841,8 @@ Value::clear() Value& Value::operator[](UInt index) { - XRPL_ASSERT(type_ == nullValue || type_ == arrayValue, "Json::Value::operator[](UInt) : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == arrayValue, "Json::Value::operator[](UInt) : valid type"); if (type_ == nullValue) *this = Value(arrayValue); @@ -830,7 +861,9 @@ Value::operator[](UInt index) Value const& Value::operator[](UInt index) const { - XRPL_ASSERT(type_ == nullValue || type_ == arrayValue, "Json::Value::operator[](UInt) const : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == arrayValue, + "Json::Value::operator[](UInt) const : valid type"); if (type_ == nullValue) return null; @@ -853,7 +886,8 @@ Value::operator[](char const* key) Value& Value::resolveReference(char const* key, bool isStatic) { - XRPL_ASSERT(type_ == nullValue || type_ == objectValue, "Json::Value::resolveReference : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == objectValue, "Json::Value::resolveReference : valid type"); if (type_ == nullValue) *this = Value(objectValue); @@ -886,7 +920,9 @@ Value::isValidIndex(UInt index) const Value const& Value::operator[](char const* key) const { - XRPL_ASSERT(type_ == nullValue || type_ == objectValue, "Json::Value::operator[](const char*) const : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == objectValue, + "Json::Value::operator[](const char*) const : valid type"); if (type_ == nullValue) return null; @@ -952,7 +988,8 @@ Value::get(std::string const& key, Value const& defaultValue) const Value Value::removeMember(char const* key) { - XRPL_ASSERT(type_ == nullValue || type_ == objectValue, "Json::Value::removeMember : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == objectValue, "Json::Value::removeMember : valid type"); if (type_ == nullValue) return null; @@ -999,7 +1036,8 @@ Value::isMember(StaticString const& key) const Value::Members Value::getMemberNames() const { - XRPL_ASSERT(type_ == nullValue || type_ == objectValue, "Json::Value::getMemberNames : valid type"); + XRPL_ASSERT( + type_ == nullValue || type_ == objectValue, "Json::Value::getMemberNames : valid type"); if (type_ == nullValue) return Value::Members(); diff --git a/src/libxrpl/json/json_valueiterator.cpp b/src/libxrpl/json/json_valueiterator.cpp index 90a1ebe44b..e49ad50f9a 100644 --- a/src/libxrpl/json/json_valueiterator.cpp +++ b/src/libxrpl/json/json_valueiterator.cpp @@ -13,11 +13,12 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) +ValueIteratorBase::ValueIteratorBase() : isNull_(true) { } -ValueIteratorBase::ValueIteratorBase(Value::ObjectValues::iterator const& current) : current_(current), isNull_(false) +ValueIteratorBase::ValueIteratorBase(Value::ObjectValues::iterator const& current) + : current_(current), isNull_(false) { } @@ -125,7 +126,8 @@ ValueIteratorBase::memberName() const // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueConstIterator::ValueConstIterator(Value::ObjectValues::iterator const& current) : ValueIteratorBase(current) +ValueConstIterator::ValueConstIterator(Value::ObjectValues::iterator const& current) + : ValueIteratorBase(current) { } @@ -144,7 +146,8 @@ ValueConstIterator::operator=(ValueIteratorBase const& other) // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIterator::ValueIterator(Value::ObjectValues::iterator const& current) : ValueIteratorBase(current) +ValueIterator::ValueIterator(Value::ObjectValues::iterator const& current) + : ValueIteratorBase(current) { } diff --git a/src/libxrpl/json/json_writer.cpp b/src/libxrpl/json/json_writer.cpp index e36cf72b1c..66507f0111 100644 --- a/src/libxrpl/json/json_writer.cpp +++ b/src/libxrpl/json/json_writer.cpp @@ -304,7 +304,9 @@ StyledWriter::writeValue(Value const& value) Value::Members members(value.getMemberNames()); if (members.empty()) + { pushValue("{}"); + } else { writeWithIndent("{"); @@ -319,7 +321,7 @@ StyledWriter::writeValue(Value const& value) document_ += " : "; writeValue(childValue); - if (++it == members.end()) + if (++it; it == members.end()) break; document_ += ","; @@ -339,7 +341,9 @@ StyledWriter::writeArrayValue(Value const& value) unsigned size = value.size(); if (size == 0) + { pushValue("[]"); + } else { bool isArrayMultiLine = isMultilineArray(value); @@ -356,7 +360,9 @@ StyledWriter::writeArrayValue(Value const& value) Value const& childValue = value[index]; if (hasChildValue) + { writeWithIndent(childValues_[index]); + } else { writeIndent(); @@ -374,7 +380,9 @@ StyledWriter::writeArrayValue(Value const& value) } else // output on a single line { - XRPL_ASSERT(childValues_.size() == size, "Json::StyledWriter::writeArrayValue : child size match"); + XRPL_ASSERT( + childValues_.size() == size, + "Json::StyledWriter::writeArrayValue : child size match"); document_ += "[ "; for (unsigned index = 0; index < size; ++index) @@ -400,7 +408,8 @@ StyledWriter::isMultilineArray(Value const& value) for (int index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; - isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); + isMultiLine = isMultiLine || + ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length @@ -426,9 +435,13 @@ void StyledWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { document_ += value; + } } void @@ -464,7 +477,9 @@ StyledWriter::indent() void StyledWriter::unindent() { - XRPL_ASSERT(int(indentString_.size()) >= indentSize_, "Json::StyledWriter::unindent : maximum indent size"); + XRPL_ASSERT( + int(indentString_.size()) >= indentSize_, + "Json::StyledWriter::unindent : maximum indent size"); indentString_.resize(indentString_.size() - indentSize_); } @@ -524,7 +539,9 @@ StyledStreamWriter::writeValue(Value const& value) Value::Members members(value.getMemberNames()); if (members.empty()) + { pushValue("{}"); + } else { writeWithIndent("{"); @@ -559,7 +576,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) unsigned size = value.size(); if (size == 0) + { pushValue("[]"); + } else { bool isArrayMultiLine = isMultilineArray(value); @@ -576,7 +595,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) Value const& childValue = value[index]; if (hasChildValue) + { writeWithIndent(childValues_[index]); + } else { writeIndent(); @@ -594,7 +615,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) } else // output on a single line { - XRPL_ASSERT(childValues_.size() == size, "Json::StyledStreamWriter::writeArrayValue : child size match"); + XRPL_ASSERT( + childValues_.size() == size, + "Json::StyledStreamWriter::writeArrayValue : child size match"); *document_ << "[ "; for (unsigned index = 0; index < size; ++index) @@ -620,7 +643,8 @@ StyledStreamWriter::isMultilineArray(Value const& value) for (int index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; - isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); + isMultiLine = isMultiLine || + ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length @@ -646,9 +670,13 @@ void StyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { *document_ << value; + } } void @@ -686,7 +714,8 @@ void StyledStreamWriter::unindent() { XRPL_ASSERT( - indentString_.size() >= indentation_.size(), "Json::StyledStreamWriter::unindent : maximum indent size"); + indentString_.size() >= indentation_.size(), + "Json::StyledStreamWriter::unindent : maximum indent size"); indentString_.resize(indentString_.size() - indentation_.size()); } diff --git a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp b/src/libxrpl/ledger/AcceptedLedgerTx.cpp similarity index 77% rename from src/xrpld/app/ledger/AcceptedLedgerTx.cpp rename to src/libxrpl/ledger/AcceptedLedgerTx.cpp index 2ef05e511d..005d48e6f6 100644 --- a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp +++ b/src/libxrpl/ledger/AcceptedLedgerTx.cpp @@ -1,7 +1,8 @@ -#include - #include #include +#include +#include +#include #include #include @@ -11,7 +12,9 @@ AcceptedLedgerTx::AcceptedLedgerTx( std::shared_ptr const& ledger, std::shared_ptr const& txn, std::shared_ptr const& met) - : mTxn(txn), mMeta(txn->getTransactionID(), ledger->seq(), *met), mAffected(mMeta.getAffectedAccounts()) + : mTxn(txn) + , mMeta(txn->getTransactionID(), ledger->seq(), *met) + , mAffected(mMeta.getAffectedAccounts()) { XRPL_ASSERT(!ledger->open(), "xrpl::AcceptedLedgerTx::AcceptedLedgerTx : valid ledger state"); @@ -42,8 +45,12 @@ AcceptedLedgerTx::AcceptedLedgerTx( // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) { - auto const ownerFunds = - accountFunds(*ledger, account, amount, fhIGNORE_FREEZE, beast::Journal{beast::Journal::getNullSink()}); + auto const ownerFunds = accountFunds( + *ledger, + account, + amount, + fhIGNORE_FREEZE, + beast::Journal{beast::Journal::getNullSink()}); mJson[jss::transaction][jss::owner_funds] = ownerFunds.getText(); } } diff --git a/src/libxrpl/ledger/ApplyStateTable.cpp b/src/libxrpl/ledger/ApplyStateTable.cpp index 31ede45f22..311ee15b8a 100644 --- a/src/libxrpl/ledger/ApplyStateTable.cpp +++ b/src/libxrpl/ledger/ApplyStateTable.cpp @@ -111,7 +111,7 @@ ApplyStateTable::apply( Mods newMod; for (auto& item : items_) { - SField const* type; + SField const* type = nullptr; switch (item.second.first) { default: @@ -131,8 +131,8 @@ ApplyStateTable::apply( auto curNode = item.second.second; if ((type == &sfModifiedNode) && (*curNode == *origNode)) continue; - std::uint16_t nodeType = - curNode ? curNode->getFieldU16(sfLedgerEntryType) : origNode->getFieldU16(sfLedgerEntryType); + std::uint16_t nodeType = curNode ? curNode->getFieldU16(sfLedgerEntryType) + : origNode->getFieldU16(sfLedgerEntryType); meta.setAffectedNode(item.first, *type, nodeType); if (type == &sfDeletedNode) { @@ -147,7 +147,8 @@ ApplyStateTable::apply( { // go through the original node for // modified fields saved on modification - if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry(obj)) + if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && + !curNode->hasMatchingEntry(obj)) prevs.emplace_back(obj); } @@ -172,15 +173,18 @@ ApplyStateTable::apply( "xrpl::detail::ApplyStateTable::apply : valid nodes for " "modification"); - if (curNode->isThreadedType(to.rules())) // thread transaction to node - // item modified + if (curNode->isThreadedType(to.rules())) + { // thread transaction to node + // item modified threadItem(meta, curNode); + } STObject prevs(sfPreviousFields); for (auto const& obj : *origNode) { // search the original node for values saved on modify - if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry(obj)) + if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && + !curNode->hasMatchingEntry(obj)) prevs.emplace_back(obj); } @@ -213,7 +217,8 @@ ApplyStateTable::apply( for (auto const& obj : *curNode) { // save non-default values - if (!obj.isDefault() && obj.getFName().shouldMeta(SField::sMD_Create | SField::sMD_Always)) + if (!obj.isDefault() && + obj.getFName().shouldMeta(SField::sMD_Create | SField::sMD_Always)) news.emplace_back(obj); } @@ -280,8 +285,10 @@ ApplyStateTable::exists(ReadView const& base, Keylet const& k) const } auto -ApplyStateTable::succ(ReadView const& base, key_type const& key, std::optional const& last) const - -> std::optional +ApplyStateTable::succ( + ReadView const& base, + key_type const& key, + std::optional const& last) const -> std::optional { std::optional next = key; items_t::const_iterator iter; @@ -396,8 +403,8 @@ void ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr const& sle) { using namespace std; - auto const result = - items_.emplace(piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::erase, sle)); + auto const result = items_.emplace( + piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::erase, sle)); if (result.second) return; auto& item = result.first->second; @@ -425,7 +432,10 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr const& sle) { using namespace std; items_.emplace_hint( - iter, piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::insert, sle)); + iter, + piecewise_construct, + forward_as_tuple(sle->key()), + forward_as_tuple(Action::insert, sle)); return; } auto& item = iter->second; @@ -452,7 +462,10 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr const& sle) { using namespace std; items_.emplace_hint( - iter, piecewise_construct, forward_as_tuple(sle->key()), forward_as_tuple(Action::modify, sle)); + iter, + piecewise_construct, + forward_as_tuple(sle->key()), + forward_as_tuple(Action::modify, sle)); return; } auto& item = iter->second; @@ -506,7 +519,7 @@ void ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) { key_type prevTxID; - LedgerIndex prevLgrID; + LedgerIndex prevLgrID = 0; if (!sle->thread(meta.getTxID(), meta.getLgrSeq(), prevTxID, prevLgrID)) return; @@ -581,7 +594,12 @@ ApplyStateTable::getForMod(ReadView const& base, key_type const& key, Mods& mods } void -ApplyStateTable::threadTx(ReadView const& base, TxMeta& meta, AccountID const& to, Mods& mods, beast::Journal j) +ApplyStateTable::threadTx( + ReadView const& base, + TxMeta& meta, + AccountID const& to, + Mods& mods, + beast::Journal j) { auto const sle = getForMod(base, keylet::account(to).key, mods, j); if (!sle) @@ -593,7 +611,8 @@ ApplyStateTable::threadTx(ReadView const& base, TxMeta& meta, AccountID const& t return; } // threadItem only applied to AccountRoot - XRPL_ASSERT(sle->isThreadedType(base.rules()), "xrpl::ApplyStateTable::threadTx : SLE is threaded"); + XRPL_ASSERT( + sle->isThreadedType(base.rules()), "xrpl::ApplyStateTable::threadTx : SLE is threaded"); threadItem(meta, sle); } diff --git a/src/libxrpl/ledger/ApplyView.cpp b/src/libxrpl/ledger/ApplyView.cpp index fc35ff5119..463b2ba538 100644 --- a/src/libxrpl/ledger/ApplyView.cpp +++ b/src/libxrpl/ledger/ApplyView.cpp @@ -363,7 +363,8 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const& // Check whether the next page is the last page and, if // so, whether it's empty. If it is, delete it. - if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage && next->getFieldV256(sfIndexes).empty()) + if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage && + next->getFieldV256(sfIndexes).empty()) { // Since next doesn't point to the root, it // can't be pointing to prev. diff --git a/src/libxrpl/ledger/ApplyViewBase.cpp b/src/libxrpl/ledger/ApplyViewBase.cpp index ecb5180557..a5bea68759 100644 --- a/src/libxrpl/ledger/ApplyViewBase.cpp +++ b/src/libxrpl/ledger/ApplyViewBase.cpp @@ -40,7 +40,8 @@ ApplyViewBase::exists(Keylet const& k) const } auto -ApplyViewBase::succ(key_type const& key, std::optional const& last) const -> std::optional +ApplyViewBase::succ(key_type const& key, std::optional const& last) const + -> std::optional { return items_.succ(*base_, key, last); } diff --git a/src/libxrpl/ledger/ApplyViewImpl.cpp b/src/libxrpl/ledger/ApplyViewImpl.cpp index 9ff7b568ee..7fbd865db8 100644 --- a/src/libxrpl/ledger/ApplyViewImpl.cpp +++ b/src/libxrpl/ledger/ApplyViewImpl.cpp @@ -15,7 +15,8 @@ ApplyViewImpl::apply( bool isDryRun, beast::Journal j) { - return items_.apply(to, tx, ter, deliver_, parentBatchId, gasUsed_, wasmReturnCode_, isDryRun, j); + return items_.apply( + to, tx, ter, deliver_, parentBatchId, gasUsed_, wasmReturnCode_, isDryRun, j); } std::size_t diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index 03dd0b0e13..2bdf6ac9a5 100644 --- a/src/libxrpl/ledger/BookDirs.cpp +++ b/src/libxrpl/ledger/BookDirs.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace xrpl { @@ -74,7 +75,10 @@ BookDirs::const_iterator::operator++() XRPL_ASSERT(index_ != zero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index"); if (!cdirNext(*view_, cur_key_, sle_, entry_, index_)) { - if (index_ != 0 || (cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero)) == zero) + if (index_ == 0) + cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero); + + if (index_ != 0 || cur_key_ == zero) { cur_key_ = key_; entry_ = 0; @@ -83,9 +87,7 @@ BookDirs::const_iterator::operator++() else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_)) { // LCOV_EXCL_START - UNREACHABLE( - "xrpl::BookDirs::const_iterator::operator++ : directory is " - "empty"); + UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty"); // LCOV_EXCL_STOP } } @@ -97,7 +99,8 @@ BookDirs::const_iterator::operator++() BookDirs::const_iterator BookDirs::const_iterator::operator++(int) { - XRPL_ASSERT(index_ != beast::zero, "xrpl::BookDirs::const_iterator::operator++(int) : nonzero index"); + XRPL_ASSERT( + index_ != beast::zero, "xrpl::BookDirs::const_iterator::operator++(int) : nonzero index"); const_iterator tmp(*this); ++(*this); return tmp; diff --git a/src/xrpld/app/ledger/BookListeners.cpp b/src/libxrpl/ledger/BookListeners.cpp similarity index 94% rename from src/xrpld/app/ledger/BookListeners.cpp rename to src/libxrpl/ledger/BookListeners.cpp index 4d72c6f3b3..22b78e46bb 100644 --- a/src/xrpld/app/ledger/BookListeners.cpp +++ b/src/libxrpl/ledger/BookListeners.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { @@ -38,7 +38,9 @@ BookListeners::publish(MultiApiJson const& jvObj, hash_set& haveP ++it; } else + { it = mListeners.erase(it); + } } } diff --git a/src/libxrpl/ledger/CachedView.cpp b/src/libxrpl/ledger/CachedView.cpp index 12704d227e..5c15ccdac4 100644 --- a/src/libxrpl/ledger/CachedView.cpp +++ b/src/libxrpl/ledger/CachedView.cpp @@ -40,11 +40,17 @@ CachedViewImpl::read(Keylet const& k) const // If the sle is null, then a failure must have occurred in base_.read() XRPL_ASSERT(sle || baseRead, "xrpl::CachedView::read : null SLE result from base"); if (cacheHit && baseRead) + { hitsexpired.increment(); + } else if (cacheHit) + { hits.increment(); + } else + { misses.increment(); + } if (!cacheHit) { diff --git a/src/libxrpl/ledger/Credit.cpp b/src/libxrpl/ledger/Credit.cpp deleted file mode 100644 index 064fcdaf3b..0000000000 --- a/src/libxrpl/ledger/Credit.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -namespace xrpl { - -STAmount -creditLimit(ReadView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency) -{ - STAmount result(Issue{currency, account}); - - auto sleRippleState = view.read(keylet::line(account, issuer, currency)); - - if (sleRippleState) - { - result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit); - result.setIssuer(account); - } - - XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match"); - XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match"); - return result; -} - -IOUAmount -creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur) -{ - return toAmount(creditLimit(v, acc, iss, cur)); -} - -STAmount -creditBalance(ReadView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency) -{ - STAmount result(Issue{currency, account}); - - auto sleRippleState = view.read(keylet::line(account, issuer, currency)); - - if (sleRippleState) - { - result = sleRippleState->getFieldAmount(sfBalance); - if (account < issuer) - result.negate(); - result.setIssuer(account); - } - - XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match"); - XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match"); - return result; -} - -} // namespace xrpl diff --git a/src/libxrpl/ledger/Dir.cpp b/src/libxrpl/ledger/Dir.cpp index cf0b01aa7f..a27171fe12 100644 --- a/src/libxrpl/ledger/Dir.cpp +++ b/src/libxrpl/ledger/Dir.cpp @@ -4,7 +4,8 @@ namespace xrpl { using const_iterator = Dir::const_iterator; -Dir::Dir(ReadView const& view, Keylet const& key) : view_(&view), root_(key), sle_(view_->read(root_)) +Dir::Dir(ReadView const& view, Keylet const& key) + : view_(&view), root_(key), sle_(view_->read(root_)) { if (sle_ != nullptr) indexes_ = &sle_->getFieldV256(sfIndexes); diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index 73bd3e16ea..b5e358053c 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -10,7 +10,8 @@ private: txs_map::const_iterator iter_; public: - explicit txs_iter_impl(bool metadata, txs_map::const_iterator iter) : metadata_(metadata), iter_(iter) + explicit txs_iter_impl(bool metadata, txs_map::const_iterator iter) + : metadata_(metadata), iter_(iter) { } @@ -56,7 +57,8 @@ public: OpenView::OpenView(OpenView const& rhs) : ReadView(rhs) , TxsRawView(rhs) - , monotonic_resource_{std::make_unique(initialBufferSize)} + , monotonic_resource_{std::make_unique( + initialBufferSize)} , txs_{rhs.txs_, monotonic_resource_.get()} , rules_{rhs.rules_} , header_{rhs.header_} @@ -65,8 +67,13 @@ OpenView::OpenView(OpenView const& rhs) , hold_{rhs.hold_} , open_{rhs.open_} {}; -OpenView::OpenView(open_ledger_t, ReadView const* base, Rules const& rules, std::shared_ptr hold) - : monotonic_resource_{std::make_unique(initialBufferSize)} +OpenView::OpenView( + open_ledger_t, + ReadView const* base, + Rules const& rules, + std::shared_ptr hold) + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , txs_{monotonic_resource_.get()} , rules_(rules) , header_(base->header()) @@ -81,7 +88,8 @@ OpenView::OpenView(open_ledger_t, ReadView const* base, Rules const& rules, std: } OpenView::OpenView(ReadView const* base, std::shared_ptr hold) - : monotonic_resource_{std::make_unique(initialBufferSize)} + : monotonic_resource_{ + std::make_unique(initialBufferSize)} , txs_{monotonic_resource_.get()} , rules_(base->rules()) , header_(base->header()) @@ -132,7 +140,8 @@ OpenView::exists(Keylet const& k) const } auto -OpenView::succ(key_type const& key, std::optional const& last) const -> std::optional +OpenView::succ(key_type const& key, std::optional const& last) const + -> std::optional { return items_.succ(*base_, key, last); } @@ -176,7 +185,7 @@ OpenView::txsEnd() const -> std::unique_ptr bool OpenView::txExists(key_type const& key) const { - return txs_.find(key) != txs_.end(); + return txs_.contains(key); } auto @@ -189,9 +198,13 @@ OpenView::txRead(key_type const& key) const -> tx_type auto stx = std::make_shared(SerialIter{item.txn->slice()}); decltype(tx_type::second) sto; if (item.meta) + { sto = std::make_shared(SerialIter{item.meta->slice()}, sfMetadata); + } else + { sto = nullptr; + } return {std::move(stx), std::move(sto)}; } @@ -231,8 +244,8 @@ OpenView::rawTxInsert( std::shared_ptr const& txn, std::shared_ptr const& metaData) { - auto const result = - txs_.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData)); + auto const result = txs_.emplace( + std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData)); if (!result.second) LogicError("rawTxInsert: duplicate TX id: " + to_string(key)); } diff --git a/src/libxrpl/ledger/PaymentSandbox.cpp b/src/libxrpl/ledger/PaymentSandbox.cpp index 67492142da..1f84da1dcb 100644 --- a/src/libxrpl/ledger/PaymentSandbox.cpp +++ b/src/libxrpl/ledger/PaymentSandbox.cpp @@ -11,9 +11,11 @@ auto DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency const& c) -> Key { if (a1 < a2) + { return std::make_tuple(a1, a2, c); - else - return std::make_tuple(a2, a1, c); + } + + return std::make_tuple(a2, a1, c); } void @@ -23,7 +25,8 @@ DeferredCredits::credit( STAmount const& amount, STAmount const& preCreditSenderBalance) { - XRPL_ASSERT(sender != receiver, "xrpl::detail::DeferredCredits::credit : sender is not receiver"); + XRPL_ASSERT( + sender != receiver, "xrpl::detail::DeferredCredits::credit : sender is not receiver"); XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::credit : positive amount"); auto const k = makeKey(sender, receiver, amount.getCurrency()); @@ -52,9 +55,13 @@ DeferredCredits::credit( // only record the balance the first time, do not record it here auto& v = i->second; if (sender < receiver) + { v.highAcctCredits += amount; + } else + { v.lowAcctCredits += amount; + } } } @@ -81,8 +88,10 @@ DeferredCredits::ownerCount(AccountID const& id) const // Get the adjustments for the balance between main and other. auto -DeferredCredits::adjustments(AccountID const& main, AccountID const& other, Currency const& currency) const - -> std::optional +DeferredCredits::adjustments( + AccountID const& main, + AccountID const& other, + Currency const& currency) const -> std::optional { std::optional result; @@ -98,11 +107,9 @@ DeferredCredits::adjustments(AccountID const& main, AccountID const& other, Curr result.emplace(v.highAcctCredits, v.lowAcctCredits, v.lowAcctOrigBalance); return result; } - else - { - result.emplace(v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance); - return result; - } + + result.emplace(v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance); + return result; } void @@ -136,7 +143,10 @@ DeferredCredits::apply(DeferredCredits& to) } // namespace detail STAmount -PaymentSandbox::balanceHook(AccountID const& account, AccountID const& issuer, STAmount const& amount) const +PaymentSandbox::balanceHook( + AccountID const& account, + AccountID const& issuer, + STAmount const& amount) const { /* There are two algorithms here. The pre-switchover algorithm takes the @@ -173,11 +183,13 @@ PaymentSandbox::balanceHook(AccountID const& account, AccountID const& issuer, S adjustedAmt.setIssuer(amount.getIssuer()); if (isXRP(issuer) && adjustedAmt < beast::zero) + { // A calculated negative XRP balance is not an error case. Consider a // payment snippet that credits a large XRP amount and then debits the // same amount. The credit can't be used but we subtract the debit and // calculate a negative value. It's not an error case. adjustedAmt.clear(); + } return adjustedAmt; } @@ -205,7 +217,10 @@ PaymentSandbox::creditHook( } void -PaymentSandbox::adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) +PaymentSandbox::adjustOwnerCountHook( + AccountID const& account, + std::uint32_t cur, + std::uint32_t next) { tab_.ownerCount(account, cur, next); } diff --git a/src/libxrpl/ledger/RawStateTable.cpp b/src/libxrpl/ledger/RawStateTable.cpp index 4b5f6837b2..b411a25b00 100644 --- a/src/libxrpl/ledger/RawStateTable.cpp +++ b/src/libxrpl/ledger/RawStateTable.cpp @@ -94,9 +94,13 @@ public: dereference() const override { if (!sle1_) + { return sle0_; - else if (!sle0_) + } + if (!sle0_) + { return sle1_; + } if (sle1_->key() <= sle0_->key()) return sle1_; return sle0_; @@ -108,9 +112,13 @@ private: { ++iter0_; if (iter0_ == end0_) + { sle0_ = nullptr; + } else + { sle0_ = *iter0_; + } } void @@ -118,15 +126,20 @@ private: { ++iter1_; if (iter1_ == end1_) + { sle1_ = nullptr; + } else + { sle1_ = iter1_->second.sle; + } } void skip() { - while (iter1_ != end1_ && iter1_->second.action == Action::erase && sle0_->key() == sle1_->key()) + while (iter1_ != end1_ && iter1_->second.action == Action::erase && + sle0_->key() == sle1_->key()) { inc1(); inc0(); @@ -182,8 +195,8 @@ RawStateTable::exists(ReadView const& base, Keylet const& k) const the lower of the two. */ auto -RawStateTable::succ(ReadView const& base, key_type const& key, std::optional const& last) const - -> std::optional +RawStateTable::succ(ReadView const& base, key_type const& key, std::optional const& last) + const -> std::optional { std::optional next = key; items_t::const_iterator iter; @@ -219,7 +232,9 @@ RawStateTable::erase(std::shared_ptr const& sle) { // The base invariant is checked during apply auto const result = items_.emplace( - std::piecewise_construct, std::forward_as_tuple(sle->key()), std::forward_as_tuple(Action::erase, sle)); + std::piecewise_construct, + std::forward_as_tuple(sle->key()), + std::forward_as_tuple(Action::erase, sle)); if (result.second) return; auto& item = result.first->second; @@ -242,7 +257,9 @@ void RawStateTable::insert(std::shared_ptr const& sle) { auto const result = items_.emplace( - std::piecewise_construct, std::forward_as_tuple(sle->key()), std::forward_as_tuple(Action::insert, sle)); + std::piecewise_construct, + std::forward_as_tuple(sle->key()), + std::forward_as_tuple(Action::insert, sle)); if (result.second) return; auto& item = result.first->second; @@ -265,7 +282,9 @@ void RawStateTable::replace(std::shared_ptr const& sle) { auto const result = items_.emplace( - std::piecewise_construct, std::forward_as_tuple(sle->key()), std::forward_as_tuple(Action::replace, sle)); + std::piecewise_construct, + std::forward_as_tuple(sle->key()), + std::forward_as_tuple(Action::replace, sle)); if (result.second) return; auto& item = result.first->second; @@ -306,13 +325,15 @@ RawStateTable::destroyXRP(XRPAmount const& fee) std::unique_ptr RawStateTable::slesBegin(ReadView const& base) const { - return std::make_unique(items_.begin(), items_.end(), base.sles.begin(), base.sles.end()); + return std::make_unique( + items_.begin(), items_.end(), base.sles.begin(), base.sles.end()); } std::unique_ptr RawStateTable::slesEnd(ReadView const& base) const { - return std::make_unique(items_.end(), items_.end(), base.sles.end(), base.sles.end()); + return std::make_unique( + items_.end(), items_.end(), base.sles.end(), base.sles.end()); } std::unique_ptr diff --git a/src/libxrpl/ledger/ReadView.cpp b/src/libxrpl/ledger/ReadView.cpp index d6674ba0da..e0764d6c81 100644 --- a/src/libxrpl/ledger/ReadView.cpp +++ b/src/libxrpl/ledger/ReadView.cpp @@ -53,7 +53,9 @@ makeRulesGivenLedger(DigestAwareReadView const& ledger, Rules const& current) } Rules -makeRulesGivenLedger(DigestAwareReadView const& ledger, std::unordered_set> const& presets) +makeRulesGivenLedger( + DigestAwareReadView const& ledger, + std::unordered_set> const& presets) { Keylet const k = keylet::amendments(); std::optional digest = ledger.digest(k.key); diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 72df44ec4a..ef6a66744d 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -2,10 +2,12 @@ #include #include #include -#include -#include #include #include +#include +#include +#include +#include #include #include #include @@ -22,103 +24,6 @@ namespace xrpl { -namespace detail { - -template < - class V, - class N, - class = std::enable_if_t, SLE> && std::is_base_of_v>> -bool -internalDirNext(V& view, uint256 const& root, std::shared_ptr& page, unsigned int& index, uint256& entry) -{ - auto const& svIndexes = page->getFieldV256(sfIndexes); - XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range"); - - if (index >= svIndexes.size()) - { - auto const next = page->getFieldU64(sfIndexNext); - - if (!next) - { - entry.zero(); - return false; - } - - if constexpr (std::is_const_v) - page = view.read(keylet::page(root, next)); - else - page = view.peek(keylet::page(root, next)); - - XRPL_ASSERT(page, "xrpl::detail::internalDirNext : non-null root"); - - if (!page) - return false; - - index = 0; - - return internalDirNext(view, root, page, index, entry); - } - - entry = svIndexes[index++]; - return true; -} - -template < - class V, - class N, - class = std::enable_if_t, SLE> && std::is_base_of_v>> -bool -internalDirFirst(V& view, uint256 const& root, std::shared_ptr& page, unsigned int& index, uint256& entry) -{ - if constexpr (std::is_const_v) - page = view.read(keylet::page(root)); - else - page = view.peek(keylet::page(root)); - - if (!page) - return false; - - index = 0; - - return internalDirNext(view, root, page, index, entry); -} - -} // namespace detail - -bool -dirFirst(ApplyView& view, uint256 const& root, std::shared_ptr& page, unsigned int& index, uint256& entry) -{ - return detail::internalDirFirst(view, root, page, index, entry); -} - -bool -dirNext(ApplyView& view, uint256 const& root, std::shared_ptr& page, unsigned int& index, uint256& entry) -{ - return detail::internalDirNext(view, root, page, index, entry); -} - -bool -cdirFirst( - ReadView const& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirFirst(view, root, page, index, entry); -} - -bool -cdirNext( - ReadView const& view, - uint256 const& root, - std::shared_ptr& page, - unsigned int& index, - uint256& entry) -{ - return detail::internalDirNext(view, root, page, index, entry); -} - //------------------------------------------------------------------------------ // // Observers @@ -135,109 +40,11 @@ hasExpired(ReadView const& view, std::optional const& exp) } bool -isGlobalFrozen(ReadView const& view, AccountID const& issuer) -{ - if (isXRP(issuer)) - return false; - if (auto const sle = view.read(keylet::account(issuer))) - return sle->isFlag(lsfGlobalFreeze); - return false; -} - -bool -isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue) -{ - if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()))) - return sle->isFlag(lsfMPTLocked); - return false; -} - -bool -isGlobalFrozen(ReadView const& view, Asset const& asset) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return isGlobalFrozen(view, issue.getIssuer()); - else - return isGlobalFrozen(view, issue); - }, - asset.value()); -} - -bool -isIndividualFrozen(ReadView const& view, AccountID const& account, Currency const& currency, AccountID const& issuer) -{ - if (isXRP(currency)) - return false; - if (issuer != account) - { - // Check if the issuer froze the line - auto const sle = view.read(keylet::line(account, issuer, currency)); - if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) - return true; - } - return false; -} - -bool -isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) -{ - if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account))) - return sle->isFlag(lsfMPTLocked); - return false; -} - -// Can the specified account spend the specified currency issued by -// the specified issuer or does the freeze flag prohibit it? -bool -isFrozen(ReadView const& view, AccountID const& account, Currency const& currency, AccountID const& issuer) -{ - if (isXRP(currency)) - return false; - auto sle = view.read(keylet::account(issuer)); - if (sle && sle->isFlag(lsfGlobalFreeze)) - return true; - if (issuer != account) - { - // Check if the issuer froze the line - sle = view.read(keylet::line(account, issuer, currency)); - if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) - return true; - } - return false; -} - -bool -isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) -{ - return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) || - isVaultPseudoAccountFrozen(view, account, mptIssue, depth); -} - -[[nodiscard]] bool -isAnyFrozen(ReadView const& view, std::initializer_list const& accounts, MPTIssue const& mptIssue, int depth) -{ - if (isGlobalFrozen(view, mptIssue)) - return true; - - for (auto const& account : accounts) - { - if (isIndividualFrozen(view, account, mptIssue)) - return true; - } - - for (auto const& account : accounts) - { - if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth)) - return true; - } - - return false; -} - -bool -isVaultPseudoAccountFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptShare, int depth) +isVaultPseudoAccountFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptShare, + int depth) { if (!view.rules().enabled(featureSingleAssetVault)) return false; @@ -274,472 +81,30 @@ isVaultPseudoAccountFrozen(ReadView const& view, AccountID const& account, MPTIs } bool -isDeepFrozen(ReadView const& view, AccountID const& account, Currency const& currency, AccountID const& issuer) -{ - if (isXRP(currency)) - { - return false; - } - - if (issuer == account) - { - return false; - } - - auto const sle = view.read(keylet::line(account, issuer, currency)); - if (!sle) - { - return false; - } - - return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze); -} - -bool -isLPTokenFrozen(ReadView const& view, AccountID const& account, Issue const& asset, Issue const& asset2) +isLPTokenFrozen( + ReadView const& view, + AccountID const& account, + Issue const& asset, + Issue const& asset2) { return isFrozen(view, account, asset.currency, asset.account) || isFrozen(view, account, asset2.currency, asset2.account); } -static SLE::const_pointer -getLineIfUsable( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen, - beast::Journal j) -{ - auto const sle = view.read(keylet::line(account, issuer, currency)); - - if (!sle) - { - return nullptr; - } - - if (zeroIfFrozen == fhZERO_IF_FROZEN) - { - if (isFrozen(view, account, currency, issuer) || isDeepFrozen(view, account, currency, issuer)) - { - return nullptr; - } - - // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken, - // we need to check if the associated assets have been frozen - if (view.rules().enabled(fixFrozenLPTokenTransfer)) - { - auto const sleIssuer = view.read(keylet::account(issuer)); - if (!sleIssuer) - { - return nullptr; // LCOV_EXCL_LINE - } - else if (sleIssuer->isFieldPresent(sfAMMID)) - { - auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID])); - - if (!sleAmm || - isLPTokenFrozen(view, account, (*sleAmm)[sfAsset].get(), (*sleAmm)[sfAsset2].get())) - { - return nullptr; - } - } - } - } - - return sle; -} - -static STAmount -getTrustLineBalance( - ReadView const& view, - SLE::const_ref sle, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - bool includeOppositeLimit, - beast::Journal j) -{ - STAmount amount; - if (sle) - { - amount = sle->getFieldAmount(sfBalance); - bool const accountHigh = account > issuer; - auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit; - if (accountHigh) - { - // Put balance in account terms. - amount.negate(); - } - if (includeOppositeLimit) - { - amount += sle->getFieldAmount(oppositeField); - } - amount.setIssuer(issuer); - } - else - { - amount.clear(Issue{currency, issuer}); - } - - JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account) - << " amount=" << amount.getFullText(); - - return view.balanceHook(account, issuer, amount); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - STAmount amount; - if (isXRP(currency)) - { - return {xrpLiquid(view, account, 0, j)}; - } - - bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); - if (returnSpendable && account == issuer) - // If the account is the issuer, then their limit is effectively - // infinite - return STAmount{Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset}; - - // IOU: Return balance on trust line modulo freeze - SLE::const_pointer const sle = getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j); - - return getTrustLineBalance(view, sle, account, currency, issuer, returnSpendable, j); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Issue const& issue, - FreezeHandling zeroIfFrozen, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - return accountHolds(view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance); -} - -STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - MPTIssue const& mptIssue, - FreezeHandling zeroIfFrozen, - AuthHandling zeroIfUnauthorized, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); - - if (returnSpendable && account == mptIssue.getIssuer()) - { - // if the account is the issuer, and the issuance exists, their limit is - // the issuance limit minus the outstanding value - auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); - - if (!issuance) - { - return STAmount{mptIssue}; - } - return STAmount{ - mptIssue, issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) - issuance->at(sfOutstandingAmount)}; - } - - STAmount amount; - - auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account)); - - if (!sleMpt) - amount.clear(mptIssue); - else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue)) - amount.clear(mptIssue); - else - { - amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)}; - - // Only if auth check is needed, as it needs to do an additional read - // operation. Note featureSingleAssetVault will affect error codes. - if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && view.rules().enabled(featureSingleAssetVault)) - { - if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); !isTesSuccess(err)) - amount.clear(mptIssue); - } - else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED) - { - auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); - - // if auth is enabled on the issuance and mpt is not authorized, - // clear amount - if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) && !sleMpt->isFlag(lsfMPTAuthorized)) - amount.clear(mptIssue); - } - } - - return amount; -} - -[[nodiscard]] STAmount -accountHolds( - ReadView const& view, - AccountID const& account, - Asset const& asset, - FreezeHandling zeroIfFrozen, - AuthHandling zeroIfUnauthorized, - beast::Journal j, - SpendableHandling includeFullBalance) -{ - return std::visit( - [&](TIss const& value) { - if constexpr (std::is_same_v) - { - return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance); - } - else if constexpr (std::is_same_v) - { - return accountHolds(view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance); - } - }, - asset.value()); -} - -STAmount -accountFunds( - ReadView const& view, - AccountID const& id, - STAmount const& saDefault, - FreezeHandling freezeHandling, - beast::Journal j) -{ - if (!saDefault.native() && saDefault.getIssuer() == id) - return saDefault; - - return accountHolds(view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j); -} - -// Prevent ownerCount from wrapping under error conditions. -// -// adjustment allows the ownerCount to be adjusted up or down in multiple steps. -// If id != std::nullopt, then do error reporting. -// -// Returns adjusted owner count. -static std::uint32_t -confineOwnerCount( - std::uint32_t current, - std::int32_t adjustment, - std::optional const& id = std::nullopt, - beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) -{ - std::uint32_t adjusted{current + adjustment}; - if (adjustment > 0) - { - // Overflow is well defined on unsigned - if (adjusted < current) - { - if (id) - { - JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; - } - adjusted = std::numeric_limits::max(); - } - } - else - { - // Underflow is well defined on unsigned - if (adjusted > current) - { - if (id) - { - JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!"; - } - adjusted = 0; - XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set"); - } - } - return adjusted; -} - -XRPAmount -xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) -{ - auto const sle = view.read(keylet::account(id)); - if (sle == nullptr) - return beast::zero; - - // Return balance minus reserve - std::uint32_t const ownerCount = - confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - - // Pseudo-accounts have no reserve requirement - auto const reserve = isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount); - - auto const fullBalance = sle->getFieldAmount(sfBalance); - - auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); - - STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve; - - JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id) << " amount=" << amount.getFullText() - << " fullBalance=" << fullBalance.getFullText() << " balance=" << balance.getFullText() - << " reserve=" << reserve << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj; - - return amount.xrp(); -} - -void -forEachItem(ReadView const& view, Keylet const& root, std::function const&)> const& f) -{ - XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); - - if (root.type != ltDIR_NODE) - return; - - auto pos = root; - - while (true) - { - auto sle = view.read(pos); - if (!sle) - return; - for (auto const& key : sle->getFieldV256(sfIndexes)) - f(view.read(keylet::child(key))); - auto const next = sle->getFieldU64(sfIndexNext); - if (!next) - return; - pos = keylet::page(root, next); - } -} - bool -forEachItemAfter( - ReadView const& view, - Keylet const& root, - uint256 const& after, - std::uint64_t const hint, - unsigned int limit, - std::function const&)> const& f) -{ - XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); - - if (root.type != ltDIR_NODE) - return false; - - auto currentIndex = root; - - // If startAfter is not zero try jumping to that page using the hint - if (after.isNonZero()) - { - auto const hintIndex = keylet::page(root, hint); - - if (auto hintDir = view.read(hintIndex)) - { - for (auto const& key : hintDir->getFieldV256(sfIndexes)) - { - if (key == after) - { - // We found the hint, we can start here - currentIndex = hintIndex; - break; - } - } - } - - bool found = false; - for (;;) - { - auto const ownerDir = view.read(currentIndex); - if (!ownerDir) - return found; - for (auto const& key : ownerDir->getFieldV256(sfIndexes)) - { - if (!found) - { - if (key == after) - found = true; - } - else if (f(view.read(keylet::child(key))) && limit-- <= 1) - { - return found; - } - } - - auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); - if (uNodeNext == 0) - return found; - currentIndex = keylet::page(root, uNodeNext); - } - } - else - { - for (;;) - { - auto const ownerDir = view.read(currentIndex); - if (!ownerDir) - return true; - for (auto const& key : ownerDir->getFieldV256(sfIndexes)) - if (f(view.read(keylet::child(key))) && limit-- <= 1) - return true; - auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); - if (uNodeNext == 0) - return true; - currentIndex = keylet::page(root, uNodeNext); - } - } -} - -Rate -transferRate(ReadView const& view, AccountID const& issuer) -{ - auto const sle = view.read(keylet::account(issuer)); - - if (sle && sle->isFieldPresent(sfTransferRate)) - return Rate{sle->getFieldU32(sfTransferRate)}; - - return parityRate; -} - -Rate -transferRate(ReadView const& view, MPTID const& issuanceID) -{ - // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000 - // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000 - // which represents 50% of 1,000,000,000 - if (auto const sle = view.read(keylet::mptIssuance(issuanceID)); sle && sle->isFieldPresent(sfTransferFee)) - return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)}; - - return parityRate; -} - -Rate -transferRate(ReadView const& view, STAmount const& amount) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return transferRate(view, issue.getIssuer()); - else - return transferRate(view, issue.getMptID()); - }, - amount.asset().value()); -} - -bool -areCompatible(ReadView const& validLedger, ReadView const& testLedger, beast::Journal::Stream& s, char const* reason) +areCompatible( + ReadView const& validLedger, + ReadView const& testLedger, + beast::Journal::Stream& s, + char const* reason) { bool ret = true; if (validLedger.header().seq < testLedger.header().seq) { // valid -> ... -> test - auto hash = hashOfSeq(testLedger, validLedger.header().seq, beast::Journal{beast::Journal::getNullSink()}); + auto hash = hashOfSeq( + testLedger, validLedger.header().seq, beast::Journal{beast::Journal::getNullSink()}); if (hash && (*hash != validLedger.header().hash)) { JLOG(s) << reason << " incompatible with valid ledger"; @@ -752,7 +117,8 @@ areCompatible(ReadView const& validLedger, ReadView const& testLedger, beast::Jo else if (validLedger.header().seq > testLedger.header().seq) { // test -> ... -> valid - auto hash = hashOfSeq(validLedger, testLedger.header().seq, beast::Journal{beast::Journal::getNullSink()}); + auto hash = hashOfSeq( + validLedger, testLedger.header().seq, beast::Journal{beast::Journal::getNullSink()}); if (hash && (*hash != testLedger.header().hash)) { JLOG(s) << reason << " incompatible preceding ledger"; @@ -774,7 +140,8 @@ areCompatible(ReadView const& validLedger, ReadView const& testLedger, beast::Jo if (!ret) { - JLOG(s) << "Val: " << validLedger.header().seq << " " << to_string(validLedger.header().hash); + JLOG(s) << "Val: " << validLedger.header().seq << " " + << to_string(validLedger.header().hash); JLOG(s) << "New: " << testLedger.header().seq << " " << to_string(testLedger.header().hash); } @@ -795,7 +162,8 @@ areCompatible( if (testLedger.header().seq > validIndex) { // Ledger we are testing follows last valid ledger - auto hash = hashOfSeq(testLedger, validIndex, beast::Journal{beast::Journal::getNullSink()}); + auto hash = + hashOfSeq(testLedger, validIndex, beast::Journal{beast::Journal::getNullSink()}); if (hash && (*hash != validHash)) { JLOG(s) << reason << " incompatible following ledger"; @@ -821,20 +189,6 @@ areCompatible( return ret; } -bool -dirIsEmpty(ReadView const& view, Keylet const& k) -{ - auto const sleNode = view.read(k); - if (!sleNode) - return true; - if (!sleNode->getFieldV256(sfIndexes).empty()) - return false; - // The first page of a directory may legitimately be empty even if there - // are other pages (the first page is the anchor page) so check to see if - // there is another page. If there is, the directory isn't empty. - return sleNode->getFieldU64(sfIndexNext) == 0; -} - std::set getEnabledAmendments(ReadView const& view) { @@ -900,12 +254,13 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) STVector256 vec = hashIndex->getFieldV256(sfHashes); if (vec.size() >= diff) return vec[vec.size() - diff]; - JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " (" << vec.size() - << "," << diff << ")"; + JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " (" + << vec.size() << "," << diff << ")"; } else { - JLOG(journal.warn()) << "Ledger " << ledger.seq() << ":" << ledger.header().hash << " missing normal list"; + JLOG(journal.warn()) << "Ledger " << ledger.seq() << ":" << ledger.header().hash + << " missing normal list"; } } @@ -937,187 +292,21 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) // //------------------------------------------------------------------------------ -void -adjustOwnerCount(ApplyView& view, std::shared_ptr const& sle, std::int32_t amount, beast::Journal j) -{ - if (!sle) - return; - XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); - std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; - AccountID const id = (*sle)[sfAccount]; - std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); - sle->at(sfOwnerCount) = adjusted; - view.update(sle); -} - -std::function -describeOwnerDir(AccountID const& account) -{ - return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; -} - TER -dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr& object, SF_UINT64 const& node) +dirLink( + ApplyView& view, + AccountID const& owner, + std::shared_ptr& object, + SF_UINT64 const& node) { - auto const page = view.dirInsert(keylet::ownerDir(owner), object->key(), describeOwnerDir(owner)); + auto const page = + view.dirInsert(keylet::ownerDir(owner), object->key(), describeOwnerDir(owner)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE object->setFieldU64(node, *page); return tesSUCCESS; } -AccountID -pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) -{ - // This number must not be changed without an amendment - constexpr std::uint16_t maxAccountAttempts = 256; - for (std::uint16_t i = 0; i < maxAccountAttempts; ++i) - { - ripesha_hasher rsh; - auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); - rsh(hash.data(), hash.size()); - AccountID const ret{static_cast(rsh)}; - if (!view.read(keylet::account(ret))) - return ret; - } - return beast::zero; -} - -// Pseudo-account designator fields MUST be maintained by including the -// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to -// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated, -// since a non-active amendment will not set any field, by definition. -// Specific properties of a pseudo-account are NOT checked here, that's what -// InvariantCheck is for. -[[nodiscard]] std::vector const& -getPseudoAccountFields() -{ - static std::vector const pseudoFields = []() { - auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT); - if (!ar) - { - // LCOV_EXCL_START - LogicError( - "xrpl::getPseudoAccountFields : unable to find account root " - "ledger format"); - // LCOV_EXCL_STOP - } - auto const& soTemplate = ar->getSOTemplate(); - - std::vector pseudoFields; - for (auto const& field : soTemplate) - { - if (field.sField().shouldMeta(SField::sMD_PseudoAccount)) - pseudoFields.emplace_back(&field.sField()); - } - return pseudoFields; - }(); - return pseudoFields; -} - -[[nodiscard]] bool -isPseudoAccount(std::shared_ptr sleAcct, std::set const& pseudoFieldFilter) -{ - auto const& fields = getPseudoAccountFields(); - - // Intentionally use defensive coding here because it's cheap and makes the - // semantics of true return value clean. - return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT && - std::count_if(fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](SField const* sf) -> bool { - return sleAcct->isFieldPresent(*sf) && (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf)); - }) > 0; -} - -Expected, TER> -createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) -{ - [[maybe_unused]] - auto const& fields = getPseudoAccountFields(); - XRPL_ASSERT( - std::count_if( - fields.begin(), fields.end(), [&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1, - "xrpl::createPseudoAccount : valid owner field"); - - auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); - if (accountId == beast::zero) - return Unexpected(tecDUPLICATE); - - // Create pseudo-account. - auto account = std::make_shared(keylet::account(accountId)); - account->setAccountID(sfAccount, accountId); - account->setFieldAmount(sfBalance, STAmount{}); - - // Pseudo-accounts can't submit transactions, so set the sequence number - // to 0 to make them easier to spot and verify, and add an extra level - // of protection. - std::uint32_t const seqno = // - view.rules().enabled(featureSingleAssetVault) || // - view.rules().enabled(featureLendingProtocol) // - ? 0 // - : view.seq(); - account->setFieldU32(sfSequence, seqno); - // Ignore reserves requirement, disable the master key, allow default - // rippling, and enable deposit authorization to prevent payments into - // pseudo-account. - account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); - // Link the pseudo-account with its owner object. - account->setFieldH256(ownerField, pseudoOwnerKey); - - view.insert(account); - - return account; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, Issue const& issue) -{ - if (issue.native()) - return tesSUCCESS; // No special checks for XRP - - auto const issuer = view.read(keylet::account(issue.getIssuer())); - if (!issuer) - return terNO_ACCOUNT; - else if (!issuer->isFlag(lsfDefaultRipple)) - return terNO_RIPPLE; - - return tesSUCCESS; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, MPTIssue const& mptIssue) -{ - auto mptID = mptIssue.getMptID(); - auto issuance = view.read(keylet::mptIssuance(mptID)); - if (!issuance) - return tecOBJECT_NOT_FOUND; - if (!issuance->isFlag(lsfMPTCanTransfer)) - return tecNO_AUTH; - - return tesSUCCESS; -} - -[[nodiscard]] TER -canAddHolding(ReadView const& view, Asset const& asset) -{ - return std::visit( - [&](TIss const& issue) -> TER { return canAddHolding(view, issue); }, asset.value()); -} - -[[nodiscard]] TER -checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag) -{ - if (toSle == nullptr) - return tecNO_DST; - - // The tag is basically account-specific information we don't - // understand, but we can require someone to fill it in. - if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag) - return tecDST_TAG_NEEDED; // Cannot send without a tag - - return tesSUCCESS; -} - /* * Checks if a withdrawal amount into the destination account exceeds * any applicable receiving limit. @@ -1135,7 +324,11 @@ checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag) * even if `from` is the issuer. */ static TER -withdrawToDestExceedsLimit(ReadView const& view, AccountID const& from, AccountID const& to, STAmount const& amount) +withdrawToDestExceedsLimit( + ReadView const& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount) { auto const& issuer = amount.getIssuer(); if (from == to || to == issuer || isXRP(issuer)) @@ -1225,14 +418,19 @@ doWithdraw( } else { - auto dstSle = view.peek(keylet::account(dstAcct)); + auto dstSle = view.read(keylet::account(dstAcct)); if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j)) return err; } // Sanity check if (accountHolds( - view, sourceAcct, amount.asset(), FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j) < amount) + view, + sourceAcct, + amount.asset(), + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j) < amount) { // LCOV_EXCL_START JLOG(j.error()) << "LoanBrokerCoverWithdraw: negative balance of " @@ -1246,1718 +444,6 @@ doWithdraw( return accountSend(view, sourceAcct, dstAcct, amount, j, WaiveTransferFee::Yes); } -[[nodiscard]] TER -addEmptyHolding( - ApplyView& view, - AccountID const& accountID, - XRPAmount priorBalance, - Issue const& issue, - beast::Journal journal) -{ - // Every account can hold XRP. An issuer can issue directly. - if (issue.native() || accountID == issue.getIssuer()) - return tesSUCCESS; - - auto const& issuerId = issue.getIssuer(); - auto const& currency = issue.currency; - if (isGlobalFrozen(view, issuerId)) - return tecFROZEN; // LCOV_EXCL_LINE - - auto const& srcId = issuerId; - auto const& dstId = accountID; - auto const high = srcId > dstId; - auto const index = keylet::line(srcId, dstId, currency); - auto const sleSrc = view.peek(keylet::account(srcId)); - auto const sleDst = view.peek(keylet::account(dstId)); - if (!sleDst || !sleSrc) - return tefINTERNAL; // LCOV_EXCL_LINE - if (!sleSrc->isFlag(lsfDefaultRipple)) - return tecINTERNAL; // LCOV_EXCL_LINE - // If the line already exists, don't create it again. - if (view.read(index)) - return tecDUPLICATE; - - // Can the account cover the trust line reserve ? - std::uint32_t const ownerCount = sleDst->at(sfOwnerCount); - if (priorBalance < view.fees().accountReserve(ownerCount + 1)) - return tecNO_LINE_INSUF_RESERVE; - - return trustCreate( - view, - high, - srcId, - dstId, - index.key, - sleDst, - /*auth=*/false, - /*noRipple=*/true, - /*freeze=*/false, - /*deepFreeze*/ false, - /*balance=*/STAmount{Issue{currency, noAccount()}}, - /*limit=*/STAmount{Issue{currency, dstId}}, - /*qualityIn=*/0, - /*qualityOut=*/0, - journal); -} - -[[nodiscard]] TER -addEmptyHolding( - ApplyView& view, - AccountID const& accountID, - XRPAmount priorBalance, - MPTIssue const& mptIssue, - beast::Journal journal) -{ - auto const& mptID = mptIssue.getMptID(); - auto const mpt = view.peek(keylet::mptIssuance(mptID)); - if (!mpt) - return tefINTERNAL; // LCOV_EXCL_LINE - if (mpt->isFlag(lsfMPTLocked)) - return tefINTERNAL; // LCOV_EXCL_LINE - if (view.peek(keylet::mptoken(mptID, accountID))) - return tecDUPLICATE; - if (accountID == mptIssue.getIssuer()) - return tesSUCCESS; - - return authorizeMPToken(view, priorBalance, mptID, accountID, journal); -} - -[[nodiscard]] TER -authorizeMPToken( - ApplyView& view, - XRPAmount const& priorBalance, - MPTID const& mptIssuanceID, - AccountID const& account, - beast::Journal journal, - std::uint32_t flags, - std::optional holderID) -{ - auto const sleAcct = view.peek(keylet::account(account)); - if (!sleAcct) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the account that submitted the tx is a holder - // Note: `account_` is holder's account - // `holderID` is NOT used - if (!holderID) - { - // When a holder wants to unauthorize/delete a MPT, the ledger must - // - delete mptokenKey from owner directory - // - delete the MPToken - if (flags & tfMPTUnauthorize) - { - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto const sleMpt = view.peek(mptokenKey); - if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0) - return tecINTERNAL; // LCOV_EXCL_LINE - - if (!view.dirRemove(keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleAcct, -1, journal); - - view.erase(sleMpt); - return tesSUCCESS; - } - - // A potential holder wants to authorize/hold a mpt, the ledger must: - // - add the new mptokenKey to the owner directory - // - create the MPToken object for the holder - - // The reserve that is required to create the MPToken. Note - // that although the reserve increases with every item - // an account owns, in the case of MPTokens we only - // *enforce* a reserve if the user owns more than two - // items. This is similar to the reserve requirements of trust lines. - std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); - XRPAmount const reserveCreate( - (uOwnerCount < 2) ? XRPAmount(beast::zero) : view.fees().accountReserve(uOwnerCount + 1)); - - if (priorBalance < reserveCreate) - return tecINSUFFICIENT_RESERVE; - - // Defensive check before we attempt to create MPToken for the issuer - auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!mpt || mpt->getAccountID(sfIssuer) == account) - { - // LCOV_EXCL_START - UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token"); - if (view.rules().enabled(featureLendingProtocol)) - return tecINTERNAL; - // LCOV_EXCL_STOP - } - - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto mptoken = std::make_shared(mptokenKey); - if (auto ter = dirLink(view, account, mptoken)) - return ter; // LCOV_EXCL_LINE - - (*mptoken)[sfAccount] = account; - (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; - (*mptoken)[sfFlags] = 0; - view.insert(mptoken); - - // Update owner count. - adjustOwnerCount(view, sleAcct, 1, journal); - - return tesSUCCESS; - } - - auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!sleMptIssuance) - return tecINTERNAL; // LCOV_EXCL_LINE - - // If the account that submitted this tx is the issuer of the MPT - // Note: `account_` is issuer's account - // `holderID` is holder's account - if (account != (*sleMptIssuance)[sfIssuer]) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID)); - if (!sleMpt) - return tecINTERNAL; // LCOV_EXCL_LINE - - std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags); - std::uint32_t flagsOut = flagsIn; - - // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on - // their MPToken - if (flags & tfMPTUnauthorize) - flagsOut &= ~lsfMPTAuthorized; - // Issuer wants to authorize a holder, set lsfMPTAuthorized on their - // MPToken - else - flagsOut |= lsfMPTAuthorized; - - if (flagsIn != flagsOut) - sleMpt->setFieldU32(sfFlags, flagsOut); - - view.update(sleMpt); - return tesSUCCESS; -} - -TER -trustCreate( - ApplyView& view, - bool const bSrcHigh, - AccountID const& uSrcAccountID, - AccountID const& uDstAccountID, - uint256 const& uIndex, // --> ripple state entry - SLE::ref sleAccount, // --> the account being set. - bool const bAuth, // --> authorize account. - bool const bNoRipple, // --> others cannot ripple through - bool const bFreeze, // --> funds cannot leave - bool bDeepFreeze, // --> can neither receive nor send funds - STAmount const& saBalance, // --> balance of account being set. - // Issuer should be noAccount() - STAmount const& saLimit, // --> limit for account being set. - // Issuer should be the account being set. - std::uint32_t uQualityIn, - std::uint32_t uQualityOut, - beast::Journal j) -{ - JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " << to_string(uDstAccountID) << ", " - << saBalance.getFullText(); - - auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; - auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; - if (uLowAccountID == uHighAccountID) - { - // LCOV_EXCL_START - UNREACHABLE("xrpl::trustCreate : trust line to self"); - if (view.rules().enabled(featureLendingProtocol)) - return tecINTERNAL; - // LCOV_EXCL_STOP - } - - auto const sleRippleState = std::make_shared(ltRIPPLE_STATE, uIndex); - view.insert(sleRippleState); - - auto lowNode = - view.dirInsert(keylet::ownerDir(uLowAccountID), sleRippleState->key(), describeOwnerDir(uLowAccountID)); - - if (!lowNode) - return tecDIR_FULL; // LCOV_EXCL_LINE - - auto highNode = - view.dirInsert(keylet::ownerDir(uHighAccountID), sleRippleState->key(), describeOwnerDir(uHighAccountID)); - - if (!highNode) - return tecDIR_FULL; // LCOV_EXCL_LINE - - bool const bSetDst = saLimit.getIssuer() == uDstAccountID; - bool const bSetHigh = bSrcHigh ^ bSetDst; - - XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE"); - if (!sleAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - XRPL_ASSERT( - sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), - "xrpl::trustCreate : matching account ID"); - auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID)); - if (!slePeer) - return tecNO_TARGET; - - // Remember deletion hints. - sleRippleState->setFieldU64(sfLowNode, *lowNode); - sleRippleState->setFieldU64(sfHighNode, *highNode); - - sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit); - sleRippleState->setFieldAmount( - bSetHigh ? sfLowLimit : sfHighLimit, - STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID})); - - if (uQualityIn) - sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); - - if (uQualityOut) - sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); - - std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; - - if (bAuth) - { - uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); - } - if (bNoRipple) - { - uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - if (bFreeze) - { - uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze); - } - if (bDeepFreeze) - { - uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze); - } - - if ((slePeer->getFlags() & lsfDefaultRipple) == 0) - { - // The other side's default is no rippling - uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); - } - - sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, 1, j); - - // ONLY: Create ripple balance. - sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); - - view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed()); - - return tesSUCCESS; -} - -[[nodiscard]] TER -removeEmptyHolding(ApplyView& view, AccountID const& accountID, Issue const& issue, beast::Journal journal) -{ - if (issue.native()) - { - auto const sle = view.read(keylet::account(accountID)); - if (!sle) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const balance = sle->getFieldAmount(sfBalance); - if (balance.xrp() != 0) - return tecHAS_OBLIGATIONS; - - return tesSUCCESS; - } - - // `asset` is an IOU. - // If the account is the issuer, then no line should exist. Check anyway. If - // a line does exist, it will get deleted. If not, return success. - bool const accountIsIssuer = accountID == issue.account; - auto const line = view.peek(keylet::line(accountID, issue)); - if (!line) - return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; - if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero) - return tecHAS_OBLIGATIONS; - - // Adjust the owner count(s) - if (line->isFlag(lsfLowReserve)) - { - // Clear reserve for low account. - auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); - if (!sleLowAccount) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleLowAccount, -1, journal); - // It's not really necessary to clear the reserve flag, since the line - // is about to be deleted, but this will make the metadata reflect an - // accurate state at the time of deletion. - line->clearFlag(lsfLowReserve); - } - - if (line->isFlag(lsfHighReserve)) - { - // Clear reserve for high account. - auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); - if (!sleHighAccount) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleHighAccount, -1, journal); - // It's not really necessary to clear the reserve flag, since the line - // is about to be deleted, but this will make the metadata reflect an - // accurate state at the time of deletion. - line->clearFlag(lsfHighReserve); - } - - return trustDelete(view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); -} - -[[nodiscard]] TER -removeEmptyHolding(ApplyView& view, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal) -{ - // If the account is the issuer, then no token should exist. MPTs do not - // have the legacy ability to create such a situation, but check anyway. If - // a token does exist, it will get deleted. If not, return success. - bool const accountIsIssuer = accountID == mptIssue.getIssuer(); - auto const& mptID = mptIssue.getMptID(); - auto const mptoken = view.peek(keylet::mptoken(mptID, accountID)); - if (!mptoken) - return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; - // Unlike a trust line, if the account is the issuer, and the token has a - // balance, it can not just be deleted, because that will throw the issuance - // accounting out of balance, so fail. Since this should be impossible - // anyway, I'm not going to put any effort into it. - if (mptoken->at(sfMPTAmount) != 0) - return tecHAS_OBLIGATIONS; - - return authorizeMPToken( - view, - {}, // priorBalance - mptID, - accountID, - journal, - tfMPTUnauthorize // flags - ); -} - -TER -trustDelete( - ApplyView& view, - std::shared_ptr const& sleRippleState, - AccountID const& uLowAccountID, - AccountID const& uHighAccountID, - beast::Journal j) -{ - // Detect legacy dirs. - std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode); - std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode); - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: low"; - - if (!view.dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(), false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: high"; - - if (!view.dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(), false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; - view.erase(sleRippleState); - - return tesSUCCESS; -} - -TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) -{ - if (!sle) - return tesSUCCESS; - auto offerIndex = sle->key(); - auto owner = sle->getAccountID(sfAccount); - - // Detect legacy directories. - uint256 uDirectory = sle->getFieldH256(sfBookDirectory); - - if (!view.dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - if (!view.dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - - if (sle->isFieldPresent(sfAdditionalBooks)) - { - XRPL_ASSERT( - sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID), - "xrpl::offerDelete : should be a hybrid domain offer"); - - auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks); - - for (auto const& bookDir : additionalBookDirs) - { - auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory); - auto const& dirNode = bookDir.getFieldU64(sfBookNode); - - if (!view.dirRemove(keylet::page(dirIndex), dirNode, offerIndex, false)) - { - return tefBAD_LEDGER; // LCOV_EXCL_LINE - } - } - } - - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); - - view.erase(sle); - - return tesSUCCESS; -} - -// Direct send w/o fees: -// - Redeeming IOUs and/or sending sender's own IOUs. -// - Create trust line if needed. -// --> bCheckIssuer : normally require issuer to be involved. -static TER -rippleCreditIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j) -{ - AccountID const& issuer = saAmount.getIssuer(); - Currency const& currency = saAmount.getCurrency(); - - // Make sure issuer is involved. - XRPL_ASSERT( - !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer, - "xrpl::rippleCreditIOU : matching issuer or don't care"); - (void)issuer; - - // Disallow sending to self. - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleCreditIOU : sender is not receiver"); - - bool const bSenderHigh = uSenderID > uReceiverID; - auto const index = keylet::line(uSenderID, uReceiverID, currency); - - XRPL_ASSERT(!isXRP(uSenderID) && uSenderID != noAccount(), "xrpl::rippleCreditIOU : sender is not XRP"); - XRPL_ASSERT(!isXRP(uReceiverID) && uReceiverID != noAccount(), "xrpl::rippleCreditIOU : receiver is not XRP"); - - // If the line exists, modify it accordingly. - if (auto const sleRippleState = view.peek(index)) - { - STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); - - if (bSenderHigh) - saBalance.negate(); // Put balance in sender terms. - - view.creditHook(uSenderID, uReceiverID, saAmount, saBalance); - - STAmount const saBefore = saBalance; - - saBalance -= saAmount; - - JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> " << to_string(uReceiverID) - << " : before=" << saBefore.getFullText() << " amount=" << saAmount.getFullText() - << " after=" << saBalance.getFullText(); - - std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags)); - bool bDelete = false; - - // FIXME This NEEDS to be cleaned up and simplified. It's impossible - // for anyone to understand. - if (saBefore > beast::zero - // Sender balance was positive. - && saBalance <= beast::zero - // Sender is zero or negative. - && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast(view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && - !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j); - - // Clear reserve flag. - sleRippleState->setFieldU32(sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - bDelete = !saBalance // Balance is zero. - && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); - // Receiver reserve is clear. - } - - if (bSenderHigh) - saBalance.negate(); - - // Want to reflect balance to zero even if we are deleting line. - sleRippleState->setFieldAmount(sfBalance, saBalance); - // ONLY: Adjust ripple balance. - - if (bDelete) - { - return trustDelete( - view, sleRippleState, bSenderHigh ? uReceiverID : uSenderID, !bSenderHigh ? uReceiverID : uSenderID, j); - } - - view.update(sleRippleState); - return tesSUCCESS; - } - - STAmount const saReceiverLimit(Issue{currency, uReceiverID}); - STAmount saBalance{saAmount}; - - saBalance.setIssuer(noAccount()); - - JLOG(j.debug()) << "rippleCreditIOU: " - "create line: " - << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " << saAmount.getFullText(); - - auto const sleAccount = view.peek(keylet::account(uReceiverID)); - if (!sleAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; - - return trustCreate( - view, - bSenderHigh, - uSenderID, - uReceiverID, - index.key, - sleAccount, - false, - noRipple, - false, - false, - saBalance, - saReceiverLimit, - 0, - 0, - j); -} - -// Send regardless of limits. -// --> saAmount: Amount/currency/issuer to deliver to receiver. -// <-- saActual: Amount actually cost. Sender pays fees. -static TER -rippleSendIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - STAmount& saActual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - auto const& issuer = saAmount.getIssuer(); - - XRPL_ASSERT(!isXRP(uSenderID) && !isXRP(uReceiverID), "xrpl::rippleSendIOU : neither sender nor receiver is XRP"); - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendIOU : sender is not receiver"); - - if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) - { - // Direct send: redeeming IOUs and/or sending own IOUs. - auto const ter = rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j); - if (ter != tesSUCCESS) - return ter; - saActual = saAmount; - return tesSUCCESS; - } - - // Sending 3rd party IOUs: transit. - - // Calculate the amount to transfer accounting - // for any transfer fees if the fee is not waived: - saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount : multiply(saAmount, transferRate(view, issuer)); - - JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > " << to_string(uReceiverID) - << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - - TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j); - - if (tesSUCCESS == terResult) - terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j); - - return terResult; -} - -// Send regardless of limits. -// --> receivers: Amount/currency/issuer to deliver to receivers. -// <-- saActual: Amount actually cost to sender. Sender pays fees. -static TER -rippleSendMultiIOU( - ApplyView& view, - AccountID const& senderID, - Issue const& issue, - MultiplePaymentDestinations const& receivers, - STAmount& actual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - auto const& issuer = issue.getIssuer(); - - XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP"); - - // These may diverge - STAmount takeFromSender{issue}; - actual = takeFromSender; - - // Failures return immediately. - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{issue, r.second}; - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP"); - - if (senderID == issuer || receiverID == issuer || issuer == noAccount()) - { - // Direct send: redeeming IOUs and/or sending own IOUs. - if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j)) - return ter; - actual += amount; - // Do not add amount to takeFromSender, because rippleCreditIOU took - // it. - - continue; - } - - // Sending 3rd party IOUs: transit. - - // Calculate the amount to transfer accounting - // for any transfer fees if the fee is not waived: - STAmount actualSend = - (waiveFee == WaiveTransferFee::Yes) ? amount : multiply(amount, transferRate(view, issuer)); - actual += actualSend; - takeFromSender += actualSend; - - JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > " << to_string(receiverID) - << " : deliver=" << amount.getFullText() << " cost=" << actual.getFullText(); - - if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j)) - return terResult; - } - - if (senderID != issuer && takeFromSender) - { - if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j)) - return terResult; - } - - return tesSUCCESS; -} - -static TER -accountSendIOU( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - if (view.rules().enabled(fixAMMv1_1)) - { - if (saAmount < beast::zero || saAmount.holds()) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - } - else - { - // LCOV_EXCL_START - XRPL_ASSERT( - saAmount >= beast::zero && !saAmount.holds(), - "xrpl::accountSendIOU : minimum amount and not MPT"); - // LCOV_EXCL_STOP - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!saAmount || (uSenderID == uReceiverID)) - return tesSUCCESS; - - if (!saAmount.native()) - { - STAmount saActual; - - JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " - << saAmount.getFullText(); - - return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); - } - - /* XRP send which does not check reserve and can do pure adjustment. - * Note that sender or receiver may be null and this not a mistake; this - * setup is used during pathfinding and it is carefully controlled to - * ensure that transfers are balanced. - */ - TER terResult(tesSUCCESS); - - SLE::pointer sender = uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); - SLE::pointer receiver = uReceiverID != beast::zero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - std::string receiver_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendIOU> " << to_string(uSenderID) << " (" << sender_bal << ") -> " << to_string(uReceiverID) - << " (" << receiver_bal << ") : " << saAmount.getFullText(); - } - - if (sender) - { - if (sender->getFieldAmount(sfBalance) < saAmount) - { - // VFALCO Its laborious to have to mutate the - // TER based on params everywhere - // LCOV_EXCL_START - terResult = view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; - // LCOV_EXCL_STOP - } - else - { - auto const sndBal = sender->getFieldAmount(sfBalance); - view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sndBal - saAmount); - view.update(sender); - } - } - - if (tesSUCCESS == terResult && receiver) - { - // Increment XRP balance. - auto const rcvBal = receiver->getFieldAmount(sfBalance); - receiver->setFieldAmount(sfBalance, rcvBal + saAmount); - view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); - - view.update(receiver); - } - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - std::string receiver_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendIOU< " << to_string(uSenderID) << " (" << sender_bal << ") -> " << to_string(uReceiverID) - << " (" << receiver_bal << ") : " << saAmount.getFullText(); - } - - return terResult; -} - -static TER -accountSendMultiIOU( - ApplyView& view, - AccountID const& senderID, - Issue const& issue, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT_PARTS(receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided"); - - if (!issue.native()) - { - STAmount actual; - JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " << receivers.size() << " IOUs"; - - return rippleSendMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee); - } - - /* XRP send which does not check reserve and can do pure adjustment. - * Note that sender or receiver may be null and this not a mistake; this - * setup could be used during pathfinding and it is carefully controlled to - * ensure that transfers are balanced. - */ - - SLE::pointer sender = senderID != beast::zero ? view.peek(keylet::account(senderID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU> " << to_string(senderID) << " (" << sender_bal << ") -> " << receivers.size() - << " receivers."; - } - - // Failures return immediately. - STAmount takeFromSender{issue}; - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{issue, r.second}; - - if (amount < beast::zero) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - SLE::pointer receiver = receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer(); - - if (auto stream = j.trace()) - { - std::string receiver_bal("-"); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU> " << to_string(senderID) << " -> " << to_string(receiverID) << " (" - << receiver_bal << ") : " << amount.getFullText(); - } - - if (receiver) - { - // Increment XRP balance. - auto const rcvBal = receiver->getFieldAmount(sfBalance); - receiver->setFieldAmount(sfBalance, rcvBal + amount); - view.creditHook(xrpAccount(), receiverID, amount, -rcvBal); - - view.update(receiver); - - // Take what is actually sent - takeFromSender += amount; - } - - if (auto stream = j.trace()) - { - std::string receiver_bal("-"); - - if (receiver) - receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU< " << to_string(senderID) << " -> " << to_string(receiverID) << " (" - << receiver_bal << ") : " << amount.getFullText(); - } - } - - if (sender) - { - if (sender->getFieldAmount(sfBalance) < takeFromSender) - { - return TER{tecFAILED_PROCESSING}; - } - else - { - auto const sndBal = sender->getFieldAmount(sfBalance); - view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal); - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sndBal - takeFromSender); - view.update(sender); - } - } - - if (auto stream = j.trace()) - { - std::string sender_bal("-"); - std::string receiver_bal("-"); - - if (sender) - sender_bal = sender->getFieldAmount(sfBalance).getFullText(); - - stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> " << receivers.size() - << " receivers."; - } - return tesSUCCESS; -} - -static TER -rippleCreditMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j) -{ - // Do not check MPT authorization here - it must have been checked earlier - auto const mptID = keylet::mptIssuance(saAmount.get().getMptID()); - auto const& issuer = saAmount.getIssuer(); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - if (uSenderID == issuer) - { - (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value(); - view.update(sleIssuance); - } - else - { - auto const mptokenID = keylet::mptoken(mptID.key, uSenderID); - if (auto sle = view.peek(mptokenID)) - { - auto const amt = sle->getFieldU64(sfMPTAmount); - auto const pay = saAmount.mpt().value(); - if (amt < pay) - return tecINSUFFICIENT_FUNDS; - (*sle)[sfMPTAmount] = amt - pay; - view.update(sle); - } - else - return tecNO_AUTH; - } - - if (uReceiverID == issuer) - { - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - auto const redeem = saAmount.mpt().value(); - if (outstanding >= redeem) - { - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); - view.update(sleIssuance); - } - else - return tecINTERNAL; // LCOV_EXCL_LINE - } - else - { - auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID); - if (auto sle = view.peek(mptokenID)) - { - (*sle)[sfMPTAmount] += saAmount.mpt().value(); - view.update(sle); - } - else - return tecNO_AUTH; - } - - return tesSUCCESS; -} - -static TER -rippleSendMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - STAmount& saActual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendMPT : sender is not receiver"); - - // Safe to get MPT since rippleSendMPT is only called by accountSendMPT - auto const& issuer = saAmount.getIssuer(); - - auto const sle = view.read(keylet::mptIssuance(saAmount.get().getMptID())); - if (!sle) - return tecOBJECT_NOT_FOUND; - - if (uSenderID == issuer || uReceiverID == issuer) - { - // if sender is issuer, check that the new OutstandingAmount will not - // exceed MaximumAmount - if (uSenderID == issuer) - { - auto const sendAmount = saAmount.mpt().value(); - auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); - if (sendAmount > maximumAmount || sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) - return tecPATH_DRY; - } - - // Direct send: redeeming MPTs and/or sending own MPTs. - auto const ter = rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); - if (ter != tesSUCCESS) - return ter; - saActual = saAmount; - return tesSUCCESS; - } - - // Sending 3rd party MPTs: transit. - saActual = (waiveFee == WaiveTransferFee::Yes) - ? saAmount - : multiply(saAmount, transferRate(view, saAmount.get().getMptID())); - - JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > " << to_string(uReceiverID) - << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - - if (auto const terResult = rippleCreditMPT(view, issuer, uReceiverID, saAmount, j); terResult != tesSUCCESS) - return terResult; - - return rippleCreditMPT(view, uSenderID, issuer, saActual, j); -} - -static TER -rippleSendMultiMPT( - ApplyView& view, - AccountID const& senderID, - MPTIssue const& mptIssue, - MultiplePaymentDestinations const& receivers, - STAmount& actual, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - // Safe to get MPT since rippleSendMultiMPT is only called by - // accountSendMultiMPT - auto const& issuer = mptIssue.getIssuer(); - - auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())); - if (!sle) - return tecOBJECT_NOT_FOUND; - - // These may diverge - STAmount takeFromSender{mptIssue}; - actual = takeFromSender; - - for (auto const& r : receivers) - { - auto const& receiverID = r.first; - STAmount amount{mptIssue, r.second}; - - if (amount < beast::zero) - { - return tecINTERNAL; // LCOV_EXCL_LINE - } - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!amount || (senderID == receiverID)) - continue; - - if (senderID == issuer || receiverID == issuer) - { - // if sender is issuer, check that the new OutstandingAmount will - // not exceed MaximumAmount - if (senderID == issuer) - { - XRPL_ASSERT_PARTS( - takeFromSender == beast::zero, - "rippler::rippleSendMultiMPT", - "sender == issuer, takeFromSender == zero"); - auto const sendAmount = amount.mpt().value(); - auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); - if (sendAmount > maximumAmount || sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) - return tecPATH_DRY; - } - - // Direct send: redeeming MPTs and/or sending own MPTs. - if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j)) - return ter; - actual += amount; - // Do not add amount to takeFromSender, because rippleCreditMPT took - // it - - continue; - } - - // Sending 3rd party MPTs: transit. - STAmount actualSend = (waiveFee == WaiveTransferFee::Yes) - ? amount - : multiply(amount, transferRate(view, amount.get().getMptID())); - actual += actualSend; - takeFromSender += actualSend; - - JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > " << to_string(receiverID) - << " : deliver=" << amount.getFullText() << " cost=" << actualSend.getFullText(); - - if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j)) - return terResult; - } - if (senderID != issuer && takeFromSender) - { - if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j)) - return terResult; - } - - return tesSUCCESS; -} - -static TER -accountSendMPT( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT(saAmount >= beast::zero && saAmount.holds(), "xrpl::accountSendMPT : minimum amount and MPT"); - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!saAmount || (uSenderID == uReceiverID)) - return tesSUCCESS; - - STAmount saActual{saAmount.asset()}; - - return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); -} - -static TER -accountSendMultiMPT( - ApplyView& view, - AccountID const& senderID, - MPTIssue const& mptIssue, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - STAmount actual; - - return rippleSendMultiMPT(view, senderID, mptIssue, receivers, actual, j, waiveFee); -} - -TER -accountSend( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee); - else - return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee); - }, - saAmount.asset().value()); -} - -TER -accountSendMulti( - ApplyView& view, - AccountID const& senderID, - Asset const& asset, - MultiplePaymentDestinations const& receivers, - beast::Journal j, - WaiveTransferFee waiveFee) -{ - XRPL_ASSERT_PARTS(receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided"); - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee); - else - return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); - }, - asset.value()); -} - -static bool -updateTrustLine( - ApplyView& view, - SLE::pointer state, - bool bSenderHigh, - AccountID const& sender, - STAmount const& before, - STAmount const& after, - beast::Journal j) -{ - if (!state) - return false; - std::uint32_t const flags(state->getFieldU32(sfFlags)); - - auto sle = view.peek(keylet::account(sender)); - if (!sle) - return false; - - // YYY Could skip this if rippling in reverse. - if (before > beast::zero - // Sender balance was positive. - && after <= beast::zero - // Sender is zero or negative. - && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast(sle->getFlags() & lsfDefaultRipple) && - !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && - !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // VFALCO Where is the line being deleted? - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, sle, -1, j); - - // Clear reserve flag. - state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - if (!after // Balance is zero. - && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) - return true; - } - return false; -} - -TER -issueIOU(ApplyView& view, AccountID const& account, STAmount const& amount, Issue const& issue, beast::Journal j) -{ - XRPL_ASSERT(!isXRP(account) && !isXRP(issue.account), "xrpl::issueIOU : neither account nor issuer is XRP"); - - // Consistency check - XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue"); - - // Can't send to self! - XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account"); - - JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": " << amount.getFullText(); - - bool bSenderHigh = issue.account > account; - - auto const index = keylet::line(issue.account, account, issue.currency); - - if (auto state = view.peek(index)) - { - STAmount final_balance = state->getFieldAmount(sfBalance); - - if (bSenderHigh) - final_balance.negate(); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = - updateTrustLine(view, state, bSenderHigh, issue.account, start_balance, final_balance, j); - - view.creditHook(issue.account, account, amount, start_balance); - - if (bSenderHigh) - final_balance.negate(); - - // Adjust the balance on the trust line if necessary. We do this even if - // we are going to delete the line to reflect the correct balance at the - // time of deletion. - state->setFieldAmount(sfBalance, final_balance); - if (must_delete) - return trustDelete( - view, state, bSenderHigh ? account : issue.account, bSenderHigh ? issue.account : account, j); - - view.update(state); - - return tesSUCCESS; - } - - // NIKB TODO: The limit uses the receiver's account as the issuer and - // this is unnecessarily inefficient as copying which could be avoided - // is now required. Consider available options. - STAmount const limit(Issue{issue.currency, account}); - STAmount final_balance = amount; - - final_balance.setIssuer(noAccount()); - - auto const receiverAccount = view.peek(keylet::account(account)); - if (!receiverAccount) - return tefINTERNAL; // LCOV_EXCL_LINE - - bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; - - return trustCreate( - view, - bSenderHigh, - issue.account, - account, - index.key, - receiverAccount, - false, - noRipple, - false, - false, - final_balance, - limit, - 0, - 0, - j); -} - -TER -redeemIOU(ApplyView& view, AccountID const& account, STAmount const& amount, Issue const& issue, beast::Journal j) -{ - XRPL_ASSERT(!isXRP(account) && !isXRP(issue.account), "xrpl::redeemIOU : neither account nor issuer is XRP"); - - // Consistency check - XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue"); - - // Can't send to self! - XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account"); - - JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": " << amount.getFullText(); - - bool bSenderHigh = account > issue.account; - - if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) - { - STAmount final_balance = state->getFieldAmount(sfBalance); - - if (bSenderHigh) - final_balance.negate(); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j); - - view.creditHook(account, issue.account, amount, start_balance); - - if (bSenderHigh) - final_balance.negate(); - - // Adjust the balance on the trust line if necessary. We do this even if - // we are going to delete the line to reflect the correct balance at the - // time of deletion. - state->setFieldAmount(sfBalance, final_balance); - - if (must_delete) - { - return trustDelete( - view, state, bSenderHigh ? issue.account : account, bSenderHigh ? account : issue.account, j); - } - - view.update(state); - return tesSUCCESS; - } - - // In order to hold an IOU, a trust line *MUST* exist to track the - // balance. If it doesn't, then something is very wrong. Don't try - // to continue. - // LCOV_EXCL_START - JLOG(j.fatal()) << "redeemIOU: " << to_string(account) << " attempts to redeem " << amount.getFullText() - << " but no trust line exists!"; - - return tefINTERNAL; - // LCOV_EXCL_STOP -} - -TER -transferXRP(ApplyView& view, AccountID const& from, AccountID const& to, STAmount const& amount, beast::Journal j) -{ - XRPL_ASSERT(from != beast::zero, "xrpl::transferXRP : nonzero from account"); - XRPL_ASSERT(to != beast::zero, "xrpl::transferXRP : nonzero to account"); - XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); - XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); - - SLE::pointer const sender = view.peek(keylet::account(from)); - SLE::pointer const receiver = view.peek(keylet::account(to)); - if (!sender || !receiver) - return tefINTERNAL; // LCOV_EXCL_LINE - - JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) << ") : " << amount.getFullText(); - - if (sender->getFieldAmount(sfBalance) < amount) - { - // VFALCO Its unfortunate we have to keep - // mutating these TER everywhere - // FIXME: this logic should be moved to callers maybe? - // LCOV_EXCL_START - return view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; - // LCOV_EXCL_STOP - } - - // Decrement XRP balance. - sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount); - view.update(sender); - - receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount); - view.update(receiver); - - return tesSUCCESS; -} - -TER -requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType) -{ - if (isXRP(issue) || issue.account == account) - return tesSUCCESS; - - auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency)); - // If account has no line, and this is a strong check, fail - if (!trustLine && authType == AuthType::StrongAuth) - return tecNO_LINE; - - // If this is a weak or legacy check, or if the account has a line, fail if - // auth is required and not set on the line - if (auto const issuerAccount = view.read(keylet::account(issue.account)); - issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth) - { - if (trustLine) - return ((*trustLine)[sfFlags] & ((account > issue.account) ? lsfLowAuth : lsfHighAuth)) ? tesSUCCESS - : TER{tecNO_AUTH}; - return TER{tecNO_LINE}; - } - - return tesSUCCESS; -} - -TER -requireAuth(ReadView const& view, MPTIssue const& mptIssue, AccountID const& account, AuthType authType, int depth) -{ - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto const sleIssuance = view.read(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - auto const mptIssuer = sleIssuance->getAccountID(sfIssuer); - - // issuer is always "authorized" - if (mptIssuer == account) // Issuer won't have MPToken - return tesSUCCESS; - - bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault); - - if (featureSAVEnabled) - { - if (depth >= maxAssetCheckDepth) - return tecINTERNAL; // LCOV_EXCL_LINE - - // requireAuth is recursive if the issuer is a vault pseudo-account - auto const sleIssuer = view.read(keylet::account(mptIssuer)); - if (!sleIssuer) - return tefINTERNAL; // LCOV_EXCL_LINE - - if (sleIssuer->isFieldPresent(sfVaultID)) - { - auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID))); - if (!sleVault) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const asset = sleVault->at(sfAsset); - if (auto const err = std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - return requireAuth(view, issue, account, authType); - else - return requireAuth(view, issue, account, authType, depth + 1); - }, - asset.value()); - !isTesSuccess(err)) - return err; - } - } - - auto const mptokenID = keylet::mptoken(mptID.key, account); - auto const sleToken = view.read(mptokenID); - - // if account has no MPToken, fail - if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy)) - return tecNO_AUTH; - - // Note, this check is not amendment-gated because DomainID will be always - // empty **unless** writing to it has been enabled by an amendment - auto const maybeDomainID = sleIssuance->at(~sfDomainID); - if (maybeDomainID) - { - XRPL_ASSERT( - sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth, - "xrpl::requireAuth : issuance requires authorization"); - // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED - if (auto const ter = credentials::validDomain(view, *maybeDomainID, account); isTesSuccess(ter)) - return ter; // Note: sleToken might be null - else if (!sleToken) - return ter; - // We ignore error from validDomain if we found sleToken, as it could - // belong to someone who is explicitly authorized e.g. a vault owner. - } - - if (featureSAVEnabled) - { - // Implicitly authorize Vault and LoanBroker pseudo-accounts - if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID})) - return tesSUCCESS; - } - - // mptoken must be authorized if issuance enabled requireAuth - if (sleIssuance->isFlag(lsfMPTRequireAuth) && (!sleToken || !sleToken->isFlag(lsfMPTAuthorized))) - return tecNO_AUTH; - - return tesSUCCESS; // Note: sleToken might be null -} - -[[nodiscard]] TER -enforceMPTokenAuthorization( - ApplyView& view, - MPTID const& mptIssuanceID, - AccountID const& account, - XRPAmount const& priorBalance, // for MPToken authorization - beast::Journal j) -{ - auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); - if (!sleIssuance) - return tefINTERNAL; // LCOV_EXCL_LINE - - XRPL_ASSERT(sleIssuance->isFlag(lsfMPTRequireAuth), "xrpl::enforceMPTokenAuthorization : authorization required"); - - if (account == sleIssuance->at(sfIssuer)) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const keylet = keylet::mptoken(mptIssuanceID, account); - auto const sleToken = view.read(keylet); // NOTE: might be null - auto const maybeDomainID = sleIssuance->at(~sfDomainID); - bool expired = false; - bool const authorizedByDomain = [&]() -> bool { - // NOTE: defensive here, should be checked in preclaim - if (!maybeDomainID.has_value()) - return false; // LCOV_EXCL_LINE - - auto const ter = verifyValidDomain(view, account, *maybeDomainID, j); - if (isTesSuccess(ter)) - return true; - if (ter == tecEXPIRED) - expired = true; - return false; - }(); - - if (!authorizedByDomain && sleToken == nullptr) - { - // Could not find MPToken and won't create one, could be either of: - // - // 1. Field sfDomainID not set in MPTokenIssuance or - // 2. Account has no matching and accepted credentials or - // 3. Account has all expired credentials (deleted in verifyValidDomain) - // - // Either way, return tecNO_AUTH and there is nothing else to do - return expired ? tecEXPIRED : tecNO_AUTH; - } - else if (!authorizedByDomain && maybeDomainID.has_value()) - { - // Found an MPToken but the account is not authorized and we expect - // it to have been authorized by the domain. This could be because the - // credentials used to create the MPToken have expired or been deleted. - return expired ? tecEXPIRED : tecNO_AUTH; - } - else if (!authorizedByDomain) - { - // We found an MPToken, but sfDomainID is not set, so this is a classic - // MPToken which requires authorization by the token issuer. - XRPL_ASSERT( - sleToken != nullptr && !maybeDomainID.has_value(), "xrpl::enforceMPTokenAuthorization : found MPToken"); - if (sleToken->isFlag(lsfMPTAuthorized)) - return tesSUCCESS; - - return tecNO_AUTH; - } - else if (authorizedByDomain && sleToken != nullptr) - { - // Found an MPToken, authorized by the domain. Ignore authorization flag - // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS - XRPL_ASSERT(maybeDomainID.has_value(), "xrpl::enforceMPTokenAuthorization : found MPToken for domain"); - return tesSUCCESS; - } - else if (authorizedByDomain) - { - // Could not find MPToken but there should be one because we are - // authorized by domain. Proceed to create it, then return tesSUCCESS - XRPL_ASSERT( - maybeDomainID.has_value() && sleToken == nullptr, - "xrpl::enforceMPTokenAuthorization : new MPToken for domain"); - if (auto const err = authorizeMPToken( - view, - priorBalance, // priorBalance - mptIssuanceID, // mptIssuanceID - account, // account - j); - !isTesSuccess(err)) - return err; - - return tesSUCCESS; - } - - // LCOV_EXCL_START - UNREACHABLE("xrpl::enforceMPTokenAuthorization : condition list is incomplete"); - return tefINTERNAL; - // LCOV_EXCL_STOP -} - -TER -canTransfer(ReadView const& view, MPTIssue const& mptIssue, AccountID const& from, AccountID const& to) -{ - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto const sleIssuance = view.read(mptID); - if (!sleIssuance) - return tecOBJECT_NOT_FOUND; - - if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer)) - { - if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer]) - return TER{tecNO_AUTH}; - } - return tesSUCCESS; -} - -[[nodiscard]] TER -canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to) -{ - if (issue.native()) - return tesSUCCESS; - - auto const& issuerId = issue.getIssuer(); - if (issuerId == from || issuerId == to) - return tesSUCCESS; - auto const sleIssuer = view.read(keylet::account(issuerId)); - if (sleIssuer == nullptr) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const isRippleDisabled = [&](AccountID account) -> bool { - // Line might not exist, but some transfers can create it. If this - // is the case, just check the default ripple on the issuer account. - auto const line = view.read(keylet::line(account, issue)); - if (line) - { - bool const issuerHigh = issuerId > account; - return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - return sleIssuer->isFlag(lsfDefaultRipple) == false; - }; - - // Fail if rippling disabled on both trust lines - if (isRippleDisabled(from) && isRippleDisabled(to)) - return terNO_RIPPLE; - - return tesSUCCESS; -} - TER cleanupOnAccountDelete( ApplyView& view, @@ -2972,7 +458,8 @@ cleanupOnAccountDelete( uint256 dirEntry{beast::zero}; std::uint32_t deleted = 0; - if (view.exists(ownerDirKeylet) && dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry)) + if (view.exists(ownerDirKeylet) && + dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry)) { do { @@ -2991,12 +478,13 @@ cleanupOnAccountDelete( // LCOV_EXCL_STOP } - LedgerEntryType const nodeType{safe_cast(sleItem->getFieldU16(sfLedgerEntryType))}; + LedgerEntryType const nodeType{ + safe_cast(sleItem->getFieldU16(sfLedgerEntryType))}; // Deleter handles the details of specific account-owned object // deletion auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; // dirFirst() and dirNext() are like iterators with exposed @@ -3032,406 +520,6 @@ cleanupOnAccountDelete( return tesSUCCESS; } -TER -deleteAMMTrustLine( - ApplyView& view, - std::shared_ptr sleState, - std::optional const& ammAccountID, - beast::Journal j) -{ - if (!sleState || sleState->getType() != ltRIPPLE_STATE) - return tecINTERNAL; // LCOV_EXCL_LINE - - auto const& [low, high] = std::minmax( - sleState->getFieldAmount(sfLowLimit).getIssuer(), sleState->getFieldAmount(sfHighLimit).getIssuer()); - auto sleLow = view.peek(keylet::account(low)); - auto sleHigh = view.peek(keylet::account(high)); - if (!sleLow || !sleHigh) - return tecINTERNAL; // LCOV_EXCL_LINE - - bool const ammLow = sleLow->isFieldPresent(sfAMMID); - bool const ammHigh = sleHigh->isFieldPresent(sfAMMID); - - // can't both be AMM - if (ammLow && ammHigh) - return tecINTERNAL; // LCOV_EXCL_LINE - - // at least one must be - if (!ammLow && !ammHigh) - return terNO_AMM; - - // one must be the target amm - if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) - return terNO_AMM; - - if (auto const ter = trustDelete(view, sleState, low, high, j); ter != tesSUCCESS) - { - JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; - return ter; - } - - auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve; - if (!(sleState->getFlags() & uFlags)) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); - - return tesSUCCESS; -} - -TER -rippleCredit( - ApplyView& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - STAmount const& saAmount, - bool bCheckIssuer, - beast::Journal j) -{ - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - { - return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); - } - else - { - XRPL_ASSERT(!bCheckIssuer, "xrpl::rippleCredit : not checking issuer"); - return rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); - } - }, - saAmount.asset().value()); -} - -[[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets) -{ - XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); - XRPL_ASSERT(assets.asset() == vault->at(sfAsset), "xrpl::assetsToSharesDeposit : assets and vault match"); - if (assets.negative() || assets.asset() != vault->at(sfAsset)) - return std::nullopt; // LCOV_EXCL_LINE - - Number const assetTotal = vault->at(sfAssetsTotal); - STAmount shares{vault->at(sfShareMPTID)}; - if (assetTotal == 0) - return STAmount{shares.asset(), Number(assets.mantissa(), assets.exponent() + vault->at(sfScale)).truncate()}; - - Number const shareTotal = issuance->at(sfOutstandingAmount); - shares = ((shareTotal * assets) / assetTotal).truncate(); - return shares; -} - -[[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) -{ - XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); - XRPL_ASSERT(shares.asset() == vault->at(sfShareMPTID), "xrpl::sharesToAssetsDeposit : shares and vault match"); - if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) - return std::nullopt; // LCOV_EXCL_LINE - - Number const assetTotal = vault->at(sfAssetsTotal); - STAmount assets{vault->at(sfAsset)}; - if (assetTotal == 0) - return STAmount{assets.asset(), shares.mantissa(), shares.exponent() - vault->at(sfScale), false}; - - Number const shareTotal = issuance->at(sfOutstandingAmount); - assets = (assetTotal * shares) / shareTotal; - return assets; -} - -[[nodiscard]] std::optional -assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets, - TruncateShares truncate) -{ - XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); - XRPL_ASSERT(assets.asset() == vault->at(sfAsset), "xrpl::assetsToSharesWithdraw : assets and vault match"); - if (assets.negative() || assets.asset() != vault->at(sfAsset)) - return std::nullopt; // LCOV_EXCL_LINE - - Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); - STAmount shares{vault->at(sfShareMPTID)}; - if (assetTotal == 0) - return shares; - Number const shareTotal = issuance->at(sfOutstandingAmount); - Number result = (shareTotal * assets) / assetTotal; - if (truncate == TruncateShares::yes) - result = result.truncate(); - shares = result; - return shares; -} - -[[nodiscard]] std::optional -sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) -{ - XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); - XRPL_ASSERT(shares.asset() == vault->at(sfShareMPTID), "xrpl::sharesToAssetsWithdraw : shares and vault match"); - if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) - return std::nullopt; // LCOV_EXCL_LINE - - Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); - STAmount assets{vault->at(sfAsset)}; - if (assetTotal == 0) - return assets; - Number const shareTotal = issuance->at(sfOutstandingAmount); - assets = (assetTotal * shares) / shareTotal; - return assets; -} - -TER -rippleLockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j) -{ - auto const mptIssue = amount.get(); - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID(); - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - if (amount.getIssuer() == sender) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs."; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - // 1. Decrease the MPT Holder MPTAmount - // 2. Increase the MPT Holder EscrowedAmount - { - auto const mptokenID = keylet::mptoken(mptID.key, sender); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: MPToken not found for " << sender; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - auto const amt = sle->getFieldU64(sfMPTAmount); - auto const pay = amount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: insufficient MPTAmount for " << to_string(sender) << ": " << amt - << " < " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - (*sle)[sfMPTAmount] = amt - pay; - - // Overflow check for addition - uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0); - - if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: overflow on locked amount for " << to_string(sender) << ": " - << locked << " + " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - if (sle->isFieldPresent(sfLockedAmount)) - (*sle)[sfLockedAmount] += pay; - else - sle->setFieldU64(sfLockedAmount, pay); - - view.update(sle); - } - - // 1. Increase the Issuance EscrowedAmount - // 2. DO NOT change the Issuance OutstandingAmount - { - uint64_t const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0); - auto const pay = amount.mpt().value(); - - // Overflow check for addition - if (!canAdd(STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance " - "locked amount for " - << mptIssue.getMptID() << ": " << issuanceEscrowed << " + " << pay; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - if (sleIssuance->isFieldPresent(sfLockedAmount)) - (*sleIssuance)[sfLockedAmount] += pay; - else - sleIssuance->setFieldU64(sfLockedAmount, pay); - - view.update(sleIssuance); - } - return tesSUCCESS; -} - -TER -rippleUnlockEscrowMPT( - ApplyView& view, - AccountID const& sender, - AccountID const& receiver, - STAmount const& netAmount, - STAmount const& grossAmount, - beast::Journal j) -{ - if (!view.rules().enabled(fixTokenEscrowV1)) - XRPL_ASSERT(netAmount == grossAmount, "xrpl::rippleUnlockEscrowMPT : netAmount == grossAmount"); - - auto const& issuer = netAmount.getIssuer(); - auto const& mptIssue = netAmount.get(); - auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); - auto sleIssuance = view.peek(mptID); - if (!sleIssuance) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID(); - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - // Decrease the Issuance EscrowedAmount - { - if (!sleIssuance->isFieldPresent(sfLockedAmount)) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in issuance for " << mptIssue.getMptID(); - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const locked = sleIssuance->getFieldU64(sfLockedAmount); - auto const redeem = grossAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, redeem))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " << mptIssue.getMptID() << ": " - << locked << " < " << redeem; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const newLocked = locked - redeem; - if (newLocked == 0) - sleIssuance->makeFieldAbsent(sfLockedAmount); - else - sleIssuance->setFieldU64(sfLockedAmount, newLocked); - view.update(sleIssuance); - } - - if (issuer != receiver) - { - // Increase the MPT Holder MPTAmount - auto const mptokenID = keylet::mptoken(mptID.key, receiver); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << receiver; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - auto current = sle->getFieldU64(sfMPTAmount); - auto delta = netAmount.mpt().value(); - - // Overflow check for addition - if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: overflow on MPTAmount for " << to_string(receiver) << ": " - << current << " + " << delta; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - (*sle)[sfMPTAmount] += delta; - view.update(sle); - } - else - { - // Decrease the Issuance OutstandingAmount - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - auto const redeem = netAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " << mptIssue.getMptID() - << ": " << outstanding << " < " << redeem; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); - view.update(sleIssuance); - } - - if (issuer == sender) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, " - "cannot unlock MPTs."; - return tecINTERNAL; - } // LCOV_EXCL_STOP - else - { - // Decrease the MPT Holder EscrowedAmount - auto const mptokenID = keylet::mptoken(mptID.key, sender); - auto sle = view.peek(mptokenID); - if (!sle) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << sender; - return tecOBJECT_NOT_FOUND; - } // LCOV_EXCL_STOP - - if (!sle->isFieldPresent(sfLockedAmount)) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in MPToken for " << to_string(sender); - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const locked = sle->getFieldU64(sfLockedAmount); - auto const delta = grossAmount.mpt().value(); - - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " << to_string(sender) << ": " - << locked << " < " << delta; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - auto const newLocked = locked - delta; - if (newLocked == 0) - sle->makeFieldAbsent(sfLockedAmount); - else - sle->setFieldU64(sfLockedAmount, newLocked); - view.update(sle); - } - - // Note: The gross amount is the amount that was locked, the net - // amount is the amount that is being unlocked. The difference is the fee - // that was charged for the transfer. If this difference is greater than - // zero, we need to update the outstanding amount. - auto const diff = grossAmount.mpt().value() - netAmount.mpt().value(); - if (diff != 0) - { - auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - // Underflow check for subtraction - if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, diff))) - { // LCOV_EXCL_START - JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " << mptIssue.getMptID() - << ": " << outstanding << " < " << diff; - return tecINTERNAL; - } // LCOV_EXCL_STOP - - sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff); - view.update(sleIssuance); - } - return tesSUCCESS; -} - bool after(NetClock::time_point now, std::uint32_t mark) { diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp new file mode 100644 index 0000000000..399494bc5f --- /dev/null +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -0,0 +1,247 @@ +#include +// +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +bool +isGlobalFrozen(ReadView const& view, AccountID const& issuer) +{ + if (isXRP(issuer)) + return false; + if (auto const sle = view.read(keylet::account(issuer))) + return sle->isFlag(lsfGlobalFreeze); + return false; +} + +// An owner count cannot be negative. If adjustment would cause a negative +// owner count, clamp the owner count at 0. Similarly for overflow. This +// adjustment allows the ownerCount to be adjusted up or down in multiple steps. +// If id != std::nullopt, then do error reporting. +// +// Returns adjusted owner count. +static std::uint32_t +confineOwnerCount( + std::uint32_t current, + std::int32_t adjustment, + std::optional const& id = std::nullopt, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) +{ + std::uint32_t adjusted{current + adjustment}; + if (adjustment > 0) + { + // Overflow is well defined on unsigned + if (adjusted < current) + { + if (id) + { + JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; + } + adjusted = std::numeric_limits::max(); + } + } + else + { + // Underflow is well defined on unsigned + if (adjusted > current) + { + if (id) + { + JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!"; + } + adjusted = 0; + XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set"); + } + } + return adjusted; +} + +XRPAmount +xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) +{ + auto const sle = view.read(keylet::account(id)); + if (sle == nullptr) + return beast::zero; + + // Return balance minus reserve + std::uint32_t const ownerCount = + confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); + + // Pseudo-accounts have no reserve requirement + auto const reserve = + isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount); + + auto const fullBalance = sle->getFieldAmount(sfBalance); + + auto const balance = view.balanceHook(id, xrpAccount(), fullBalance); + + STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve; + + JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id) + << " amount=" << amount.getFullText() + << " fullBalance=" << fullBalance.getFullText() + << " balance=" << balance.getFullText() << " reserve=" << reserve + << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj; + + return amount.xrp(); +} + +Rate +transferRate(ReadView const& view, AccountID const& issuer) +{ + auto const sle = view.read(keylet::account(issuer)); + + if (sle && sle->isFieldPresent(sfTransferRate)) + return Rate{sle->getFieldU32(sfTransferRate)}; + + return parityRate; +} + +void +adjustOwnerCount( + ApplyView& view, + std::shared_ptr const& sle, + std::int32_t amount, + beast::Journal j) +{ + if (!sle) + return; + XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); + std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; + AccountID const id = (*sle)[sfAccount]; + std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); + view.adjustOwnerCountHook(id, current, adjusted); + sle->at(sfOwnerCount) = adjusted; + view.update(sle); +} + +AccountID +pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) +{ + // This number must not be changed without an amendment + constexpr std::uint16_t maxAccountAttempts = 256; + for (std::uint16_t i = 0; i < maxAccountAttempts; ++i) + { + ripesha_hasher rsh; + auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); + rsh(hash.data(), hash.size()); + AccountID const ret{static_cast(rsh)}; + if (!view.read(keylet::account(ret))) + return ret; + } + return beast::zero; +} + +// Pseudo-account designator fields MUST be maintained by including the +// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to +// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated, +// since a non-active amendment will not set any field, by definition. +// Specific properties of a pseudo-account are NOT checked here, that's what +// InvariantCheck is for. +[[nodiscard]] std::vector const& +getPseudoAccountFields() +{ + static std::vector const pseudoFields = []() { + auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT); + if (!ar) + { + // LCOV_EXCL_START + LogicError( + "xrpl::getPseudoAccountFields : unable to find account root " + "ledger format"); + // LCOV_EXCL_STOP + } + auto const& soTemplate = ar->getSOTemplate(); + + std::vector pseudoFields; + for (auto const& field : soTemplate) + { + if (field.sField().shouldMeta(SField::sMD_PseudoAccount)) + pseudoFields.emplace_back(&field.sField()); + } + return pseudoFields; + }(); + return pseudoFields; +} + +[[nodiscard]] bool +isPseudoAccount( + std::shared_ptr sleAcct, + std::set const& pseudoFieldFilter) +{ + auto const& fields = getPseudoAccountFields(); + + // Intentionally use defensive coding here because it's cheap and makes the + // semantics of true return value clean. + return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT && + std::count_if( + fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](SField const* sf) -> bool { + return sleAcct->isFieldPresent(*sf) && + (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf)); + }) > 0; +} + +Expected, TER> +createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) +{ + [[maybe_unused]] + auto const& fields = getPseudoAccountFields(); + XRPL_ASSERT( + std::count_if( + fields.begin(), + fields.end(), + [&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1, + "xrpl::createPseudoAccount : valid owner field"); + + auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); + if (accountId == beast::zero) + return Unexpected(tecDUPLICATE); + + // Create pseudo-account. + auto account = std::make_shared(keylet::account(accountId)); + account->setAccountID(sfAccount, accountId); + account->setFieldAmount(sfBalance, STAmount{}); + + // Pseudo-accounts can't submit transactions, so set the sequence number + // to 0 to make them easier to spot and verify, and add an extra level + // of protection. + std::uint32_t const seqno = // + view.rules().enabled(featureSingleAssetVault) || // + view.rules().enabled(featureLendingProtocol) // + ? 0 // + : view.seq(); + account->setFieldU32(sfSequence, seqno); + // Ignore reserves requirement, disable the master key, allow default + // rippling, and enable deposit authorization to prevent payments into + // pseudo-account. + account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + // Link the pseudo-account with its owner object. + account->setFieldH256(ownerField, pseudoOwnerKey); + + view.insert(account); + + return account; +} + +[[nodiscard]] TER +checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag) +{ + if (toSle == nullptr) + return tecNO_DST; + + // The tag is basically account-specific information we don't + // understand, but we can require someone to fill it in. + if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag) + return tecDST_TAG_NEEDED; // Cannot send without a tag + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp similarity index 91% rename from src/libxrpl/ledger/CredentialHelpers.cpp rename to src/libxrpl/ledger/helpers/CredentialHelpers.cpp index c737d42f07..234ca7ea17 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -1,5 +1,7 @@ -#include +#include +// #include +#include #include #include @@ -11,7 +13,8 @@ namespace credentials { bool checkExpired(std::shared_ptr const& sleCredential, NetClock::time_point const& closed) { - std::uint32_t const exp = (*sleCredential)[~sfExpiration].value_or(std::numeric_limits::max()); + std::uint32_t const exp = + (*sleCredential)[~sfExpiration].value_or(std::numeric_limits::max()); std::uint32_t const now = closed.time_since_epoch().count(); return now > exp; } @@ -46,7 +49,8 @@ deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Jou if (!sleCredential) return tecNO_ENTRY; - auto delSLE = [&view, &sleCredential, j](AccountID const& account, SField const& node, bool isOwner) -> TER { + auto delSLE = [&view, &sleCredential, j]( + AccountID const& account, SField const& node, bool isOwner) -> TER { auto const sleAccount = view.peek(keylet::account(account)); if (!sleAccount) { @@ -102,7 +106,8 @@ checkFields(STTx const& tx, beast::Journal j) auto const& credentials = tx.getFieldV256(sfCredentialIDs); if (credentials.empty() || (credentials.size() > maxCredentialsArraySize)) { - JLOG(j.trace()) << "Malformed transaction: Credentials array size is invalid: " << credentials.size(); + JLOG(j.trace()) << "Malformed transaction: Credentials array size is invalid: " + << credentials.size(); return temMALFORMED; } @@ -183,10 +188,12 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject) foundExpired = true; continue; } - else if (sleCredential->getFlags() & lsfAccepted) + if (sleCredential->getFlags() & lsfAccepted) + { return tesSUCCESS; - else - continue; + } + + continue; } } @@ -194,7 +201,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject) } TER -authorizedDepositPreauth(ApplyView const& view, STVector256 const& credIDs, AccountID const& dst) +authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst) { std::set> sorted; std::vector> lifeExtender; @@ -315,7 +322,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + std::shared_ptr const& sleDst, beast::Journal j) { // If depositPreauth is enabled, then an account that requires @@ -334,9 +341,11 @@ verifyDepositPreauth( if (src != dst) { if (!view.exists(keylet::depositPreauth(dst, src))) - return !credentialsPresent - ? tecNO_PERMISSION - : credentials::authorizedDepositPreauth(view, tx.getFieldV256(sfCredentialIDs), dst); + { + return !credentialsPresent ? tecNO_PERMISSION + : credentials::authorizedDepositPreauth( + view, tx.getFieldV256(sfCredentialIDs), dst); + } } } diff --git a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp new file mode 100644 index 0000000000..f0ca83f2cd --- /dev/null +++ b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp @@ -0,0 +1,177 @@ +#include +// +#include + +namespace xrpl { + +bool +dirFirst( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirFirst(view, root, page, index, entry); +} + +bool +dirNext( + ApplyView& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirNext(view, root, page, index, entry); +} + +bool +cdirFirst( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirFirst(view, root, page, index, entry); +} + +bool +cdirNext( + ReadView const& view, + uint256 const& root, + std::shared_ptr& page, + unsigned int& index, + uint256& entry) +{ + return detail::internalDirNext(view, root, page, index, entry); +} + +void +forEachItem( + ReadView const& view, + Keylet const& root, + std::function const&)> const& f) +{ + XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); + + if (root.type != ltDIR_NODE) + return; + + auto pos = root; + + while (true) + { + auto sle = view.read(pos); + if (!sle) + return; + for (auto const& key : sle->getFieldV256(sfIndexes)) + f(view.read(keylet::child(key))); + auto const next = sle->getFieldU64(sfIndexNext); + if (!next) + return; + pos = keylet::page(root, next); + } +} + +bool +forEachItemAfter( + ReadView const& view, + Keylet const& root, + uint256 const& after, + std::uint64_t const hint, + unsigned int limit, + std::function const&)> const& f) +{ + XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); + + if (root.type != ltDIR_NODE) + return false; + + auto currentIndex = root; + + // If startAfter is not zero try jumping to that page using the hint + if (after.isNonZero()) + { + auto const hintIndex = keylet::page(root, hint); + + if (auto hintDir = view.read(hintIndex)) + { + for (auto const& key : hintDir->getFieldV256(sfIndexes)) + { + if (key == after) + { + // We found the hint, we can start here + currentIndex = hintIndex; + break; + } + } + } + + bool found = false; + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (!ownerDir) + return found; + for (auto const& key : ownerDir->getFieldV256(sfIndexes)) + { + if (!found) + { + if (key == after) + found = true; + } + else if (f(view.read(keylet::child(key))) && limit-- <= 1) + { + return found; + } + } + + auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); + if (uNodeNext == 0) + return found; + currentIndex = keylet::page(root, uNodeNext); + } + } + else + { + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (!ownerDir) + return true; + for (auto const& key : ownerDir->getFieldV256(sfIndexes)) + { + if (f(view.read(keylet::child(key))) && limit-- <= 1) + return true; + } + auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); + if (uNodeNext == 0) + return true; + currentIndex = keylet::page(root, uNodeNext); + } + } +} + +bool +dirIsEmpty(ReadView const& view, Keylet const& k) +{ + auto const sleNode = view.read(k); + if (!sleNode) + return true; + if (!sleNode->getFieldV256(sfIndexes).empty()) + return false; + // The first page of a directory may legitimately be empty even if there + // are other pages (the first page is the anchor page) so check to see if + // there is another page. If there is, the directory isn't empty. + return sleNode->getFieldU64(sfIndexNext) == 0; +} + +std::function +describeOwnerDir(AccountID const& account) +{ + return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp new file mode 100644 index 0000000000..cbf37a06a8 --- /dev/null +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -0,0 +1,766 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +// Forward declarations for functions that remain in View.h/cpp +bool +isVaultPseudoAccountFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptShare, + int depth); + +[[nodiscard]] TER +dirLink( + ApplyView& view, + AccountID const& owner, + std::shared_ptr& object, + SF_UINT64 const& node = sfOwnerNode); + +bool +isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue) +{ + if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()))) + return sle->isFlag(lsfMPTLocked); + return false; +} + +bool +isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account))) + return sle->isFlag(lsfMPTLocked); + return false; +} + +bool +isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +{ + return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) || + isVaultPseudoAccountFrozen(view, account, mptIssue, depth); +} + +[[nodiscard]] bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + MPTIssue const& mptIssue, + int depth) +{ + if (isGlobalFrozen(view, mptIssue)) + return true; + + for (auto const& account : accounts) + { + if (isIndividualFrozen(view, account, mptIssue)) + return true; + } + + for (auto const& account : accounts) + { + if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth)) + return true; + } + + return false; +} + +Rate +transferRate(ReadView const& view, MPTID const& issuanceID) +{ + // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000 + // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000 + // which represents 50% of 1,000,000,000 + if (auto const sle = view.read(keylet::mptIssuance(issuanceID)); + sle && sle->isFieldPresent(sfTransferFee)) + return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)}; + + return parityRate; +} + +[[nodiscard]] TER +canAddHolding(ReadView const& view, MPTIssue const& mptIssue) +{ + auto mptID = mptIssue.getMptID(); + auto issuance = view.read(keylet::mptIssuance(mptID)); + if (!issuance) + { + return tecOBJECT_NOT_FOUND; + } + if (!issuance->isFlag(lsfMPTCanTransfer)) + { + return tecNO_AUTH; + } + + return tesSUCCESS; +} + +[[nodiscard]] TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + MPTIssue const& mptIssue, + beast::Journal journal) +{ + auto const& mptID = mptIssue.getMptID(); + auto const mpt = view.peek(keylet::mptIssuance(mptID)); + if (!mpt) + return tefINTERNAL; // LCOV_EXCL_LINE + if (mpt->isFlag(lsfMPTLocked)) + return tefINTERNAL; // LCOV_EXCL_LINE + if (view.peek(keylet::mptoken(mptID, accountID))) + return tecDUPLICATE; + if (accountID == mptIssue.getIssuer()) + return tesSUCCESS; + + return authorizeMPToken(view, priorBalance, mptID, accountID, journal); +} + +[[nodiscard]] TER +authorizeMPToken( + ApplyView& view, + XRPAmount const& priorBalance, + MPTID const& mptIssuanceID, + AccountID const& account, + beast::Journal journal, + std::uint32_t flags, + std::optional holderID) +{ + auto const sleAcct = view.peek(keylet::account(account)); + if (!sleAcct) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the account that submitted the tx is a holder + // Note: `account_` is holder's account + // `holderID` is NOT used + if (!holderID) + { + // When a holder wants to unauthorize/delete a MPT, the ledger must + // - delete mptokenKey from owner directory + // - delete the MPToken + if (flags & tfMPTUnauthorize) + { + auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); + auto const sleMpt = view.peek(mptokenKey); + if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!view.dirRemove( + keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleAcct, -1, journal); + + view.erase(sleMpt); + return tesSUCCESS; + } + + // A potential holder wants to authorize/hold a mpt, the ledger must: + // - add the new mptokenKey to the owner directory + // - create the MPToken object for the holder + + // The reserve that is required to create the MPToken. Note + // that although the reserve increases with every item + // an account owns, in the case of MPTokens we only + // *enforce* a reserve if the user owns more than two + // items. This is similar to the reserve requirements of trust lines. + std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); + XRPAmount const reserveCreate( + (uOwnerCount < 2) ? XRPAmount(beast::zero) + : view.fees().accountReserve(uOwnerCount + 1)); + + if (priorBalance < reserveCreate) + return tecINSUFFICIENT_RESERVE; + + // Defensive check before we attempt to create MPToken for the issuer + auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!mpt || mpt->getAccountID(sfIssuer) == account) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token"); + if (view.rules().enabled(featureLendingProtocol)) + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); + auto mptoken = std::make_shared(mptokenKey); + if (auto ter = dirLink(view, account, mptoken)) + return ter; // LCOV_EXCL_LINE + + (*mptoken)[sfAccount] = account; + (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; + (*mptoken)[sfFlags] = 0; + view.insert(mptoken); + + // Update owner count. + adjustOwnerCount(view, sleAcct, 1, journal); + + return tesSUCCESS; + } + + auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!sleMptIssuance) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the account that submitted this tx is the issuer of the MPT + // Note: `account_` is issuer's account + // `holderID` is holder's account + if (account != (*sleMptIssuance)[sfIssuer]) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID)); + if (!sleMpt) + return tecINTERNAL; // LCOV_EXCL_LINE + + std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags); + std::uint32_t flagsOut = flagsIn; + + // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on + // their MPToken + if (flags & tfMPTUnauthorize) + { + flagsOut &= ~lsfMPTAuthorized; + } + // Issuer wants to authorize a holder, set lsfMPTAuthorized on their + // MPToken + else + { + flagsOut |= lsfMPTAuthorized; + } + + if (flagsIn != flagsOut) + sleMpt->setFieldU32(sfFlags, flagsOut); + + view.update(sleMpt); + return tesSUCCESS; +} + +[[nodiscard]] TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + MPTIssue const& mptIssue, + beast::Journal journal) +{ + // If the account is the issuer, then no token should exist. MPTs do not + // have the legacy ability to create such a situation, but check anyway. If + // a token does exist, it will get deleted. If not, return success. + bool const accountIsIssuer = accountID == mptIssue.getIssuer(); + auto const& mptID = mptIssue.getMptID(); + auto const mptoken = view.peek(keylet::mptoken(mptID, accountID)); + if (!mptoken) + return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; + // Unlike a trust line, if the account is the issuer, and the token has a + // balance, it can not just be deleted, because that will throw the issuance + // accounting out of balance, so fail. Since this should be impossible + // anyway, I'm not going to put any effort into it. + if (mptoken->at(sfMPTAmount) != 0) + return tecHAS_OBLIGATIONS; + + return authorizeMPToken( + view, + {}, // priorBalance + mptID, + accountID, + journal, + tfMPTUnauthorize // flags + ); +} + +[[nodiscard]] TER +requireAuth( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& account, + AuthType authType, + int depth) +{ + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto const sleIssuance = view.read(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + auto const mptIssuer = sleIssuance->getAccountID(sfIssuer); + + // issuer is always "authorized" + if (mptIssuer == account) // Issuer won't have MPToken + return tesSUCCESS; + + bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault); + + if (featureSAVEnabled) + { + if (depth >= maxAssetCheckDepth) + return tecINTERNAL; // LCOV_EXCL_LINE + + // requireAuth is recursive if the issuer is a vault pseudo-account + auto const sleIssuer = view.read(keylet::account(mptIssuer)); + if (!sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (sleIssuer->isFieldPresent(sfVaultID)) + { + auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID))); + if (!sleVault) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const asset = sleVault->at(sfAsset); + if (auto const err = std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return requireAuth(view, issue, account, authType); + } + else + { + return requireAuth(view, issue, account, authType, depth + 1); + } + }, + asset.value()); + !isTesSuccess(err)) + return err; + } + } + + auto const mptokenID = keylet::mptoken(mptID.key, account); + auto const sleToken = view.read(mptokenID); + + // if account has no MPToken, fail + if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy)) + return tecNO_AUTH; + + // Note, this check is not amendment-gated because DomainID will be always + // empty **unless** writing to it has been enabled by an amendment + auto const maybeDomainID = sleIssuance->at(~sfDomainID); + if (maybeDomainID) + { + XRPL_ASSERT( + sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth, + "xrpl::requireAuth : issuance requires authorization"); + // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED + auto const ter = credentials::validDomain(view, *maybeDomainID, account); + if (isTesSuccess(ter)) + { + return ter; // Note: sleToken might be null + } + if (!sleToken) + { + return ter; + } + // We ignore error from validDomain if we found sleToken, as it could + // belong to someone who is explicitly authorized e.g. a vault owner. + } + + if (featureSAVEnabled) + { + // Implicitly authorize Vault and LoanBroker pseudo-accounts + if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID})) + return tesSUCCESS; + } + + // mptoken must be authorized if issuance enabled requireAuth + if (sleIssuance->isFlag(lsfMPTRequireAuth) && + (!sleToken || !sleToken->isFlag(lsfMPTAuthorized))) + return tecNO_AUTH; + + return tesSUCCESS; // Note: sleToken might be null +} + +[[nodiscard]] TER +enforceMPTokenAuthorization( + ApplyView& view, + MPTID const& mptIssuanceID, + AccountID const& account, + XRPAmount const& priorBalance, // for MPToken authorization + beast::Journal j) +{ + auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID)); + if (!sleIssuance) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + sleIssuance->isFlag(lsfMPTRequireAuth), + "xrpl::enforceMPTokenAuthorization : authorization required"); + + if (account == sleIssuance->at(sfIssuer)) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const keylet = keylet::mptoken(mptIssuanceID, account); + auto const sleToken = view.read(keylet); // NOTE: might be null + auto const maybeDomainID = sleIssuance->at(~sfDomainID); + bool expired = false; + bool const authorizedByDomain = [&]() -> bool { + // NOTE: defensive here, should be checked in preclaim + if (!maybeDomainID.has_value()) + return false; // LCOV_EXCL_LINE + + auto const ter = verifyValidDomain(view, account, *maybeDomainID, j); + if (isTesSuccess(ter)) + return true; + if (ter == tecEXPIRED) + expired = true; + return false; + }(); + + if (!authorizedByDomain && sleToken == nullptr) + { + // Could not find MPToken and won't create one, could be either of: + // + // 1. Field sfDomainID not set in MPTokenIssuance or + // 2. Account has no matching and accepted credentials or + // 3. Account has all expired credentials (deleted in verifyValidDomain) + // + // Either way, return tecNO_AUTH and there is nothing else to do + return expired ? tecEXPIRED : tecNO_AUTH; + } + if (!authorizedByDomain && maybeDomainID.has_value()) + { + // Found an MPToken but the account is not authorized and we expect + // it to have been authorized by the domain. This could be because the + // credentials used to create the MPToken have expired or been deleted. + return expired ? tecEXPIRED : tecNO_AUTH; + } + if (!authorizedByDomain) + { + // We found an MPToken, but sfDomainID is not set, so this is a classic + // MPToken which requires authorization by the token issuer. + XRPL_ASSERT( + sleToken != nullptr && !maybeDomainID.has_value(), + "xrpl::enforceMPTokenAuthorization : found MPToken"); + if (sleToken->isFlag(lsfMPTAuthorized)) + return tesSUCCESS; + + return tecNO_AUTH; + } + if (authorizedByDomain && sleToken != nullptr) + { + // Found an MPToken, authorized by the domain. Ignore authorization flag + // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS + XRPL_ASSERT( + maybeDomainID.has_value(), + "xrpl::enforceMPTokenAuthorization : found MPToken for domain"); + return tesSUCCESS; + } + if (authorizedByDomain) + { + // Could not find MPToken but there should be one because we are + // authorized by domain. Proceed to create it, then return tesSUCCESS + XRPL_ASSERT( + maybeDomainID.has_value() && sleToken == nullptr, + "xrpl::enforceMPTokenAuthorization : new MPToken for domain"); + if (auto const err = authorizeMPToken( + view, + priorBalance, // priorBalance + mptIssuanceID, // mptIssuanceID + account, // account + j); + !isTesSuccess(err)) + return err; + + return tesSUCCESS; + } + + // LCOV_EXCL_START + UNREACHABLE("xrpl::enforceMPTokenAuthorization : condition list is incomplete"); + return tefINTERNAL; + // LCOV_EXCL_STOP +} + +TER +canTransfer( + ReadView const& view, + MPTIssue const& mptIssue, + AccountID const& from, + AccountID const& to) +{ + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto const sleIssuance = view.read(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer)) + { + if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer]) + return TER{tecNO_AUTH}; + } + return tesSUCCESS; +} + +TER +rippleLockEscrowMPT( + ApplyView& view, + AccountID const& sender, + STAmount const& amount, + beast::Journal j) +{ + auto const mptIssue = amount.get(); + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for " + << mptIssue.getMptID(); + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + if (amount.getIssuer() == sender) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs."; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + // 1. Decrease the MPT Holder MPTAmount + // 2. Increase the MPT Holder EscrowedAmount + { + auto const mptokenID = keylet::mptoken(mptID.key, sender); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: MPToken not found for " << sender; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + auto const amt = sle->getFieldU64(sfMPTAmount); + auto const pay = amount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: insufficient MPTAmount for " + << to_string(sender) << ": " << amt << " < " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + (*sle)[sfMPTAmount] = amt - pay; + + // Overflow check for addition + uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0); + + if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: overflow on locked amount for " + << to_string(sender) << ": " << locked << " + " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + if (sle->isFieldPresent(sfLockedAmount)) + { + (*sle)[sfLockedAmount] += pay; + } + else + { + sle->setFieldU64(sfLockedAmount, pay); + } + + view.update(sle); + } + + // 1. Increase the Issuance EscrowedAmount + // 2. DO NOT change the Issuance OutstandingAmount + { + uint64_t const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0); + auto const pay = amount.mpt().value(); + + // Overflow check for addition + if (!canAdd(STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance " + "locked amount for " + << mptIssue.getMptID() << ": " << issuanceEscrowed << " + " << pay; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + if (sleIssuance->isFieldPresent(sfLockedAmount)) + { + (*sleIssuance)[sfLockedAmount] += pay; + } + else + { + sleIssuance->setFieldU64(sfLockedAmount, pay); + } + + view.update(sleIssuance); + } + return tesSUCCESS; +} + +TER +rippleUnlockEscrowMPT( + ApplyView& view, + AccountID const& sender, + AccountID const& receiver, + STAmount const& netAmount, + STAmount const& grossAmount, + beast::Journal j) +{ + if (!view.rules().enabled(fixTokenEscrowV1)) + { + XRPL_ASSERT( + netAmount == grossAmount, "xrpl::rippleUnlockEscrowMPT : netAmount == grossAmount"); + } + + auto const& issuer = netAmount.getIssuer(); + auto const& mptIssue = netAmount.get(); + auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for " + << mptIssue.getMptID(); + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + // Decrease the Issuance EscrowedAmount + { + if (!sleIssuance->isFieldPresent(sfLockedAmount)) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in issuance for " + << mptIssue.getMptID(); + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const locked = sleIssuance->getFieldU64(sfLockedAmount); + auto const redeem = grossAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, redeem))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " + << mptIssue.getMptID() << ": " << locked << " < " << redeem; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const newLocked = locked - redeem; + if (newLocked == 0) + { + sleIssuance->makeFieldAbsent(sfLockedAmount); + } + else + { + sleIssuance->setFieldU64(sfLockedAmount, newLocked); + } + view.update(sleIssuance); + } + + if (issuer != receiver) + { + // Increase the MPT Holder MPTAmount + auto const mptokenID = keylet::mptoken(mptID.key, receiver); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << receiver; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + auto current = sle->getFieldU64(sfMPTAmount); + auto delta = netAmount.mpt().value(); + + // Overflow check for addition + if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: overflow on MPTAmount for " + << to_string(receiver) << ": " << current << " + " << delta; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + (*sle)[sfMPTAmount] += delta; + view.update(sle); + } + else + { + // Decrease the Issuance OutstandingAmount + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + auto const redeem = netAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " + << mptIssue.getMptID() << ": " << outstanding << " < " << redeem; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); + view.update(sleIssuance); + } + + if (issuer == sender) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, " + "cannot unlock MPTs."; + return tecINTERNAL; + } // LCOV_EXCL_STOP + // Decrease the MPT Holder EscrowedAmount + auto const mptokenID = keylet::mptoken(mptID.key, sender); + auto sle = view.peek(mptokenID); + if (!sle) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: MPToken not found for " << sender; + return tecOBJECT_NOT_FOUND; + } // LCOV_EXCL_STOP + + if (!sle->isFieldPresent(sfLockedAmount)) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in MPToken for " + << to_string(sender); + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const locked = sle->getFieldU64(sfLockedAmount); + auto const delta = grossAmount.mpt().value(); + + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient locked amount for " + << to_string(sender) << ": " << locked << " < " << delta; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + auto const newLocked = locked - delta; + if (newLocked == 0) + { + sle->makeFieldAbsent(sfLockedAmount); + } + else + { + sle->setFieldU64(sfLockedAmount, newLocked); + } + view.update(sle); + + // Note: The gross amount is the amount that was locked, the net + // amount is the amount that is being unlocked. The difference is the fee + // that was charged for the transfer. If this difference is greater than + // zero, we need to update the outstanding amount. + auto const diff = grossAmount.mpt().value() - netAmount.mpt().value(); + if (diff != 0) + { + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + // Underflow check for subtraction + if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, diff))) + { // LCOV_EXCL_START + JLOG(j.error()) << "rippleUnlockEscrowMPT: insufficient outstanding amount for " + << mptIssue.getMptID() << ": " << outstanding << " < " << diff; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff); + view.update(sleIssuance); + } + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp new file mode 100644 index 0000000000..9422153b10 --- /dev/null +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -0,0 +1,58 @@ +#include +// +#include +#include +#include +#include + +namespace xrpl { + +TER +offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +{ + if (!sle) + return tesSUCCESS; + auto offerIndex = sle->key(); + auto owner = sle->getAccountID(sfAccount); + + // Detect legacy directories. + uint256 uDirectory = sle->getFieldH256(sfBookDirectory); + + if (!view.dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + if (!view.dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + if (sle->isFieldPresent(sfAdditionalBooks)) + { + XRPL_ASSERT( + sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID), + "xrpl::offerDelete : should be a hybrid domain offer"); + + auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks); + + for (auto const& bookDir : additionalBookDirs) + { + auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory); + auto const& dirNode = bookDir.getFieldU64(sfBookNode); + + if (!view.dirRemove(keylet::page(dirIndex), dirNode, offerIndex, false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + } + } + + adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); + + view.erase(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp new file mode 100644 index 0000000000..e88afe0cfb --- /dev/null +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -0,0 +1,759 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +//------------------------------------------------------------------------------ +// +// Credit functions (from Credit.cpp) +// +//------------------------------------------------------------------------------ + +STAmount +creditLimit( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency) +{ + STAmount result(Issue{currency, account}); + + auto sleRippleState = view.read(keylet::line(account, issuer, currency)); + + if (sleRippleState) + { + result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit); + result.setIssuer(account); + } + + XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match"); + XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match"); + return result; +} + +IOUAmount +creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur) +{ + return toAmount(creditLimit(v, acc, iss, cur)); +} + +STAmount +creditBalance( + ReadView const& view, + AccountID const& account, + AccountID const& issuer, + Currency const& currency) +{ + STAmount result(Issue{currency, account}); + + auto sleRippleState = view.read(keylet::line(account, issuer, currency)); + + if (sleRippleState) + { + result = sleRippleState->getFieldAmount(sfBalance); + if (account < issuer) + result.negate(); + result.setIssuer(account); + } + + XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match"); + XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match"); + return result; +} + +//------------------------------------------------------------------------------ +// +// Freeze checking (IOU-specific) +// +//------------------------------------------------------------------------------ + +bool +isIndividualFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + return false; + if (issuer != account) + { + // Check if the issuer froze the line + auto const sle = view.read(keylet::line(account, issuer, currency)); + if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) + return true; + } + return false; +} + +// Can the specified account spend the specified currency issued by +// the specified issuer or does the freeze flag prohibit it? +bool +isFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + return false; + auto sle = view.read(keylet::account(issuer)); + if (sle && sle->isFlag(lsfGlobalFreeze)) + return true; + if (issuer != account) + { + // Check if the issuer froze the line + sle = view.read(keylet::line(account, issuer, currency)); + if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) + return true; + } + return false; +} + +bool +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer) +{ + if (isXRP(currency)) + { + return false; + } + + if (issuer == account) + { + return false; + } + + auto const sle = view.read(keylet::line(account, issuer, currency)); + if (!sle) + { + return false; + } + + return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze); +} + +//------------------------------------------------------------------------------ +// +// Trust line operations +// +//------------------------------------------------------------------------------ + +TER +trustCreate( + ApplyView& view, + bool const bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + bool const bAuth, // --> authorize account. + bool const bNoRipple, // --> others cannot ripple through + bool const bFreeze, // --> funds cannot leave + bool bDeepFreeze, // --> can neither receive nor send funds + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + std::uint32_t uQualityIn, + std::uint32_t uQualityOut, + beast::Journal j) +{ + JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " + << to_string(uDstAccountID) << ", " << saBalance.getFullText(); + + auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; + auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; + if (uLowAccountID == uHighAccountID) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::trustCreate : trust line to self"); + if (view.rules().enabled(featureLendingProtocol)) + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + auto const sleRippleState = std::make_shared(ltRIPPLE_STATE, uIndex); + view.insert(sleRippleState); + + auto lowNode = view.dirInsert( + keylet::ownerDir(uLowAccountID), sleRippleState->key(), describeOwnerDir(uLowAccountID)); + + if (!lowNode) + return tecDIR_FULL; // LCOV_EXCL_LINE + + auto highNode = view.dirInsert( + keylet::ownerDir(uHighAccountID), sleRippleState->key(), describeOwnerDir(uHighAccountID)); + + if (!highNode) + return tecDIR_FULL; // LCOV_EXCL_LINE + + bool const bSetDst = saLimit.getIssuer() == uDstAccountID; + bool const bSetHigh = bSrcHigh ^ bSetDst; + + XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE"); + if (!sleAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), + "xrpl::trustCreate : matching account ID"); + auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID)); + if (!slePeer) + return tecNO_TARGET; + + // Remember deletion hints. + sleRippleState->setFieldU64(sfLowNode, *lowNode); + sleRippleState->setFieldU64(sfHighNode, *highNode); + + sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit); + sleRippleState->setFieldAmount( + bSetHigh ? sfLowLimit : sfHighLimit, + STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID})); + + if (uQualityIn) + sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); + + if (uQualityOut) + sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); + + std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; + + if (bAuth) + { + uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); + } + if (bNoRipple) + { + uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + if (bFreeze) + { + uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze); + } + if (bDeepFreeze) + { + uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze); + } + + if ((slePeer->getFlags() & lsfDefaultRipple) == 0) + { + // The other side's default is no rippling + uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); + } + + sleRippleState->setFieldU32(sfFlags, uFlags); + adjustOwnerCount(view, sleAccount, 1, j); + + // ONLY: Create ripple balance. + sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); + + view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed()); + + return tesSUCCESS; +} + +TER +trustDelete( + ApplyView& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID, + beast::Journal j) +{ + // Detect legacy dirs. + std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode); + std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode); + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: low"; + + if (!view.dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(), false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: high"; + + if (!view.dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(), false)) + { + return tefBAD_LEDGER; // LCOV_EXCL_LINE + } + + JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; + view.erase(sleRippleState); + + return tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// IOU issuance/redemption +// +//------------------------------------------------------------------------------ + +static bool +updateTrustLine( + ApplyView& view, + SLE::pointer state, + bool bSenderHigh, + AccountID const& sender, + STAmount const& before, + STAmount const& after, + beast::Journal j) +{ + if (!state) + return false; + std::uint32_t const flags(state->getFieldU32(sfFlags)); + + auto sle = view.peek(keylet::account(sender)); + if (!sle) + return false; + + // YYY Could skip this if rippling in reverse. + if (before > beast::zero + // Sender balance was positive. + && after <= beast::zero + // Sender is zero or negative. + && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) + // Sender reserve is set. + && static_cast(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast(sle->getFlags() & lsfDefaultRipple) && + !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && + !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) + // Sender quality in is 0. + && !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) + // Sender quality out is 0. + { + // VFALCO Where is the line being deleted? + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, sle, -1, j); + + // Clear reserve flag. + state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + if (!after // Balance is zero. + && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) + return true; + } + return false; +} + +TER +issueIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j) +{ + XRPL_ASSERT( + !isXRP(account) && !isXRP(issue.account), + "xrpl::issueIOU : neither account nor issuer is XRP"); + + // Consistency check + XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue"); + + // Can't send to self! + XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account"); + + JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": " << amount.getFullText(); + + bool bSenderHigh = issue.account > account; + + auto const index = keylet::line(issue.account, account, issue.currency); + + if (auto state = view.peek(index)) + { + STAmount final_balance = state->getFieldAmount(sfBalance); + + if (bSenderHigh) + final_balance.negate(); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = updateTrustLine( + view, state, bSenderHigh, issue.account, start_balance, final_balance, j); + + view.creditHook(issue.account, account, amount, start_balance); + + if (bSenderHigh) + final_balance.negate(); + + // Adjust the balance on the trust line if necessary. We do this even + // if we are going to delete the line to reflect the correct balance + // at the time of deletion. + state->setFieldAmount(sfBalance, final_balance); + if (must_delete) + { + return trustDelete( + view, + state, + bSenderHigh ? account : issue.account, + bSenderHigh ? issue.account : account, + j); + } + + view.update(state); + + return tesSUCCESS; + } + + // NIKB TODO: The limit uses the receiver's account as the issuer and + // this is unnecessarily inefficient as copying which could be avoided + // is now required. Consider available options. + STAmount const limit(Issue{issue.currency, account}); + STAmount final_balance = amount; + + final_balance.setIssuer(noAccount()); + + auto const receiverAccount = view.peek(keylet::account(account)); + if (!receiverAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; + + return trustCreate( + view, + bSenderHigh, + issue.account, + account, + index.key, + receiverAccount, + false, + noRipple, + false, + false, + final_balance, + limit, + 0, + 0, + j); +} + +TER +redeemIOU( + ApplyView& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue, + beast::Journal j) +{ + XRPL_ASSERT( + !isXRP(account) && !isXRP(issue.account), + "xrpl::redeemIOU : neither account nor issuer is XRP"); + + // Consistency check + XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue"); + + // Can't send to self! + XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account"); + + JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": " << amount.getFullText(); + + bool bSenderHigh = account > issue.account; + + if (auto state = view.peek(keylet::line(account, issue.account, issue.currency))) + { + STAmount final_balance = state->getFieldAmount(sfBalance); + + if (bSenderHigh) + final_balance.negate(); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = + updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j); + + view.creditHook(account, issue.account, amount, start_balance); + + if (bSenderHigh) + final_balance.negate(); + + // Adjust the balance on the trust line if necessary. We do this even + // if we are going to delete the line to reflect the correct balance + // at the time of deletion. + state->setFieldAmount(sfBalance, final_balance); + + if (must_delete) + { + return trustDelete( + view, + state, + bSenderHigh ? issue.account : account, + bSenderHigh ? account : issue.account, + j); + } + + view.update(state); + return tesSUCCESS; + } + + // In order to hold an IOU, a trust line *MUST* exist to track the + // balance. If it doesn't, then something is very wrong. Don't try + // to continue. + // LCOV_EXCL_START + JLOG(j.fatal()) << "redeemIOU: " << to_string(account) << " attempts to " + << "redeem " << amount.getFullText() << " but no trust line exists!"; + + return tefINTERNAL; + // LCOV_EXCL_STOP +} + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks (IOU-specific) +// +//------------------------------------------------------------------------------ + +TER +requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType) +{ + if (isXRP(issue) || issue.account == account) + return tesSUCCESS; + + auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency)); + // If account has no line, and this is a strong check, fail + if (!trustLine && authType == AuthType::StrongAuth) + return tecNO_LINE; + + // If this is a weak or legacy check, or if the account has a line, fail if + // auth is required and not set on the line + if (auto const issuerAccount = view.read(keylet::account(issue.account)); + issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth) + { + if (trustLine) + { + return ((*trustLine)[sfFlags] & ((account > issue.account) ? lsfLowAuth : lsfHighAuth)) + ? tesSUCCESS + : TER{tecNO_AUTH}; + } + return TER{tecNO_LINE}; + } + + return tesSUCCESS; +} + +TER +canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to) +{ + if (issue.native()) + return tesSUCCESS; + + auto const& issuerId = issue.getIssuer(); + if (issuerId == from || issuerId == to) + return tesSUCCESS; + auto const sleIssuer = view.read(keylet::account(issuerId)); + if (sleIssuer == nullptr) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const isRippleDisabled = [&](AccountID account) -> bool { + // Line might not exist, but some transfers can create it. If this + // is the case, just check the default ripple on the issuer account. + auto const line = view.read(keylet::line(account, issue)); + if (line) + { + bool const issuerHigh = issuerId > account; + return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + return sleIssuer->isFlag(lsfDefaultRipple) == false; + }; + + // Fail if rippling disabled on both trust lines + if (isRippleDisabled(from) && isRippleDisabled(to)) + return terNO_RIPPLE; + + return tesSUCCESS; +} + +//------------------------------------------------------------------------------ +// +// Empty holding operations (IOU-specific) +// +//------------------------------------------------------------------------------ + +TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Issue const& issue, + beast::Journal journal) +{ + // Every account can hold XRP. An issuer can issue directly. + if (issue.native() || accountID == issue.getIssuer()) + return tesSUCCESS; + + auto const& issuerId = issue.getIssuer(); + auto const& currency = issue.currency; + if (isGlobalFrozen(view, issuerId)) + return tecFROZEN; // LCOV_EXCL_LINE + + auto const& srcId = issuerId; + auto const& dstId = accountID; + auto const high = srcId > dstId; + auto const index = keylet::line(srcId, dstId, currency); + auto const sleSrc = view.peek(keylet::account(srcId)); + auto const sleDst = view.peek(keylet::account(dstId)); + if (!sleDst || !sleSrc) + return tefINTERNAL; // LCOV_EXCL_LINE + if (!sleSrc->isFlag(lsfDefaultRipple)) + return tecINTERNAL; // LCOV_EXCL_LINE + // If the line already exists, don't create it again. + if (view.read(index)) + return tecDUPLICATE; + + // Can the account cover the trust line reserve ? + std::uint32_t const ownerCount = sleDst->at(sfOwnerCount); + if (priorBalance < view.fees().accountReserve(ownerCount + 1)) + return tecNO_LINE_INSUF_RESERVE; + + return trustCreate( + view, + high, + srcId, + dstId, + index.key, + sleDst, + /*bAuth=*/false, + /*bNoRipple=*/true, + /*bFreeze=*/false, + /*deepFreeze*/ false, + /*saBalance=*/STAmount{Issue{currency, noAccount()}}, + /*saLimit=*/STAmount{Issue{currency, dstId}}, + /*uQualityIn=*/0, + /*uQualityOut=*/0, + journal); +} + +TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Issue const& issue, + beast::Journal journal) +{ + if (issue.native()) + { + auto const sle = view.read(keylet::account(accountID)); + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const balance = sle->getFieldAmount(sfBalance); + if (balance.xrp() != 0) + return tecHAS_OBLIGATIONS; + + return tesSUCCESS; + } + + // `asset` is an IOU. + // If the account is the issuer, then no line should exist. Check anyway. + // If a line does exist, it will get deleted. If not, return success. + bool const accountIsIssuer = accountID == issue.account; + auto const line = view.peek(keylet::line(accountID, issue)); + if (!line) + return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; + if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero) + return tecHAS_OBLIGATIONS; + + // Adjust the owner count(s) + if (line->isFlag(lsfLowReserve)) + { + // Clear reserve for low account. + auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); + if (!sleLowAccount) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleLowAccount, -1, journal); + // It's not really necessary to clear the reserve flag, since the line + // is about to be deleted, but this will make the metadata reflect an + // accurate state at the time of deletion. + line->clearFlag(lsfLowReserve); + } + + if (line->isFlag(lsfHighReserve)) + { + // Clear reserve for high account. + auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); + if (!sleHighAccount) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleHighAccount, -1, journal); + // It's not really necessary to clear the reserve flag, since the line + // is about to be deleted, but this will make the metadata reflect an + // accurate state at the time of deletion. + line->clearFlag(lsfHighReserve); + } + + return trustDelete( + view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); +} + +TER +deleteAMMTrustLine( + ApplyView& view, + std::shared_ptr sleState, + std::optional const& ammAccountID, + beast::Journal j) +{ + if (!sleState || sleState->getType() != ltRIPPLE_STATE) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const& [low, high] = std::minmax( + sleState->getFieldAmount(sfLowLimit).getIssuer(), + sleState->getFieldAmount(sfHighLimit).getIssuer()); + auto sleLow = view.peek(keylet::account(low)); + auto sleHigh = view.peek(keylet::account(high)); + if (!sleLow || !sleHigh) + return tecINTERNAL; // LCOV_EXCL_LINE + + bool const ammLow = sleLow->isFieldPresent(sfAMMID); + bool const ammHigh = sleHigh->isFieldPresent(sfAMMID); + + // can't both be AMM + if (ammLow && ammHigh) + return tecINTERNAL; // LCOV_EXCL_LINE + + // at least one must be + if (!ammLow && !ammHigh) + return terNO_AMM; + + // one must be the target amm + if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) + return terNO_AMM; + + if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) + { + JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; + return ter; + } + + auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve; + if (!(sleState->getFlags() & uFlags)) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp new file mode 100644 index 0000000000..7a98445b0a --- /dev/null +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -0,0 +1,1392 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +// Forward declaration for function that remains in View.h/cpp +bool +isLPTokenFrozen( + ReadView const& view, + AccountID const& account, + Issue const& asset, + Issue const& asset2); + +//------------------------------------------------------------------------------ +// +// Freeze checking (Asset-based) +// +//------------------------------------------------------------------------------ + +bool +isGlobalFrozen(ReadView const& view, Asset const& asset) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return isGlobalFrozen(view, issue.getIssuer()); + } + else + { + return isGlobalFrozen(view, issue); + } + }, + asset.value()); +} + +bool +isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value()); +} + +bool +isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +{ + return std::visit( + [&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value()); +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue) +{ + return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS; +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS; +} + +TER +checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return checkFrozen(view, account, issue); }, asset.value()); +} + +bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Issue const& issue) +{ + for (auto const& account : accounts) + { + if (isFrozen(view, account, issue.currency, issue.account)) + return true; + } + return false; +} + +bool +isAnyFrozen( + ReadView const& view, + std::initializer_list const& accounts, + Asset const& asset, + int depth) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return isAnyFrozen(view, accounts, issue); + } + else + { + return isAnyFrozen(view, accounts, issue, depth); + } + }, + asset.value()); +} + +bool +isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +{ + // Unlike IOUs, frozen / locked MPTs are not allowed to send or receive + // funds, so checking "deep frozen" is the same as checking "frozen". + return isFrozen(view, account, mptIssue, depth); +} + +bool +isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +{ + return std::visit( + [&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); }, + asset.value()); +} + +TER +checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue) +{ + return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS; +} + +TER +checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + return std::visit( + [&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Account balance functions +// +//------------------------------------------------------------------------------ + +static SLE::const_pointer +getLineIfUsable( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j) +{ + auto const sle = view.read(keylet::line(account, issuer, currency)); + + if (!sle) + { + return nullptr; + } + + if (zeroIfFrozen == fhZERO_IF_FROZEN) + { + if (isFrozen(view, account, currency, issuer) || + isDeepFrozen(view, account, currency, issuer)) + { + return nullptr; + } + + // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken, + // we need to check if the associated assets have been frozen + if (view.rules().enabled(fixFrozenLPTokenTransfer)) + { + auto const sleIssuer = view.read(keylet::account(issuer)); + if (!sleIssuer) + { + return nullptr; // LCOV_EXCL_LINE + } + if (sleIssuer->isFieldPresent(sfAMMID)) + { + auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID])); + + if (!sleAmm || + isLPTokenFrozen( + view, + account, + (*sleAmm)[sfAsset].get(), + (*sleAmm)[sfAsset2].get())) + { + return nullptr; + } + } + } + } + + return sle; +} + +static STAmount +getTrustLineBalance( + ReadView const& view, + SLE::const_ref sle, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + bool includeOppositeLimit, + beast::Journal j) +{ + STAmount amount; + if (sle) + { + amount = sle->getFieldAmount(sfBalance); + bool const accountHigh = account > issuer; + auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit; + if (accountHigh) + { + // Put balance in account terms. + amount.negate(); + } + if (includeOppositeLimit) + { + amount += sle->getFieldAmount(oppositeField); + } + amount.setIssuer(issuer); + } + else + { + amount.clear(Issue{currency, issuer}); + } + + JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account) + << " amount=" << amount.getFullText(); + + return view.balanceHook(account, issuer, amount); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Currency const& currency, + AccountID const& issuer, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + STAmount amount; + if (isXRP(currency)) + { + return {xrpLiquid(view, account, 0, j)}; + } + + bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); + if (returnSpendable && account == issuer) + { + // If the account is the issuer, then their limit is effectively + // infinite + return STAmount{Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset}; + } + + // IOU: Return balance on trust line modulo freeze + SLE::const_pointer const sle = + getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j); + + return getTrustLineBalance(view, sle, account, currency, issuer, returnSpendable, j); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Issue const& issue, + FreezeHandling zeroIfFrozen, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + return accountHolds( + view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance); +} + +STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + bool const returnSpendable = (includeFullBalance == shFULL_BALANCE); + + if (returnSpendable && account == mptIssue.getIssuer()) + { + // if the account is the issuer, and the issuance exists, their limit is + // the issuance limit minus the outstanding value + auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); + + if (!issuance) + { + return STAmount{mptIssue}; + } + return STAmount{ + mptIssue, + issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) - + issuance->at(sfOutstandingAmount)}; + } + + STAmount amount; + + auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account)); + + if (!sleMpt) + { + amount.clear(mptIssue); + } + else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue)) + { + amount.clear(mptIssue); + } + else + { + amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)}; + + // Only if auth check is needed, as it needs to do an additional read + // operation. Note featureSingleAssetVault will affect error codes. + if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && + view.rules().enabled(featureSingleAssetVault)) + { + if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); + !isTesSuccess(err)) + amount.clear(mptIssue); + } + else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED) + { + auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID())); + + // if auth is enabled on the issuance and mpt is not authorized, + // clear amount + if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) && + !sleMpt->isFlag(lsfMPTAuthorized)) + amount.clear(mptIssue); + } + } + + return amount; +} + +[[nodiscard]] STAmount +accountHolds( + ReadView const& view, + AccountID const& account, + Asset const& asset, + FreezeHandling zeroIfFrozen, + AuthHandling zeroIfUnauthorized, + beast::Journal j, + SpendableHandling includeFullBalance) +{ + return std::visit( + [&](TIss const& value) { + if constexpr (std::is_same_v) + { + return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance); + } + else if constexpr (std::is_same_v) + { + return accountHolds( + view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance); + } + }, + asset.value()); +} + +STAmount +accountFunds( + ReadView const& view, + AccountID const& id, + STAmount const& saDefault, + FreezeHandling freezeHandling, + beast::Journal j) +{ + if (!saDefault.native() && saDefault.getIssuer() == id) + return saDefault; + + return accountHolds( + view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j); +} + +Rate +transferRate(ReadView const& view, STAmount const& amount) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return transferRate(view, issue.getIssuer()); + } + else + { + return transferRate(view, issue.getMptID()); + } + }, + amount.asset().value()); +} + +//------------------------------------------------------------------------------ +// +// Holding operations +// +//------------------------------------------------------------------------------ + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Issue const& issue) +{ + if (issue.native()) + { + return tesSUCCESS; // No special checks for XRP + } + + auto const issuer = view.read(keylet::account(issue.getIssuer())); + if (!issuer) + { + return terNO_ACCOUNT; + } + if (!issuer->isFlag(lsfDefaultRipple)) + { + return terNO_RIPPLE; + } + + return tesSUCCESS; +} + +[[nodiscard]] TER +canAddHolding(ReadView const& view, Asset const& asset) +{ + return std::visit( + [&](TIss const& issue) -> TER { return canAddHolding(view, issue); }, + asset.value()); +} + +TER +addEmptyHolding( + ApplyView& view, + AccountID const& accountID, + XRPAmount priorBalance, + Asset const& asset, + beast::Journal journal) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return addEmptyHolding(view, accountID, priorBalance, issue, journal); + }, + asset.value()); +} + +TER +removeEmptyHolding( + ApplyView& view, + AccountID const& accountID, + Asset const& asset, + beast::Journal journal) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return removeEmptyHolding(view, accountID, issue, journal); + }, + asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Authorization and transfer checks +// +//------------------------------------------------------------------------------ + +TER +requireAuth(ReadView const& view, Asset const& asset, AccountID const& account, AuthType authType) +{ + return std::visit( + [&](TIss const& issue_) { + return requireAuth(view, issue_, account, authType); + }, + asset.value()); +} + +TER +canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to) +{ + return std::visit( + [&](TIss const& issue) -> TER { + return canTransfer(view, issue, from, to); + }, + asset.value()); +} + +//------------------------------------------------------------------------------ +// +// Money Transfers +// +//------------------------------------------------------------------------------ + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line if needed. +// --> bCheckIssuer : normally require issuer to be involved. +static TER +rippleCreditIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j) +{ + AccountID const& issuer = saAmount.getIssuer(); + Currency const& currency = saAmount.getCurrency(); + + // Make sure issuer is involved. + XRPL_ASSERT( + !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer, + "xrpl::rippleCreditIOU : matching issuer or don't care"); + (void)issuer; + + // Disallow sending to self. + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleCreditIOU : sender is not receiver"); + + bool const bSenderHigh = uSenderID > uReceiverID; + auto const index = keylet::line(uSenderID, uReceiverID, currency); + + XRPL_ASSERT( + !isXRP(uSenderID) && uSenderID != noAccount(), "xrpl::rippleCreditIOU : sender is not XRP"); + XRPL_ASSERT( + !isXRP(uReceiverID) && uReceiverID != noAccount(), + "xrpl::rippleCreditIOU : receiver is not XRP"); + + // If the line exists, modify it accordingly. + if (auto const sleRippleState = view.peek(index)) + { + STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); + + if (bSenderHigh) + saBalance.negate(); // Put balance in sender terms. + + view.creditHook(uSenderID, uReceiverID, saAmount, saBalance); + + STAmount const saBefore = saBalance; + + saBalance -= saAmount; + + JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> " + << to_string(uReceiverID) << " : before=" << saBefore.getFullText() + << " amount=" << saAmount.getFullText() + << " after=" << saBalance.getFullText(); + + std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags)); + bool bDelete = false; + + // FIXME This NEEDS to be cleaned up and simplified. It's impossible + // for anyone to understand. + if (saBefore > beast::zero + // Sender balance was positive. + && saBalance <= beast::zero + // Sender is zero or negative. + && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) + // Sender reserve is set. + && static_cast(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast( + view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) && + !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && + !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) + // Sender quality in is 0. + && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) + // Sender quality out is 0. + { + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j); + + // Clear reserve flag. + sleRippleState->setFieldU32( + sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + bDelete = !saBalance // Balance is zero. + && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); + // Receiver reserve is clear. + } + + if (bSenderHigh) + saBalance.negate(); + + // Want to reflect balance to zero even if we are deleting line. + sleRippleState->setFieldAmount(sfBalance, saBalance); + // ONLY: Adjust ripple balance. + + if (bDelete) + { + return trustDelete( + view, + sleRippleState, + bSenderHigh ? uReceiverID : uSenderID, + !bSenderHigh ? uReceiverID : uSenderID, + j); + } + + view.update(sleRippleState); + return tesSUCCESS; + } + + STAmount const saReceiverLimit(Issue{currency, uReceiverID}); + STAmount saBalance{saAmount}; + + saBalance.setIssuer(noAccount()); + + JLOG(j.debug()) << "rippleCreditIOU: " + "create line: " + << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " + << saAmount.getFullText(); + + auto const sleAccount = view.peek(keylet::account(uReceiverID)); + if (!sleAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; + + return trustCreate( + view, + bSenderHigh, + uSenderID, + uReceiverID, + index.key, + sleAccount, + false, + noRipple, + false, + false, + saBalance, + saReceiverLimit, + 0, + 0, + j); +} + +// Send regardless of limits. +// --> saAmount: Amount/currency/issuer to deliver to receiver. +// <-- saActual: Amount actually cost. Sender pays fees. +static TER +rippleSendIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + STAmount& saActual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + auto const& issuer = saAmount.getIssuer(); + + XRPL_ASSERT( + !isXRP(uSenderID) && !isXRP(uReceiverID), + "xrpl::rippleSendIOU : neither sender nor receiver is XRP"); + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendIOU : sender is not receiver"); + + if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) + { + // Direct send: redeeming IOUs and/or sending own IOUs. + auto const ter = rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j); + if (!isTesSuccess(ter)) + return ter; + saActual = saAmount; + return tesSUCCESS; + } + + // Sending 3rd party IOUs: transit. + + // Calculate the amount to transfer accounting + // for any transfer fees if the fee is not waived: + saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount + : multiply(saAmount, transferRate(view, issuer)); + + JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > " + << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() + << " cost=" << saActual.getFullText(); + + TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j); + + if (tesSUCCESS == terResult) + terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j); + + return terResult; +} + +// Send regardless of limits. +// --> receivers: Amount/currency/issuer to deliver to receivers. +// <-- saActual: Amount actually cost to sender. Sender pays fees. +static TER +rippleSendMultiIOU( + ApplyView& view, + AccountID const& senderID, + Issue const& issue, + MultiplePaymentDestinations const& receivers, + STAmount& actual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + auto const& issuer = issue.getIssuer(); + + XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP"); + + // These may diverge + STAmount takeFromSender{issue}; + actual = takeFromSender; + + // Failures return immediately. + for (auto const& r : receivers) + { + auto const& receiverID = r.first; + STAmount amount{issue, r.second}; + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!amount || (senderID == receiverID)) + continue; + + XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP"); + + if (senderID == issuer || receiverID == issuer || issuer == noAccount()) + { + // Direct send: redeeming IOUs and/or sending own IOUs. + if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j)) + return ter; + actual += amount; + // Do not add amount to takeFromSender, because rippleCreditIOU took + // it. + + continue; + } + + // Sending 3rd party IOUs: transit. + + // Calculate the amount to transfer accounting + // for any transfer fees if the fee is not waived: + STAmount actualSend = (waiveFee == WaiveTransferFee::Yes) + ? amount + : multiply(amount, transferRate(view, issuer)); + actual += actualSend; + takeFromSender += actualSend; + + JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > " + << to_string(receiverID) << " : deliver=" << amount.getFullText() + << " cost=" << actual.getFullText(); + + if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j)) + return terResult; + } + + if (senderID != issuer && takeFromSender) + { + if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j)) + return terResult; + } + + return tesSUCCESS; +} + +static TER +accountSendIOU( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + if (view.rules().enabled(fixAMMv1_1)) + { + if (saAmount < beast::zero || saAmount.holds()) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + } + else + { + // LCOV_EXCL_START + XRPL_ASSERT( + saAmount >= beast::zero && !saAmount.holds(), + "xrpl::accountSendIOU : minimum amount and not MPT"); + // LCOV_EXCL_STOP + } + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!saAmount || (uSenderID == uReceiverID)) + return tesSUCCESS; + + if (!saAmount.native()) + { + STAmount saActual; + + JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " + << to_string(uReceiverID) << " : " << saAmount.getFullText(); + + return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); + } + + /* XRP send which does not check reserve and can do pure adjustment. + * Note that sender or receiver may be null and this not a mistake; this + * setup is used during pathfinding and it is carefully controlled to + * ensure that transfers are balanced. + */ + TER terResult(tesSUCCESS); + + SLE::pointer sender = + uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); + SLE::pointer receiver = + uReceiverID != beast::zero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + std::string receiver_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendIOU> " << to_string(uSenderID) << " (" << sender_bal << ") -> " + << to_string(uReceiverID) << " (" << receiver_bal + << ") : " << saAmount.getFullText(); + } + + if (sender) + { + if (sender->getFieldAmount(sfBalance) < saAmount) + { + // VFALCO Its laborious to have to mutate the + // TER based on params everywhere + // LCOV_EXCL_START + terResult = view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; + // LCOV_EXCL_STOP + } + else + { + auto const sndBal = sender->getFieldAmount(sfBalance); + view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal); + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sndBal - saAmount); + view.update(sender); + } + } + + if (tesSUCCESS == terResult && receiver) + { + // Increment XRP balance. + auto const rcvBal = receiver->getFieldAmount(sfBalance); + receiver->setFieldAmount(sfBalance, rcvBal + saAmount); + view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal); + + view.update(receiver); + } + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + std::string receiver_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendIOU< " << to_string(uSenderID) << " (" << sender_bal << ") -> " + << to_string(uReceiverID) << " (" << receiver_bal + << ") : " << saAmount.getFullText(); + } + + return terResult; +} + +static TER +accountSendMultiIOU( + ApplyView& view, + AccountID const& senderID, + Issue const& issue, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT_PARTS( + receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided"); + + if (!issue.native()) + { + STAmount actual; + JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " + << receivers.size() << " IOUs"; + + return rippleSendMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee); + } + + /* XRP send which does not check reserve and can do pure adjustment. + * Note that sender or receiver may be null and this not a mistake; this + * setup could be used during pathfinding and it is carefully controlled to + * ensure that transfers are balanced. + */ + + SLE::pointer sender = + senderID != beast::zero ? view.peek(keylet::account(senderID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU> " << to_string(senderID) << " (" << sender_bal << ") -> " + << receivers.size() << " receivers."; + } + + // Failures return immediately. + STAmount takeFromSender{issue}; + for (auto const& r : receivers) + { + auto const& receiverID = r.first; + STAmount amount{issue, r.second}; + + if (amount < beast::zero) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!amount || (senderID == receiverID)) + continue; + + SLE::pointer receiver = + receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer(); + + if (auto stream = j.trace()) + { + std::string receiver_bal("-"); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU> " << to_string(senderID) << " -> " + << to_string(receiverID) << " (" << receiver_bal + << ") : " << amount.getFullText(); + } + + if (receiver) + { + // Increment XRP balance. + auto const rcvBal = receiver->getFieldAmount(sfBalance); + receiver->setFieldAmount(sfBalance, rcvBal + amount); + view.creditHook(xrpAccount(), receiverID, amount, -rcvBal); + + view.update(receiver); + + // Take what is actually sent + takeFromSender += amount; + } + + if (auto stream = j.trace()) + { + std::string receiver_bal("-"); + + if (receiver) + receiver_bal = receiver->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU< " << to_string(senderID) << " -> " + << to_string(receiverID) << " (" << receiver_bal + << ") : " << amount.getFullText(); + } + } + + if (sender) + { + if (sender->getFieldAmount(sfBalance) < takeFromSender) + { + return TER{tecFAILED_PROCESSING}; + } + auto const sndBal = sender->getFieldAmount(sfBalance); + view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal); + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sndBal - takeFromSender); + view.update(sender); + } + + if (auto stream = j.trace()) + { + std::string sender_bal("-"); + + if (sender) + sender_bal = sender->getFieldAmount(sfBalance).getFullText(); + + stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> " + << receivers.size() << " receivers."; + } + return tesSUCCESS; +} + +static TER +rippleCreditMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j) +{ + // Do not check MPT authorization here - it must have been checked earlier + auto const mptID = keylet::mptIssuance(saAmount.get().getMptID()); + auto const& issuer = saAmount.getIssuer(); + auto sleIssuance = view.peek(mptID); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + if (uSenderID == issuer) + { + (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value(); + view.update(sleIssuance); + } + else + { + auto const mptokenID = keylet::mptoken(mptID.key, uSenderID); + if (auto sle = view.peek(mptokenID)) + { + auto const amt = sle->getFieldU64(sfMPTAmount); + auto const pay = saAmount.mpt().value(); + if (amt < pay) + return tecINSUFFICIENT_FUNDS; + (*sle)[sfMPTAmount] = amt - pay; + view.update(sle); + } + else + { + return tecNO_AUTH; + } + } + + if (uReceiverID == issuer) + { + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + auto const redeem = saAmount.mpt().value(); + if (outstanding >= redeem) + { + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem); + view.update(sleIssuance); + } + else + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + } + else + { + auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID); + if (auto sle = view.peek(mptokenID)) + { + (*sle)[sfMPTAmount] += saAmount.mpt().value(); + view.update(sle); + } + else + { + return tecNO_AUTH; + } + } + + return tesSUCCESS; +} + +static TER +rippleSendMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + STAmount& saActual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendMPT : sender is not receiver"); + + // Safe to get MPT since rippleSendMPT is only called by accountSendMPT + auto const& issuer = saAmount.getIssuer(); + + auto const sle = view.read(keylet::mptIssuance(saAmount.get().getMptID())); + if (!sle) + return tecOBJECT_NOT_FOUND; + + if (uSenderID == issuer || uReceiverID == issuer) + { + // if sender is issuer, check that the new OutstandingAmount will not + // exceed MaximumAmount + if (uSenderID == issuer) + { + auto const sendAmount = saAmount.mpt().value(); + auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); + if (sendAmount > maximumAmount || + sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) + return tecPATH_DRY; + } + + // Direct send: redeeming MPTs and/or sending own MPTs. + auto const ter = rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); + if (!isTesSuccess(ter)) + return ter; + saActual = saAmount; + return tesSUCCESS; + } + + // Sending 3rd party MPTs: transit. + saActual = (waiveFee == WaiveTransferFee::Yes) + ? saAmount + : multiply(saAmount, transferRate(view, saAmount.get().getMptID())); + + JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > " + << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() + << " cost=" << saActual.getFullText(); + + if (auto const terResult = rippleCreditMPT(view, issuer, uReceiverID, saAmount, j); + !isTesSuccess(terResult)) + return terResult; + + return rippleCreditMPT(view, uSenderID, issuer, saActual, j); +} + +static TER +rippleSendMultiMPT( + ApplyView& view, + AccountID const& senderID, + MPTIssue const& mptIssue, + MultiplePaymentDestinations const& receivers, + STAmount& actual, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + // Safe to get MPT since rippleSendMultiMPT is only called by + // accountSendMultiMPT + auto const& issuer = mptIssue.getIssuer(); + + auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())); + if (!sle) + return tecOBJECT_NOT_FOUND; + + // These may diverge + STAmount takeFromSender{mptIssue}; + actual = takeFromSender; + + for (auto const& r : receivers) + { + auto const& receiverID = r.first; + STAmount amount{mptIssue, r.second}; + + if (amount < beast::zero) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!amount || (senderID == receiverID)) + continue; + + if (senderID == issuer || receiverID == issuer) + { + // if sender is issuer, check that the new OutstandingAmount will + // not exceed MaximumAmount + if (senderID == issuer) + { + XRPL_ASSERT_PARTS( + takeFromSender == beast::zero, + "xrpl::rippleSendMultiMPT", + "sender == issuer, takeFromSender == zero"); + auto const sendAmount = amount.mpt().value(); + auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount); + if (sendAmount > maximumAmount || + sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount) + return tecPATH_DRY; + } + + // Direct send: redeeming MPTs and/or sending own MPTs. + if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j)) + return ter; + actual += amount; + // Do not add amount to takeFromSender, because rippleCreditMPT took + // it + + continue; + } + + // Sending 3rd party MPTs: transit. + STAmount actualSend = (waiveFee == WaiveTransferFee::Yes) + ? amount + : multiply(amount, transferRate(view, amount.get().getMptID())); + actual += actualSend; + takeFromSender += actualSend; + + JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > " + << to_string(receiverID) << " : deliver=" << amount.getFullText() + << " cost=" << actualSend.getFullText(); + + if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j)) + return terResult; + } + if (senderID != issuer && takeFromSender) + { + if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j)) + return terResult; + } + + return tesSUCCESS; +} + +static TER +accountSendMPT( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT( + saAmount >= beast::zero && saAmount.holds(), + "xrpl::accountSendMPT : minimum amount and MPT"); + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!saAmount || (uSenderID == uReceiverID)) + return tesSUCCESS; + + STAmount saActual{saAmount.asset()}; + + return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); +} + +static TER +accountSendMultiMPT( + ApplyView& view, + AccountID const& senderID, + MPTIssue const& mptIssue, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + STAmount actual; + + return rippleSendMultiMPT(view, senderID, mptIssue, receivers, actual, j, waiveFee); +} + +//------------------------------------------------------------------------------ +// +// Public Dispatcher Functions +// +//------------------------------------------------------------------------------ + +TER +rippleCredit( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + bool bCheckIssuer, + beast::Journal j) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); + } + else + { + XRPL_ASSERT(!bCheckIssuer, "xrpl::rippleCredit : not checking issuer"); + return rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j); + } + }, + saAmount.asset().value()); +} + +TER +accountSend( + ApplyView& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + STAmount const& saAmount, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee); + } + else + { + return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee); + } + }, + saAmount.asset().value()); +} + +TER +accountSendMulti( + ApplyView& view, + AccountID const& senderID, + Asset const& asset, + MultiplePaymentDestinations const& receivers, + beast::Journal j, + WaiveTransferFee waiveFee) +{ + XRPL_ASSERT_PARTS( + receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided"); + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee); + } + else + { + return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); + } + }, + asset.value()); +} + +TER +transferXRP( + ApplyView& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount, + beast::Journal j) +{ + XRPL_ASSERT(from != beast::zero, "xrpl::transferXRP : nonzero from account"); + XRPL_ASSERT(to != beast::zero, "xrpl::transferXRP : nonzero to account"); + XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); + XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); + + SLE::pointer const sender = view.peek(keylet::account(from)); + SLE::pointer const receiver = view.peek(keylet::account(to)); + if (!sender || !receiver) + return tefINTERNAL; // LCOV_EXCL_LINE + + JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to) + << ") : " << amount.getFullText(); + + if (sender->getFieldAmount(sfBalance) < amount) + { + // VFALCO Its unfortunate we have to keep + // mutating these TER everywhere + // FIXME: this logic should be moved to callers maybe? + // LCOV_EXCL_START + return view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING}; + // LCOV_EXCL_STOP + } + + // Decrement XRP balance. + sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount); + view.update(sender); + + receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount); + view.update(receiver); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp new file mode 100644 index 0000000000..3ded720289 --- /dev/null +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -0,0 +1,112 @@ +#include +// +#include +#include +#include + +namespace xrpl { + +[[nodiscard]] std::optional +assetsToSharesDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets) +{ + XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); + XRPL_ASSERT( + assets.asset() == vault->at(sfAsset), + "xrpl::assetsToSharesDeposit : assets and vault match"); + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount shares{vault->at(sfShareMPTID)}; + if (assetTotal == 0) + { + return STAmount{ + shares.asset(), + Number(assets.mantissa(), assets.exponent() + vault->at(sfScale)).truncate()}; + } + + Number const shareTotal = issuance->at(sfOutstandingAmount); + shares = ((shareTotal * assets) / assetTotal).truncate(); + return shares; +} + +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares) +{ + XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); + XRPL_ASSERT( + shares.asset() == vault->at(sfShareMPTID), + "xrpl::sharesToAssetsDeposit : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount assets{vault->at(sfAsset)}; + if (assetTotal == 0) + { + return STAmount{ + assets.asset(), shares.mantissa(), shares.exponent() - vault->at(sfScale), false}; + } + + Number const shareTotal = issuance->at(sfOutstandingAmount); + assets = (assetTotal * shares) / shareTotal; + return assets; +} + +[[nodiscard]] std::optional +assetsToSharesWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& assets, + TruncateShares truncate) +{ + XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesWithdraw : non-negative assets"); + XRPL_ASSERT( + assets.asset() == vault->at(sfAsset), + "xrpl::assetsToSharesWithdraw : assets and vault match"); + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + + Number assetTotal = vault->at(sfAssetsTotal); + assetTotal -= vault->at(sfLossUnrealized); + STAmount shares{vault->at(sfShareMPTID)}; + if (assetTotal == 0) + return shares; + Number const shareTotal = issuance->at(sfOutstandingAmount); + Number result = (shareTotal * assets) / assetTotal; + if (truncate == TruncateShares::yes) + result = result.truncate(); + shares = result; + return shares; +} + +[[nodiscard]] std::optional +sharesToAssetsWithdraw( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares) +{ + XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); + XRPL_ASSERT( + shares.asset() == vault->at(sfShareMPTID), + "xrpl::sharesToAssetsWithdraw : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + + Number assetTotal = vault->at(sfAssetsTotal); + assetTotal -= vault->at(sfLossUnrealized); + STAmount assets{vault->at(sfAsset)}; + if (assetTotal == 0) + return assets; + Number const shareTotal = issuance->at(sfOutstandingAmount); + assets = (assetTotal * shares) / shareTotal; + return assets; +} + +} // namespace xrpl diff --git a/src/libxrpl/net/HTTPClient.cpp b/src/libxrpl/net/HTTPClient.cpp index e5a1196cb0..8ddae12a7e 100644 --- a/src/libxrpl/net/HTTPClient.cpp +++ b/src/libxrpl/net/HTTPClient.cpp @@ -26,6 +26,12 @@ HTTPClient::initializeSSLContext( httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j); } +void +HTTPClient::cleanupSSLContext() +{ + httpClientSSLContext.reset(); +} + //------------------------------------------------------------------------------ // // Fetch a web page via http or https. @@ -74,8 +80,10 @@ public: std::deque deqSites, std::function build, std::chrono::seconds timeout, - std::function - complete) + std::function complete) { mSSL = bSSL; mDeqSites = deqSites; @@ -93,8 +101,10 @@ public: std::deque deqSites, std::string const& strPath, std::chrono::seconds timeout, - std::function - complete) + std::function complete) { mComplete = complete; mTimeout = timeout; @@ -103,7 +113,11 @@ public: bSSL, deqSites, std::bind( - &HTTPClientImp::makeGet, shared_from_this(), strPath, std::placeholders::_1, std::placeholders::_2), + &HTTPClientImp::makeGet, + shared_from_this(), + strPath, + std::placeholders::_1, + std::placeholders::_2), timeout, complete); } @@ -116,7 +130,9 @@ public: JLOG(j_.trace()) << "Fetch: " << mDeqSites[0]; auto query = std::make_shared( - mDeqSites[0], std::to_string(mPort), boost::asio::ip::resolver_query_base::numeric_service); + mDeqSites[0], + std::to_string(mPort), + boost::asio::ip::resolver_query_base::numeric_service); mQuery = query; try @@ -128,7 +144,9 @@ public: mShutdown = e.code(); JLOG(j_.trace()) << "expires_after: " << mShutdown.message(); - mDeadline.async_wait(std::bind(&HTTPClientImp::handleDeadline, shared_from_this(), std::placeholders::_1)); + mDeadline.async_wait( + std::bind( + &HTTPClientImp::handleDeadline, shared_from_this(), std::placeholders::_1)); } if (!mShutdown) @@ -140,7 +158,10 @@ public: mQuery->port, mQuery->flags, std::bind( - &HTTPClientImp::handleResolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + &HTTPClientImp::handleResolve, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } if (mShutdown) @@ -170,14 +191,16 @@ public: // Mark us as shutting down. // XXX Use our own error code. - mShutdown = boost::system::error_code{boost::system::errc::bad_address, boost::system::system_category()}; + mShutdown = boost::system::error_code{ + boost::system::errc::bad_address, boost::system::system_category()}; // Cancel any resolving. mResolver.cancel(); // Stop the transaction. mSocket.async_shutdown( - std::bind(&HTTPClientImp::handleShutdown, shared_from_this(), std::placeholders::_1)); + std::bind( + &HTTPClientImp::handleShutdown, shared_from_this(), std::placeholders::_1)); } } @@ -191,11 +214,15 @@ public: } void - handleResolve(boost::system::error_code const& ecResult, boost::asio::ip::tcp::resolver::results_type result) + handleResolve( + boost::system::error_code const& ecResult, + boost::asio::ip::tcp::resolver::results_type result) { if (!mShutdown) { - mShutdown = ecResult ? ecResult : httpClientSSLContext->preConnectVerify(mSocket.SSLSocket(), mDeqSites[0]); + mShutdown = ecResult + ? ecResult + : httpClientSSLContext->preConnectVerify(mSocket.SSLSocket(), mDeqSites[0]); } if (mShutdown) @@ -211,7 +238,8 @@ public: boost::asio::async_connect( mSocket.lowest_layer(), result, - std::bind(&HTTPClientImp::handleConnect, shared_from_this(), std::placeholders::_1)); + std::bind( + &HTTPClientImp::handleConnect, shared_from_this(), std::placeholders::_1)); } } @@ -234,7 +262,8 @@ public: if (mShutdown) { - JLOG(j_.trace()) << "postConnectVerify: " << mDeqSites[0] << ": " << mShutdown.message(); + JLOG(j_.trace()) << "postConnectVerify: " << mDeqSites[0] << ": " + << mShutdown.message(); } } @@ -246,7 +275,8 @@ public: { mSocket.async_handshake( AutoSocket::ssl_socket::client, - std::bind(&HTTPClientImp::handleRequest, shared_from_this(), std::placeholders::_1)); + std::bind( + &HTTPClientImp::handleRequest, shared_from_this(), std::placeholders::_1)); } else { @@ -275,7 +305,10 @@ public: mSocket.async_write( mRequest, std::bind( - &HTTPClientImp::handleWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + &HTTPClientImp::handleWrite, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } } @@ -299,18 +332,23 @@ public: mHeader, "\r\n\r\n", std::bind( - &HTTPClientImp::handleHeader, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + &HTTPClientImp::handleHeader, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } } void handleHeader(boost::system::error_code const& ecResult, std::size_t bytes_transferred) { - std::string strHeader{{std::istreambuf_iterator(&mHeader)}, std::istreambuf_iterator()}; + std::string strHeader{ + {std::istreambuf_iterator(&mHeader)}, std::istreambuf_iterator()}; JLOG(j_.trace()) << "Header: \"" << strHeader << "\""; static boost::regex reStatus{"\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK - static boost::regex reSize{"\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase}; + static boost::regex reSize{ + "\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase}; static boost::regex reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"}; boost::smatch smMatch; @@ -320,7 +358,8 @@ public: // XXX Use our own error code. JLOG(j_.trace()) << "No status code"; invokeComplete( - boost::system::error_code{boost::system::errc::bad_address, boost::system::system_category()}); + boost::system::error_code{ + boost::system::errc::bad_address, boost::system::system_category()}); return; } @@ -339,7 +378,8 @@ public: { JLOG(j_.trace()) << "Response field too large"; invokeComplete( - boost::system::error_code{boost::system::errc::value_too_large, boost::system::system_category()}); + boost::system::error_code{ + boost::system::errc::value_too_large, boost::system::system_category()}); return; } @@ -359,7 +399,10 @@ public: mResponse.prepare(responseSize - mBody.size()), boost::asio::transfer_all(), std::bind( - &HTTPClientImp::handleData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + &HTTPClientImp::handleData, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } } @@ -384,7 +427,8 @@ public: else { mResponse.commit(bytes_transferred); - std::string strBody{{std::istreambuf_iterator(&mResponse)}, std::istreambuf_iterator()}; + std::string strBody{ + {std::istreambuf_iterator(&mResponse)}, std::istreambuf_iterator()}; invokeComplete(ecResult, mStatus, mBody + strBody); } } @@ -392,7 +436,10 @@ public: // Call cancel the deadline timer and invoke the completion routine. void - invokeComplete(boost::system::error_code const& ecResult, int iStatus = 0, std::string const& strData = "") + invokeComplete( + boost::system::error_code const& ecResult, + int iStatus = 0, + std::string const& strData = "") { boost::system::error_code ecCancel; try @@ -431,7 +478,7 @@ public: private: using pointer = std::shared_ptr; - bool mSSL; + bool mSSL{}; AutoSocket mSocket; boost::asio::ip::tcp::resolver mResolver; @@ -449,9 +496,11 @@ private: std::string mBody; unsigned short const mPort; std::size_t const maxResponseSize_; - int mStatus; + int mStatus{}; std::function mBuild; - std::function mComplete; + std::function< + bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> + mComplete; boost::asio::basic_waitable_timer mDeadline; @@ -459,7 +508,7 @@ private: boost::system::error_code mShutdown; std::deque mDeqSites; - std::chrono::seconds mTimeout; + std::chrono::seconds mTimeout{}; beast::Journal j_; }; @@ -474,7 +523,9 @@ HTTPClient::get( std::string const& strPath, std::size_t responseMax, std::chrono::seconds timeout, - std::function complete, + std::function< + bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> + complete, beast::Journal& j) { auto client = std::make_shared(io_context, port, responseMax, j); @@ -490,7 +541,9 @@ HTTPClient::get( std::string const& strPath, std::size_t responseMax, std::chrono::seconds timeout, - std::function complete, + std::function< + bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> + complete, beast::Journal& j) { std::deque deqSites(1, strSite); @@ -508,7 +561,9 @@ HTTPClient::request( std::function setRequest, std::size_t responseMax, std::chrono::seconds timeout, - std::function complete, + std::function< + bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> + complete, beast::Journal& j) { std::deque deqSites(1, strSite); diff --git a/src/libxrpl/net/RegisterSSLCerts.cpp b/src/libxrpl/net/RegisterSSLCerts.cpp index a15472969e..1f21a76ddb 100644 --- a/src/libxrpl/net/RegisterSSLCerts.cpp +++ b/src/libxrpl/net/RegisterSSLCerts.cpp @@ -23,7 +23,8 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec, if (h != nullptr) CertCloseStore(h, 0); }; - std::unique_ptr hStore{CertOpenSystemStore(0, "ROOT"), certStoreDelete}; + std::unique_ptr hStore{ + CertOpenSystemStore(0, "ROOT"), certStoreDelete}; if (!hStore) { @@ -33,11 +34,13 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec, ERR_clear_error(); - std::unique_ptr store{X509_STORE_new(), X509_STORE_free}; + std::unique_ptr store{ + X509_STORE_new(), X509_STORE_free}; if (!store) { - ec = boost::system::error_code(static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category()); + ec = boost::system::error_code( + static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category()); return; } @@ -76,6 +79,7 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec, SSL_CTX_set_cert_store(ctx.native_handle(), store.release()); #else + // NOLINTNEXTLINE(bugprone-unused-return-value) ctx.set_default_verify_paths(ec); #endif } diff --git a/src/libxrpl/nodestore/BatchWriter.cpp b/src/libxrpl/nodestore/BatchWriter.cpp index 73213fe3ba..98fe78c489 100644 --- a/src/libxrpl/nodestore/BatchWriter.cpp +++ b/src/libxrpl/nodestore/BatchWriter.cpp @@ -61,7 +61,8 @@ BatchWriter::writeBatch() std::lock_guard sl(mWriteMutex); mWriteSet.swap(set); - XRPL_ASSERT(mWriteSet.empty(), "xrpl::NodeStore::BatchWriter::writeBatch : writes not set"); + XRPL_ASSERT( + mWriteSet.empty(), "xrpl::NodeStore::BatchWriter::writeBatch : writes not set"); mWriteLoad = set.size(); if (set.empty()) @@ -74,14 +75,14 @@ BatchWriter::writeBatch() } } - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = set.size(); auto const before = std::chrono::steady_clock::now(); m_callback.writeBatch(set); - report.elapsed = - std::chrono::duration_cast(std::chrono::steady_clock::now() - before); + report.elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - before); m_scheduler.onBatchWrite(report); } diff --git a/src/libxrpl/nodestore/Database.cpp b/src/libxrpl/nodestore/Database.cpp index f393bf27c9..4e0d5be05d 100644 --- a/src/libxrpl/nodestore/Database.cpp +++ b/src/libxrpl/nodestore/Database.cpp @@ -10,7 +10,11 @@ namespace xrpl { namespace NodeStore { -Database::Database(Scheduler& scheduler, int readThreads, Section const& config, beast::Journal journal) +Database::Database( + Scheduler& scheduler, + int readThreads, + Section const& config, + beast::Journal journal) : j_(journal) , scheduler_(scheduler) , earliestLedgerSeq_(get(config, "earliest_seq", XRP_LEDGER_EARLIEST_SEQ)) @@ -136,13 +140,16 @@ Database::stop() while (readThreads_.load() != 0) { - XRPL_ASSERT(steady_clock::now() - start < 30s, "xrpl::NodeStore::Database::stop : maximum stop duration"); + XRPL_ASSERT( + steady_clock::now() - start < 30s, + "xrpl::NodeStore::Database::stop : maximum stop duration"); std::this_thread::yield(); } - JLOG(j_.debug()) << "Stop request completed in " - << duration_cast(steady_clock::now() - start).count() - << " milliseconds"; + JLOG(j_.debug()) + << "Stop request completed in " + << duration_cast(steady_clock::now() - start).count() + << " milliseconds"; } void @@ -199,7 +206,11 @@ Database::importInternal(Backend& dstBackend, Database& srcDB) // Perform a fetch and report the time it took std::shared_ptr -Database::fetchNodeObject(uint256 const& hash, std::uint32_t ledgerSeq, FetchType fetchType, bool duplicate) +Database::fetchNodeObject( + uint256 const& hash, + std::uint32_t ledgerSeq, + FetchType fetchType, + bool duplicate) { FetchReport fetchReport(fetchType); diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index 11152a2027..a24379aea9 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -22,18 +22,23 @@ DatabaseNodeImp::asyncFetch( } std::shared_ptr -DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) +DatabaseNodeImp::fetchNodeObject( + uint256 const& hash, + std::uint32_t, + FetchReport& fetchReport, + bool duplicate) { std::shared_ptr nodeObject = nullptr; - Status status; + Status status = ok; try { - status = backend_->fetch(hash.data(), &nodeObject); + status = backend_->fetch(hash, &nodeObject); } catch (std::exception const& e) { - JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what(); + JLOG(j_.fatal()) << "fetchNodeObject " << hash + << ": Exception fetching from backend: " << e.what(); Rethrow(); } @@ -46,7 +51,8 @@ DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted"; break; default: - JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status; + JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " + << status; break; } @@ -62,18 +68,10 @@ DatabaseNodeImp::fetchBatch(std::vector const& hashes) using namespace std::chrono; auto const before = steady_clock::now(); - std::vector batch{}; - batch.reserve(hashes.size()); - for (size_t i = 0; i < hashes.size(); ++i) - { - auto const& hash = hashes[i]; - batch.push_back(&hash); - } - // Get the node objects that match the hashes from the backend. To protect // against the backends returning fewer or more results than expected, the // container is resized to the number of hashes. - auto results = backend_->fetchBatch(batch).first; + auto results = backend_->fetchBatch(hashes).first; XRPL_ASSERT( results.size() == hashes.size() || results.empty(), "number of output objects either matches number of input hashes or is empty"); @@ -87,7 +85,8 @@ DatabaseNodeImp::fetchBatch(std::vector const& hashes) } } - auto fetchDurationUs = std::chrono::duration_cast(steady_clock::now() - before).count(); + auto fetchDurationUs = + std::chrono::duration_cast(steady_clock::now() - before).count(); updateFetchMetrics(hashes.size(), 0, fetchDurationUs); return results; } diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index f25b9d068e..6cf51fbf31 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -94,14 +94,18 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash } std::shared_ptr -DatabaseRotatingImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate) +DatabaseRotatingImp::fetchNodeObject( + uint256 const& hash, + std::uint32_t, + FetchReport& fetchReport, + bool duplicate) { auto fetch = [&](std::shared_ptr const& backend) { - Status status; + Status status = ok; std::shared_ptr nodeObject; try { - status = backend->fetch(hash.data(), &nodeObject); + status = backend->fetch(hash, &nodeObject); } catch (std::exception const& e) { diff --git a/src/libxrpl/nodestore/ManagerImp.cpp b/src/libxrpl/nodestore/ManagerImp.cpp index 9d78097aae..b749a329ff 100644 --- a/src/libxrpl/nodestore/ManagerImp.cpp +++ b/src/libxrpl/nodestore/ManagerImp.cpp @@ -44,7 +44,11 @@ ManagerImp::ManagerImp() } std::unique_ptr -ManagerImp::make_Backend(Section const& parameters, std::size_t burstSize, Scheduler& scheduler, beast::Journal journal) +ManagerImp::make_Backend( + Section const& parameters, + std::size_t burstSize, + Scheduler& scheduler, + beast::Journal journal) { std::string const type{get(parameters, "type")}; if (type.empty()) @@ -69,7 +73,8 @@ ManagerImp::make_Database( { auto backend{make_Backend(config, burstSize, scheduler, journal)}; backend->open(); - return std::make_unique(scheduler, readThreads, std::move(backend), config, journal); + return std::make_unique( + scheduler, readThreads, std::move(backend), config, journal); } void @@ -83,8 +88,8 @@ void ManagerImp::erase(Factory& factory) { std::lock_guard _(mutex_); - auto const iter = - std::find_if(list_.begin(), list_.end(), [&factory](Factory* other) { return other == &factory; }); + auto const iter = std::find_if( + list_.begin(), list_.end(), [&factory](Factory* other) { return other == &factory; }); XRPL_ASSERT(iter != list_.end(), "xrpl::NodeStore::ManagerImp::erase : valid input"); list_.erase(iter); } @@ -93,8 +98,9 @@ Factory* ManagerImp::find(std::string const& name) { std::lock_guard _(mutex_); - auto const iter = std::find_if( - list_.begin(), list_.end(), [&name](Factory* other) { return boost::iequals(name, other->getName()); }); + auto const iter = std::find_if(list_.begin(), list_.end(), [&name](Factory* other) { + return boost::iequals(name, other->getName()); + }); if (iter == list_.end()) return nullptr; return *iter; diff --git a/src/libxrpl/nodestore/backend/MemoryFactory.cpp b/src/libxrpl/nodestore/backend/MemoryFactory.cpp index 3cb8fb7f5e..0366fe3573 100644 --- a/src/libxrpl/nodestore/backend/MemoryFactory.cpp +++ b/src/libxrpl/nodestore/backend/MemoryFactory.cpp @@ -46,7 +46,8 @@ public: open(std::string const& path) { std::lock_guard _(mutex_); - auto const result = map_.emplace(std::piecewise_construct, std::make_tuple(path), std::make_tuple()); + auto const result = + map_.emplace(std::piecewise_construct, std::make_tuple(path), std::make_tuple()); MemoryDB& db = result.first->second; if (db.open) Throw("already open"); @@ -115,10 +116,9 @@ public: //-------------------------------------------------------------------------- Status - fetch(void const* key, std::shared_ptr* pObject) override + fetch(uint256 const& hash, std::shared_ptr* pObject) override { XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::fetch : non-null database"); - uint256 const hash(uint256::fromVoid(key)); std::lock_guard _(db_->mutex); @@ -133,18 +133,22 @@ public: } std::pair>, Status> - fetchBatch(std::vector const& hashes) override + fetchBatch(std::vector const& hashes) override { std::vector> results; results.reserve(hashes.size()); for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h->begin(), &nObj); + Status status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; diff --git a/src/libxrpl/nodestore/backend/NuDBFactory.cpp b/src/libxrpl/nodestore/backend/NuDBFactory.cpp index eb3ccb3181..2d8cffa85a 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -83,7 +83,7 @@ public: // close can throw and we don't want the destructor to throw. close(); } - catch (nudb::system_error const&) + catch (nudb::system_error const&) // NOLINT(bugprone-empty-catch) { // Don't allow exceptions to propagate out of destructors. // close() has already logged the error. @@ -124,7 +124,8 @@ public: if (createIfMissing) { create_directories(folder); - nudb::create(dp, kp, lp, appType, uid, salt, keyBytes_, blockSize_, 0.50, ec); + nudb::create( + dp, kp, lp, appType, uid, salt, keyBytes_, blockSize_, 0.50, ec); if (ec == nudb::errc::file_exists) ec = {}; if (ec) @@ -170,24 +171,25 @@ public: boost::filesystem::remove_all(name_, ec); if (ec) { - JLOG(j_.fatal()) << "Filesystem remove_all of " << name_ << " failed with: " << ec.message(); + JLOG(j_.fatal()) + << "Filesystem remove_all of " << name_ << " failed with: " << ec.message(); } } } } Status - fetch(void const* key, std::shared_ptr* pno) override + fetch(uint256 const& hash, std::shared_ptr* pno) override { - Status status; + Status status = ok; pno->reset(); nudb::error_code ec; db_.fetch( - key, - [key, pno, &status](void const* data, std::size_t size) { + hash.data(), + [&hash, pno, &status](void const* data, std::size_t size) { nudb::detail::buffer bf; auto const result = nodeobject_decompress(data, size, bf); - DecodedBlob decoded(key, result.first, result.second); + DecodedBlob decoded(hash.data(), result.first, result.second); if (!decoded.wasOk()) { status = dataCorrupt; @@ -205,18 +207,22 @@ public: } std::pair>, Status> - fetchBatch(std::vector const& hashes) override + fetchBatch(std::vector const& hashes) override { std::vector> results; results.reserve(hashes.size()); for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h->begin(), &nObj); + Status status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -237,25 +243,25 @@ public: void store(std::shared_ptr const& no) override { - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = 1; auto const start = std::chrono::steady_clock::now(); do_insert(no); - report.elapsed = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + report.elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start); scheduler_.onBatchWrite(report); } void storeBatch(Batch const& batch) override { - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = batch.size(); auto const start = std::chrono::steady_clock::now(); for (auto const& e : batch) do_insert(e); - report.elapsed = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + report.elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start); scheduler_.onBatchWrite(report); } @@ -277,7 +283,11 @@ public: Throw(ec); nudb::visit( dp, - [&](void const* key, std::size_t key_bytes, void const* data, std::size_t size, nudb::error_code&) { + [&](void const* key, + std::size_t key_bytes, + void const* data, + std::size_t size, + nudb::error_code&) { nudb::detail::buffer bf; auto const result = nodeobject_decompress(data, size, bf); DecodedBlob decoded(key, result.first, result.second); @@ -356,10 +366,12 @@ private: std::size_t const parsedBlockSize = beast::lexicalCastThrow(blockSizeStr); // Validate: must be power of 2 between 4K and 32K - if (parsedBlockSize < 4096 || parsedBlockSize > 32768 || (parsedBlockSize & (parsedBlockSize - 1)) != 0) + if (parsedBlockSize < 4096 || parsedBlockSize > 32768 || + (parsedBlockSize & (parsedBlockSize - 1)) != 0) { std::stringstream s; - s << "Invalid nudb_block_size: " << parsedBlockSize << ". Must be power of 2 between 4096 and 32768."; + s << "Invalid nudb_block_size: " << parsedBlockSize + << ". Must be power of 2 between 4096 and 32768."; Throw(s.str()); } @@ -414,7 +426,8 @@ public: nudb::context& context, beast::Journal journal) override { - return std::make_unique(keyBytes, keyValues, burstSize, scheduler, context, journal); + return std::make_unique( + keyBytes, keyValues, burstSize, scheduler, context, journal); } }; diff --git a/src/libxrpl/nodestore/backend/NullFactory.cpp b/src/libxrpl/nodestore/backend/NullFactory.cpp index 4ecca46a9a..ab5b7d0117 100644 --- a/src/libxrpl/nodestore/backend/NullFactory.cpp +++ b/src/libxrpl/nodestore/backend/NullFactory.cpp @@ -36,13 +36,13 @@ public: } Status - fetch(void const*, std::shared_ptr*) override + fetch(uint256 const&, std::shared_ptr*) override { return notFound; } std::pair>, Status> - fetchBatch(std::vector const& hashes) override + fetchBatch(std::vector const& hashes) override { return {}; } diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index a35f737528..67c329bb4a 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -103,7 +103,8 @@ public: if (auto const v = get(keyValues, "filter_bits")) { - bool const filter_blocks = !keyValues.exists("filter_full") || (get(keyValues, "filter_full") == 0); + bool const filter_blocks = + !keyValues.exists("filter_full") || (get(keyValues, "filter_full") == 0); table_options.filter_policy.reset(rocksdb::NewBloomFilterPolicy(v, filter_blocks)); } @@ -131,7 +132,8 @@ public: if (keyValues.exists("bg_threads")) { - m_options.env->SetBackgroundThreads(get(keyValues, "bg_threads"), rocksdb::Env::LOW); + m_options.env->SetBackgroundThreads( + get(keyValues, "bg_threads"), rocksdb::Env::LOW); } if (keyValues.exists("high_threads")) @@ -149,7 +151,8 @@ public: get_if_exists(keyValues, "block_size", table_options.block_size); - if (keyValues.exists("universal_compaction") && (get(keyValues, "universal_compaction") != 0)) + if (keyValues.exists("universal_compaction") && + (get(keyValues, "universal_compaction") != 0)) { m_options.compaction_style = rocksdb::kCompactionStyleUniversal; m_options.min_write_buffer_number_to_merge = 2; @@ -163,16 +166,23 @@ public: auto const s = rocksdb::GetBlockBasedTableOptionsFromString( config_options, table_options, get(keyValues, "bbt_options"), &table_options); if (!s.ok()) - Throw(std::string("Unable to set RocksDB bbt_options: ") + s.ToString()); + { + Throw( + std::string("Unable to set RocksDB bbt_options: ") + s.ToString()); + } } m_options.table_factory.reset(NewBlockBasedTableFactory(table_options)); if (keyValues.exists("options")) { - auto const s = rocksdb::GetOptionsFromString(m_options, get(keyValues, "options"), &m_options); + auto const s = + rocksdb::GetOptionsFromString(m_options, get(keyValues, "options"), &m_options); if (!s.ok()) - Throw(std::string("Unable to set RocksDB options: ") + s.ToString()); + { + Throw( + std::string("Unable to set RocksDB options: ") + s.ToString()); + } } std::string s1, s2; @@ -204,7 +214,10 @@ public: m_options.create_if_missing = createIfMissing; rocksdb::Status status = rocksdb::DB::Open(m_options, m_name, &db); if (!status.ok() || !db) - Throw(std::string("Unable to open/create RocksDB: ") + status.ToString()); + { + Throw( + std::string("Unable to open/create RocksDB: ") + status.ToString()); + } m_db.reset(db); } @@ -237,7 +250,7 @@ public: //-------------------------------------------------------------------------- Status - fetch(void const* key, std::shared_ptr* pObject) override + fetch(uint256 const& hash, std::shared_ptr* pObject) override { XRPL_ASSERT(m_db, "xrpl::NodeStore::RocksDBBackend::fetch : non-null database"); pObject->reset(); @@ -245,7 +258,7 @@ public: Status status(ok); rocksdb::ReadOptions const options; - rocksdb::Slice const slice(static_cast(key), m_keyBytes); + rocksdb::Slice const slice(std::bit_cast(hash.data()), m_keyBytes); std::string string; @@ -253,7 +266,7 @@ public: if (getStatus.ok()) { - DecodedBlob decoded(key, string.data(), string.size()); + DecodedBlob decoded(hash.data(), string.data(), string.size()); if (decoded.wasOk()) { @@ -288,18 +301,22 @@ public: } std::pair>, Status> - fetchBatch(std::vector const& hashes) override + fetchBatch(std::vector const& hashes) override { std::vector> results; results.reserve(hashes.size()); for (auto const& h : hashes) { std::shared_ptr nObj; - Status status = fetch(h->begin(), &nObj); + Status status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -325,8 +342,8 @@ public: EncodedBlob encoded(e); wb.Put( - rocksdb::Slice(reinterpret_cast(encoded.getKey()), m_keyBytes), - rocksdb::Slice(reinterpret_cast(encoded.getData()), encoded.getSize())); + rocksdb::Slice(std::bit_cast(encoded.getKey()), m_keyBytes), + rocksdb::Slice(std::bit_cast(encoded.getData()), encoded.getSize())); } rocksdb::WriteOptions const options; @@ -425,8 +442,12 @@ public: } std::unique_ptr - createInstance(size_t keyBytes, Section const& keyValues, std::size_t, Scheduler& scheduler, beast::Journal journal) - override + createInstance( + size_t keyBytes, + Section const& keyValues, + std::size_t, + Scheduler& scheduler, + beast::Journal journal) override { return std::make_unique(keyBytes, keyValues, scheduler, journal, &m_env); } diff --git a/src/libxrpl/protocol/AMMCore.cpp b/src/libxrpl/protocol/AMMCore.cpp index bb83be2724..9061d04689 100644 --- a/src/libxrpl/protocol/AMMCore.cpp +++ b/src/libxrpl/protocol/AMMCore.cpp @@ -52,7 +52,10 @@ invalidAMMAsset(Issue const& issue, std::optional> const } NotTEC -invalidAMMAssetPair(Issue const& issue1, Issue const& issue2, std::optional> const& pair) +invalidAMMAssetPair( + Issue const& issue1, + Issue const& issue2, + std::optional> const& pair) { if (issue1 == issue2) return temBAD_AMM_TOKENS; @@ -64,7 +67,10 @@ invalidAMMAssetPair(Issue const& issue1, Issue const& issue2, std::optional> const& pair, bool validZero) +invalidAMMAmount( + STAmount const& amount, + std::optional> const& pair, + bool validZero) { if (auto const res = invalidAMMAsset(amount.issue(), pair)) return res; @@ -79,7 +85,8 @@ ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot) // It should be impossible for expiration to be < TOTAL_TIME_SLOT_SECS, // but check just to be safe auto const expiration = auctionSlot[sfExpiration]; - XRPL_ASSERT(expiration >= TOTAL_TIME_SLOT_SECS, "xrpl::ammAuctionTimeSlot : minimum expiration"); + XRPL_ASSERT( + expiration >= TOTAL_TIME_SLOT_SECS, "xrpl::ammAuctionTimeSlot : minimum expiration"); if (expiration >= TOTAL_TIME_SLOT_SECS) { if (auto const start = expiration - TOTAL_TIME_SLOT_SECS; current >= start) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index c86ef17449..7d9a2047a9 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -1,7 +1,9 @@ #include #include #include +#include #include +#include #include @@ -14,44 +16,60 @@ namespace xrpl { namespace BuildInfo { +namespace { + //-------------------------------------------------------------------------- // The build version number. You must edit this for each release // and follow the format described at http://semver.org/ //------------------------------------------------------------------------------ // clang-format off char const* const versionString = "3.2.0-b0" -// clang-format on - -#if defined(DEBUG) || defined(SANITIZERS) - "+" -#ifdef GIT_COMMIT_HASH - GIT_COMMIT_HASH - "." -#endif -#ifdef DEBUG - "DEBUG" -#ifdef SANITIZERS - "." -#endif -#endif - -#ifdef SANITIZERS - BOOST_PP_STRINGIZE(SANITIZERS) // cspell: disable-line -#endif -#endif - - //-------------------------------------------------------------------------- + // clang-format on ; // // Don't touch anything below this line // +std::string +buildVersionString() +{ + std::string version = versionString; + +#if defined(DEBUG) || defined(SANITIZERS) + std::string metadata; + + std::string const& commitHash = xrpl::git::getCommitHash(); + if (!commitHash.empty()) + metadata += commitHash + "."; + +#ifdef DEBUG + metadata += "DEBUG"; +#endif + +#if defined(DEBUG) && defined(SANITIZERS) + metadata += "."; +#endif + +#ifdef SANITIZERS + metadata += BOOST_PP_STRINGIZE(SANITIZERS); // cspell: disable-line +#endif + + if (!metadata.empty()) + version += "+" + metadata; +#endif + + return version; +} + +} // namespace + std::string const& getVersionString() { static std::string const value = [] { - std::string const s = versionString; + std::string const s = buildVersionString(); + beast::SemanticVersion v; if (!v.parse(s) || v.print() != s) LogicError(s + ": Bad server version string"); @@ -63,7 +81,7 @@ getVersionString() std::string const& getFullVersionString() { - static std::string const value = "rippled-" + getVersionString(); + static std::string const value = systemName() + "-" + getVersionString(); return value; } @@ -71,13 +89,13 @@ static constexpr std::uint64_t implementationVersionIdentifier = 0x183B'0000'000 static constexpr std::uint64_t implementationVersionIdentifierMask = 0xFFFF'0000'0000'0000LLU; std::uint64_t -encodeSoftwareVersion(char const* const versionStr) +encodeSoftwareVersion(std::string_view versionStr) { std::uint64_t c = implementationVersionIdentifier; beast::SemanticVersion v; - if (v.parse(std::string(versionStr))) + if (v.parse(versionStr)) { if (v.majorVersion >= 0 && v.majorVersion <= 255) c |= static_cast(v.majorVersion) << 40; @@ -107,7 +125,8 @@ encodeSoftwareVersion(char const* const versionStr) if (prefix != identifier.substr(0, prefix.length())) return 0; - if (!beast::lexicalCastChecked(ret, std::string(identifier.substr(prefix.length())))) + if (!beast::lexicalCastChecked( + ret, std::string(identifier.substr(prefix.length())))) return 0; if (std::clamp(ret, lok, hik) != ret) @@ -136,7 +155,7 @@ encodeSoftwareVersion(char const* const versionStr) std::uint64_t getEncodedVersion() { - static std::uint64_t const cookie = {encodeSoftwareVersion(versionString)}; + static std::uint64_t const cookie = {encodeSoftwareVersion(getVersionString())}; return cookie; } diff --git a/src/libxrpl/protocol/Feature.cpp b/src/libxrpl/protocol/Feature.cpp index 834341f8d6..f91e164c92 100644 --- a/src/libxrpl/protocol/Feature.cpp +++ b/src/libxrpl/protocol/Feature.cpp @@ -70,7 +70,8 @@ class FeatureCollections uint256 feature; Feature() = delete; - explicit Feature(std::string const& name_, uint256 const& feature_) : name(name_), feature(feature_) + explicit Feature(std::string const& name_, uint256 const& feature_) + : name(name_), feature(feature_) { } @@ -89,9 +90,10 @@ class FeatureCollections }; // Intermediate types to help with readability - template - using feature_hashed_unique = boost::multi_index:: - hashed_unique, boost::multi_index::member>; + template + using feature_hashed_unique = boost::multi_index::hashed_unique< + boost::multi_index::tag, + boost::multi_index::member>; // Intermediate types to help with readability using feature_indexing = boost::multi_index::indexed_by< @@ -204,7 +206,8 @@ FeatureCollections::FeatureCollections() std::optional FeatureCollections::getRegisteredFeature(std::string const& name) const { - XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::getRegisteredFeature : startup completed"); + XRPL_ASSERT( + readOnly.load(), "xrpl::FeatureCollections::getRegisteredFeature : startup completed"); Feature const* feature = getByName(name); if (feature) return feature->feature; @@ -237,7 +240,8 @@ FeatureCollections::registerFeature(std::string const& name, Supported support, auto const getAmendmentSupport = [=]() { if (vote == VoteBehavior::Obsolete) return AmendmentSupport::Retired; - return support == Supported::yes ? AmendmentSupport::Supported : AmendmentSupport::Unsupported; + return support == Supported::yes ? AmendmentSupport::Supported + : AmendmentSupport::Unsupported; }; all.emplace(name, getAmendmentSupport()); @@ -246,18 +250,22 @@ FeatureCollections::registerFeature(std::string const& name, Supported support, supported.emplace(name, vote); if (vote == VoteBehavior::DefaultYes) + { ++upVotes; + } else + { ++downVotes; + } } check(upVotes + downVotes == supported.size(), "Feature counting logic broke"); check(supported.size() <= features.size(), "More supported features than defined features"); check(features.size() == all.size(), "The 'all' features list is populated incorrectly"); return f; } - else - // Each feature should only be registered once - LogicError("Duplicate feature registration"); + + // Each feature should only be registered once + LogicError("Duplicate feature registration"); } /** Tell FeatureCollections when registration is complete. */ @@ -271,7 +279,8 @@ FeatureCollections::registrationIsDone() size_t FeatureCollections::featureToBitsetIndex(uint256 const& f) const { - XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::featureToBitsetIndex : startup completed"); + XRPL_ASSERT( + readOnly.load(), "xrpl::FeatureCollections::featureToBitsetIndex : startup completed"); Feature const* feature = getByFeature(f); if (!feature) @@ -283,7 +292,8 @@ FeatureCollections::featureToBitsetIndex(uint256 const& f) const uint256 const& FeatureCollections::bitsetIndexToFeature(size_t i) const { - XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::bitsetIndexToFeature : startup completed"); + XRPL_ASSERT( + readOnly.load(), "xrpl::FeatureCollections::bitsetIndexToFeature : startup completed"); Feature const& feature = getByIndex(i); return feature.feature; } @@ -296,7 +306,7 @@ FeatureCollections::featureToName(uint256 const& f) const return feature ? feature->name : to_string(f); } -static FeatureCollections featureCollections; +FeatureCollections featureCollections; } // namespace @@ -389,8 +399,20 @@ featureToName(uint256 const& f) #pragma push_macro("XRPL_RETIRE_FIX") #undef XRPL_RETIRE_FIX -#define XRPL_FEATURE(name, supported, vote) uint256 const feature##name = registerFeature(#name, supported, vote); -#define XRPL_FIX(name, supported, vote) uint256 const fix##name = registerFeature("fix" #name, supported, vote); +consteval auto +enforceValidFeatureName(auto fn) -> char const* +{ + static_assert(validFeatureName(fn), "Invalid feature name"); + static_assert(validFeatureNameSize(fn), "Invalid feature name size"); + return fn(); +} + +#define XRPL_FEATURE(name, supported, vote) \ + uint256 const feature##name = \ + registerFeature(enforceValidFeatureName([] { return #name; }), supported, vote); +#define XRPL_FIX(name, supported, vote) \ + uint256 const fix##name = \ + registerFeature(enforceValidFeatureName([] { return "fix" #name; }), supported, vote); // clang-format off #define XRPL_RETIRE_FEATURE(name) \ diff --git a/src/libxrpl/protocol/IOUAmount.cpp b/src/libxrpl/protocol/IOUAmount.cpp index cfad753edc..eba78e6051 100644 --- a/src/libxrpl/protocol/IOUAmount.cpp +++ b/src/libxrpl/protocol/IOUAmount.cpp @@ -57,7 +57,8 @@ IOUAmount::fromNumber(Number const& number) // Need to create a default IOUAmount and assign directly so it doesn't try // to normalize, which calls fromNumber IOUAmount result{}; - std::tie(result.mantissa_, result.exponent_) = number.normalizeToRange(minMantissa, maxMantissa); + std::tie(result.mantissa_, result.exponent_) = + number.normalizeToRange(minMantissa, maxMantissa); return result; } diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 33c836eab6..61a64b2a54 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -99,15 +99,19 @@ getBookBase(Book const& book) { XRPL_ASSERT(isConsistent(book), "xrpl::getBookBase : input is consistent"); - auto const index = book.domain - ? indexHash( - LedgerNameSpace::BOOK_DIR, - book.in.currency, - book.out.currency, - book.in.account, - book.out.account, - *(book.domain)) - : indexHash(LedgerNameSpace::BOOK_DIR, book.in.currency, book.out.currency, book.in.account, book.out.account); + auto const index = book.domain ? indexHash( + LedgerNameSpace::BOOK_DIR, + book.in.currency, + book.out.currency, + book.in.account, + book.out.account, + *(book.domain)) + : indexHash( + LedgerNameSpace::BOOK_DIR, + book.in.currency, + book.out.currency, + book.in.account, + book.out.account); // Return with quality 0. auto k = keylet::quality({ltDIR_NODE, index}, 0); @@ -118,7 +122,8 @@ getBookBase(Book const& book) uint256 getQualityNext(uint256 const& uBase) { - static constexpr uint256 nextQuality("0000000000000000000000000000000000000000000000010000000000000000"); + static constexpr uint256 nextQuality( + "0000000000000000000000000000000000000000000000010000000000000000"); return uBase + nextQuality; } @@ -132,7 +137,7 @@ getQuality(uint256 const& uBase) uint256 getTicketIndex(AccountID const& account, std::uint32_t ticketSeq) { - return indexHash(LedgerNameSpace::TICKET, account, std::uint32_t(ticketSeq)); + return indexHash(LedgerNameSpace::TICKET, account, ticketSeq); } uint256 @@ -180,7 +185,8 @@ skip(LedgerIndex ledger) noexcept { return { ltLEDGER_HASHES, - indexHash(LedgerNameSpace::SKIP_LIST, std::uint32_t(static_cast(ledger) >> 16))}; + indexHash( + LedgerNameSpace::SKIP_LIST, std::uint32_t(static_cast(ledger) >> 16))}; } Keylet const& @@ -213,7 +219,7 @@ book_t::operator()(Book const& b) const Keylet line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept { - // There is code in SetTrust that calls us with id0 == id1, to allow users + // There is code in TrustSet that calls us with id0 == id1, to allow users // to locate and delete such "weird" trustlines. If we remove that code, we // could enable this assert: // XRPL_ASSERT(id0 != id1, "xrpl::keylet::line : accounts must be @@ -228,7 +234,9 @@ line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexc // two accounts (smallest then largest) and hash them in that order: auto const accounts = std::minmax(id0, id1); - return {ltRIPPLE_STATE, indexHash(LedgerNameSpace::TRUST_LINE, accounts.first, accounts.second, currency)}; + return { + ltRIPPLE_STATE, + indexHash(LedgerNameSpace::TRUST_LINE, accounts.first, accounts.second, currency)}; } Keylet @@ -303,14 +311,17 @@ depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept // Credentials should be sorted here, use credentials::makeSorted Keylet -depositPreauth(AccountID const& owner, std::set> const& authCreds) noexcept +depositPreauth( + AccountID const& owner, + std::set> const& authCreds) noexcept { std::vector hashes; hashes.reserve(authCreds.size()); for (auto const& o : authCreds) hashes.emplace_back(sha512Half(o.first, o.second)); - return {ltDEPOSIT_PREAUTH, indexHash(LedgerNameSpace::DEPOSIT_PREAUTH_CREDENTIALS, owner, hashes)}; + return { + ltDEPOSIT_PREAUTH, indexHash(LedgerNameSpace::DEPOSIT_PREAUTH_CREDENTIALS, owner, hashes)}; } //------------------------------------------------------------------------------ @@ -393,7 +404,8 @@ Keylet amm(Asset const& issue1, Asset const& issue2) noexcept { auto const& [minI, maxI] = std::minmax(issue1.get(), issue2.get()); - return amm(indexHash(LedgerNameSpace::AMM, minI.account, minI.currency, maxI.account, maxI.currency)); + return amm( + indexHash(LedgerNameSpace::AMM, minI.account, minI.currency, maxI.account, maxI.currency)); } Keylet diff --git a/src/libxrpl/protocol/Issue.cpp b/src/libxrpl/protocol/Issue.cpp index f540b52050..c73c6c8513 100644 --- a/src/libxrpl/protocol/Issue.cpp +++ b/src/libxrpl/protocol/Issue.cpp @@ -25,11 +25,17 @@ Issue::getText() const ret += "/"; if (isXRP(account)) + { ret += "0"; + } else if (account == noAccount()) + { ret += "1"; + } else + { ret += to_string(account); + } } return ret; @@ -83,7 +89,8 @@ issueFromJson(Json::Value const& v) { if (!v.isObject()) { - Throw("issueFromJson can only be specified with an 'object' Json value"); + Throw( + "issueFromJson can only be specified with an 'object' Json value"); } if (v.isMember(jss::mpt_issuance_id)) diff --git a/src/libxrpl/protocol/Keylet.cpp b/src/libxrpl/protocol/Keylet.cpp index 18bd76440a..6f9656c4ea 100644 --- a/src/libxrpl/protocol/Keylet.cpp +++ b/src/libxrpl/protocol/Keylet.cpp @@ -8,7 +8,9 @@ namespace xrpl { bool Keylet::check(STLedgerEntry const& sle) const { - XRPL_ASSERT(sle.getType() != ltANY || sle.getType() != ltCHILD, "xrpl::Keylet::check : valid input type"); + XRPL_ASSERT( + sle.getType() != ltANY && sle.getType() != ltCHILD, + "xrpl::Keylet::check : valid input type"); if (type == ltANY) return true; diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 8d9a24f741..30725f44a9 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -3,26 +3,31 @@ #include #include -#include +#include namespace xrpl { -LedgerFormats::LedgerFormats() +std::vector const& +LedgerFormats::getCommonFields() { - // Fields shared by all ledger formats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, }; + return commonFields; +} +LedgerFormats::LedgerFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("LEDGER_ENTRY") #undef LEDGER_ENTRY #define UNWRAP(...) __VA_ARGS__ -#define LEDGER_ENTRY(tag, value, name, rpcName, fields) add(jss::name, tag, UNWRAP fields, commonFields); +#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \ + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/protocol/MPTIssue.cpp b/src/libxrpl/protocol/MPTIssue.cpp index 9673d6071f..c2f7f1fa50 100644 --- a/src/libxrpl/protocol/MPTIssue.cpp +++ b/src/libxrpl/protocol/MPTIssue.cpp @@ -23,7 +23,8 @@ MPTIssue::getIssuer() const // MPTID is concatenation of sequence + account static_assert(sizeof(MPTID) == (sizeof(std::uint32_t) + sizeof(AccountID))); // copy from id skipping the sequence - AccountID const* account = reinterpret_cast(mptID_.data() + sizeof(std::uint32_t)); + AccountID const* account = + reinterpret_cast(mptID_.data() + sizeof(std::uint32_t)); return *account; } diff --git a/src/libxrpl/protocol/NFTokenID.cpp b/src/libxrpl/protocol/NFTokenID.cpp index deed26360a..d867cdb8c9 100644 --- a/src/libxrpl/protocol/NFTokenID.cpp +++ b/src/libxrpl/protocol/NFTokenID.cpp @@ -30,7 +30,7 @@ canHaveNFTokenID(std::shared_ptr const& serializedTx, TxMeta const& return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; @@ -53,11 +53,13 @@ getNFTokenIDFromPage(TxMeta const& transactionMeta) SField const& fName = node.getFName(); if (fName == sfCreatedNode) { - STArray const& toAddPrevNFTs = node.peekAtField(sfNewFields).downcast().getFieldArray(sfNFTokens); + STArray const& toAddPrevNFTs = + node.peekAtField(sfNewFields).downcast().getFieldArray(sfNFTokens); std::transform( - toAddPrevNFTs.begin(), toAddPrevNFTs.end(), std::back_inserter(finalIDs), [](STObject const& nft) { - return nft.getFieldH256(sfNFTokenID); - }); + toAddPrevNFTs.begin(), + toAddPrevNFTs.end(), + std::back_inserter(finalIDs), + [](STObject const& nft) { return nft.getFieldH256(sfNFTokenID); }); } else if (fName == sfModifiedNode) { @@ -70,22 +72,25 @@ getNFTokenIDFromPage(TxMeta const& transactionMeta) // However, there will always be NFTs listed in the final fields, // as rippled outputs all fields in final fields even if they were // not changed. - STObject const& previousFields = node.peekAtField(sfPreviousFields).downcast(); + STObject const& previousFields = + node.peekAtField(sfPreviousFields).downcast(); if (!previousFields.isFieldPresent(sfNFTokens)) continue; STArray const& toAddPrevNFTs = previousFields.getFieldArray(sfNFTokens); std::transform( - toAddPrevNFTs.begin(), toAddPrevNFTs.end(), std::back_inserter(prevIDs), [](STObject const& nft) { - return nft.getFieldH256(sfNFTokenID); - }); + toAddPrevNFTs.begin(), + toAddPrevNFTs.end(), + std::back_inserter(prevIDs), + [](STObject const& nft) { return nft.getFieldH256(sfNFTokenID); }); STArray const& toAddFinalNFTs = node.peekAtField(sfFinalFields).downcast().getFieldArray(sfNFTokens); std::transform( - toAddFinalNFTs.begin(), toAddFinalNFTs.end(), std::back_inserter(finalIDs), [](STObject const& nft) { - return nft.getFieldH256(sfNFTokenID); - }); + toAddFinalNFTs.begin(), + toAddFinalNFTs.end(), + std::back_inserter(finalIDs), + [](STObject const& nft) { return nft.getFieldH256(sfNFTokenID); }); } } @@ -96,7 +101,8 @@ getNFTokenIDFromPage(TxMeta const& transactionMeta) // Find the first NFT ID that doesn't match. We're looking for an // added NFT, so the one we want will be the mismatch in finalIDs. - auto const diff = std::mismatch(finalIDs.begin(), finalIDs.end(), prevIDs.begin(), prevIDs.end()); + auto const diff = + std::mismatch(finalIDs.begin(), finalIDs.end(), prevIDs.begin(), prevIDs.end()); // There should always be a difference so the returned finalIDs // iterator should never be end(). But better safe than sorry. @@ -112,10 +118,12 @@ getNFTokenIDFromDeletedOffer(TxMeta const& transactionMeta) std::vector tokenIDResult; for (STObject const& node : transactionMeta.getNodes()) { - if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER || node.getFName() != sfDeletedNode) + if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER || + node.getFName() != sfDeletedNode) continue; - auto const& toAddNFT = node.peekAtField(sfFinalFields).downcast().getFieldH256(sfNFTokenID); + auto const& toAddNFT = + node.peekAtField(sfFinalFields).downcast().getFieldH256(sfNFTokenID); tokenIDResult.push_back(toAddNFT); } @@ -127,7 +135,10 @@ getNFTokenIDFromDeletedOffer(TxMeta const& transactionMeta) } void -insertNFTokenID(Json::Value& response, std::shared_ptr const& transaction, TxMeta const& transactionMeta) +insertNFTokenID( + Json::Value& response, + std::shared_ptr const& transaction, + TxMeta const& transactionMeta) { if (!canHaveNFTokenID(transaction, transactionMeta)) return; @@ -143,7 +154,7 @@ insertNFTokenID(Json::Value& response, std::shared_ptr const& transa { std::vector result = getNFTokenIDFromDeletedOffer(transactionMeta); - if (result.size() > 0) + if (!result.empty()) response[jss::nftoken_id] = to_string(result.front()); } else if (type == ttNFTOKEN_CANCEL_OFFER) diff --git a/src/libxrpl/protocol/NFTokenOfferID.cpp b/src/libxrpl/protocol/NFTokenOfferID.cpp index bbd67d710c..e7ebbcf84b 100644 --- a/src/libxrpl/protocol/NFTokenOfferID.cpp +++ b/src/libxrpl/protocol/NFTokenOfferID.cpp @@ -16,17 +16,20 @@ namespace xrpl { bool -canHaveNFTokenOfferID(std::shared_ptr const& serializedTx, TxMeta const& transactionMeta) +canHaveNFTokenOfferID( + std::shared_ptr const& serializedTx, + TxMeta const& transactionMeta) { if (!serializedTx) return false; TxType const tt = serializedTx->getTxnType(); - if (!(tt == ttNFTOKEN_MINT && serializedTx->isFieldPresent(sfAmount)) && tt != ttNFTOKEN_CREATE_OFFER) + if (!(tt == ttNFTOKEN_MINT && serializedTx->isFieldPresent(sfAmount)) && + tt != ttNFTOKEN_CREATE_OFFER) return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; @@ -37,7 +40,8 @@ getOfferIDFromCreatedOffer(TxMeta const& transactionMeta) { for (STObject const& node : transactionMeta.getNodes()) { - if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER || node.getFName() != sfCreatedNode) + if (node.getFieldU16(sfLedgerEntryType) != ltNFTOKEN_OFFER || + node.getFName() != sfCreatedNode) continue; return node.getFieldH256(sfLedgerIndex); diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index 7e592fdc7f..4137c73624 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -68,10 +68,12 @@ Permission::Permission() }; for ([[maybe_unused]] auto const& permission : granularPermissionMap_) + { XRPL_ASSERT( permission.second > UINT16_MAX, "xrpl::Permission::granularPermissionMap_ : granular permission " "value must not exceed the maximum uint16_t value."); + } } Permission const& @@ -86,7 +88,7 @@ Permission::getPermissionName(std::uint32_t const value) const { auto const permissionValue = static_cast(value); if (auto const granular = getGranularName(permissionValue)) - return *granular; + return granular; // not a granular permission, check if it maps to a transaction type auto const txType = permissionToTxType(value); @@ -126,11 +128,13 @@ Permission::getGranularTxType(GranularPermissionType const& gpType) const return std::nullopt; } -std::optional> const +std::optional> Permission::getTxFeature(TxType txType) const { auto const txFeaturesIt = txFeatureMap_.find(txType); - XRPL_ASSERT(txFeaturesIt != txFeatureMap_.end(), "xrpl::Permissions::getTxFeature : tx exists in txFeatureMap_"); + XRPL_ASSERT( + txFeaturesIt != txFeatureMap_.end(), + "xrpl::Permissions::getTxFeature : tx exists in txFeatureMap_"); if (txFeaturesIt->second == uint256{}) return std::nullopt; @@ -140,10 +144,13 @@ Permission::getTxFeature(TxType txType) const bool Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const { - auto const granularPermission = getGranularName(static_cast(permissionValue)); + auto const granularPermission = + getGranularName(static_cast(permissionValue)); if (granularPermission) + { // granular permissions are always allowed to be delegated return true; + } auto const txType = permissionToTxType(permissionValue); auto const it = delegableTx_.find(txType); @@ -152,7 +159,9 @@ Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules return false; auto const txFeaturesIt = txFeatureMap_.find(txType); - XRPL_ASSERT(txFeaturesIt != txFeatureMap_.end(), "xrpl::Permissions::isDelegable : tx exists in txFeatureMap_"); + XRPL_ASSERT( + txFeaturesIt != txFeatureMap_.end(), + "xrpl::Permissions::isDelegable : tx exists in txFeatureMap_"); // Delegation is only allowed if the required amendment for the transaction // is enabled. For transactions that do not require an amendment, delegation diff --git a/src/libxrpl/protocol/Protocol.cpp b/src/libxrpl/protocol/Protocol.cpp new file mode 100644 index 0000000000..6f86f49fc6 --- /dev/null +++ b/src/libxrpl/protocol/Protocol.cpp @@ -0,0 +1,15 @@ +#include + +namespace xrpl { +bool +isVotingLedger(LedgerIndex seq) +{ + return seq % FLAG_LEDGER_INTERVAL == 0; +} + +bool +isFlagLedger(LedgerIndex seq) +{ + return seq % FLAG_LEDGER_INTERVAL == 0; +} +} // namespace xrpl diff --git a/src/libxrpl/protocol/PublicKey.cpp b/src/libxrpl/protocol/PublicKey.cpp index b336b2d4b5..2c63ddac64 100644 --- a/src/libxrpl/protocol/PublicKey.cpp +++ b/src/libxrpl/protocol/PublicKey.cpp @@ -109,9 +109,12 @@ sliceToHex(Slice const& slice) std::optional ecdsaCanonicality(Slice const& sig) { - using uint264 = boost::multiprecision::number< - boost::multiprecision:: - cpp_int_backend<264, 264, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>; + using uint264 = boost::multiprecision::number>; static uint264 const G("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); @@ -150,8 +153,9 @@ ed25519Canonical(Slice const& sig) return false; // Big-endian Order, the Ed25519 subgroup order std::uint8_t const Order[] = { - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7, 0x9C, 0xD6, 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7, + 0x9C, 0xD6, 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED, }; // Take the second half of signature // and byte-reverse it to big-endian. @@ -167,9 +171,11 @@ ed25519Canonical(Slice const& sig) PublicKey::PublicKey(Slice const& slice) { if (slice.size() < size_) + { LogicError( "PublicKey::PublicKey - Input slice cannot be an undersized " "buffer"); + } if (!publicKeyType(slice)) LogicError("PublicKey::PublicKey invalid type"); @@ -210,7 +216,11 @@ publicKeyType(Slice const& slice) } bool -verifyDigest(PublicKey const& publicKey, uint256 const& digest, Slice const& sig, bool mustBeFullyCanonical) noexcept +verifyDigest( + PublicKey const& publicKey, + uint256 const& digest, + Slice const& sig, + bool mustBeFullyCanonical) noexcept { if (publicKeyType(publicKey) != KeyType::secp256k1) LogicError("sign: secp256k1 required for digest signing"); @@ -230,7 +240,10 @@ verifyDigest(PublicKey const& publicKey, uint256 const& digest, Slice const& sig secp256k1_ecdsa_signature sig_imp; if (secp256k1_ecdsa_signature_parse_der( - secp256k1Context(), &sig_imp, reinterpret_cast(sig.data()), sig.size()) != 1) + secp256k1Context(), + &sig_imp, + reinterpret_cast(sig.data()), + sig.size()) != 1) return false; if (*canonicality != ECDSACanonicality::fullyCanonical) { @@ -238,11 +251,16 @@ verifyDigest(PublicKey const& publicKey, uint256 const& digest, Slice const& sig if (secp256k1_ecdsa_signature_normalize(secp256k1Context(), &sig_norm, &sig_imp) != 1) return false; return secp256k1_ecdsa_verify( - secp256k1Context(), &sig_norm, reinterpret_cast(digest.data()), &pubkey_imp) == - 1; + secp256k1Context(), + &sig_norm, + reinterpret_cast(digest.data()), + &pubkey_imp) == 1; } return secp256k1_ecdsa_verify( - secp256k1Context(), &sig_imp, reinterpret_cast(digest.data()), &pubkey_imp) == 1; + secp256k1Context(), + &sig_imp, + reinterpret_cast(digest.data()), + &pubkey_imp) == 1; } bool @@ -254,7 +272,7 @@ verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept { return verifyDigest(publicKey, sha512Half(m), sig); } - else if (*type == KeyType::ed25519) + if (*type == KeyType::ed25519) { if (!ed25519Canonical(sig)) return false; diff --git a/src/libxrpl/protocol/Quality.cpp b/src/libxrpl/protocol/Quality.cpp index fda0ac2e1e..ba9807318a 100644 --- a/src/libxrpl/protocol/Quality.cpp +++ b/src/libxrpl/protocol/Quality.cpp @@ -36,7 +36,9 @@ Quality::operator++(int) Quality& Quality::operator--() { - XRPL_ASSERT(m_value < std::numeric_limits::max(), "xrpl::Quality::operator--() : maximum value"); + XRPL_ASSERT( + m_value < std::numeric_limits::max(), + "xrpl::Quality::operator--() : maximum value"); ++m_value; return *this; } @@ -121,7 +123,9 @@ composed_quality(Quality const& lhs, Quality const& rhs) std::uint64_t const stored_exponent(rate.exponent() + 100); std::uint64_t const stored_mantissa(rate.mantissa()); - XRPL_ASSERT((stored_exponent > 0) && (stored_exponent <= 255), "xrpl::composed_quality : valid exponent"); + XRPL_ASSERT( + (stored_exponent > 0) && (stored_exponent <= 255), + "xrpl::composed_quality : valid exponent"); return Quality((stored_exponent << (64 - 8)) | stored_mantissa); } diff --git a/src/libxrpl/protocol/QualityFunction.cpp b/src/libxrpl/protocol/QualityFunction.cpp index db004bf478..ebc59dcc83 100644 --- a/src/libxrpl/protocol/QualityFunction.cpp +++ b/src/libxrpl/protocol/QualityFunction.cpp @@ -9,7 +9,8 @@ namespace xrpl { -QualityFunction::QualityFunction(Quality const& quality, QualityFunction::CLOBLikeTag) : m_(0), b_(0), quality_(quality) +QualityFunction::QualityFunction(Quality const& quality, QualityFunction::CLOBLikeTag) + : m_(0), b_(0), quality_(quality) { if (quality.rate() <= beast::zero) Throw("QualityFunction quality rate is 0."); diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index e649b15ea2..4ef052d2cc 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -39,7 +39,8 @@ setCurrentTransactionRules(std::optional r) // Make global changes associated with the rules before the value is moved. // Push the appropriate setting, instead of having the class pull every time // the value is needed. That could get expensive fast. - bool enableLargeNumbers = !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); + bool enableLargeNumbers = + !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); Number::setMantissaScale(enableLargeNumbers ? MantissaRange::large : MantissaRange::small); *getCurrentTransactionRulesRef() = std::move(r); @@ -76,9 +77,9 @@ public: bool enabled(uint256 const& feature) const { - if (presets_.count(feature) > 0) + if (presets_.contains(feature)) return true; - return set_.count(feature) > 0; + return set_.contains(feature); } bool @@ -96,7 +97,8 @@ public: } }; -Rules::Rules(std::unordered_set> const& presets) : impl_(std::make_shared(presets)) +Rules::Rules(std::unordered_set> const& presets) + : impl_(std::make_shared(presets)) { } diff --git a/src/libxrpl/protocol/SField.cpp b/src/libxrpl/protocol/SField.cpp index c04c82f2bc..8015b34e73 100644 --- a/src/libxrpl/protocol/SField.cpp +++ b/src/libxrpl/protocol/SField.cpp @@ -22,7 +22,8 @@ static SField::private_access_tag_t access; template template -TypedField::TypedField(private_access_tag_t pat, Args&&... args) : SField(pat, std::forward(args)...) +TypedField::TypedField(private_access_tag_t pat, Args&&... args) + : SField(pat, std::forward(args)...) { } @@ -36,10 +37,19 @@ TypedField::TypedField(private_access_tag_t pat, Args&&... args) : SField(pat #undef TYPED_SFIELD #define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \ - SField const sfName(access, STI_##stiSuffix, fieldValue, std::string_view(#sfName).substr(2).data(), ##__VA_ARGS__); + SField const sfName( \ + access, \ + STI_##stiSuffix, \ + fieldValue, \ + std::string_view(#sfName).substr(2).data(), \ + ##__VA_ARGS__); #define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \ SF_##stiSuffix const sfName( \ - access, STI_##stiSuffix, fieldValue, std::string_view(#sfName).substr(2).data(), ##__VA_ARGS__); + access, \ + STI_##stiSuffix, \ + fieldValue, \ + std::string_view(#sfName).substr(2).data(), \ + ##__VA_ARGS__); // SFields which, for historical reasons, do not follow naming conventions. SField const sfInvalid(access, -1, ""); @@ -56,7 +66,13 @@ SField const sfIndex(access, STI_UINT256, 258, "index"); #undef UNTYPED_SFIELD #pragma pop_macro("UNTYPED_SFIELD") -SField::SField(private_access_tag_t, SerializedTypeID tid, int fv, char const* fn, int meta, IsSigning signing) +SField::SField( + private_access_tag_t, + SerializedTypeID tid, + int fv, + char const* fn, + int meta, + IsSigning signing) : fieldCode(field_code(tid, fv)) , fieldType(tid) , fieldValue(fv) @@ -67,9 +83,11 @@ SField::SField(private_access_tag_t, SerializedTypeID tid, int fv, char const* f , jsonName(fieldName.c_str()) { XRPL_ASSERT( - !knownCodeToField.contains(fieldCode), "xrpl::SField::SField(tid,fv,fn,meta,signing) : fieldCode is unique"); + !knownCodeToField.contains(fieldCode), + "xrpl::SField::SField(tid,fv,fn,meta,signing) : fieldCode is unique"); XRPL_ASSERT( - !knownNameToField.contains(fieldName), "xrpl::SField::SField(tid,fv,fn,meta,signing) : fieldName is unique"); + !knownNameToField.contains(fieldName), + "xrpl::SField::SField(tid,fv,fn,meta,signing) : fieldName is unique"); knownCodeToField[fieldCode] = this; knownNameToField[fieldName] = this; } @@ -84,8 +102,10 @@ SField::SField(private_access_tag_t, int fc, char const* fn) , signingField(IsSigning::yes) , jsonName(fieldName.c_str()) { - XRPL_ASSERT(!knownCodeToField.contains(fieldCode), "xrpl::SField::SField(fc,fn) : fieldCode is unique"); - XRPL_ASSERT(!knownNameToField.contains(fieldName), "xrpl::SField::SField(fc,fn) : fieldName is unique"); + XRPL_ASSERT( + !knownCodeToField.contains(fieldCode), "xrpl::SField::SField(fc,fn) : fieldCode is unique"); + XRPL_ASSERT( + !knownNameToField.contains(fieldName), "xrpl::SField::SField(fc,fn) : fieldName is unique"); knownCodeToField[fieldCode] = this; knownNameToField[fieldName] = this; } diff --git a/src/libxrpl/protocol/SOTemplate.cpp b/src/libxrpl/protocol/SOTemplate.cpp index 23d086f493..b90bff6192 100644 --- a/src/libxrpl/protocol/SOTemplate.cpp +++ b/src/libxrpl/protocol/SOTemplate.cpp @@ -2,21 +2,32 @@ #include #include +#include #include #include +#include #include +#include namespace xrpl { -SOTemplate::SOTemplate(std::initializer_list uniqueFields, std::initializer_list commonFields) +SOTemplate::SOTemplate( + std::initializer_list uniqueFields, + std::initializer_list commonFields) + : SOTemplate(std::vector(uniqueFields), std::vector(commonFields)) +{ +} + +SOTemplate::SOTemplate(std::vector uniqueFields, std::vector commonFields) : indices_(SField::getNumFields() + 1, -1) // Unmapped indices == -1 { // Add all SOElements. - elements_.reserve(uniqueFields.size() + commonFields.size()); - elements_.assign(uniqueFields); - elements_.insert(elements_.end(), commonFields); + // + elements_ = std::move(uniqueFields); + std::ranges::move(commonFields, std::back_inserter(elements_)); // Validate and index elements_. + // for (std::size_t i = 0; i < elements_.size(); ++i) { SField const& sField{elements_[i].sField()}; diff --git a/src/libxrpl/protocol/STAccount.cpp b/src/libxrpl/protocol/STAccount.cpp index b85341daf3..ffd0b5d3c8 100644 --- a/src/libxrpl/protocol/STAccount.cpp +++ b/src/libxrpl/protocol/STAccount.cpp @@ -16,7 +16,7 @@ namespace xrpl { -STAccount::STAccount() : STBase(), value_(beast::zero), default_(true) +STAccount::STAccount() : value_(beast::zero), default_(true) { } @@ -24,7 +24,7 @@ STAccount::STAccount(SField const& n) : STBase(n), value_(beast::zero), default_ { } -STAccount::STAccount(SField const& n, Buffer&& v) : STAccount(n) +STAccount::STAccount(SField const& n, Buffer const& v) : STAccount(n) { if (v.empty()) return; // Zero is a valid size for a defaulted STAccount. diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index b7c33bb907..92ce129825 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -65,7 +65,9 @@ getInt64Value(STAmount const& amount, bool valid, char const* error) auto ret = static_cast(amount.mantissa()); - XRPL_ASSERT(static_cast(ret) == amount.mantissa(), "xrpl::getInt64Value : mantissa must roundtrip"); + XRPL_ASSERT( + static_cast(ret) == amount.mantissa(), + "xrpl::getInt64Value : mantissa must roundtrip"); if (amount.negative()) ret = -ret; @@ -178,7 +180,8 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) canonicalize(); } -STAmount::STAmount(SField const& name, std::int64_t mantissa) : STBase(name), mAsset(xrpIssue()), mOffset(0) +STAmount::STAmount(SField const& name, std::int64_t mantissa) + : STBase(name), mAsset(xrpIssue()), mValue(0), mOffset(0), mIsNegative(false) { set(mantissa); } @@ -193,7 +196,11 @@ STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative) } STAmount::STAmount(SField const& name, STAmount const& from) - : STBase(name), mAsset(from.mAsset), mValue(from.mValue), mOffset(from.mOffset), mIsNegative(from.mIsNegative) + : STBase(name) + , mAsset(from.mAsset) + , mValue(from.mValue) + , mOffset(from.mOffset) + , mIsNegative(from.mIsNegative) { XRPL_ASSERT( mValue <= std::numeric_limits::max(), @@ -212,12 +219,17 @@ STAmount::STAmount(std::uint64_t mantissa, bool negative) "input"); } -STAmount::STAmount(XRPAmount const& amount) : mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero) +STAmount::STAmount(XRPAmount const& amount) + : mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero) { if (mIsNegative) + { mValue = unsafe_cast(-amount.drops()); + } else + { mValue = unsafe_cast(amount.drops()); + } canonicalize(); } @@ -297,9 +309,13 @@ STAmount::operator=(IOUAmount const& iou) mOffset = iou.exponent(); mIsNegative = iou < beast::zero; if (mIsNegative) + { mValue = static_cast(-iou.mantissa()); + } else + { mValue = static_cast(iou.mantissa()); + } return *this; } @@ -435,21 +451,23 @@ getRate(STAmount const& offerOut, STAmount const& offerIn) { if (offerOut == beast::zero) return 0; + try { STAmount r = divide(offerIn, offerOut, noIssue()); if (r == beast::zero) // offer is too good return 0; - XRPL_ASSERT((r.exponent() >= -100) && (r.exponent() <= 155), "xrpl::getRate : exponent inside range"); + XRPL_ASSERT( + (r.exponent() >= -100) && (r.exponent() <= 155), + "xrpl::getRate : exponent inside range"); std::uint64_t ret = r.exponent() + 100; return (ret << (64 - 8)) | r.mantissa(); } - catch (std::exception const&) + catch (...) { + // overflow -- very bad offer + return 0; } - - // overflow -- very bad offer - return 0; } /** @@ -487,8 +505,10 @@ canAdd(STAmount const& a, STAmount const& b) XRPAmount A = a.xrp(); XRPAmount B = b.xrp(); - if ((B > XRPAmount{0} && A > XRPAmount{std::numeric_limits::max()} - B) || - (B < XRPAmount{0} && A < XRPAmount{std::numeric_limits::min()} - B)) + if ((B > XRPAmount{0} && + A > XRPAmount{std::numeric_limits::max()} - B) || + (B < XRPAmount{0} && + A < XRPAmount{std::numeric_limits::min()} - B)) { return false; } @@ -510,8 +530,10 @@ canAdd(STAmount const& a, STAmount const& b) { MPTAmount A = a.mpt(); MPTAmount B = b.mpt(); - if ((B > MPTAmount{0} && A > MPTAmount{std::numeric_limits::max()} - B) || - (B < MPTAmount{0} && A < MPTAmount{std::numeric_limits::min()} - B)) + if ((B > MPTAmount{0} && + A > MPTAmount{std::numeric_limits::max()} - B) || + (B < MPTAmount{0} && + A < MPTAmount{std::numeric_limits::min()} - B)) { return false; } @@ -562,7 +584,8 @@ canSubtract(STAmount const& a, STAmount const& b) return false; // Check for overflow - if (B < XRPAmount{0} && A > XRPAmount{std::numeric_limits::max()} + B) + if (B < XRPAmount{0} && + A > XRPAmount{std::numeric_limits::max()} + B) return false; return true; @@ -585,7 +608,8 @@ canSubtract(STAmount const& a, STAmount const& b) return false; // Overflow check - if (B < MPTAmount{0} && A > MPTAmount{std::numeric_limits::max()} + B) + if (B < MPTAmount{0} && + A > MPTAmount{std::numeric_limits::max()} + B) return false; return true; } @@ -698,15 +722,21 @@ STAmount::getText() const XRPL_ASSERT(post_to >= post_from, "xrpl::STAmount::getText : second distance check"); - post_to = std::find_if(std::make_reverse_iterator(post_to), std::make_reverse_iterator(post_from), [](char c) { - return c != '0'; - }).base(); + post_to = std::find_if( + std::make_reverse_iterator(post_to), + std::make_reverse_iterator(post_from), + [](char c) { return c != '0'; }) + .base(); // Assemble the output: if (pre_from == pre_to) + { ret.append(1, '0'); + } else + { ret.append(pre_from, pre_to); + } if (post_to != post_from) { @@ -733,9 +763,13 @@ STAmount::add(Serializer& s) const XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::add : zero offset"); if (!mIsNegative) + { s.add64(mValue | cPositive); + } else + { s.add64(mValue); + } } else if (mAsset.holds()) { @@ -749,11 +783,17 @@ STAmount::add(Serializer& s) const else { if (*this == beast::zero) + { s.add64(cIssuedCurrency); - else if (mIsNegative) // 512 = not native + } + else if (mIsNegative) + { // 512 = not native s.add64(mValue | (static_cast(mOffset + 512 + 97) << (64 - 10))); - else // 256 = positive + } + else + { // 256 = positive s.add64(mValue | (static_cast(mOffset + 512 + 256 + 97) << (64 - 10))); + } s.addBitString(mAsset.get().currency); s.addBitString(mAsset.get().account); } @@ -821,11 +861,17 @@ STAmount::canonicalize() mValue = mIsNegative ? -value : value; }; if (native()) + { set(XRPAmount{num}); + } else if (mAsset.holds()) + { set(MPTAmount{num}); + } else + { Throw("Unknown integral asset type"); + } mOffset = 0; } else @@ -841,9 +887,13 @@ STAmount::canonicalize() // N.B. do not move the overflow check to after the // multiplication if (native() && mValue > cMaxNativeN) + { Throw("Native currency amount out of range"); + } else if (!native() && mValue > maxMPTokenAmount) + { Throw("MPT amount out of range"); + } mValue *= 10; --mOffset; @@ -851,9 +901,13 @@ STAmount::canonicalize() } if (native() && mValue > cMaxNativeN) + { Throw("Native currency amount out of range"); + } else if (!native() && mValue > maxMPTokenAmount) + { Throw("MPT amount out of range"); + } return; } @@ -903,7 +957,8 @@ STAmount::canonicalize() XRPL_ASSERT( (mValue == 0) || ((mOffset >= cMinOffset) && (mOffset <= cMaxOffset)), "xrpl::STAmount::canonicalize : offset inside range"); - XRPL_ASSERT((mValue != 0) || (mOffset != -100), "xrpl::STAmount::canonicalize : value or offset set"); + XRPL_ASSERT( + (mValue != 0) || (mOffset != -100), "xrpl::STAmount::canonicalize : value or offset set"); } void @@ -1092,8 +1147,8 @@ amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource) bool operator==(STAmount const& lhs, STAmount const& rhs) { - return areComparable(lhs, rhs) && lhs.negative() == rhs.negative() && lhs.exponent() == rhs.exponent() && - lhs.mantissa() == rhs.mantissa(); + return areComparable(lhs, rhs) && lhs.negative() == rhs.negative() && + lhs.exponent() == rhs.exponent() && lhs.mantissa() == rhs.mantissa(); } bool @@ -1136,7 +1191,12 @@ operator-(STAmount const& value) if (value.mantissa() == 0) return value; return STAmount( - value.getFName(), value.asset(), value.mantissa(), value.exponent(), !value.negative(), STAmount::unchecked{}); + value.getFName(), + value.asset(), + value.mantissa(), + value.exponent(), + !value.negative(), + STAmount::unchecked{}); } //------------------------------------------------------------------------------ @@ -1158,15 +1218,19 @@ muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divis if (ret > std::numeric_limits::max()) { Throw( - "overflow: (" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + ") / " + - std::to_string(divisor)); + "overflow: (" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + + ") / " + std::to_string(divisor)); } return static_cast(ret); } static std::uint64_t -muldiv_round(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding) +muldiv_round( + std::uint64_t multiplier, + std::uint64_t multiplicand, + std::uint64_t divisor, + std::uint64_t rounding) { boost::multiprecision::uint128_t ret; @@ -1177,8 +1241,8 @@ muldiv_round(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t if (ret > std::numeric_limits::max()) { Throw( - "overflow: ((" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + ") + " + - std::to_string(rounding) + ") / " + std::to_string(divisor)); + "overflow: ((" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + + ") + " + std::to_string(rounding) + ") / " + std::to_string(divisor)); } return static_cast(ret); @@ -1223,7 +1287,10 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset) // 10^32 to 10^33) followed by a division, so the result // is in the range of 10^16 to 10^15. return STAmount( - asset, muldiv(numVal, tenTo17, denVal) + 5, numOffset - denOffset - 17, num.negative() != den.negative()); + asset, + muldiv(numVal, tenTo17, denVal) + 5, + numOffset - denOffset - 17, + num.negative() != den.negative()); } STAmount @@ -1292,7 +1359,11 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) // and 10^16), so their product is in the 10^30 to 10^32 // range. Dividing their product by 10^14 maintains the // precision, by scaling the result to 10^16 to 10^18. - return STAmount(asset, muldiv(value1, value2, tenTo14) + 7, offset1 + offset2 + 14, v1.negative() != v2.negative()); + return STAmount( + asset, + muldiv(value1, value2, tenTo14) + 7, + offset1 + offset2 + 14, + v1.negative() != v2.negative()); } // This is the legacy version of canonicalizeRound. It's been in use @@ -1509,7 +1580,8 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro // If the we're rounding up, we want to round up away // from zero, and if we're rounding down, truncation // is implicit. - std::uint64_t amount = muldiv_round(value1, value2, tenTo14, (resultNegative != roundUp) ? tenTo14m1 : 0); + std::uint64_t amount = + muldiv_round(value1, value2, tenTo14, (resultNegative != roundUp) ? tenTo14m1 : 0); int offset = offset1 + offset2 + 14; if (resultNegative != roundUp) @@ -1597,7 +1669,8 @@ divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool // // We round away from zero if we're rounding up or // truncate if we're rounding down. - std::uint64_t amount = muldiv_round(numVal, tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0); + std::uint64_t amount = + muldiv_round(numVal, tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0); int offset = numOffset - denOffset - 17; diff --git a/src/libxrpl/protocol/STArray.cpp b/src/libxrpl/protocol/STArray.cpp index 8e7827cccc..91acc9ddd3 100644 --- a/src/libxrpl/protocol/STArray.cpp +++ b/src/libxrpl/protocol/STArray.cpp @@ -44,7 +44,7 @@ STArray::STArray(SerialIter& sit, SField const& f, int depth) : STBase(f) { while (!sit.empty()) { - int type, field; + int type = 0, field = 0; sit.getFieldID(type, field); if ((type == STI_ARRAY) && (field == 1)) diff --git a/src/libxrpl/protocol/STBase.cpp b/src/libxrpl/protocol/STBase.cpp index f6b1dcec58..3c5c34ae4e 100644 --- a/src/libxrpl/protocol/STBase.cpp +++ b/src/libxrpl/protocol/STBase.cpp @@ -23,6 +23,9 @@ STBase::STBase(SField const& n) : fName(&n) STBase& STBase::operator=(STBase const& t) { + if (this == &t) + return *this; + if (!fName->isUseful()) fName = t.fName; return *this; diff --git a/src/libxrpl/protocol/STCurrency.cpp b/src/libxrpl/protocol/STCurrency.cpp index cfbbe509c6..1616de59b9 100644 --- a/src/libxrpl/protocol/STCurrency.cpp +++ b/src/libxrpl/protocol/STCurrency.cpp @@ -23,7 +23,8 @@ STCurrency::STCurrency(SerialIter& sit, SField const& name) : STBase{name} currency_ = sit.get160(); } -STCurrency::STCurrency(SField const& name, Currency const& currency) : STBase{name}, currency_{currency} +STCurrency::STCurrency(SField const& name, Currency const& currency) + : STBase{name}, currency_{currency} { } diff --git a/src/libxrpl/protocol/STInteger.cpp b/src/libxrpl/protocol/STInteger.cpp index ae7b3ff279..1fccee2285 100644 --- a/src/libxrpl/protocol/STInteger.cpp +++ b/src/libxrpl/protocol/STInteger.cpp @@ -20,7 +20,8 @@ namespace xrpl { template <> -STInteger::STInteger(SerialIter& sit, SField const& name) : STInteger(name, sit.get8()) +STInteger::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, sit.get8()) { } @@ -72,7 +73,8 @@ STUInt8::getJson(JsonOptions) const //------------------------------------------------------------------------------ template <> -STInteger::STInteger(SerialIter& sit, SField const& name) : STInteger(name, sit.get16()) +STInteger::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, sit.get16()) { } @@ -132,7 +134,8 @@ STUInt16::getJson(JsonOptions) const //------------------------------------------------------------------------------ template <> -STInteger::STInteger(SerialIter& sit, SField const& name) : STInteger(name, sit.get32()) +STInteger::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, sit.get32()) { } @@ -173,7 +176,8 @@ STUInt32::getJson(JsonOptions) const //------------------------------------------------------------------------------ template <> -STInteger::STInteger(SerialIter& sit, SField const& name) : STInteger(name, sit.get64()) +STInteger::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, sit.get64()) { } @@ -215,7 +219,8 @@ STUInt64::getJson(JsonOptions) const //------------------------------------------------------------------------------ template <> -STInteger::STInteger(SerialIter& sit, SField const& name) : STInteger(name, sit.get32()) +STInteger::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, sit.get32()) { } diff --git a/src/libxrpl/protocol/STIssue.cpp b/src/libxrpl/protocol/STIssue.cpp index 627c61fc58..1d37554251 100644 --- a/src/libxrpl/protocol/STIssue.cpp +++ b/src/libxrpl/protocol/STIssue.cpp @@ -46,7 +46,10 @@ STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name} std::uint32_t sequence = sit.get32(); static_assert(MPTID::size() == sizeof(sequence) + sizeof(currencyOrAccount)); memcpy(mptID.data(), &sequence, sizeof(sequence)); - memcpy(mptID.data() + sizeof(sequence), currencyOrAccount.data(), sizeof(currencyOrAccount)); + memcpy( + mptID.data() + sizeof(sequence), + currencyOrAccount.data(), + sizeof(currencyOrAccount)); MPTIssue issue{mptID}; asset_ = issue; } @@ -97,7 +100,7 @@ STIssue::add(Serializer& s) const auto const& issue = asset_.get(); s.addBitString(issue.getIssuer()); s.addBitString(noAccount()); - std::uint32_t sequence; + std::uint32_t sequence = 0; memcpy(&sequence, issue.getMptID().data(), sizeof(sequence)); s.add32(sequence); } diff --git a/src/libxrpl/protocol/STLedgerEntry.cpp b/src/libxrpl/protocol/STLedgerEntry.cpp index d73da2eb3a..aa97b6b3a2 100644 --- a/src/libxrpl/protocol/STLedgerEntry.cpp +++ b/src/libxrpl/protocol/STLedgerEntry.cpp @@ -33,21 +33,26 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k. auto const format = LedgerFormats::getInstance().findByType(type_); if (format == nullptr) + { Throw( - "Attempt to create a SLE of unknown type " + std::to_string(safe_cast(k.type))); + "Attempt to create a SLE of unknown type " + + std::to_string(safe_cast(k.type))); + } set(format->getSOTemplate()); setFieldU16(sfLedgerEntryType, static_cast(type_)); } -STLedgerEntry::STLedgerEntry(SerialIter& sit, uint256 const& index) : STObject(sfLedgerEntry), key_(index) +STLedgerEntry::STLedgerEntry(SerialIter& sit, uint256 const& index) + : STObject(sfLedgerEntry), key_(index), type_(ltANY) { set(sit); setSLEType(); } -STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index) : STObject(object), key_(index) +STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index) + : STObject(object), key_(index), type_(ltANY) { setSLEType(); } @@ -55,7 +60,8 @@ STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index) : STO void STLedgerEntry::setSLEType() { - auto format = LedgerFormats::getInstance().findByType(safe_cast(getFieldU16(sfLedgerEntryType))); + auto format = LedgerFormats::getInstance().findByType( + safe_cast(getFieldU16(sfLedgerEntryType))); if (format == nullptr) Throw("invalid ledger entry type"); @@ -114,7 +120,10 @@ STLedgerEntry::getJson(JsonOptions options) const ret[jss::index] = to_string(key_); if (getType() == ltMPTOKEN_ISSUANCE) - ret[jss::mpt_issuance_id] = to_string(makeMptID(getFieldU32(sfSequence), getAccountID(sfIssuer))); + { + ret[jss::mpt_issuance_id] = + to_string(makeMptID(getFieldU32(sfSequence), getAccountID(sfIssuer))); + } return ret; } @@ -132,7 +141,11 @@ STLedgerEntry::isThreadedType(Rules const& rules) const } bool -STLedgerEntry::thread(uint256 const& txID, std::uint32_t ledgerSeq, uint256& prevTxID, std::uint32_t& prevLedgerID) +STLedgerEntry::thread( + uint256 const& txID, + std::uint32_t ledgerSeq, + uint256& prevTxID, + std::uint32_t& prevLedgerID) { uint256 oldPrevTxID = getFieldH256(sfPreviousTxnID); @@ -142,7 +155,8 @@ STLedgerEntry::thread(uint256 const& txID, std::uint32_t ledgerSeq, uint256& pre { // this transaction is already threaded XRPL_ASSERT( - getFieldU32(sfPreviousTxnLgrSeq) == ledgerSeq, "xrpl::STLedgerEntry::thread : ledger sequence match"); + getFieldU32(sfPreviousTxnLgrSeq) == ledgerSeq, + "xrpl::STLedgerEntry::thread : ledger sequence match"); return false; } diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index a549a0e482..aab4bdc0f5 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -50,7 +50,10 @@ STNumber::associateAsset(Asset const& a) { STTakesAsset::associateAsset(a); - XRPL_ASSERT_PARTS(getFName().shouldMeta(SField::sMD_NeedsAsset), "STNumber::associateAsset", "field needs asset"); + XRPL_ASSERT_PARTS( + getFName().shouldMeta(SField::sMD_NeedsAsset), + "STNumber::associateAsset", + "field needs asset"); roundToAsset(a, value_); } @@ -94,7 +97,8 @@ STNumber::add(Serializer& s) const } XRPL_ASSERT_PARTS( - mantissa <= std::numeric_limits::max() && mantissa >= std::numeric_limits::min(), + mantissa <= std::numeric_limits::max() && + mantissa >= std::numeric_limits::min(), "xrpl::STNumber::add", "mantissa in valid range"); s.add64(mantissa); @@ -128,7 +132,8 @@ STNumber::move(std::size_t n, void* buf) bool STNumber::isEquivalent(STBase const& t) const { - XRPL_ASSERT(t.getSType() == this->getSType(), "xrpl::STNumber::isEquivalent : field type match"); + XRPL_ASSERT( + t.getSType() == this->getSType(), "xrpl::STNumber::isEquivalent : field type match"); STNumber const& v = dynamic_cast(t); return value_ == v; } @@ -174,8 +179,8 @@ partsFromString(std::string const& number) bool negative = (match[1].matched && (match[1] == "-")); - std::uint64_t mantissa; - int exponent; + std::uint64_t mantissa = 0; + int exponent = 0; if (!match[4].matched) // integer only { @@ -193,9 +198,13 @@ partsFromString(std::string const& number) { // we have an exponent if (match[6].matched && (match[6] == "-")) + { exponent -= boost::lexical_cast(std::string(match[7])); + } else + { exponent += boost::lexical_cast(std::string(match[7])); + } } return {mantissa, exponent, negative}; @@ -226,19 +235,22 @@ numberFromJson(SField const& field, Json::Value const& value) { parts = partsFromString(value.asString()); - XRPL_ASSERT_PARTS(!getCurrentTransactionRules(), "xrpld::numberFromJson", "Not in a Transactor context"); + XRPL_ASSERT_PARTS( + !getCurrentTransactionRules(), "xrpld::numberFromJson", "Not in a Transactor context"); // Number mantissas are much bigger than the allowable parsed values, so // it can't be out of range. static_assert( - std::numeric_limits::max() >= std::numeric_limits::max()); + std::numeric_limits::max() >= + std::numeric_limits::max()); } else { Throw("not a number"); } - return STNumber{field, Number{parts.negative, parts.mantissa, parts.exponent, Number::normalized{}}}; + return STNumber{ + field, Number{parts.negative, parts.mantissa, parts.exponent, Number::normalized{}}}; } } // namespace xrpl diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index ad5b6fe352..fe9e95cc82 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -41,11 +41,12 @@ namespace xrpl { -STObject::STObject(STObject&& other) : STBase(other.getFName()), v_(std::move(other.v_)), mType(other.mType) +STObject::STObject(STObject&& other) + : STBase(other.getFName()), v_(std::move(other.v_)), mType(other.mType) { } -STObject::STObject(SField const& name) : STBase(name), mType(nullptr) +STObject::STObject(SField const& name) : STBase(name) { } @@ -61,7 +62,7 @@ STObject::STObject(SOTemplate const& type, SerialIter& sit, SField const& name) applyTemplate(type); // May throw } -STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) : STBase(name), mType(nullptr) +STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) : STBase(name) { if (depth > 10) Throw("Maximum nesting depth of STObject exceeded"); @@ -83,7 +84,8 @@ STObject::makeInnerObject(SField const& name) if (!rules || (rules->enabled(fixInnerObjTemplate) && isAMMObj) || (rules->enabled(fixInnerObjTemplate2) && !isAMMObj)) { - if (SOTemplate const* elements = InnerObjectFormats::getInstance().findSOTemplateBySField(name)) + if (SOTemplate const* elements = + InnerObjectFormats::getInstance().findSOTemplateBySField(name)) obj.set(*elements); } return obj; @@ -138,9 +140,13 @@ STObject::set(SOTemplate const& type) for (auto const& elem : type) { if (elem.style() != soeREQUIRED) + { v_.emplace_back(detail::nonPresentObject, elem.sField()); + } else + { v_.emplace_back(detail::defaultObject, elem.sField()); + } } } @@ -160,8 +166,9 @@ STObject::applyTemplate(SOTemplate const& type) v.reserve(type.size()); for (auto const& e : type) { - auto const iter = std::find_if( - v_.begin(), v_.end(), [&](detail::STVar const& b) { return b.get().getFName() == e.sField(); }); + auto const iter = std::find_if(v_.begin(), v_.end(), [&](detail::STVar const& b) { + return b.get().getFName() == e.sField(); + }); if (iter != v_.end()) { if ((e.style() == soeDEFAULT) && iter->get().isDefault()) @@ -212,8 +219,8 @@ STObject::set(SerialIter& sit, int depth) // Consume data in the pipe until we run out or reach the end while (!sit.empty()) { - int type; - int field; + int type = 0; + int field = 0; // Get the metadata for the next field sit.getFieldID(type, field); @@ -236,7 +243,8 @@ STObject::set(SerialIter& sit, int depth) if (fn.isInvalid()) { - JLOG(debugLog().error()) << "Unknown field: field_type=" << type << ", field_name=" << field; + JLOG(debugLog().error()) + << "Unknown field: field_type=" << type << ", field_name=" << field; Throw("Unknown field"); } @@ -252,9 +260,10 @@ STObject::set(SerialIter& sit, int depth) // duplicate fields. This is a key invariant: auto const sf = getSortedFields(*this, withAllFields); - auto const dup = std::adjacent_find(sf.cbegin(), sf.cend(), [](STBase const* lhs, STBase const* rhs) { - return lhs->getFName() == rhs->getFName(); - }); + auto const dup = + std::adjacent_find(sf.cbegin(), sf.cend(), [](STBase const* lhs, STBase const* rhs) { + return lhs->getFName() == rhs->getFName(); + }); if (dup != sf.cend()) Throw("Duplicate field detected"); @@ -263,7 +272,7 @@ STObject::set(SerialIter& sit, int depth) } bool -STObject::hasMatchingEntry(STBase const& t) +STObject::hasMatchingEntry(STBase const& t) const { STBase const* o = peekAtPField(t.getFName()); @@ -285,16 +294,22 @@ STObject::getFullText() const ret += " = {"; } else + { ret = "{"; + } for (auto const& elem : v_) { if (elem->getSType() != STI_NOTPRESENT) { if (!first) + { ret += ", "; + } else + { first = false; + } ret += elem->getFullText(); } @@ -333,17 +348,19 @@ STObject::isEquivalent(STBase const& t) const if (mType != nullptr && v->mType == mType) { - return std::equal(begin(), end(), v->begin(), v->end(), [](STBase const& st1, STBase const& st2) { - return (st1.getSType() == st2.getSType()) && st1.isEquivalent(st2); - }); + return std::equal( + begin(), end(), v->begin(), v->end(), [](STBase const& st1, STBase const& st2) { + return (st1.getSType() == st2.getSType()) && st1.isEquivalent(st2); + }); } auto const sf1 = getSortedFields(*this, withAllFields); auto const sf2 = getSortedFields(*v, withAllFields); - return std::equal(sf1.begin(), sf1.end(), sf2.begin(), sf2.end(), [](STBase const* st1, STBase const* st2) { - return (st1->getSType() == st2->getSType()) && st1->isEquivalent(*st2); - }); + return std::equal( + sf1.begin(), sf1.end(), sf2.begin(), sf2.end(), [](STBase const* st1, STBase const* st2) { + return (st1->getSType() == st2->getSType()) && st1->isEquivalent(*st2); + }); } uint256 diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index 878f6c3605..0bf0c827af 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -75,7 +75,8 @@ make_name(std::string const& object, std::string const& field) static inline Json::Value not_an_object(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is not a JSON object."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is not a JSON object."); } static inline Json::Value @@ -93,25 +94,29 @@ not_an_array(std::string const& object) static inline Json::Value unknown_field(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is unknown."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is unknown."); } static inline Json::Value out_of_range(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is out of range."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' is out of range."); } static inline Json::Value bad_type(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has bad type."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has bad type."); } static inline Json::Value invalid_data(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has invalid data."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' has invalid data."); } static inline Json::Value @@ -123,19 +128,22 @@ invalid_data(std::string const& object) static inline Json::Value array_expected(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a JSON array."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a JSON array."); } static inline Json::Value string_expected(std::string const& object, std::string const& field) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a string."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + make_name(object, field) + "' must be a string."); } static inline Json::Value too_deep(std::string const& object) { - return RPC::make_error(rpcINVALID_PARAMS, "Field '" + object + "' exceeds nesting depth limit."); + return RPC::make_error( + rpcINVALID_PARAMS, "Field '" + object + "' exceeds nesting depth limit."); } static inline Json::Value @@ -143,14 +151,16 @@ singleton_expected(std::string const& object, unsigned int index) { return RPC::make_error( rpcINVALID_PARAMS, - "Field '" + object + "[" + std::to_string(index) + "]' must be an object with a single key/object value."); + "Field '" + object + "[" + std::to_string(index) + + "]' must be an object with a single key/object value."); } static inline Json::Value template_mismatch(SField const& sField) { return RPC::make_error( - rpcINVALID_PARAMS, "Object '" + sField.getName() + "' contents did not meet requirements for that type."); + rpcINVALID_PARAMS, + "Object '" + sField.getName() + "' contents did not meet requirements for that type."); } static inline Json::Value @@ -180,15 +190,19 @@ parseUnsigned( if (value.isString()) { ret = detail::make_stvar( - field, safe_cast(beast::lexicalCastThrow(value.asString()))); + field, + safe_cast( + beast::lexicalCastThrow(value.asString()))); } else if (value.isInt()) { - ret = detail::make_stvar(field, to_unsigned(value.asInt())); + ret = detail::make_stvar( + field, to_unsigned(value.asInt())); } else if (value.isUInt()) { - ret = detail::make_stvar(field, to_unsigned(value.asUInt())); + ret = detail::make_stvar( + field, to_unsigned(value.asUInt())); } else { @@ -229,8 +243,8 @@ parseUint16( { ret = detail::make_stvar( field, - safe_cast( - static_cast(TxFormats::getInstance().findTypeByName(strValue)))); + safe_cast(static_cast( + TxFormats::getInstance().findTypeByName(strValue)))); if (*name == sfGeneric) name = &sfTransaction; @@ -239,8 +253,8 @@ parseUint16( { ret = detail::make_stvar( field, - safe_cast( - static_cast(LedgerFormats::getInstance().findTypeByName(strValue)))); + safe_cast(static_cast( + LedgerFormats::getInstance().findTypeByName(strValue)))); if (*name == sfGeneric) name = &sfLedgerEntry; @@ -253,7 +267,10 @@ parseUint16( } } if (!ret) - return parseUnsigned(field, json_name, fieldName, name, value, error); + { + return parseUnsigned( + field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -283,7 +300,8 @@ parseUint32( if (field == sfPermissionValue) { std::string const strValue = value.asString(); - auto const granularPermission = Permission::getInstance().getGranularValue(strValue); + auto const granularPermission = + Permission::getInstance().getGranularValue(strValue); if (granularPermission) { ret = detail::make_stvar(field, *granularPermission); @@ -291,18 +309,23 @@ parseUint32( else { auto const& txType = TxFormats::getInstance().findTypeByName(strValue); - ret = detail::make_stvar(field, Permission::getInstance().txToPermissionType(txType)); + ret = detail::make_stvar( + field, Permission::getInstance().txToPermissionType(txType)); } } else { ret = detail::make_stvar( field, - safe_cast(beast::lexicalCastThrow(value.asString()))); + safe_cast( + beast::lexicalCastThrow(value.asString()))); } } if (!ret) - return parseUnsigned(field, json_name, fieldName, name, value, error); + { + return parseUnsigned( + field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -359,7 +382,8 @@ parseLeaf( return ret; } - ret = detail::make_stvar(field, static_cast(TERtoInt(*ter))); + ret = detail::make_stvar( + field, static_cast(TERtoInt(*ter))); } else { @@ -369,7 +393,8 @@ parseLeaf( } else { - ret = detail::make_stvar(field, beast::lexicalCastThrow(strValue)); + ret = detail::make_stvar( + field, beast::lexicalCastThrow(strValue)); } } else if (value.isInt()) @@ -380,7 +405,8 @@ parseLeaf( return ret; } - ret = detail::make_stvar(field, static_cast(value.asInt())); + ret = detail::make_stvar( + field, static_cast(value.asInt())); } else if (value.isUInt()) { @@ -390,7 +416,8 @@ parseLeaf( return ret; } - ret = detail::make_stvar(field, static_cast(value.asUInt())); + ret = detail::make_stvar( + field, static_cast(value.asUInt())); } else { @@ -426,12 +453,13 @@ parseLeaf( { auto const str = value.asString(); - std::uint64_t val; + std::uint64_t val = 0; bool const useBase10 = field.shouldMeta(SField::sMD_BaseTen); // if the field is amount, serialize as base 10 - auto [p, ec] = std::from_chars(str.data(), str.data() + str.size(), val, useBase10 ? 10 : 16); + auto [p, ec] = std::from_chars( + str.data(), str.data() + str.size(), val, useBase10 ? 10 : 16); if (ec != std::errc() || (p != str.data() + str.size())) Throw("invalid data"); @@ -440,11 +468,13 @@ parseLeaf( } else if (value.isInt()) { - ret = detail::make_stvar(field, to_unsigned(value.asInt())); + ret = detail::make_stvar( + field, to_unsigned(value.asInt())); } else if (value.isUInt()) { - ret = detail::make_stvar(field, safe_cast(value.asUInt())); + ret = detail::make_stvar( + field, safe_cast(value.asUInt())); } else { @@ -561,7 +591,8 @@ parseLeaf( { if (value.isString()) { - ret = detail::make_stvar(field, beast::lexicalCastThrow(value.asString())); + ret = detail::make_stvar( + field, beast::lexicalCastThrow(value.asString())); } else if (value.isInt()) { @@ -574,7 +605,8 @@ parseLeaf( else if (value.isUInt()) { auto const uintValue = value.asUInt(); - if (uintValue > static_cast(std::numeric_limits::max())) + if (uintValue > + static_cast(std::numeric_limits::max())) { error = out_of_range(json_name, fieldName); return ret; @@ -883,10 +915,20 @@ static int const maxDepth = 64; // Forward declaration since parseObject() and parseArray() call each other. static std::optional -parseArray(std::string const& json_name, Json::Value const& json, SField const& inName, int depth, Json::Value& error); +parseArray( + std::string const& json_name, + Json::Value const& json, + SField const& inName, + int depth, + Json::Value& error); static std::optional -parseObject(std::string const& json_name, Json::Value const& json, SField const& inName, int depth, Json::Value& error) +parseObject( + std::string const& json_name, + Json::Value const& json, + SField const& inName, + int depth, + Json::Value& error) { if (!json.isObjectOrNull()) { @@ -931,7 +973,8 @@ parseObject(std::string const& json_name, Json::Value const& json, SField const& try { - auto ret = parseObject(json_name + "." + fieldName, value, field, depth + 1, error); + auto ret = parseObject( + json_name + "." + fieldName, value, field, depth + 1, error); if (!ret) return std::nullopt; data.emplace_back(std::move(*ret)); @@ -948,7 +991,8 @@ parseObject(std::string const& json_name, Json::Value const& json, SField const& case STI_ARRAY: try { - auto array = parseArray(json_name + "." + fieldName, value, field, depth + 1, error); + auto array = + parseArray(json_name + "." + fieldName, value, field, depth + 1, error); if (!array.has_value()) return std::nullopt; data.emplace_back(std::move(*array)); @@ -993,7 +1037,12 @@ parseObject(std::string const& json_name, Json::Value const& json, SField const& } static std::optional -parseArray(std::string const& json_name, Json::Value const& json, SField const& inName, int depth, Json::Value& error) +parseArray( + std::string const& json_name, + Json::Value const& json, + SField const& inName, + int depth, + Json::Value& error) { if (!json.isArrayOrNull()) { diff --git a/src/libxrpl/protocol/STTakesAsset.cpp b/src/libxrpl/protocol/STTakesAsset.cpp index ba57f4f2a8..fcf4ad749c 100644 --- a/src/libxrpl/protocol/STTakesAsset.cpp +++ b/src/libxrpl/protocol/STTakesAsset.cpp @@ -23,9 +23,13 @@ associateAsset(SLE& sle, Asset const& asset) // SField auto& ta = entry.downcast(); auto const style = sle.getStyle(ta.getFName()); - XRPL_ASSERT_PARTS(style != soeINVALID, "xrpl::associateAsset", "valid template element style"); + XRPL_ASSERT_PARTS( + style != soeINVALID, "xrpl::associateAsset", "valid template element style"); - XRPL_ASSERT_PARTS(style != soeDEFAULT || !ta.isDefault(), "xrpl::associateAsset", "non-default value"); + XRPL_ASSERT_PARTS( + style != soeDEFAULT || !ta.isDefault(), + "xrpl::associateAsset", + "non-default value"); ta.associateAsset(asset); // associateAsset in derived classes may change the underlying diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 5dea8be56d..00a2acd788 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -61,7 +61,8 @@ getTxFormat(TxType type) if (format == nullptr) { Throw( - "Invalid transaction type " + std::to_string(safe_cast>(type))); + "Invalid transaction type " + + std::to_string(safe_cast>(type))); } return format; @@ -198,8 +199,10 @@ STTx::getSeqProxy() const std::optional const ticketSeq{operator[](~sfTicketSequence)}; if (!ticketSeq) + { // No TicketSequence specified. Return the Sequence, whatever it is. return SeqProxy::sequence(seq); + } return SeqProxy{SeqProxy::ticket, *ticketSeq}; } @@ -210,6 +213,20 @@ STTx::getSeqValue() const return getSeqProxy().value(); } +AccountID +STTx::getFeePayer() const +{ + // If sfDelegate is present, the delegate account is the payer + // note: if a delegate is specified, its authorization to act on behalf of the account is + // enforced in `Transactor::checkPermission` + // cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`) + if (isFieldPresent(sfDelegate)) + return getAccountID(sfDelegate); + + // Default payer + return getAccountID(sfAccount); +} + void STTx::sign( PublicKey const& publicKey, @@ -242,12 +259,13 @@ STTx::checkSign(Rules const& rules, STObject const& sigObject) const // multi-signing. Otherwise we're single-signing. Blob const& signingPubKey = sigObject.getFieldVL(sfSigningPubKey); - return signingPubKey.empty() ? checkMultiSign(rules, sigObject) : checkSingleSign(sigObject); + return signingPubKey.empty() ? checkMultiSign(rules, sigObject) + : checkSingleSign(sigObject); } - catch (std::exception const&) + catch (...) { + return Unexpected("Internal signature check failure."); } - return Unexpected("Internal signature check failure."); } Expected @@ -280,8 +298,8 @@ STTx::checkBatchSign(Rules const& rules) const for (auto const& signer : signers) { Blob const& signingPubKey = signer.getFieldVL(sfSigningPubKey); - auto const result = - signingPubKey.empty() ? checkBatchMultiSign(signer, rules) : checkBatchSingleSign(signer); + auto const result = signingPubKey.empty() ? checkBatchMultiSign(signer, rules) + : checkBatchSingleSign(signer); if (!result) return result; @@ -321,8 +339,8 @@ STTx::getJson(JsonOptions options, bool binary) const ret[jss::hash] = to_string(getTransactionID()); return ret; } - else - return Json::Value{dataBin}; + + return Json::Value{dataBin}; } Json::Value ret = STObject::getJson(JsonOptions::none); @@ -349,12 +367,16 @@ STTx::getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) con { Serializer s; add(s); - return getMetaSQL(s, inLedger, txnSqlValidated, escapedMetaData); + return getMetaSQL(s, inLedger, TxnSql::txnSqlValidated, escapedMetaData); } // VFALCO This could be a free function elsewhere std::string -STTx::getMetaSQL(Serializer rawTxn, std::uint32_t inLedger, char status, std::string const& escapedMetaData) const +STTx::getMetaSQL( + Serializer rawTxn, + std::uint32_t inLedger, + TxnSql status, + std::string const& escapedMetaData) const { static boost::format bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); std::string rTxn = sqlBlobLiteral(rawTxn.peekData()); @@ -363,8 +385,9 @@ STTx::getMetaSQL(Serializer rawTxn, std::uint32_t inLedger, char status, std::st XRPL_ASSERT(format, "xrpl::STTx::getMetaSQL : non-null type format"); return str( - boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() % toBase58(getAccountID(sfAccount)) % - getFieldU32(sfSequence) % inLedger % status % rTxn % escapedMetaData); + boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() % + toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % + safe_cast(status) % rTxn % escapedMetaData); } static Expected @@ -468,7 +491,8 @@ multiSignHelper( if (publicKeyType(makeSlice(spk))) { Blob const signature = signer.getFieldVL(sfTxnSignature); - validSig = verify(PublicKey(makeSlice(spk)), makeMsg(accountID).slice(), makeSlice(signature)); + validSig = verify( + PublicKey(makeSlice(spk)), makeMsg(accountID).slice(), makeSlice(signature)); } } catch (std::exception const& e) @@ -478,8 +502,11 @@ multiSignHelper( errorWhat = e.what(); } if (!validSig) + { return Unexpected( - std::string("Invalid signature on account ") + toBase58(accountID) + errorWhat.value_or("") + "."); + std::string("Invalid signature on account ") + toBase58(accountID) + + errorWhat.value_or("") + "."); + } } // All signatures verified. return {}; @@ -509,7 +536,8 @@ STTx::checkMultiSign(Rules const& rules, STObject const& sigObject) const { // Used inside the loop in multiSignHelper to enforce that // the account owner may not multisign for themselves. - auto const txnAccountID = &sigObject != this ? std::nullopt : std::optional(getAccountID(sfAccount)); + auto const txnAccountID = + &sigObject != this ? std::nullopt : std::optional(getAccountID(sfAccount)); // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer @@ -545,12 +573,14 @@ std::vector const& STTx::getBatchTransactionIDs() const { XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::getBatchTransactionIDs : not a batch transaction"); - XRPL_ASSERT(getFieldArray(sfRawTransactions).size() != 0, "STTx::getBatchTransactionIDs : empty raw transactions"); + XRPL_ASSERT( + getFieldArray(sfRawTransactions).size() != 0, + "STTx::getBatchTransactionIDs : empty raw transactions"); // The list of inner ids is built once, then reused on subsequent calls. // After the list is built, it must always have the same size as the array // `sfRawTransactions`. The assert below verifies that. - if (batchTxnIds_.size() == 0) + if (batchTxnIds_.empty()) { for (STObject const& rb : getFieldArray(sfRawTransactions)) batchTxnIds_.push_back(rb.getHash(HashPrefix::transactionID)); @@ -681,8 +711,10 @@ invalidMPTAmountInTx(STObject const& tx) if (tx.isFieldPresent(e.sField()) && e.supportMPT() != soeMPTNone) { if (auto const& field = tx.peekAtField(e.sField()); - (field.getSType() == STI_AMOUNT && static_cast(field).holds()) || - (field.getSType() == STI_ISSUE && static_cast(field).holds())) + (field.getSType() == STI_AMOUNT && + safe_downcast(field).holds()) || + (field.getSType() == STI_ISSUE && + safe_downcast(field).holds())) { if (e.supportMPT() != soeMPTSupported) return true; @@ -699,7 +731,8 @@ isRawTransactionOkay(STObject const& st, std::string& reason) if (!st.isFieldPresent(sfRawTransactions)) return true; - if (st.isFieldPresent(sfBatchSigners) && st.getFieldArray(sfBatchSigners).size() > maxBatchTxCount) + if (st.isFieldPresent(sfBatchSigners) && + st.getFieldArray(sfBatchSigners).size() > maxBatchTxCount) { reason = "Batch Signers array exceeds max entries."; return false; diff --git a/src/libxrpl/protocol/STValidation.cpp b/src/libxrpl/protocol/STValidation.cpp index e1baf79c77..7362a45d84 100644 --- a/src/libxrpl/protocol/STValidation.cpp +++ b/src/libxrpl/protocol/STValidation.cpp @@ -104,10 +104,14 @@ STValidation::isValid() const noexcept if (!valid_) { XRPL_ASSERT( - publicKeyType(getSignerPublic()) == KeyType::secp256k1, "xrpl::STValidation::isValid : valid key type"); + publicKeyType(getSignerPublic()) == KeyType::secp256k1, + "xrpl::STValidation::isValid : valid key type"); valid_ = verifyDigest( - getSignerPublic(), getSigningHash(), makeSlice(getFieldVL(sfSignature)), getFlags() & vfFullyCanonicalSig); + getSignerPublic(), + getSigningHash(), + makeSlice(getFieldVL(sfSignature)), + getFlags() & vfFullyCanonicalSig); } return valid_.value(); diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index d77aabc312..255514d70c 100644 --- a/src/libxrpl/protocol/STVar.cpp +++ b/src/libxrpl/protocol/STVar.cpp @@ -61,9 +61,13 @@ STVar::operator=(STVar const& rhs) { destroy(); if (rhs.p_) + { p_ = rhs.p_->copy(max_size, &d_); + } else + { p_ = nullptr; + } } return *this; @@ -116,9 +120,13 @@ void STVar::destroy() { if (on_heap()) + { delete p_; + } else + { p_->~STBase(); + } p_ = nullptr; } @@ -133,7 +141,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args) { construct(std::forward(args)...); } - else if constexpr (std::is_same_v...>, std::tuple>) + else if constexpr ( + std:: + is_same_v...>, std::tuple>) { construct(std::forward(args)..., depth); } diff --git a/src/libxrpl/protocol/STVector256.cpp b/src/libxrpl/protocol/STVector256.cpp index 312184dcc0..6357e2f2cb 100644 --- a/src/libxrpl/protocol/STVector256.cpp +++ b/src/libxrpl/protocol/STVector256.cpp @@ -19,7 +19,10 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name) auto const slice = sit.getSlice(sit.getVLDataLength()); if (slice.size() % uint256::size() != 0) - Throw("Bad serialization for STVector256: " + std::to_string(slice.size())); + { + Throw( + "Bad serialization for STVector256: " + std::to_string(slice.size())); + } auto const cnt = slice.size() / uint256::size(); diff --git a/src/libxrpl/protocol/STXChainBridge.cpp b/src/libxrpl/protocol/STXChainBridge.cpp index ed79c28f29..85d36f9b46 100644 --- a/src/libxrpl/protocol/STXChainBridge.cpp +++ b/src/libxrpl/protocol/STXChainBridge.cpp @@ -58,7 +58,8 @@ STXChainBridge::STXChainBridge(SField const& name, Json::Value const& v) : STBas { if (!v.isObject()) { - Throw("STXChainBridge can only be specified with a 'object' Json value"); + Throw( + "STXChainBridge can only be specified with a 'object' Json value"); } auto checkExtra = [](Json::Value const& v) { @@ -172,8 +173,8 @@ STXChainBridge::isEquivalent(STBase const& t) const bool STXChainBridge::isDefault() const { - return lockingChainDoor_.isDefault() && lockingChainIssue_.isDefault() && issuingChainDoor_.isDefault() && - issuingChainIssue_.isDefault(); + return lockingChainDoor_.isDefault() && lockingChainIssue_.isDefault() && + issuingChainDoor_.isDefault() && issuingChainIssue_.isDefault(); } std::unique_ptr diff --git a/src/libxrpl/protocol/SecretKey.cpp b/src/libxrpl/protocol/SecretKey.cpp index ee112b9072..aa398e9fe1 100644 --- a/src/libxrpl/protocol/SecretKey.cpp +++ b/src/libxrpl/protocol/SecretKey.cpp @@ -74,7 +74,7 @@ deriveDeterministicRootKey(Seed const& seed) // buf |----------------|----| // | seed | seq| - std::array buf; + std::array buf{}; std::copy(seed.begin(), seed.end(), buf.begin()); // The odds that this loop executes more than once are negligible @@ -119,7 +119,7 @@ class Generator { private: uint256 root_; - std::array generator_; + std::array generator_{}; uint256 calculateTweak(std::uint32_t seq) const @@ -133,7 +133,7 @@ private: // buf |---------------------------------|----|----| // | generator | seq| cnt| - std::array buf; + std::array buf{}; std::copy(generator_.begin(), generator_.end(), buf.begin()); copy_uint32(buf.data() + 33, seq); @@ -254,7 +254,8 @@ sign(PublicKey const& pk, SecretKey const& sk, Slice const& m) unsigned char sig[72]; size_t len = sizeof(sig); - if (secp256k1_ecdsa_signature_serialize_der(secp256k1Context(), sig, &len, &sig_imp) != 1) + if (secp256k1_ecdsa_signature_serialize_der(secp256k1Context(), sig, &len, &sig_imp) != + 1) LogicError("sign: secp256k1_ecdsa_signature_serialize_der failed"); return Buffer{sig, len}; @@ -304,13 +305,15 @@ derivePublicKey(KeyType type, SecretKey const& sk) case KeyType::secp256k1: { secp256k1_pubkey pubkey_imp; if (secp256k1_ec_pubkey_create( - secp256k1Context(), &pubkey_imp, reinterpret_cast(sk.data())) != 1) + secp256k1Context(), + &pubkey_imp, + reinterpret_cast(sk.data())) != 1) LogicError("derivePublicKey: secp256k1_ec_pubkey_create failed"); unsigned char pubkey[33]; std::size_t len = sizeof(pubkey); - if (secp256k1_ec_pubkey_serialize(secp256k1Context(), pubkey, &len, &pubkey_imp, SECP256K1_EC_COMPRESSED) != - 1) + if (secp256k1_ec_pubkey_serialize( + secp256k1Context(), pubkey, &len, &pubkey_imp, SECP256K1_EC_COMPRESSED) != 1) LogicError("derivePublicKey: secp256k1_ec_pubkey_serialize failed"); return PublicKey{Slice{pubkey, len}}; diff --git a/src/libxrpl/protocol/Seed.cpp b/src/libxrpl/protocol/Seed.cpp index 2629d08439..8aa1d8b67d 100644 --- a/src/libxrpl/protocol/Seed.cpp +++ b/src/libxrpl/protocol/Seed.cpp @@ -46,7 +46,7 @@ Seed::Seed(uint128 const& seed) Seed randomSeed() { - std::array buffer; + std::array buffer{}; beast::rngfill(buffer.data(), buffer.size(), crypto_prng()); Seed seed(makeSlice(buffer)); secure_erase(buffer.data(), buffer.size()); @@ -81,7 +81,8 @@ parseGenericSeed(std::string const& str, bool rfc1751) return std::nullopt; if (parseBase58(str) || parseBase58(TokenType::NodePublic, str) || - parseBase58(TokenType::AccountPublic, str) || parseBase58(TokenType::NodePrivate, str) || + parseBase58(TokenType::AccountPublic, str) || + parseBase58(TokenType::NodePrivate, str) || parseBase58(TokenType::AccountSecret, str)) { return std::nullopt; diff --git a/src/libxrpl/protocol/Serializer.cpp b/src/libxrpl/protocol/Serializer.cpp index bb3eb17ae3..08d221f75b 100644 --- a/src/libxrpl/protocol/Serializer.cpp +++ b/src/libxrpl/protocol/Serializer.cpp @@ -107,12 +107,15 @@ Serializer::addFieldID(int type, int name) { int ret = mData.size(); XRPL_ASSERT( - (type > 0) && (type < 256) && (name > 0) && (name < 256), "xrpl::Serializer::addFieldID : inputs inside range"); + (type > 0) && (type < 256) && (name > 0) && (name < 256), + "xrpl::Serializer::addFieldID : inputs inside range"); if (type < 16) { - if (name < 16) // common type, common name + if (name < 16) + { // common type, common name mData.push_back(static_cast((type << 4) | name)); + } else { // common type, uncommon name @@ -186,7 +189,7 @@ int Serializer::addVL(Slice const& slice) { int ret = addEncoded(slice.size()); - if (slice.size()) + if (!slice.empty()) addRaw(slice.data(), slice.size()); return ret; } @@ -205,7 +208,7 @@ Serializer::addVL(void const* ptr, int len) int Serializer::addEncoded(int length) { - std::array bytes; + std::array bytes{}; int numBytes = 0; if (length <= 192) @@ -229,7 +232,9 @@ Serializer::addEncoded(int length) numBytes = 3; } else + { Throw("lenlen"); + } return addRaw(&bytes[0], numBytes); } @@ -366,7 +371,8 @@ SerialIter::get32() p_ += 4; used_ += 4; remain_ -= 4; - return (std::uint64_t(t[0]) << 24) + (std::uint64_t(t[1]) << 16) + (std::uint64_t(t[2]) << 8) + std::uint64_t(t[3]); + return (std::uint64_t(t[0]) << 24) + (std::uint64_t(t[1]) << 16) + (std::uint64_t(t[2]) << 8) + + std::uint64_t(t[3]); } std::uint64_t @@ -464,7 +470,7 @@ int SerialIter::getVLDataLength() { int b1 = get8(); - int datLen; + int datLen = 0; int lenLen = Serializer::decodeLengthLength(b1); if (lenLen == 1) { diff --git a/src/libxrpl/protocol/Sign.cpp b/src/libxrpl/protocol/Sign.cpp index fe18941940..9105be8294 100644 --- a/src/libxrpl/protocol/Sign.cpp +++ b/src/libxrpl/protocol/Sign.cpp @@ -12,7 +12,12 @@ namespace xrpl { void -sign(STObject& st, HashPrefix const& prefix, KeyType type, SecretKey const& sk, SF_VL const& sigField) +sign( + STObject& st, + HashPrefix const& prefix, + KeyType type, + SecretKey const& sk, + SF_VL const& sigField) { Serializer ss; ss.add32(prefix); diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 1e33ee5490..09c8acd4e3 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -268,9 +268,10 @@ transCode(std::string const& token) static auto const results = [] { auto& byTer = transResults(); auto range = boost::make_iterator_range(byTer.begin(), byTer.end()); - auto tRange = - boost::adaptors::transform(range, [](auto const& r) { return std::make_pair(r.second.first, r.first); }); - std::unordered_map const byToken(tRange.begin(), tRange.end()); + auto tRange = boost::adaptors::transform( + range, [](auto const& r) { return std::make_pair(r.second.first, r.first); }); + std::unordered_map const byToken( + tRange.begin(), tRange.end()); return byToken; }(); diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 00a560de1b..4492ae271b 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -3,14 +3,14 @@ #include #include -#include +#include namespace xrpl { -TxFormats::TxFormats() +std::vector const& +TxFormats::getCommonFields() { - // Fields shared by all txFormats: - static std::initializer_list const commonFields{ + static auto const commonFields = std::vector{ {sfTransactionType, soeREQUIRED}, {sfFlags, soeOPTIONAL}, {sfSourceTag, soeOPTIONAL}, @@ -29,7 +29,11 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, }; + return commonFields; +} +TxFormats::TxFormats() +{ #pragma push_macro("UNWRAP") #undef UNWRAP #pragma push_macro("TRANSACTION") @@ -37,7 +41,7 @@ TxFormats::TxFormats() #define UNWRAP(...) __VA_ARGS__ #define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \ - add(jss::name, tag, UNWRAP fields, commonFields); + add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/protocol/TxMeta.cpp b/src/libxrpl/protocol/TxMeta.cpp index 8f23e9dc2c..ed9f98f8f3 100644 --- a/src/libxrpl/protocol/TxMeta.cpp +++ b/src/libxrpl/protocol/TxMeta.cpp @@ -89,7 +89,8 @@ TxMeta::getAffectedAccounts() const // Meta#getAffectedAccounts for (auto const& node : nodes_) { - int index = node.getFieldIndex((node.getFName() == sfCreatedNode) ? sfNewFields : sfFinalFields); + int index = + node.getFieldIndex((node.getFName() == sfCreatedNode) ? sfNewFields : sfFinalFields); if (index != -1) { @@ -154,7 +155,8 @@ TxMeta::getAffectedNode(SLE::ref node, SField const& type) nodes_.push_back(STObject(type)); STObject& obj = nodes_.back(); - XRPL_ASSERT(obj.getFName() == type, "xrpl::TxMeta::getAffectedNode(SLE::ref) : field type match"); + XRPL_ASSERT( + obj.getFName() == type, "xrpl::TxMeta::getAffectedNode(SLE::ref) : field type match"); obj.setFieldH256(sfLedgerIndex, index); obj.setFieldU16(sfLedgerEntryType, node->getFieldU16(sfLedgerEntryType)); @@ -204,7 +206,9 @@ TxMeta::addRaw(Serializer& s, TER result, std::uint32_t index) { result_ = TERtoInt(result); index_ = index; - XRPL_ASSERT((result_ == 0) || ((result_ > 100) && (result_ <= 255)), "xrpl::TxMeta::addRaw : valid TER input"); + XRPL_ASSERT( + (result_ == 0) || ((result_ > 100) && (result_ <= 255)), + "xrpl::TxMeta::addRaw : valid TER input"); nodes_.sort([](STObject const& o1, STObject const& o2) { return o1.getFieldH256(sfLedgerIndex) < o2.getFieldH256(sfLedgerIndex); diff --git a/src/libxrpl/protocol/UintTypes.cpp b/src/libxrpl/protocol/UintTypes.cpp index ca29147989..df9eadb511 100644 --- a/src/libxrpl/protocol/UintTypes.cpp +++ b/src/libxrpl/protocol/UintTypes.cpp @@ -45,11 +45,13 @@ to_string(Currency const& currency) if ((currency & sIsoBits).isZero()) { std::string const iso( - currency.data() + detail::isoCodeOffset, currency.data() + detail::isoCodeOffset + detail::isoCodeLength); + currency.data() + detail::isoCodeOffset, + currency.data() + detail::isoCodeOffset + detail::isoCodeLength); // Specifying the system currency code using ISO-style representation // is not allowed. - if ((iso != systemCurrencyCode()) && (iso.find_first_not_of(detail::isoCharSet) == std::string::npos)) + if ((iso != systemCurrencyCode()) && + (iso.find_first_not_of(detail::isoCharSet) == std::string::npos)) { return iso; } diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index 5f109ea272..314bf9f6d3 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -227,13 +227,15 @@ AttestationClaim::validAmounts() const bool AttestationClaim::sameEvent(AttestationClaim const& rhs) const { - return AttestationClaim::sameEventHelper(*this, rhs) && tie(claimID, dst) == tie(rhs.claimID, rhs.dst); + return AttestationClaim::sameEventHelper(*this, rhs) && + tie(claimID, dst) == tie(rhs.claimID, rhs.dst); } bool operator==(AttestationClaim const& lhs, AttestationClaim const& rhs) { - return AttestationClaim::equalHelper(lhs, rhs) && tie(lhs.claimID, lhs.dst) == tie(rhs.claimID, rhs.dst); + return AttestationClaim::equalHelper(lhs, rhs) && + tie(lhs.claimID, lhs.dst) == tie(rhs.claimID, rhs.dst); } AttestationCreateAccount::AttestationCreateAccount(STObject const& o) @@ -350,7 +352,14 @@ std::vector AttestationCreateAccount::message(STXChainBridge const& bridge) const { return AttestationCreateAccount::message( - bridge, sendingAccount, sendingAmount, rewardAmount, rewardAccount, wasLockingChainSend, createCount, toCreate); + bridge, + sendingAccount, + sendingAmount, + rewardAmount, + rewardAccount, + wasLockingChainSend, + createCount, + toCreate); } bool @@ -363,7 +372,8 @@ bool AttestationCreateAccount::sameEvent(AttestationCreateAccount const& rhs) const { return AttestationCreateAccount::sameEventHelper(*this, rhs) && - std::tie(createCount, toCreate, rewardAmount) == std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount); + std::tie(createCount, toCreate, rewardAmount) == + std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount); } bool @@ -434,7 +444,8 @@ XChainClaimAttestation::XChainClaimAttestation(Json::Value const& v) dst = Json::getOrThrow(v, sfDestination); }; -XChainClaimAttestation::XChainClaimAttestation(XChainClaimAttestation::TSignedAttestation const& claimAtt) +XChainClaimAttestation::XChainClaimAttestation( + XChainClaimAttestation::TSignedAttestation const& claimAtt) : XChainClaimAttestation{ claimAtt.attestationSignerAccount, claimAtt.publicKey, @@ -462,11 +473,24 @@ XChainClaimAttestation::toSTObject() const bool operator==(XChainClaimAttestation const& lhs, XChainClaimAttestation const& rhs) { - return std::tie(lhs.keyAccount, lhs.publicKey, lhs.amount, lhs.rewardAccount, lhs.wasLockingChainSend, lhs.dst) == - std::tie(rhs.keyAccount, rhs.publicKey, rhs.amount, rhs.rewardAccount, rhs.wasLockingChainSend, rhs.dst); + return std::tie( + lhs.keyAccount, + lhs.publicKey, + lhs.amount, + lhs.rewardAccount, + lhs.wasLockingChainSend, + lhs.dst) == + std::tie( + rhs.keyAccount, + rhs.publicKey, + rhs.amount, + rhs.rewardAccount, + rhs.wasLockingChainSend, + rhs.dst); } -XChainClaimAttestation::MatchFields::MatchFields(XChainClaimAttestation::TSignedAttestation const& att) +XChainClaimAttestation::MatchFields::MatchFields( + XChainClaimAttestation::TSignedAttestation const& att) : amount{att.sendingAmount}, wasLockingChainSend{att.wasLockingChainSend}, dst{att.dst} { } @@ -552,7 +576,8 @@ XChainCreateAccountAttestation::toSTObject() const return o; } -XChainCreateAccountAttestation::MatchFields::MatchFields(XChainCreateAccountAttestation::TSignedAttestation const& att) +XChainCreateAccountAttestation::MatchFields::MatchFields( + XChainCreateAccountAttestation::TSignedAttestation const& att) : amount{att.sendingAmount} , rewardAmount(att.rewardAmount) , wasLockingChainSend{att.wasLockingChainSend} @@ -595,7 +620,8 @@ operator==(XChainCreateAccountAttestation const& lhs, XChainCreateAccountAttesta //------------------------------------------------------------------------------ // template -XChainAttestationsBase::XChainAttestationsBase(XChainAttestationsBase::AttCollection&& atts) +XChainAttestationsBase::XChainAttestationsBase( + XChainAttestationsBase::AttCollection&& atts) : attestations_{std::move(atts)} { } diff --git a/src/libxrpl/protocol/digest.cpp b/src/libxrpl/protocol/digest.cpp index aa5600dde0..0e03882c3c 100644 --- a/src/libxrpl/protocol/digest.cpp +++ b/src/libxrpl/protocol/digest.cpp @@ -21,7 +21,8 @@ openssl_ripemd160_hasher::operator()(void const* data, std::size_t size) noexcep RIPEMD160_Update(ctx, data, size); } -openssl_ripemd160_hasher::operator result_type() noexcept +openssl_ripemd160_hasher:: +operator result_type() noexcept { auto const ctx = reinterpret_cast(ctx_); result_type digest; @@ -45,7 +46,8 @@ openssl_sha512_hasher::operator()(void const* data, std::size_t size) noexcept SHA512_Update(ctx, data, size); } -openssl_sha512_hasher::operator result_type() noexcept +openssl_sha512_hasher:: +operator result_type() noexcept { auto const ctx = reinterpret_cast(ctx_); result_type digest; @@ -69,7 +71,8 @@ openssl_sha256_hasher::operator()(void const* data, std::size_t size) noexcept SHA256_Update(ctx, data, size); } -openssl_sha256_hasher::operator result_type() noexcept +openssl_sha256_hasher:: +operator result_type() noexcept { auto const ctx = reinterpret_cast(ctx_); result_type digest; diff --git a/src/libxrpl/protocol/tokens.cpp b/src/libxrpl/protocol/tokens.cpp index 1b73b06380..b46591725f 100644 --- a/src/libxrpl/protocol/tokens.cpp +++ b/src/libxrpl/protocol/tokens.cpp @@ -121,7 +121,8 @@ coefficients sizes greatly speeds up the multi-precision computations. namespace xrpl { -static constexpr char const* alphabetForward = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; +static constexpr char const* alphabetForward = + "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; static constexpr std::array const alphabetReverse = []() { std::array map{}; @@ -328,7 +329,7 @@ decodeBase58Token(std::string const& s, TokenType type) return {}; // And the checksum must as well. - std::array guard; + std::array guard{}; checksum(guard.data(), ret.data(), ret.size() - guard.size()); if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin())) return {}; @@ -412,8 +413,8 @@ b256_to_b58_be(std::span input, std::span out) // compute the base 58^10 coeffs while (cur_2_64_end > 0) { - base_58_10_coeff[num_58_10_coeffs] = - xrpl::b58_fast::detail::inplace_bigint_div_rem(base_2_64_coeff.subspan(0, cur_2_64_end), B_58_10); + base_58_10_coeff[num_58_10_coeffs] = xrpl::b58_fast::detail::inplace_bigint_div_rem( + base_2_64_coeff.subspan(0, cur_2_64_end), B_58_10); num_58_10_coeffs += 1; if (base_2_64_coeff[cur_2_64_end - 1] == 0) { @@ -441,7 +442,8 @@ b256_to_b58_be(std::span input, std::span out) { return Unexpected(TokenCodecErrc::inputTooLarge); } - std::array const b58_be = xrpl::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]); + std::array const b58_be = + xrpl::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]); std::size_t to_skip = 0; std::span b58_be_s{b58_be.data(), b58_be.size()}; if (skip_zeros) @@ -502,7 +504,9 @@ b58_to_b256_be(std::string_view input, std::span out) auto [num_full_coeffs, partial_coeff_len] = xrpl::b58_fast::detail::div_rem(input.size(), 10); auto const num_partial_coeffs = partial_coeff_len ? 1 : 0; auto const num_b_58_10_coeffs = num_full_coeffs + num_partial_coeffs; - XRPL_ASSERT(num_b_58_10_coeffs <= b_58_10_coeff.size(), "xrpl::b58_fast::detail::b58_to_b256_be : maximum coeff"); + XRPL_ASSERT( + num_b_58_10_coeffs <= b_58_10_coeff.size(), + "xrpl::b58_fast::detail::b58_to_b256_be : maximum coeff"); for (unsigned char c : input.substr(0, partial_coeff_len)) { auto cur_val = ::xrpl::alphabetReverse[c]; @@ -539,14 +543,16 @@ b58_to_b256_be(std::string_view input, std::span out) std::uint64_t const c = b_58_10_coeff[i]; { - auto code = xrpl::b58_fast::detail::inplace_bigint_mul(std::span(&result[0], cur_result_size + 1), B_58_10); + auto code = xrpl::b58_fast::detail::inplace_bigint_mul( + std::span(&result[0], cur_result_size + 1), B_58_10); if (code != TokenCodecErrc::success) { return Unexpected(code); } } { - auto code = xrpl::b58_fast::detail::inplace_bigint_add(std::span(&result[0], cur_result_size + 1), c); + auto code = xrpl::b58_fast::detail::inplace_bigint_add( + std::span(&result[0], cur_result_size + 1), c); if (code != TokenCodecErrc::success) { return Unexpected(code); @@ -598,15 +604,18 @@ b58_to_b256_be(std::string_view input, std::span out) } // namespace detail B58Result> -encodeBase58Token(TokenType token_type, std::span input, std::span out) +encodeBase58Token( + TokenType token_type, + std::span input, + std::span out) { constexpr std::size_t tmpBufSize = 128; - std::array buf; + std::array buf{}; if (input.size() > tmpBufSize - 5) { return Unexpected(TokenCodecErrc::inputTooLarge); } - if (input.size() == 0) + if (input.empty()) { return Unexpected(TokenCodecErrc::inputTooSmall); } @@ -628,7 +637,7 @@ encodeBase58Token(TokenType token_type, std::span input, std B58Result> decodeBase58Token(TokenType type, std::string_view s, std::span outBuf) { - std::array tmpBuf; + std::array tmpBuf{}; auto const decodeResult = detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size())); if (!decodeResult) @@ -645,7 +654,7 @@ decodeBase58Token(TokenType type, std::string_view s, std::span ou return Unexpected(TokenCodecErrc::mismatchedTokenType); // And the checksum must as well. - std::array guard; + std::array guard{}; checksum(guard.data(), ret.data(), ret.size() - guard.size()); if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin())) { diff --git a/src/libxrpl/protocol_autogen/placeholder.cpp b/src/libxrpl/protocol_autogen/placeholder.cpp new file mode 100644 index 0000000000..b48581e5b8 --- /dev/null +++ b/src/libxrpl/protocol_autogen/placeholder.cpp @@ -0,0 +1,5 @@ +// This file is a placeholder to ensure the protocol_autogen module can be built. +#include +#include +#include +#include diff --git a/src/libxrpl/rdb/DatabaseCon.cpp b/src/libxrpl/rdb/DatabaseCon.cpp new file mode 100644 index 0000000000..344df85b4a --- /dev/null +++ b/src/libxrpl/rdb/DatabaseCon.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace xrpl { + +class CheckpointersCollection +{ + std::uintptr_t nextId_{0}; + // Mutex protects the CheckpointersCollection + std::mutex mutex_; + // Each checkpointer is given a unique id. All the checkpointers that are + // part of a DatabaseCon are part of this collection. When the DatabaseCon + // is destroyed, its checkpointer is removed from the collection + std::unordered_map> checkpointers_; + +public: + std::shared_ptr + fromId(std::uintptr_t id) + { + std::lock_guard l{mutex_}; + auto it = checkpointers_.find(id); + if (it != checkpointers_.end()) + return it->second; + return {}; + } + + void + erase(std::uintptr_t id) + { + std::lock_guard lock{mutex_}; + checkpointers_.erase(id); + } + + std::shared_ptr + create(std::shared_ptr const& session, JobQueue& jobQueue, Logs& logs) + { + std::lock_guard lock{mutex_}; + auto const id = nextId_++; + auto const r = makeCheckpointer(id, session, jobQueue, logs); + checkpointers_[id] = r; + return r; + } +}; + +CheckpointersCollection checkpointers; + +std::shared_ptr +checkpointerFromId(std::uintptr_t id) +{ + return checkpointers.fromId(id); +} + +DatabaseCon::~DatabaseCon() +{ + if (checkpointer_) + { + checkpointers.erase(checkpointer_->id()); + + std::weak_ptr wk(checkpointer_); + checkpointer_.reset(); + + // The references to our Checkpointer held by 'checkpointer_' and + // 'checkpointers' have been removed, so if the use count is nonzero, a + // checkpoint is currently in progress. Wait for it to end, otherwise + // creating a new DatabaseCon to the same database may fail due to the + // database being locked by our (now old) Checkpointer. + while (wk.use_count()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } +} + +std::unique_ptr const> DatabaseCon::Setup::globalPragma; + +void +DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l) +{ + if (!q) + Throw("No JobQueue"); + checkpointer_ = checkpointers.create(session_, *q, l); +} + +} // namespace xrpl diff --git a/src/xrpld/core/detail/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp similarity index 92% rename from src/xrpld/core/detail/SociDB.cpp rename to src/libxrpl/rdb/SociDB.cpp index ff2fa1d9c1..baadeef00a 100644 --- a/src/xrpld/core/detail/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -3,12 +3,10 @@ #pragma clang diagnostic ignored "-Wdeprecated" #endif -#include -#include -#include - #include #include +#include +#include #include @@ -27,7 +25,8 @@ getSociSqliteInit(std::string const& name, std::string const& dir, std::string c { if (name.empty()) { - Throw("Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir); + Throw( + "Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir); } boost::filesystem::path file(dir); if (is_directory(file)) @@ -55,7 +54,8 @@ DBConfig::DBConfig(std::string const& dbPath) : connectionString_(dbPath) { } -DBConfig::DBConfig(BasicConfig const& config, std::string const& dbName) : DBConfig(detail::getSociInit(config, dbName)) +DBConfig::DBConfig(BasicConfig const& config, std::string const& dbName) + : DBConfig(detail::getSociInit(config, dbName)) { } @@ -81,9 +81,13 @@ void open(soci::session& s, std::string const& beName, std::string const& connectionString) { if (beName == "sqlite") + { s.open(soci::sqlite3, connectionString); + } else + { Throw("Unsupported soci backend: " + beName); + } } static sqlite_api::sqlite3* @@ -143,18 +147,26 @@ void convert(std::vector const& from, soci::blob& to) { if (!from.empty()) + { to.write(0, reinterpret_cast(&from[0]), from.size()); + } else + { to.trim(0); + } } void convert(std::string const& from, soci::blob& to) { if (!from.empty()) + { to.write(0, from.data(), from.size()); + } else + { to.trim(0); + } } namespace { @@ -171,7 +183,11 @@ namespace { class WALCheckpointer : public Checkpointer { public: - WALCheckpointer(std::uintptr_t id, std::weak_ptr session, JobQueue& q, Logs& logs) + WALCheckpointer( + std::uintptr_t id, + std::weak_ptr session, + JobQueue& q, + Logs& logs) : id_(id), session_(std::move(session)), jobQueue_(q), j_(logs.journal("WALCheckpointer")) { if (auto [conn, keepAlive] = getConnection(); conn) @@ -287,7 +303,11 @@ protected: } // namespace std::shared_ptr -makeCheckpointer(std::uintptr_t id, std::weak_ptr session, JobQueue& queue, Logs& logs) +makeCheckpointer( + std::uintptr_t id, + std::weak_ptr session, + JobQueue& queue, + Logs& logs) { return std::make_shared(id, std::move(session), queue, logs); } diff --git a/src/libxrpl/resource/Consumer.cpp b/src/libxrpl/resource/Consumer.cpp index 11c23b1044..e9435bd340 100644 --- a/src/libxrpl/resource/Consumer.cpp +++ b/src/libxrpl/resource/Consumer.cpp @@ -39,6 +39,9 @@ Consumer::~Consumer() Consumer& Consumer::operator=(Consumer const& other) { + if (this == &other) + return *this; + // remove old ref if (m_logic && m_entry) m_logic->release(*m_entry); diff --git a/src/libxrpl/resource/ResourceManager.cpp b/src/libxrpl/resource/ResourceManager.cpp index 787ceff768..1e7aadfa8d 100644 --- a/src/libxrpl/resource/ResourceManager.cpp +++ b/src/libxrpl/resource/ResourceManager.cpp @@ -64,7 +64,10 @@ public: } Consumer - newInboundEndpoint(beast::IP::Endpoint const& address, bool const proxy, std::string_view forwardedFor) override + newInboundEndpoint( + beast::IP::Endpoint const& address, + bool const proxy, + std::string_view forwardedFor) override { if (!proxy) return newInboundEndpoint(address); @@ -73,7 +76,8 @@ public: auto const proxiedIp = boost::asio::ip::make_address(forwardedFor, ec); if (ec) { - journal_.warn() << "forwarded for (" << forwardedFor << ") from proxy " << address.to_string() + journal_.warn() << "forwarded for (" << forwardedFor << ") from proxy " + << address.to_string() << " doesn't convert to IP endpoint: " << ec.message(); return newInboundEndpoint(address); } diff --git a/src/xrpld/rpc/detail/InfoSub.cpp b/src/libxrpl/server/InfoSub.cpp similarity index 92% rename from src/xrpld/rpc/detail/InfoSub.cpp rename to src/libxrpl/server/InfoSub.cpp index 27e3a65b2f..5857e4d6ae 100644 --- a/src/xrpld/rpc/detail/InfoSub.cpp +++ b/src/libxrpl/server/InfoSub.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { @@ -17,7 +17,8 @@ InfoSub::InfoSub(Source& source) : m_source(source), mSeq(assign_id()) { } -InfoSub::InfoSub(Source& source, Consumer consumer) : m_consumer(consumer), m_source(source), mSeq(assign_id()) +InfoSub::InfoSub(Source& source, Consumer consumer) + : m_consumer(consumer), m_source(source), mSeq(assign_id()) { } @@ -51,7 +52,7 @@ InfoSub::getConsumer() } std::uint64_t -InfoSub::getSeq() +InfoSub::getSeq() const { return mSeq; } @@ -67,9 +68,13 @@ InfoSub::insertSubAccountInfo(AccountID const& account, bool rt) std::lock_guard sl(mLock); if (rt) + { realTimeSubscriptions_.insert(account); + } else + { normalSubscriptions_.insert(account); + } } void @@ -78,9 +83,13 @@ InfoSub::deleteSubAccountInfo(AccountID const& account, bool rt) std::lock_guard sl(mLock); if (rt) + { realTimeSubscriptions_.erase(account); + } else + { normalSubscriptions_.erase(account); + } } bool diff --git a/src/libxrpl/server/JSONRPCUtil.cpp b/src/libxrpl/server/JSONRPCUtil.cpp index 634bb07850..db2ea450a2 100644 --- a/src/libxrpl/server/JSONRPCUtil.cpp +++ b/src/libxrpl/server/JSONRPCUtil.cpp @@ -17,11 +17,9 @@ getHTTPHeaderTimestamp() // sense. There's no point in doing all this work if this function // gets called multiple times a second. char buffer[96]; - time_t now; + time_t now = 0; time(&now); - struct tm now_gmt - { - }; + struct tm now_gmt{}; #ifndef _MSC_VER gmtime_r(&now, &now_gmt); #else @@ -70,6 +68,7 @@ HTTPReply(int nStatus, std::string const& content, Json::Output const& output, b return; } + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (nStatus) { case 200: diff --git a/src/xrpld/app/misc/detail/LoadFeeTrack.cpp b/src/libxrpl/server/LoadFeeTrack.cpp similarity index 87% rename from src/xrpld/app/misc/detail/LoadFeeTrack.cpp rename to src/libxrpl/server/LoadFeeTrack.cpp index 91a40c6a9d..e206b50863 100644 --- a/src/xrpld/app/misc/detail/LoadFeeTrack.cpp +++ b/src/libxrpl/server/LoadFeeTrack.cpp @@ -1,10 +1,10 @@ -#include - #include #include #include #include +#include +#include #include namespace xrpl { @@ -20,14 +20,12 @@ LoadFeeTrack::raiseLocalFee() std::uint32_t const origFee = localTxnLoadFee_; // make sure this fee takes effect - if (localTxnLoadFee_ < remoteTxnLoadFee_) - localTxnLoadFee_ = remoteTxnLoadFee_; + localTxnLoadFee_ = std::max(localTxnLoadFee_, remoteTxnLoadFee_); // Increase slowly localTxnLoadFee_ += (localTxnLoadFee_ / lftFeeIncFraction); - if (localTxnLoadFee_ > lftFeeMax) - localTxnLoadFee_ = lftFeeMax; + localTxnLoadFee_ = std::min(localTxnLoadFee_, lftFeeMax); if (origFee == localTxnLoadFee_) return false; @@ -46,8 +44,7 @@ LoadFeeTrack::lowerLocalFee() // Reduce slowly localTxnLoadFee_ -= (localTxnLoadFee_ / lftFeeDecFraction); - if (localTxnLoadFee_ < lftNormalFee) - localTxnLoadFee_ = lftNormalFee; + localTxnLoadFee_ = std::max(localTxnLoadFee_, lftNormalFee); if (origFee == localTxnLoadFee_) return false; diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 389714a40f..2d0b4ec257 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -44,7 +44,7 @@ operator<<(std::ostream& os, Port const& p) { os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", "; - if (p.admin_nets_v4.size() || p.admin_nets_v6.size()) + if (!p.admin_nets_v4.empty() || !p.admin_nets_v6.empty()) { os << "admin nets:"; for (auto const& net : p.admin_nets_v4) @@ -59,7 +59,7 @@ operator<<(std::ostream& os, Port const& p) } } - if (p.secure_gateway_nets_v4.size() || p.secure_gateway_nets_v6.size()) + if (!p.secure_gateway_nets_v4.empty() || !p.secure_gateway_nets_v6.empty()) { os << "secure_gateway nets:"; for (auto const& net : p.secure_gateway_nets_v4) @@ -98,7 +98,7 @@ populate( while (std::getline(ss, ip, ',')) { boost::algorithm::trim(ip); - bool v4; + bool v4 = false; boost::asio::ip::network_v4 v4Net; boost::asio::ip::network_v6 v6Net; @@ -163,7 +163,8 @@ populate( if (v4Net != v4Net.canonical()) { log << "The configured subnet " << v4Net.to_string() - << " is not the same as the network address, which is " << v4Net.canonical().to_string(); + << " is not the same as the network address, which is " + << v4Net.canonical().to_string(); Throw(); } nets4.push_back(v4Net); @@ -173,7 +174,8 @@ populate( if (v6Net != v6Net.canonical()) { log << "The configured subnet " << v6Net.to_string() - << " is not the same as the network address, which is " << v6Net.canonical().to_string(); + << " is not the same as the network address, which is " + << v6Net.canonical().to_string(); Throw(); } nets6.push_back(v6Net); @@ -181,7 +183,8 @@ populate( } catch (boost::system::system_error const& e) { - log << "Invalid value '" << ip << "' for key '" << field << "' in [" << section.name() << "]: " << e.what(); + log << "Invalid value '" << ip << "' for key '" << field << "' in [" << section.name() + << "]: " << e.what(); Throw(); } } @@ -201,7 +204,8 @@ parse_Port(ParsedPort& port, Section const& section, std::ostream& log) } catch (std::exception const&) { - log << "Invalid value '" << *optResult << "' for key 'ip' in [" << section.name() << "]"; + log << "Invalid value '" << *optResult << "' for key 'ip' in [" << section.name() + << "]"; Rethrow(); } } @@ -282,7 +286,8 @@ parse_Port(ParsedPort& port, Section const& section, std::ostream& log) } populate(section, "admin", log, port.admin_nets_v4, port.admin_nets_v6); - populate(section, "secure_gateway", log, port.secure_gateway_nets_v4, port.secure_gateway_nets_v6); + populate( + section, "secure_gateway", log, port.secure_gateway_nets_v4, port.secure_gateway_nets_v6); set(port.user, "user", section); set(port.password, "password", section); @@ -296,8 +301,10 @@ parse_Port(ParsedPort& port, Section const& section, std::ostream& log) port.pmd_options.server_enable = section.value_or("permessage_deflate", true); port.pmd_options.client_max_window_bits = section.value_or("client_max_window_bits", 15); port.pmd_options.server_max_window_bits = section.value_or("server_max_window_bits", 15); - port.pmd_options.client_no_context_takeover = section.value_or("client_no_context_takeover", false); - port.pmd_options.server_no_context_takeover = section.value_or("server_no_context_takeover", false); + port.pmd_options.client_no_context_takeover = + section.value_or("client_no_context_takeover", false); + port.pmd_options.server_no_context_takeover = + section.value_or("server_no_context_takeover", false); port.pmd_options.compLevel = section.value_or("compress_level", 8); port.pmd_options.memLevel = section.value_or("memory_level", 4); } diff --git a/src/xrpld/app/rdb/detail/State.cpp b/src/libxrpl/server/State.cpp similarity index 96% rename from src/xrpld/app/rdb/detail/State.cpp rename to src/libxrpl/server/State.cpp index ad8944e54d..371f2b4d0a 100644 --- a/src/xrpld/app/rdb/detail/State.cpp +++ b/src/libxrpl/server/State.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { @@ -54,7 +54,7 @@ initStateDB(soci::session& session, BasicConfig const& config, std::string const LedgerIndex getCanDelete(soci::session& session) { - LedgerIndex seq; + LedgerIndex seq = 0; session << "SELECT CanDeleteSeq FROM CanDelete WHERE Key = 1;", soci::into(seq); ; return seq; @@ -63,7 +63,8 @@ getCanDelete(soci::session& session) LedgerIndex setCanDelete(soci::session& session, LedgerIndex canDelete) { - session << "UPDATE CanDelete SET CanDeleteSeq = :canDelete WHERE Key = 1;", soci::use(canDelete); + session << "UPDATE CanDelete SET CanDeleteSeq = :canDelete WHERE Key = 1;", + soci::use(canDelete); return canDelete; } diff --git a/src/xrpld/app/rdb/detail/Vacuum.cpp b/src/libxrpl/server/Vacuum.cpp similarity index 94% rename from src/xrpld/app/rdb/detail/Vacuum.cpp rename to src/libxrpl/server/Vacuum.cpp index 5aaa04f040..89764b777a 100644 --- a/src/xrpld/app/rdb/detail/Vacuum.cpp +++ b/src/libxrpl/server/Vacuum.cpp @@ -1,7 +1,9 @@ -#include +#include #include +#include + namespace xrpl { bool @@ -23,7 +25,7 @@ doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j) auto txnDB = std::make_unique(setup, TxDBName, setup.txPragma, TxDBInit, j); auto& session = txnDB->getSession(); - std::uint32_t pageSize; + std::uint32_t pageSize = 0; // Only the most trivial databases will fit in memory on typical // (recommended) hardware. Force temp files to be written to disk diff --git a/src/xrpld/app/rdb/detail/Wallet.cpp b/src/libxrpl/server/Wallet.cpp similarity index 88% rename from src/xrpld/app/rdb/detail/Wallet.cpp rename to src/libxrpl/server/Wallet.cpp index 88a5dcf985..95c2e89a20 100644 --- a/src/xrpld/app/rdb/detail/Wallet.cpp +++ b/src/libxrpl/server/Wallet.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include @@ -8,18 +9,24 @@ std::unique_ptr makeWalletDB(DatabaseCon::Setup const& setup, beast::Journal j) { // wallet database - return std::make_unique(setup, WalletDBName, std::array(), WalletDBInit, j); + return std::make_unique( + setup, WalletDBName, std::array(), WalletDBInit, j); } std::unique_ptr makeTestWalletDB(DatabaseCon::Setup const& setup, std::string const& dbname, beast::Journal j) { // wallet database - return std::make_unique(setup, dbname.data(), std::array(), WalletDBInit, j); + return std::make_unique( + setup, dbname.data(), std::array(), WalletDBInit, j); } void -getManifests(soci::session& session, std::string const& dbTable, ManifestCache& mCache, beast::Journal j) +getManifests( + soci::session& session, + std::string const& dbTable, + ManifestCache& mCache, + beast::Journal j) { // Load manifests stored in database std::string const sql = "SELECT RawData FROM " + dbTable + ";"; @@ -123,9 +130,11 @@ getNodeIdentity(soci::session& session) auto [newpublicKey, newsecretKey] = randomKeyPair(KeyType::secp256k1); session << str( - boost::format("INSERT INTO NodeIdentity (PublicKey,PrivateKey) " - "VALUES ('%s','%s');") % - toBase58(TokenType::NodePublic, newpublicKey) % toBase58(TokenType::NodePrivate, newsecretKey)); + boost::format( + "INSERT INTO NodeIdentity (PublicKey,PrivateKey) " + "VALUES ('%s','%s');") % + toBase58(TokenType::NodePublic, newpublicKey) % + toBase58(TokenType::NodePrivate, newsecretKey)); return {newpublicKey, newsecretKey}; } @@ -166,7 +175,10 @@ getPeerReservationTable(soci::session& session, beast::Journal j) } void -insertPeerReservation(soci::session& session, PublicKey const& nodeId, std::string const& description) +insertPeerReservation( + soci::session& session, + PublicKey const& nodeId, + std::string const& description) { auto const sNodeId = toBase58(TokenType::NodePublic, nodeId); session << "INSERT INTO PeerReservations (PublicKey, Description) " @@ -231,7 +243,10 @@ readAmendments( boost::optional amendment_name; boost::optional vote_to_veto; soci::statement st = - (session.prepare << sql, soci::into(amendment_hash), soci::into(amendment_name), soci::into(vote_to_veto)); + (session.prepare << sql, + soci::into(amendment_hash), + soci::into(amendment_name), + soci::into(vote_to_veto)); st.execute(); while (st.fetch()) { @@ -240,7 +255,11 @@ readAmendments( } void -voteAmendment(soci::session& session, uint256 const& amendment, std::string const& name, AmendmentVote vote) +voteAmendment( + soci::session& session, + uint256 const& amendment, + std::string const& name, + AmendmentVote vote) { soci::transaction tr(session); std::string sql = diff --git a/src/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index e84a620b77..a6aacfabc9 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -26,7 +27,8 @@ makeTypedLeaf(SHAMapNodeType type, boost::intrusive_ptr item, std::to_string(static_cast>(type))); } -SHAMap::SHAMap(SHAMapType t, Family& f) : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t) +SHAMap::SHAMap(SHAMapType t, Family& f) + : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t) { root_ = intr_ptr::make_shared(cowid_); } @@ -65,7 +67,10 @@ SHAMap::snapShot(bool isMutable) const } void -SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, intr_ptr::SharedPtr child) +SHAMap::dirtyUp( + SharedPtrNodeStack& stack, + uint256 const& target, + intr_ptr::SharedPtr child) { // walk the tree up from through the inner nodes to the root_ // update hashes and links @@ -73,7 +78,8 @@ SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, intr_ptr::Shar // child can be an inner node or a leaf XRPL_ASSERT( - (state_ != SHAMapState::Synching) && (state_ != SHAMapState::Immutable), "xrpl::SHAMap::dirtyUp : valid state"); + (state_ != SHAMapState::Synching) && (state_ != SHAMapState::Immutable), + "xrpl::SHAMap::dirtyUp : valid state"); XRPL_ASSERT(child && (child->cowid() == cowid_), "xrpl::SHAMap::dirtyUp : valid child input"); while (!stack.empty()) @@ -96,7 +102,8 @@ SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, intr_ptr::Shar SHAMapLeafNode* SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const { - XRPL_ASSERT(stack == nullptr || stack->empty(), "xrpl::SHAMap::walkTowardsKey : empty stack input"); + XRPL_ASSERT( + stack == nullptr || stack->empty(), "xrpl::SHAMap::walkTowardsKey : empty stack input"); auto inNode = root_; SHAMapNodeID nodeID; @@ -116,7 +123,7 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const if (stack != nullptr) stack->push({inNode, nodeID}); - return static_cast(inNode.get()); + return safe_downcast(inNode.get()); } SHAMapLeafNode* @@ -307,11 +314,17 @@ SHAMap::descendNoStore(SHAMapInnerNode& parent, int branch) const } std::pair -SHAMap::descend(SHAMapInnerNode* parent, SHAMapNodeID const& parentID, int branch, SHAMapSyncFilter* filter) const +SHAMap::descend( + SHAMapInnerNode* parent, + SHAMapNodeID const& parentID, + int branch, + SHAMapSyncFilter* filter) const { XRPL_ASSERT(parent->isInner(), "xrpl::SHAMap::descend : valid parent input"); - XRPL_ASSERT((branch >= 0) && (branch < branchFactor), "xrpl::SHAMap::descend : valid branch input"); - XRPL_ASSERT(!parent->isEmptyBranch(branch), "xrpl::SHAMap::descend : parent branch is non-empty"); + XRPL_ASSERT( + (branch >= 0) && (branch < branchFactor), "xrpl::SHAMap::descend : valid branch input"); + XRPL_ASSERT( + !parent->isEmptyBranch(branch), "xrpl::SHAMap::descend : parent branch is non-empty"); SHAMapTreeNode* child = parent->getChildPointer(branch); @@ -405,9 +418,13 @@ SHAMap::belowHelper( } auto inner = intr_ptr::static_pointer_cast(node); if (stack.empty()) + { stack.push({inner, SHAMapNodeID{}}); + } else + { stack.push({inner, stack.top().second.getChildNodeID(branch)}); + } for (int i = init; cmp(i);) { if (!inner->isEmptyBranch(i)) @@ -425,12 +442,15 @@ SHAMap::belowHelper( i = init; // descend and reset loop } else + { incr(i); // scan next branch + } } return nullptr; } SHAMapLeafNode* -SHAMap::lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) const +SHAMap::lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) + const { auto init = branchFactor - 1; auto cmp = [](int i) { return i >= 0; }; @@ -439,7 +459,8 @@ SHAMap::lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& return belowHelper(node, stack, branch, {init, cmp, incr}); } SHAMapLeafNode* -SHAMap::firstBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) const +SHAMap::firstBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) + const { auto init = 0; auto cmp = [](int i) { return i <= branchFactor; }; @@ -457,7 +478,7 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const while (!node->isLeaf()) { SHAMapTreeNode* nextNode = nullptr; - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < branchFactor; ++i) { if (!inner->isEmptyBranch(i)) @@ -482,8 +503,9 @@ SHAMap::onlyBelow(SHAMapTreeNode* node) const // An inner node must have at least one leaf // below it, unless it's the root_ - auto const leaf = static_cast(node); - XRPL_ASSERT(leaf->peekItem() || (leaf == root_.get()), "xrpl::SHAMap::onlyBelow : valid inner node"); + auto const leaf = safe_downcast(node); + XRPL_ASSERT( + leaf->peekItem() || (leaf == root_.get()), "xrpl::SHAMap::onlyBelow : valid inner node"); return leaf->peekItem(); } @@ -563,7 +585,7 @@ SHAMap::upper_bound(uint256 const& id) const auto [node, nodeID] = stack.top(); if (node->isLeaf()) { - auto leaf = static_cast(node.get()); + auto leaf = safe_downcast(node.get()); if (leaf->peekItem()->key() > id) return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -596,7 +618,7 @@ SHAMap::lower_bound(uint256 const& id) const auto [node, nodeID] = stack.top(); if (node->isLeaf()) { - auto leaf = static_cast(node.get()); + auto leaf = safe_downcast(node.get()); if (leaf->peekItem()->key() < id) return const_iterator(this, leaf->peekItem().get(), std::move(stack)); } @@ -647,9 +669,10 @@ SHAMap::delItem(uint256 const& id) SHAMapNodeType type = leaf->getType(); - // What gets attached to the end of the chain - // (For now, nothing, since we deleted the leaf) - intr_ptr::SharedPtr prevNode; + using TreeNodeType = intr_ptr::SharedPtr; + + // What gets attached to the end of the chain (For now, nothing, since we deleted the leaf) + TreeNodeType prevNode; while (!stack.empty()) { @@ -658,7 +681,12 @@ SHAMap::delItem(uint256 const& id) stack.pop(); node = unshareNode(std::move(node), nodeID); - node->setChild(selectBranch(nodeID, id), std::move(prevNode)); + node->setChild( + selectBranch(nodeID, id), std::move(prevNode)); // NOLINT(bugprone-use-after-move) + + XRPL_ASSERT( + not prevNode, // NOLINT(bugprone-use-after-move) + "xrpl::SHAMap::delItem : prevNode should be nullptr after std::move"); if (!nodeID.isRoot()) { @@ -668,7 +696,9 @@ SHAMap::delItem(uint256 const& id) if (bc == 0) { // no children below this branch - prevNode.reset(); + // + // Note: This is unnecessary due to the std::move above but left here for safety + prevNode = TreeNodeType{}; } else if (bc == 1) { @@ -681,7 +711,7 @@ SHAMap::delItem(uint256 const& id) { if (!node->isEmptyBranch(i)) { - node->setChild(i, intr_ptr::SharedPtr{}); + node->setChild(i, TreeNodeType{}); break; } } @@ -734,7 +764,8 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr // easy case, we end on an inner node auto inner = intr_ptr::static_pointer_cast(node); int branch = selectBranch(nodeID, tag); - XRPL_ASSERT(inner->isEmptyBranch(branch), "xrpl::SHAMap::addGiveItem : inner branch is empty"); + XRPL_ASSERT( + inner->isEmptyBranch(branch), "xrpl::SHAMap::addGiveItem : inner branch is empty"); inner->setChild(branch, makeTypedLeaf(type, std::move(item), cowid_)); } else @@ -743,11 +774,12 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr // items auto leaf = intr_ptr::static_pointer_cast(node); auto otherItem = leaf->peekItem(); - XRPL_ASSERT(otherItem && (tag != otherItem->key()), "xrpl::SHAMap::addGiveItem : non-null item"); + XRPL_ASSERT( + otherItem && (tag != otherItem->key()), "xrpl::SHAMap::addGiveItem : non-null item"); node = intr_ptr::make_shared(node->cowid()); - unsigned int b1, b2; + unsigned int b1 = 0, b2 = 0; while ((b1 = selectBranch(nodeID, tag)) == (b2 = selectBranch(nodeID, otherItem->key()))) { @@ -762,7 +794,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr // we can add the two leaf nodes here XRPL_ASSERT(node->isInner(), "xrpl::SHAMap::addGiveItem : node is inner"); - auto inner = static_cast(node.get()); + auto inner = safe_downcast(node.get()); inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_)); inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_)); } @@ -1071,7 +1103,7 @@ SHAMap::dump(bool hash) const if (node->isInner()) { - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < branchFactor; ++i) { if (!inner->isEmptyBranch(i)) @@ -1080,14 +1112,17 @@ SHAMap::dump(bool hash) const if (child) { XRPL_ASSERT( - child->getHash() == inner->getChildHash(i), "xrpl::SHAMap::dump : child hash do match"); + child->getHash() == inner->getChildHash(i), + "xrpl::SHAMap::dump : child hash do match"); stack.push({child, nodeID.getChildNodeID(i)}); } } } } else + { ++leafCount; + } } while (!stack.empty()); JLOG(journal_.info()) << leafCount << " resident leaves"; @@ -1119,7 +1154,8 @@ SHAMap::invariants() const XRPL_ASSERT(node, "xrpl::SHAMap::invariants : non-null root node"); XRPL_ASSERT(!node->isLeaf(), "xrpl::SHAMap::invariants : root node is not leaf"); SharedPtrNodeStack stack; - for (auto leaf = peekFirstItem(stack); leaf != nullptr; leaf = peekNextItem(leaf->peekItem()->key(), stack)) + for (auto leaf = peekFirstItem(stack); leaf != nullptr; + leaf = peekNextItem(leaf->peekItem()->key(), stack)) ; node->invariants(true); } diff --git a/src/libxrpl/shamap/SHAMapDelta.cpp b/src/libxrpl/shamap/SHAMapDelta.cpp index d8aabcc67c..98a31fea55 100644 --- a/src/libxrpl/shamap/SHAMapDelta.cpp +++ b/src/libxrpl/shamap/SHAMapDelta.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -39,23 +40,29 @@ SHAMap::walkBranch( if (node->isInner()) { // This is an inner node, add all non-empty branches - auto inner = static_cast(node); + auto inner = safe_downcast(node); for (int i = 0; i < 16; ++i) + { if (!inner->isEmptyBranch(i)) nodeStack.push({descendThrow(inner, i)}); + } } else { // This is a leaf node, process its item - auto item = static_cast(node)->peekItem(); + auto item = safe_downcast(node)->peekItem(); if (emptyBranch || (item->key() != otherMapItem->key())) { // unmatched if (isFirstMap) + { differences.insert(std::make_pair(item->key(), DeltaRef(item, nullptr))); + } else + { differences.insert(std::make_pair(item->key(), DeltaRef(nullptr, item))); + } if (--maxCount <= 0) return false; @@ -64,9 +71,13 @@ SHAMap::walkBranch( { // non-matching items with same tag if (isFirstMap) + { differences.insert(std::make_pair(item->key(), DeltaRef(item, otherMapItem))); + } else + { differences.insert(std::make_pair(item->key(), DeltaRef(otherMapItem, item))); + } if (--maxCount <= 0) return false; @@ -84,10 +95,16 @@ SHAMap::walkBranch( if (!emptyBranch) { // otherMapItem was unmatched, must add - if (isFirstMap) // this is first map, so other item is from second - differences.insert(std::make_pair(otherMapItem->key(), DeltaRef(nullptr, otherMapItem))); + if (isFirstMap) + { // this is first map, so other item is from second + differences.insert( + std::make_pair(otherMapItem->key(), DeltaRef(nullptr, otherMapItem))); + } else - differences.insert(std::make_pair(otherMapItem->key(), DeltaRef(otherMapItem, nullptr))); + { + differences.insert( + std::make_pair(otherMapItem->key(), DeltaRef(otherMapItem, nullptr))); + } if (--maxCount <= 0) return false; @@ -104,7 +121,8 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const // many differences throws on corrupt tables or missing nodes CAUTION: // otherMap is not locked and must be immutable - XRPL_ASSERT(isValid() && otherMap.isValid(), "xrpl::SHAMap::compare : valid state and valid input"); + XRPL_ASSERT( + isValid() && otherMap.isValid(), "xrpl::SHAMap::compare : valid state and valid input"); if (getHash() == otherMap.getHash()) return true; @@ -129,48 +147,53 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (ourNode->isLeaf() && otherNode->isLeaf()) { // two leaves - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (ours->peekItem()->key() == other->peekItem()->key()) { if (ours->peekItem()->slice() != other->peekItem()->slice()) { differences.insert( - std::make_pair(ours->peekItem()->key(), DeltaRef(ours->peekItem(), other->peekItem()))); + std::make_pair( + ours->peekItem()->key(), + DeltaRef(ours->peekItem(), other->peekItem()))); if (--maxCount <= 0) return false; } } else { - differences.insert(std::make_pair(ours->peekItem()->key(), DeltaRef(ours->peekItem(), nullptr))); + differences.insert( + std::make_pair(ours->peekItem()->key(), DeltaRef(ours->peekItem(), nullptr))); if (--maxCount <= 0) return false; - differences.insert(std::make_pair(other->peekItem()->key(), DeltaRef(nullptr, other->peekItem()))); + differences.insert( + std::make_pair(other->peekItem()->key(), DeltaRef(nullptr, other->peekItem()))); if (--maxCount <= 0) return false; } } else if (ourNode->isInner() && otherNode->isLeaf()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (!walkBranch(ours, other->peekItem(), true, differences, maxCount)) return false; } else if (ourNode->isLeaf() && otherNode->isInner()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); if (!otherMap.walkBranch(other, ours->peekItem(), false, differences, maxCount)) return false; } else if (ourNode->isInner() && otherNode->isInner()) { - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = safe_downcast(ourNode); + auto other = safe_downcast(otherNode); for (int i = 0; i < 16; ++i) + { if (ours->getChildHash(i) != other->getChildHash(i)) { if (other->isEmptyBranch(i)) @@ -187,9 +210,12 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (!otherMap.walkBranch(iNode, nullptr, false, differences, maxCount)) return false; } - else // The two trees have different non-empty branches + else + { // The two trees have different non-empty branches nodeStack.push({descendThrow(ours, i), otherMap.descendThrow(other, i)}); + } } + } } else { @@ -276,45 +302,51 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis nodeStacks[rootChildIndex].push(intr_ptr::static_pointer_cast(child)); JLOG(journal_.debug()) << "starting worker " << rootChildIndex; - workers.push_back(std::thread( - [&m, &missingNodes, &maxMissing, &exceptions, this]( - std::stack> nodeStack) { - try - { - while (!nodeStack.empty()) + workers.push_back( + std::thread( + [&m, &missingNodes, &maxMissing, &exceptions, this]( + std::stack> nodeStack) { + try { - intr_ptr::SharedPtr node = std::move(nodeStack.top()); - XRPL_ASSERT(node, "xrpl::SHAMap::walkMapParallel : non-null node"); - nodeStack.pop(); - - for (int i = 0; i < 16; ++i) + while (!nodeStack.empty()) { - if (node->isEmptyBranch(i)) - continue; - intr_ptr::SharedPtr nextNode = descendNoStore(*node, i); + intr_ptr::SharedPtr node = std::move(nodeStack.top()); + XRPL_ASSERT(node, "xrpl::SHAMap::walkMapParallel : non-null node"); + nodeStack.pop(); - if (nextNode) + for (int i = 0; i < 16; ++i) { - if (nextNode->isInner()) - nodeStack.push(intr_ptr::static_pointer_cast(nextNode)); - } - else - { - std::lock_guard l{m}; - missingNodes.emplace_back(type_, node->getChildHash(i)); - if (--maxMissing <= 0) - return; + if (node->isEmptyBranch(i)) + continue; + intr_ptr::SharedPtr nextNode = + descendNoStore(*node, i); + + if (nextNode) + { + if (nextNode->isInner()) + { + nodeStack.push( + intr_ptr::static_pointer_cast( + nextNode)); + } + } + else + { + std::lock_guard l{m}; + missingNodes.emplace_back(type_, node->getChildHash(i)); + if (--maxMissing <= 0) + return; + } } } } - } - catch (SHAMapMissingNode const& e) - { - std::lock_guard l(m); - exceptions.push_back(e); - } - }, - std::move(nodeStacks[rootChildIndex]))); + catch (SHAMapMissingNode const& e) + { + std::lock_guard l(m); + exceptions.push_back(e); + } + }, + std::move(nodeStacks[rootChildIndex]))); } for (std::thread& worker : workers) diff --git a/src/libxrpl/shamap/SHAMapInnerNode.cpp b/src/libxrpl/shamap/SHAMapInnerNode.cpp index 5ccca9c2f4..599d343745 100644 --- a/src/libxrpl/shamap/SHAMapInnerNode.cpp +++ b/src/libxrpl/shamap/SHAMapInnerNode.cpp @@ -20,7 +20,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default; void SHAMapInnerNode::partialDestructor() { - intr_ptr::SharedPtr* children; + intr_ptr::SharedPtr* children = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, std::ignore, children) = hashesAndChildren_.getHashesAndChildren(); iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { children[indexNum].reset(); }); @@ -61,21 +61,24 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const p->hash_ = hash_; p->isBranch_ = isBranch_; p->fullBelowGen_ = fullBelowGen_; - SHAMapHash *cloneHashes, *thisHashes; - intr_ptr::SharedPtr*cloneChildren, *thisChildren; + SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr; + intr_ptr::SharedPtr*cloneChildren = nullptr, *thisChildren = nullptr; // structured bindings can't be captured in c++ 17; use tie instead - std::tie(std::ignore, cloneHashes, cloneChildren) = p->hashesAndChildren_.getHashesAndChildren(); + std::tie(std::ignore, cloneHashes, cloneChildren) = + p->hashesAndChildren_.getHashesAndChildren(); std::tie(std::ignore, thisHashes, thisChildren) = hashesAndChildren_.getHashesAndChildren(); if (thisIsSparse) { int cloneChildIndex = 0; - iterNonEmptyChildIndexes( - [&](auto branchNum, auto indexNum) { cloneHashes[cloneChildIndex++] = thisHashes[indexNum]; }); + iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { + cloneHashes[cloneChildIndex++] = thisHashes[indexNum]; + }); } else { - iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { cloneHashes[branchNum] = thisHashes[indexNum]; }); + iterNonEmptyChildIndexes( + [&](auto branchNum, auto indexNum) { cloneHashes[branchNum] = thisHashes[indexNum]; }); } spinlock sl(lock_); @@ -84,13 +87,15 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const if (thisIsSparse) { int cloneChildIndex = 0; - iterNonEmptyChildIndexes( - [&](auto branchNum, auto indexNum) { cloneChildren[cloneChildIndex++] = thisChildren[indexNum]; }); + iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { + cloneChildren[cloneChildIndex++] = thisChildren[indexNum]; + }); } else { - iterNonEmptyChildIndexes( - [&](auto branchNum, auto indexNum) { cloneChildren[branchNum] = thisChildren[indexNum]; }); + iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { + cloneChildren[branchNum] = thisChildren[indexNum]; + }); } return p; @@ -120,9 +125,13 @@ SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashVali ret->resizeChildArrays(ret->getBranchCount()); if (hashValid) + { ret->hash_ = hash; + } else + { ret->updateHash(); + } return ret; } @@ -180,8 +189,8 @@ SHAMapInnerNode::updateHash() void SHAMapInnerNode::updateHashDeep() { - SHAMapHash* hashes; - intr_ptr::SharedPtr* children; + SHAMapHash* hashes = nullptr; + intr_ptr::SharedPtr* children = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, hashes, children) = hashesAndChildren_.getHashesAndChildren(); iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { @@ -241,21 +250,25 @@ SHAMapInnerNode::getString(SHAMapNodeID const& id) const void SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) { - XRPL_ASSERT((m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::setChild : valid branch input"); + XRPL_ASSERT( + (m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::setChild : valid branch input"); XRPL_ASSERT(cowid_, "xrpl::SHAMapInnerNode::setChild : nonzero cowid"); XRPL_ASSERT(child.get() != this, "xrpl::SHAMapInnerNode::setChild : valid child input"); auto const dstIsBranch = [&] { if (child) + { return isBranch_ | (1u << m); - else - return isBranch_ & ~(1u << m); + } + + return isBranch_ & ~(1u << m); }(); auto const dstToAllocate = popcnt16(dstIsBranch); // change hashesAndChildren to remove the element, or make room for the // added element, if necessary - hashesAndChildren_ = TaggedPointer(std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate); + hashesAndChildren_ = + TaggedPointer(std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate); isBranch_ = dstIsBranch; @@ -270,14 +283,16 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) hash_.zero(); XRPL_ASSERT( - getBranchCount() <= hashesAndChildren_.capacity(), "xrpl::SHAMapInnerNode::setChild : maximum branch count"); + getBranchCount() <= hashesAndChildren_.capacity(), + "xrpl::SHAMapInnerNode::setChild : maximum branch count"); } // finished modifying, now make shareable void SHAMapInnerNode::shareChild(int m, intr_ptr::SharedPtr const& child) { - XRPL_ASSERT((m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::shareChild : valid branch input"); + XRPL_ASSERT( + (m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::shareChild : valid branch input"); XRPL_ASSERT(cowid_, "xrpl::SHAMapInnerNode::shareChild : nonzero cowid"); XRPL_ASSERT(child, "xrpl::SHAMapInnerNode::shareChild : non-null child input"); XRPL_ASSERT(child.get() != this, "xrpl::SHAMapInnerNode::shareChild : valid child input"); @@ -289,8 +304,11 @@ SHAMapInnerNode::shareChild(int m, intr_ptr::SharedPtr const& ch SHAMapTreeNode* SHAMapInnerNode::getChildPointer(int branch) { - XRPL_ASSERT(branch >= 0 && branch < branchFactor, "xrpl::SHAMapInnerNode::getChildPointer : valid branch input"); - XRPL_ASSERT(!isEmptyBranch(branch), "xrpl::SHAMapInnerNode::getChildPointer : non-empty branch input"); + XRPL_ASSERT( + branch >= 0 && branch < branchFactor, + "xrpl::SHAMapInnerNode::getChildPointer : valid branch input"); + XRPL_ASSERT( + !isEmptyBranch(branch), "xrpl::SHAMapInnerNode::getChildPointer : non-empty branch input"); auto const index = *getChildIndex(branch); @@ -302,7 +320,9 @@ SHAMapInnerNode::getChildPointer(int branch) intr_ptr::SharedPtr SHAMapInnerNode::getChild(int branch) { - XRPL_ASSERT(branch >= 0 && branch < branchFactor, "xrpl::SHAMapInnerNode::getChild : valid branch input"); + XRPL_ASSERT( + branch >= 0 && branch < branchFactor, + "xrpl::SHAMapInnerNode::getChild : valid branch input"); XRPL_ASSERT(!isEmptyBranch(branch), "xrpl::SHAMapInnerNode::getChild : non-empty branch input"); auto const index = *getChildIndex(branch); @@ -315,7 +335,8 @@ SHAMapInnerNode::getChild(int branch) SHAMapHash const& SHAMapInnerNode::getChildHash(int m) const { - XRPL_ASSERT((m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::getChildHash : valid branch input"); + XRPL_ASSERT( + (m >= 0) && (m < branchFactor), "xrpl::SHAMapInnerNode::getChildHash : valid branch input"); if (auto const i = getChildIndex(m)) return hashesAndChildren_.getHashes()[*i]; @@ -325,9 +346,13 @@ SHAMapInnerNode::getChildHash(int m) const intr_ptr::SharedPtr SHAMapInnerNode::canonicalizeChild(int branch, intr_ptr::SharedPtr node) { - XRPL_ASSERT(branch >= 0 && branch < branchFactor, "xrpl::SHAMapInnerNode::canonicalizeChild : valid branch input"); + XRPL_ASSERT( + branch >= 0 && branch < branchFactor, + "xrpl::SHAMapInnerNode::canonicalizeChild : valid branch input"); XRPL_ASSERT(node != nullptr, "xrpl::SHAMapInnerNode::canonicalizeChild : valid node input"); - XRPL_ASSERT(!isEmptyBranch(branch), "xrpl::SHAMapInnerNode::canonicalizeChild : non-empty branch input"); + XRPL_ASSERT( + !isEmptyBranch(branch), + "xrpl::SHAMapInnerNode::canonicalizeChild : non-empty branch input"); auto const childIndex = *getChildIndex(branch); auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren(); XRPL_ASSERT( @@ -362,7 +387,9 @@ SHAMapInnerNode::invariants(bool is_root) const auto const branchCount = getBranchCount(); for (int i = 0; i < branchCount; ++i) { - XRPL_ASSERT(hashes[i].isNonZero(), "xrpl::SHAMapInnerNode::invariants : nonzero hash in branch"); + XRPL_ASSERT( + hashes[i].isNonZero(), + "xrpl::SHAMapInnerNode::invariants : nonzero hash in branch"); if (children[i] != nullptr) children[i]->invariants(); ++count; diff --git a/src/libxrpl/shamap/SHAMapLeafNode.cpp b/src/libxrpl/shamap/SHAMapLeafNode.cpp index 7f449af3a7..3776de3a35 100644 --- a/src/libxrpl/shamap/SHAMapLeafNode.cpp +++ b/src/libxrpl/shamap/SHAMapLeafNode.cpp @@ -11,7 +11,10 @@ SHAMapLeafNode::SHAMapLeafNode(boost::intrusive_ptr item, std: "SHAMapItem const>, std::uint32_t) : minimum input size"); } -SHAMapLeafNode::SHAMapLeafNode(boost::intrusive_ptr item, std::uint32_t cowid, SHAMapHash const& hash) +SHAMapLeafNode::SHAMapLeafNode( + boost::intrusive_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) : SHAMapTreeNode(cowid, hash), item_(std::move(item)) { XRPL_ASSERT( @@ -48,13 +51,21 @@ SHAMapLeafNode::getString(SHAMapNodeID const& id) const auto const type = getType(); if (type == SHAMapNodeType::tnTRANSACTION_NM) + { ret += ",txn\n"; + } else if (type == SHAMapNodeType::tnTRANSACTION_MD) + { ret += ",txn+md\n"; + } else if (type == SHAMapNodeType::tnACCOUNT_STATE) + { ret += ",as\n"; + } else + { ret += ",leaf\n"; + } ret += " Tag="; ret += to_string(item_->key()); diff --git a/src/libxrpl/shamap/SHAMapNodeID.cpp b/src/libxrpl/shamap/SHAMapNodeID.cpp index e8840f398d..6536e628ef 100644 --- a/src/libxrpl/shamap/SHAMapNodeID.cpp +++ b/src/libxrpl/shamap/SHAMapNodeID.cpp @@ -36,8 +36,11 @@ depthMask(unsigned int depth) // canonicalize the hash to a node ID for this depth SHAMapNodeID::SHAMapNodeID(unsigned int depth, uint256 const& hash) : id_(hash), depth_(depth) { - XRPL_ASSERT(depth <= SHAMap::leafDepth, "xrpl::SHAMapNodeID::SHAMapNodeID : maximum depth input"); - XRPL_ASSERT(id_ == (id_ & depthMask(depth)), "xrpl::SHAMapNodeID::SHAMapNodeID : hash and depth inputs do match"); + XRPL_ASSERT( + depth <= SHAMap::leafDepth, "xrpl::SHAMapNodeID::SHAMapNodeID : maximum depth input"); + XRPL_ASSERT( + id_ == (id_ & depthMask(depth)), + "xrpl::SHAMapNodeID::SHAMapNodeID : hash and depth inputs do match"); } std::string @@ -52,7 +55,8 @@ SHAMapNodeID::getRawString() const SHAMapNodeID SHAMapNodeID::getChildNodeID(unsigned int m) const { - XRPL_ASSERT(m < SHAMap::branchFactor, "xrpl::SHAMapNodeID::getChildNodeID : valid branch input"); + XRPL_ASSERT( + m < SHAMap::branchFactor, "xrpl::SHAMapNodeID::getChildNodeID : valid branch input"); // A SHAMap has exactly 65 levels, so nodes must not exceed that // depth; if they do, this breaks the invariant of never allowing @@ -62,7 +66,8 @@ SHAMapNodeID::getChildNodeID(unsigned int m) const // We throw (but never assert) if the node is at level 64, since // entries at that depth are leaf nodes and have no children and even // constructing a child node from them would break the above invariant. - XRPL_ASSERT(depth_ <= SHAMap::leafDepth, "xrpl::SHAMapNodeID::getChildNodeID : maximum leaf depth"); + XRPL_ASSERT( + depth_ <= SHAMap::leafDepth, "xrpl::SHAMapNodeID::getChildNodeID : maximum leaf depth"); if (depth_ >= SHAMap::leafDepth) Throw("Request for child node ID of " + to_string(*this)); @@ -102,9 +107,13 @@ selectBranch(SHAMapNodeID const& id, uint256 const& hash) auto branch = static_cast(*(hash.begin() + (depth / 2))); if (depth & 1) + { branch &= 0xf; + } else + { branch >>= 4; + } XRPL_ASSERT(branch < SHAMap::branchFactor, "xrpl::selectBranch : maximum result"); return branch; diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index 503505b80c..c6e4c3dcb0 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,11 +7,13 @@ namespace xrpl { void -SHAMap::visitLeaves(std::function const& item)> const& leafFunction) const +SHAMap::visitLeaves( + std::function const& item)> const& leafFunction) + const { visitNodes([&leafFunction](SHAMapTreeNode& node) { if (!node.isInner()) - leafFunction(static_cast(node).peekItem()); + leafFunction(safe_downcast(node).peekItem()); return true; }); } @@ -43,7 +46,9 @@ SHAMap::visitNodes(std::function const& function) const return; if (child->isLeaf()) + { ++pos; + } else { // If there are no more children, don't push this node @@ -76,7 +81,9 @@ SHAMap::visitNodes(std::function const& function) const } void -SHAMap::visitDifferences(SHAMap const* have, std::function const& function) const +SHAMap::visitDifferences( + SHAMap const* have, + std::function const& function) const { // Visit every node in this SHAMap that is not present // in the specified SHAMap @@ -100,7 +107,7 @@ SHAMap::visitDifferences(SHAMap const* have, std::function; std::stack> stack; - stack.push({static_cast(root_.get()), SHAMapNodeID{}}); + stack.push({safe_downcast(root_.get()), SHAMapNodeID{}}); while (!stack.empty()) { @@ -123,9 +130,12 @@ SHAMap::visitDifferences(SHAMap const* have, std::functionisInner()) { if (!have || !have->hasInnerNode(childID, childHash)) - stack.push({static_cast(next), childID}); + stack.push({safe_downcast(next), childID}); } - else if (!have || !have->hasLeafNode(static_cast(next)->peekItem()->key(), childHash)) + else if ( + !have || + !have->hasLeafNode( + safe_downcast(next)->peekItem()->key(), childHash)) { if (!function(*next)) return; @@ -156,7 +166,7 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) auto const& childHash = node->getChildHash(branch); - if (mn.missingHashes_.count(childHash) != 0) + if (mn.missingHashes_.contains(childHash)) { // we already know this child node is missing fullBelow = false; @@ -169,7 +179,8 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) branch, mn.filter_, pending, - [node, nodeID, branch, &mn](intr_ptr::SharedPtr found, SHAMapHash const&) { + [node, nodeID, branch, &mn]( + intr_ptr::SharedPtr found, SHAMapHash const&) { // a read completed asynchronously std::unique_lock lock{mn.deferLock_}; mn.finishedReads_.emplace_back(node, nodeID, branch, std::move(found)); @@ -187,17 +198,19 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) fullBelow = false; // for now, not known full below mn.missingHashes_.insert(childHash); - mn.missingNodes_.emplace_back(nodeID.getChildNodeID(branch), childHash.as_uint256()); + mn.missingNodes_.emplace_back( + nodeID.getChildNodeID(branch), childHash.as_uint256()); if (--mn.max_ <= 0) return; } - else if (d->isInner() && !static_cast(d)->isFullBelow(mn.generation_)) + else if ( + d->isInner() && !safe_downcast(d)->isFullBelow(mn.generation_)) { mn.stack_.push(se); // Switch to processing the child node - node = static_cast(d); + node = safe_downcast(d); nodeID = nodeID.getChildNodeID(branch); firstChild = rand_int(255); currentChild = 0; @@ -230,7 +243,8 @@ SHAMap::gmn_ProcessDeferredReads(MissingNodes& mn) int complete = 0; while (complete != mn.deferred_) { - std::tuple> deferredNode; + std::tuple> + deferredNode; { std::unique_lock lock{mn.deferLock_}; @@ -281,7 +295,8 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) 512, // number of async reads per pass f_.getFullBelowCache()->getGeneration()); - if (!root_->isInner() || intr_ptr::static_pointer_cast(root_)->isFullBelow(mn.generation_)) + if (!root_->isInner() || + intr_ptr::static_pointer_cast(root_)->isFullBelow(mn.generation_)) { clearSynching(); return std::move(mn.missingNodes_); @@ -293,7 +308,8 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) // (randomly selected) inner node. This increases the likelihood // that the two threads will produce different request sets (which is // more efficient than sending identical requests). - MissingNodes::StackEntry pos{static_cast(root_.get()), SHAMapNodeID(), rand_int(255), 0, true}; + MissingNodes::StackEntry pos{ + safe_downcast(root_.get()), SHAMapNodeID(), rand_int(255), 0, true}; auto& node = std::get<0>(pos); auto& nextChild = std::get<3>(pos); auto& fullBelow = std::get<4>(pos); @@ -344,8 +360,10 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) { // Recheck nodes we could not finish before for (auto const& [innerNode, nodeId] : mn.resumes_) + { if (!innerNode->isFullBelow(mn.generation_)) mn.stack_.push(std::make_tuple(innerNode, nodeId, rand_int(255), 0, true)); + } mn.resumes_.clear(); } @@ -387,7 +405,7 @@ SHAMap::getNodeFat( while (node && node->isInner() && (nodeID.getDepth() < wanted.getDepth())) { int branch = selectBranch(nodeID, wanted.getNodeID()); - auto inner = static_cast(node); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; node = descendThrow(inner, branch); @@ -396,11 +414,12 @@ SHAMap::getNodeFat( if (node == nullptr || wanted != nodeID) { - JLOG(journal_.info()) << "peer requested node that is not in the map: " << wanted << " but found " << nodeID; + JLOG(journal_.info()) << "peer requested node that is not in the map: " << wanted + << " but found " << nodeID; return false; } - if (node->isInner() && static_cast(node)->isEmpty()) + if (node->isInner() && safe_downcast(node)->isEmpty()) { JLOG(journal_.warn()) << "peer requests empty node"; return false; @@ -425,7 +444,7 @@ SHAMap::getNodeFat( { // We descend inner nodes with only a single child // without decrementing the depth - auto inner = static_cast(node); + auto inner = safe_downcast(node); int bc = inner->getBranchCount(); if ((depth > 0) || (bc == 1)) @@ -494,7 +513,8 @@ SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFil { Serializer s; root_->serializeWithPrefix(s); - filter->gotNode(false, root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType()); + filter->gotNode( + false, root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType()); } return SHAMapAddNode::useful(); @@ -515,12 +535,13 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF SHAMapNodeID currNodeID; auto currNode = root_.get(); - while (currNode->isInner() && !static_cast(currNode)->isFullBelow(generation) && + while (currNode->isInner() && + !safe_downcast(currNode)->isFullBelow(generation) && (currNodeID.getDepth() < node.getDepth())) { int const branch = selectBranch(currNodeID, node.getNodeID()); XRPL_ASSERT(branch >= 0, "xrpl::SHAMap::addKnownNode : valid branch"); - auto inner = static_cast(currNode); + auto inner = safe_downcast(currNode); if (inner->isEmptyBranch(branch)) { JLOG(journal_.warn()) << "Add known node for empty branch" << node; @@ -554,14 +575,16 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF // propagate further down the line. if (newNode->isLeaf()) { - auto const& actualKey = static_cast(newNode.get())->peekItem()->key(); + auto const& actualKey = + safe_downcast(newNode.get())->peekItem()->key(); // Validate that this leaf belongs at the target position auto const expectedNodeID = SHAMapNodeID::createID(node.getDepth(), actualKey); if (expectedNodeID.getNodeID() != node.getNodeID()) { - JLOG(journal_.debug()) << "Leaf node position mismatch: " - << "expected=" << expectedNodeID.getNodeID() << ", actual=" << node.getNodeID(); + JLOG(journal_.debug()) + << "Leaf node position mismatch: " + << "expected=" << expectedNodeID.getNodeID() << ", actual=" << node.getNodeID(); return SHAMapAddNode::invalid(); } } @@ -569,7 +592,8 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF // Inner nodes must be at a level strictly less than 64 // but leaf nodes (while notionally at level 64) can be // at any depth up to and including 64: - if ((currNodeID.getDepth() > leafDepth) || (newNode->isInner() && currNodeID.getDepth() == leafDepth)) + if ((currNodeID.getDepth() > leafDepth) || + (newNode->isInner() && currNodeID.getDepth() == leafDepth)) { // Map is provably invalid state_ = SHAMapState::Invalid; @@ -581,7 +605,8 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF // Either this node is broken or we didn't request it (yet) JLOG(journal_.warn()) << "unable to hook node " << node; JLOG(journal_.info()) << " stuck at " << currNodeID; - JLOG(journal_.info()) << "got depth=" << node.getDepth() << ", walked to= " << currNodeID.getDepth(); + JLOG(journal_.info()) << "got depth=" << node.getDepth() + << ", walked to= " << currNodeID.getDepth(); return SHAMapAddNode::useful(); } @@ -594,7 +619,8 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF { Serializer s; newNode->serializeWithPrefix(s); - filter->gotNode(false, childHash, ledgerSeq_, std::move(s.modData()), newNode->getType()); + filter->gotNode( + false, childHash, ledgerSeq_, std::move(s.modData()), newNode->getType()); } return SHAMapAddNode::useful(); @@ -622,7 +648,7 @@ SHAMap::deepCompare(SHAMap& other) const JLOG(journal_.info()) << "unable to fetch node"; return false; } - else if (otherNode->getHash() != node->getHash()) + if (otherNode->getHash() != node->getHash()) { JLOG(journal_.warn()) << "node hash mismatch"; return false; @@ -632,8 +658,8 @@ SHAMap::deepCompare(SHAMap& other) const { if (!otherNode->isLeaf()) return false; - auto& nodePeek = static_cast(node)->peekItem(); - auto& otherNodePeek = static_cast(otherNode)->peekItem(); + auto& nodePeek = safe_downcast(node)->peekItem(); + auto& otherNodePeek = safe_downcast(otherNode)->peekItem(); if (nodePeek->key() != otherNodePeek->key()) return false; if (nodePeek->slice() != otherNodePeek->slice()) @@ -643,8 +669,8 @@ SHAMap::deepCompare(SHAMap& other) const { if (!otherNode->isInner()) return false; - auto node_inner = static_cast(node); - auto other_inner = static_cast(otherNode); + auto node_inner = safe_downcast(node); + auto other_inner = safe_downcast(otherNode); for (int i = 0; i < 16; ++i) { if (node_inner->isEmptyBranch(i)) @@ -684,7 +710,7 @@ SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetN while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth())) { int branch = selectBranch(nodeID, targetNodeID.getNodeID()); - auto inner = static_cast(node); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; @@ -709,7 +735,7 @@ SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const do { int branch = selectBranch(nodeID, tag); - auto inner = static_cast(node); + auto inner = safe_downcast(node); if (inner->isEmptyBranch(branch)) return false; // Dead end, node must not be here @@ -736,8 +762,8 @@ SHAMap::getProofPath(uint256 const& key) const return {}; } - if (auto const& node = stack.top().first; - !node || node->isInner() || intr_ptr::static_pointer_cast(node)->peekItem()->key() != key) + if (auto const& node = stack.top().first; !node || node->isInner() || + intr_ptr::static_pointer_cast(node)->peekItem()->key() != key) { JLOG(journal_.debug()) << "no path to " << key; return {}; @@ -780,7 +806,8 @@ SHAMap::verifyProofPath(uint256 const& rootHash, uint256 const& key, std::vector if (node->isInner()) { auto nodeId = SHAMapNodeID::createID(depth, key); - hash = static_cast(node.get())->getChildHash(selectBranch(nodeId, key)); + hash = safe_downcast(node.get()) + ->getChildHash(selectBranch(nodeId, key)); } else { diff --git a/src/libxrpl/shamap/SHAMapTreeNode.cpp b/src/libxrpl/shamap/SHAMapTreeNode.cpp index 156570a60f..9497b4dc54 100644 --- a/src/libxrpl/shamap/SHAMapTreeNode.cpp +++ b/src/libxrpl/shamap/SHAMapTreeNode.cpp @@ -114,8 +114,9 @@ SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) // FIXME: Use SerialIter::get32? // Extract the prefix auto const type = safe_cast( - (safe_cast(rawNode[0]) << 24) + (safe_cast(rawNode[1]) << 16) + - (safe_cast(rawNode[2]) << 8) + (safe_cast(rawNode[3]))); + (safe_cast(rawNode[0]) << 24) + + (safe_cast(rawNode[1]) << 16) + (safe_cast(rawNode[2]) << 8) + + (safe_cast(rawNode[3]))); rawNode.remove_prefix(4); @@ -134,7 +135,8 @@ SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) return makeTransactionWithMeta(rawNode, hash, hashValid); Throw( - "prefix: unknown type (" + std::to_string(safe_cast>(type)) + ")"); + "prefix: unknown type (" + + std::to_string(safe_cast>(type)) + ")"); } std::string diff --git a/src/xrpld/app/tx/detail/ApplyContext.cpp b/src/libxrpl/tx/ApplyContext.cpp similarity index 78% rename from src/xrpld/app/tx/detail/ApplyContext.cpp rename to src/libxrpl/tx/ApplyContext.cpp index 6d317c251b..5a71a0d4ae 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.cpp +++ b/src/libxrpl/tx/ApplyContext.cpp @@ -1,14 +1,14 @@ -#include -#include - +#include +// #include #include #include +#include namespace xrpl { ApplyContext::ApplyContext( - Application& app_, + ServiceRegistry& registry_, OpenView& base, std::optional const& parentBatchId, STTx const& tx_, @@ -16,7 +16,7 @@ ApplyContext::ApplyContext( XRPAmount baseFee_, ApplyFlags flags, beast::Journal journal_) - : app(app_) + : registry(registry_) , tx(tx_) , preclaimResult(preclaimResult_) , baseFee(baseFee_) @@ -56,8 +56,11 @@ ApplyContext::size() void ApplyContext::visit( - std::function< - void(uint256 const&, bool, std::shared_ptr const&, std::shared_ptr const&)> const& func) + std::function const&, + std::shared_ptr const&)> const& func) { view_->visit(base_, func); } @@ -70,13 +73,17 @@ ApplyContext::failInvariantCheck(TER const result) // very wrong. We switch to tefINVARIANT_FAILED, which does NOT get included // in a ledger. - return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED) ? TER{tefINVARIANT_FAILED} - : TER{tecINVARIANT_FAILED}; + return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED) + ? TER{tefINVARIANT_FAILED} + : TER{tecINVARIANT_FAILED}; } template TER -ApplyContext::checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence) +ApplyContext::checkInvariantsHelper( + TER const result, + XRPAmount const fee, + std::index_sequence) { try { @@ -111,7 +118,8 @@ ApplyContext::checkInvariantsHelper(TER const result, XRPAmount const fee, std:: catch (std::exception const& ex) { JLOG(journal.fatal()) << "Transaction caused an exception in an invariant" - << ", ex: " << ex.what() << ", tx: " << to_string(tx.getJson(JsonOptions::none)); + << ", ex: " << ex.what() + << ", tx: " << to_string(tx.getJson(JsonOptions::none)); return failInvariantCheck(result); } @@ -123,9 +131,11 @@ TER ApplyContext::checkInvariants(TER const result, XRPAmount const fee) { XRPL_ASSERT( - isTesSuccess(result) || isTecClaim(result), "xrpl::ApplyContext::checkInvariants : is tesSUCCESS or tecCLAIM"); + isTesSuccess(result) || isTecClaim(result), + "xrpl::ApplyContext::checkInvariants : is tesSUCCESS or tecCLAIM"); - return checkInvariantsHelper(result, fee, std::make_index_sequence::value>{}); + return checkInvariantsHelper( + result, fee, std::make_index_sequence::value>{}); } } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/SignerEntries.cpp b/src/libxrpl/tx/SignerEntries.cpp similarity index 95% rename from src/xrpld/app/tx/detail/SignerEntries.cpp rename to src/libxrpl/tx/SignerEntries.cpp index 3526148638..aca1f2c19a 100644 --- a/src/xrpld/app/tx/detail/SignerEntries.cpp +++ b/src/libxrpl/tx/SignerEntries.cpp @@ -1,8 +1,8 @@ -#include - #include #include #include +#include +#include #include #include diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp similarity index 89% rename from src/xrpld/app/tx/detail/Transactor.cpp rename to src/libxrpl/tx/Transactor.cpp index 0980505315..27531bb5fb 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1,22 +1,24 @@ -#include -#include -#include -#include -#include -#include -#include -#include - #include #include +#include #include -#include #include +#include +#include +#include +#include #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -33,7 +35,7 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) if (!isPseudoTx(ctx.tx) || ctx.tx.isFieldPresent(sfNetworkID)) { - uint32_t nodeNID = ctx.app.config().NETWORK_ID; + uint32_t nodeNID = ctx.registry.getNetworkIDService().getNetworkID(); std::optional txNID = ctx.tx[~sfNetworkID]; if (nodeNID <= 1024) @@ -65,7 +67,8 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) if (ctx.tx.getFlags() & flagMask) { - JLOG(ctx.j.debug()) << ctx.tx.peekAtField(sfTransactionType).getFullText() << ": invalid flags."; + JLOG(ctx.j.debug()) << ctx.tx.peekAtField(sfTransactionType).getFullText() + << ": invalid flags."; return temINVALID_FLAG; } @@ -81,7 +84,8 @@ namespace detail { NotTEC preflightCheckSigningKey(STObject const& sigObject, beast::Journal j) { - if (auto const spk = sigObject.getFieldVL(sfSigningPubKey); !spk.empty() && !publicKeyType(makeSlice(spk))) + if (auto const spk = sigObject.getFieldVL(sfSigningPubKey); + !spk.empty() && !publicKeyType(makeSlice(spk))) { JLOG(j.debug()) << "preflightCheckSigningKey: invalid signing key"; return temBAD_SIGNATURE; @@ -178,7 +182,8 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; XRPL_ASSERT( - ctx.tx.isFlag(tfInnerBatchTxn) == ctx.parentBatchId.has_value() || !ctx.rules.enabled(featureBatch), + ctx.tx.isFlag(tfInnerBatchTxn) == ctx.parentBatchId.has_value() || + !ctx.rules.enabled(featureBatch), "Inner batch transaction must have a parent batch ID."); return tesSUCCESS; @@ -189,9 +194,11 @@ NotTEC Transactor::preflight2(PreflightContext const& ctx) { if (auto const ret = detail::preflightCheckSimulateKeys(ctx.flags, ctx.tx, ctx.j)) + { // Skips following checks if the transaction is being simulated, // regardless of success or failure return *ret; + } // It should be impossible for the InnerBatchTxn flag to be set without // featureBatch being enabled @@ -205,7 +212,7 @@ Transactor::preflight2(PreflightContext const& ctx) // Do not add any checks after this point that are relevant for // batch inner transactions. They will be skipped. - auto const sigValid = checkValidity(ctx.app.getHashRouter(), ctx.tx, ctx.rules, ctx.app.config()); + auto const sigValid = checkValidity(ctx.registry.getHashRouter(), ctx.tx, ctx.rules); if (sigValid.first == Validity::SigBad) { // LCOV_EXCL_START JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second; @@ -277,7 +284,8 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) // Each signer adds one more baseFee to the minimum required fee // for the transaction. - std::size_t const signerCount = tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; + std::size_t const signerCount = + tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; return baseFee + (signerCount * baseFee); } @@ -304,9 +312,13 @@ Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx) } XRPAmount -Transactor::minimumFee(Application& app, XRPAmount baseFee, Fees const& fees, ApplyFlags flags) +Transactor::minimumFee( + ServiceRegistry& registry, + XRPAmount baseFee, + Fees const& fees, + ApplyFlags flags) { - return scaleFeeLoad(baseFee, app.getFeeTrack(), fees, flags & tapUNLIMITED); + return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, flags & tapUNLIMITED); } TER @@ -332,11 +344,12 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) // Only check fee is sufficient when the ledger is open. if (ctx.view.open()) { - auto const feeDue = minimumFee(ctx.app, baseFee, ctx.view.fees(), ctx.flags); + auto const feeDue = minimumFee(ctx.registry, baseFee, ctx.view.fees(), ctx.flags); if (feePaid < feeDue) { - JLOG(ctx.j.trace()) << "Insufficient fee paid: " << to_string(feePaid) << "/" << to_string(feeDue); + JLOG(ctx.j.trace()) << "Insufficient fee paid: " << to_string(feePaid) << "/" + << to_string(feeDue); return telINSUF_FEE_P; } } @@ -344,8 +357,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = - ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate) : ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getFeePayer(); auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; @@ -374,32 +386,18 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - if (ctx_.tx.isFieldPresent(sfDelegate)) - { - // Delegated transactions are paid by the delegated account. - auto const delegate = ctx_.tx.getAccountID(sfDelegate); - auto const delegatedSle = view().peek(keylet::account(delegate)); - if (!delegatedSle) - return tefINTERNAL; // LCOV_EXCL_LINE + auto const feePayer = ctx_.tx.getFeePayer(); + auto const sle = view().peek(keylet::account(feePayer)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE - delegatedSle->setFieldAmount(sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid); - view().update(delegatedSle); - } - else - { - auto const sle = view().peek(keylet::account(account_)); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE - - // Deduct the fee, so it's not available during the transaction. - // Will only write the account back if the transaction succeeds. - - mSourceBalance -= feePaid; - sle->setFieldAmount(sfBalance, mSourceBalance); - - // VFALCO Should we call view().rawDestroyXRP() here as well? - } + // Deduct the fee, so it's not available during the transaction. + // Will only write the account back if the transaction succeeds. + sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid); + if (feePayer != account_) + view().update(sle); // done in `apply()` for the account + // VFALCO Should we call view().rawDestroyXRP() here as well? return tesSUCCESS; } @@ -412,7 +410,8 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j if (!sle) { - JLOG(j.trace()) << "applyTransaction: delay: source account does not exist " << toBase58(id); + JLOG(j.trace()) << "applyTransaction: delay: source account does not exist " + << toBase58(id); return terNO_ACCOUNT; } @@ -476,7 +475,8 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx) if (!sle) { - JLOG(ctx.j.trace()) << "applyTransaction: delay: source account does not exist " << toBase58(id); + JLOG(ctx.j.trace()) << "applyTransaction: delay: source account does not exist " + << toBase58(id); return terNO_ACCOUNT; } @@ -484,7 +484,8 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx) (sle->getFieldH256(sfAccountTxnID) != ctx.tx.getFieldH256(sfAccountTxnID))) return tefWRONG_PRIOR; - if (ctx.tx.isFieldPresent(sfLastLedgerSequence) && (ctx.view.seq() > ctx.tx.getFieldU32(sfLastLedgerSequence))) + if (ctx.tx.isFieldPresent(sfLastLedgerSequence) && + (ctx.view.seq() > ctx.tx.getFieldU32(sfLastLedgerSequence))) return tefMAX_LEDGER; if (ctx.view.txExists(ctx.tx.getTransactionID())) @@ -511,7 +512,11 @@ Transactor::consumeSeqProxy(SLE::pointer const& sleAccount) // Remove a single Ticket from the ledger. TER -Transactor::ticketDelete(ApplyView& view, AccountID const& account, uint256 const& ticketIndex, beast::Journal j) +Transactor::ticketDelete( + ApplyView& view, + AccountID const& account, + uint256 const& ticketIndex, + beast::Journal j) { // Delete the Ticket, adjust the account root ticket count, and // reduce the owner count. @@ -547,9 +552,13 @@ Transactor::ticketDelete(ApplyView& view, AccountID const& account, uint256 cons if (auto ticketCount = (*sleAccount)[~sfTicketCount]) { if (*ticketCount == 1) + { sleAccount->makeFieldAbsent(sfTicketCount); + } else + { ticketCount = *ticketCount - 1; + } } else { @@ -585,19 +594,20 @@ Transactor::apply() // sle must exist except for transactions // that allow zero account. - XRPL_ASSERT(sle != nullptr || account_ == beast::zero, "xrpl::Transactor::apply : non-null SLE or zero account"); + XRPL_ASSERT( + sle != nullptr || account_ == beast::zero, + "xrpl::Transactor::apply : non-null SLE or zero account"); if (sle) { - mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp(); - mSourceBalance = mPriorBalance; + preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp(); TER result = consumeSeqProxy(sle); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; result = payFee(); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; if (sle->isFieldPresent(sfAccountTxnID)) @@ -622,10 +632,12 @@ Transactor::checkSign( auto const sle = view.read(keylet::account(idAccount)); if (view.rules().enabled(featureLendingProtocol) && isPseudoAccount(sle)) + { // Pseudo-accounts can't sign transactions. This check is gated on // the Lending Protocol amendment because that's the project it was // added under, and it doesn't justify another amendment return tefBAD_AUTH; + } } auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey); @@ -633,7 +645,8 @@ Transactor::checkSign( if (parentBatchId && view.rules().enabled(featureBatch)) { // Defensive Check: These values are also checked in Batch::preflight - if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() || sigObject.isFieldPresent(sfSigners)) + if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() || + sigObject.isFieldPresent(sfSigners)) { return temINVALID_FLAG; // LCOV_EXCL_LINE } @@ -664,7 +677,8 @@ Transactor::checkSign( } // Look up the account. - auto const idSigner = pkSigner.empty() ? idAccount : calcAccountID(PublicKey(makeSlice(pkSigner))); + auto const idSigner = + pkSigner.empty() ? idAccount : calcAccountID(PublicKey(makeSlice(pkSigner))); auto const sleAccount = view.read(keylet::account(idAccount)); if (!sleAccount) return terNO_ACCOUNT; @@ -675,8 +689,8 @@ Transactor::checkSign( NotTEC Transactor::checkSign(PreclaimContext const& ctx) { - auto const idAccount = - ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate) : ctx.tx.getAccountID(sfAccount); + auto const idAccount = ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate) + : ctx.tx.getAccountID(sfAccount); return checkSign(ctx.view, ctx.flags, ctx.parentBatchId, idAccount, ctx.tx, ctx.j); } @@ -692,7 +706,8 @@ Transactor::checkBatchSign(PreclaimContext const& ctx) Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey); if (pkSigner.empty()) { - if (ret = checkMultiSign(ctx.view, ctx.flags, idAccount, signer, ctx.j); !isTesSuccess(ret)) + if (ret = checkMultiSign(ctx.view, ctx.flags, idAccount, signer, ctx.j); + !isTesSuccess(ret)) return ret; } else @@ -715,7 +730,8 @@ Transactor::checkBatchSign(PreclaimContext const& ctx) return tesSUCCESS; } - if (ret = checkSingleSign(ctx.view, idSigner, idAccount, sleAccount, ctx.j); !isTesSuccess(ret)) + if (ret = checkSingleSign(ctx.view, idSigner, idAccount, sleAccount, ctx.j); + !isTesSuccess(ret)) return ret; } } @@ -774,9 +790,11 @@ Transactor::checkMultiSign( // We have plans to support multiple SignerLists in the future. The // presence and defaulted value of the SignerListID field will enable that. XRPL_ASSERT( - sleAccountSigners->isFieldPresent(sfSignerListID), "xrpl::Transactor::checkMultiSign : has signer list ID"); + sleAccountSigners->isFieldPresent(sfSignerListID), + "xrpl::Transactor::checkMultiSign : has signer list ID"); XRPL_ASSERT( - sleAccountSigners->getFieldU32(sfSignerListID) == 0, "xrpl::Transactor::checkMultiSign : signer list ID is 0"); + sleAccountSigners->getFieldU32(sfSignerListID) == 0, + "xrpl::Transactor::checkMultiSign : signer list ID is 0"); auto accountSigners = SignerEntries::deserialize(*sleAccountSigners, j, "ledger"); if (!accountSigners) @@ -932,7 +950,10 @@ removeUnfundedOffers(ApplyView& view, std::vector const& offers, beast: } static void -removeExpiredNFTokenOffers(ApplyView& view, std::vector const& offers, beast::Journal viewJ) +removeExpiredNFTokenOffers( + ApplyView& view, + std::vector const& offers, + beast::Journal viewJ) { std::size_t removed = 0; @@ -958,7 +979,10 @@ removeExpiredCredentials(ApplyView& view, std::vector const& creds, bea } static void -modifyWasmDataFields(ApplyView& view, std::vector> const& wasmObjects, beast::Journal viewJ) +modifyWasmDataFields( + ApplyView& view, + std::vector> const& wasmObjects, + beast::Journal viewJ) { for (auto const& [index, data] : wasmObjects) { @@ -971,18 +995,22 @@ modifyWasmDataFields(ApplyView& view, std::vector> cons } static void -removeDeletedTrustLines(ApplyView& view, std::vector const& trustLines, beast::Journal viewJ) +removeDeletedTrustLines( + ApplyView& view, + std::vector const& trustLines, + beast::Journal viewJ) { if (trustLines.size() > maxDeletableAMMTrustLines) { - JLOG(viewJ.error()) << "removeDeletedTrustLines: deleted trustlines exceed max " << trustLines.size(); + JLOG(viewJ.error()) << "removeDeletedTrustLines: deleted trustlines exceed max " + << trustLines.size(); return; } for (auto const& index : trustLines) { if (auto const sleState = view.peek({ltRIPPLE_STATE, index}); - deleteAMMTrustLine(view, sleState, std::nullopt, viewJ) != tesSUCCESS) + !isTesSuccess(deleteAMMTrustLine(view, sleState, std::nullopt, viewJ))) { JLOG(viewJ.error()) << "removeDeletedTrustLines: failed to delete AMM trustline"; } @@ -1006,8 +1034,7 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::zero}; - auto const payerSle = - ctx_.tx.isFieldPresent(sfDelegate) ? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate))) : txnAcct; + auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer())); if (!payerSle) return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE @@ -1015,7 +1042,8 @@ Transactor::reset(XRPAmount fee) // balance should have already been checked in checkFee / preFlight. XRPL_ASSERT( - balance != beast::zero && (!view().open() || balance >= fee), "xrpl::Transactor::reset : valid balance"); + balance != beast::zero && (!view().open() || balance >= fee), + "xrpl::Transactor::reset : valid balance"); // We retry/reject the transaction if the account balance is zero or // we're applying against an open ledger and the balance is less than @@ -1085,13 +1113,13 @@ Transactor::operator()() } #endif - if (auto const& trap = ctx_.app.trapTxID(); trap && *trap == ctx_.tx.getTransactionID()) + if (auto const& trap = ctx_.registry.trapTxID(); trap && *trap == ctx_.tx.getTransactionID()) { trapTransaction(*trap); } auto result = ctx_.preclaimResult; - if (result == tesSUCCESS) + if (isTesSuccess(result)) result = apply(); // No transaction can return temUNKNOWN from apply, @@ -1115,8 +1143,9 @@ Transactor::operator()() applied = false; } else if ( - (result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) || (result == tecEXPIRED) || - (result == tecWASM_REJECTED) || (isTecClaimHardFail(result, view().flags()))) + (result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) || + (result == tecEXPIRED) || (result == tecWASM_REJECTED) || + (isTecClaimHardFail(result, view().flags()))) { JLOG(j_.trace()) << "reapplying because of " << transToken(result); @@ -1170,7 +1199,8 @@ Transactor::operator()() removedTrustLines.push_back(index); } - if (doNFTokenOffers && before && after && (before->getType() == ltNFTOKEN_OFFER)) + if (doNFTokenOffers && before && after && + (before->getType() == ltNFTOKEN_OFFER)) expiredNFTokenOffers.push_back(index); if (doCredentials && before && after && (before->getType() == ltCREDENTIAL)) @@ -1195,19 +1225,19 @@ Transactor::operator()() // If necessary, remove any offers found unfunded during processing if ((result == tecOVERSIZE) || (result == tecKILLED)) - removeUnfundedOffers(view(), removedOffers, ctx_.app.journal("View")); + removeUnfundedOffers(view(), removedOffers, ctx_.registry.journal("View")); if (result == tecEXPIRED) - removeExpiredNFTokenOffers(view(), expiredNFTokenOffers, ctx_.app.journal("View")); + removeExpiredNFTokenOffers(view(), expiredNFTokenOffers, ctx_.registry.journal("View")); if (result == tecINCOMPLETE) - removeDeletedTrustLines(view(), removedTrustLines, ctx_.app.journal("View")); + removeDeletedTrustLines(view(), removedTrustLines, ctx_.registry.journal("View")); if (result == tecEXPIRED) - removeExpiredCredentials(view(), expiredCredentials, ctx_.app.journal("View")); + removeExpiredCredentials(view(), expiredCredentials, ctx_.registry.journal("View")); if (result == tecWASM_REJECTED) - modifyWasmDataFields(view(), modifiedWasmObjects, ctx_.app.journal("View")); + modifyWasmDataFields(view(), modifiedWasmObjects, ctx_.registry.journal("View")); applied = isTecClaim(result); } diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/libxrpl/tx/apply.cpp similarity index 83% rename from src/xrpld/app/tx/detail/apply.cpp rename to src/libxrpl/tx/apply.cpp index 7babc415d5..8fd898366e 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/libxrpl/tx/apply.cpp @@ -1,10 +1,10 @@ -#include -#include -#include - #include +#include +#include #include #include +#include +#include namespace xrpl { @@ -18,7 +18,7 @@ constexpr HashRouterFlags SF_LOCALGOOD = HashRouterFlags::PRIVATE4; // Local ch //------------------------------------------------------------------------------ std::pair -checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config const& config) +checkValidity(HashRouter& router, STTx const& tx, Rules const& rules) { auto const id = tx.getTransactionID(); auto const flags = router.getFlags(id); @@ -27,7 +27,8 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config con if (tx.isFlag(tfInnerBatchTxn) && rules.enabled(featureBatch)) { // Defensive Check: These values are also checked in Batch::preflight - if (tx.isFieldPresent(sfTxnSignature) || !tx.getSigningPubKey().empty() || tx.isFieldPresent(sfSigners)) + if (tx.isFieldPresent(sfTxnSignature) || !tx.getSigningPubKey().empty() || + tx.isFieldPresent(sfSigners)) return {Validity::SigBad, "Malformed: Invalid inner batch transaction."}; // This block should probably have never been included in the @@ -49,8 +50,10 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config con } if (any(flags & SF_SIGBAD)) + { // Signature is known bad return {Validity::SigBad, "Transaction has bad signature."}; + } if (!any(flags & SF_SIGGOOD)) { @@ -65,14 +68,18 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config con // Signature is now known good if (any(flags & SF_LOCALBAD)) + { // ...but the local checks // are known bad. return {Validity::SigGoodOnly, "Local checks failed."}; + } if (any(flags & SF_LOCALGOOD)) + { // ...and the local checks // are known good. return {Validity::Valid, ""}; + } // Do the local checks std::string reason; @@ -107,32 +114,39 @@ forceValidity(HashRouter& router, uint256 const& txid, Validity validity) template ApplyResult -apply(Application& app, OpenView& view, PreflightChecks&& preflightChecks) +apply(ServiceRegistry& registry, OpenView& view, PreflightChecks&& preflightChecks) { NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; - return doApply(preclaim(preflightChecks(), app, view), app, view); + return doApply(preclaim(preflightChecks(), registry, view), registry, view); } ApplyResult -apply(Application& app, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal j) +apply(ServiceRegistry& registry, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal j) { - return apply(app, view, [&]() mutable { return preflight(app, view.rules(), tx, flags, j); }); + return apply( + registry, view, [&]() mutable { return preflight(registry, view.rules(), tx, flags, j); }); } ApplyResult apply( - Application& app, + ServiceRegistry& registry, OpenView& view, uint256 const& parentBatchId, STTx const& tx, ApplyFlags flags, beast::Journal j) { - return apply(app, view, [&]() mutable { return preflight(app, view.rules(), parentBatchId, tx, flags, j); }); + return apply(registry, view, [&]() mutable { + return preflight(registry, view.rules(), parentBatchId, tx, flags, j); + }); } static bool -applyBatchTransactions(Application& app, OpenView& batchView, STTx const& batchTxn, beast::Journal j) +applyBatchTransactions( + ServiceRegistry& registry, + OpenView& batchView, + STTx const& batchTxn, + beast::Journal j) { XRPL_ASSERT( batchTxn.getTxnType() == ttBATCH && batchTxn.getFieldArray(sfRawTransactions).size() != 0, @@ -141,12 +155,13 @@ applyBatchTransactions(Application& app, OpenView& batchView, STTx const& batchT auto const parentBatchId = batchTxn.getTransactionID(); auto const mode = batchTxn.getFlags(); - auto applyOneTransaction = [&app, &j, &parentBatchId, &batchView](STTx&& tx) { + auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) { OpenView perTxBatchView(batch_view, batchView); - auto const ret = apply(app, perTxBatchView, parentBatchId, tx, tapBATCH, j); + auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, tapBATCH, j); XRPL_ASSERT( - ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)), "Inner transaction should not be applied"); + ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)), + "Inner transaction should not be applied"); JLOG(j.debug()) << "BatchTrace[" << parentBatchId << "]: " << tx.getTransactionID() << " " << (ret.applied ? "applied" : "failure") << ": " << transToken(ret.ter); @@ -180,7 +195,9 @@ applyBatchTransactions(Application& app, OpenView& batchView, STTx const& batchT break; } else if (mode & tfOnlyOne) + { break; + } } return applied != 0; @@ -188,7 +205,7 @@ applyBatchTransactions(Application& app, OpenView& batchView, STTx const& batchT ApplyTransactionResult applyTransaction( - Application& app, + ServiceRegistry& registry, OpenView& view, STTx const& txn, bool retryAssured, @@ -203,7 +220,7 @@ applyTransaction( try { - auto const result = apply(app, view, txn, flags, j); + auto const result = apply(registry, view, txn, flags, j); if (result.applied) { @@ -215,7 +232,7 @@ applyTransaction( { OpenView wholeBatchView(batch_view, view); - if (applyBatchTransactions(app, wholeBatchView, txn, j)) + if (applyBatchTransactions(registry, wholeBatchView, txn, j)) wholeBatchView.apply(view); } diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp similarity index 90% rename from src/xrpld/app/tx/detail/applySteps.cpp rename to src/libxrpl/tx/applySteps.cpp index 06f0db1a79..6c6b3f8c98 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -1,4 +1,4 @@ -#include +#include #pragma push_macro("TRANSACTION") #undef TRANSACTION @@ -14,6 +14,7 @@ // DO NOT INCLUDE TRANSACTOR HEADER FILES HERE. // See the instructions at the top of transactions.macro instead. +#include #include #include @@ -130,7 +131,8 @@ invoke_preflight(PreflightContext const& ctx) { return with_txn_type(ctx.rules, ctx.tx.getTxnType(), [&]() { auto const tec = Transactor::invokePreflight(ctx); - return std::make_pair(tec, isTesSuccess(tec) ? consequences_helper(ctx) : TxConsequences{tec}); + return std::make_pair( + tec, isTesSuccess(tec) ? consequences_helper(ctx) : TxConsequences{tec}); }); } catch (UnknownTxnType const& e) @@ -224,8 +226,9 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) { try { - return with_txn_type( - view.rules(), tx.getTxnType(), [&]() { return T::calculateBaseFee(view, tx); }); + return with_txn_type(view.rules(), tx.getTxnType(), [&]() { + return T::calculateBaseFee(view, tx); + }); } catch (UnknownTxnType const& e) { @@ -243,7 +246,8 @@ TxConsequences::TxConsequences(NotTEC pfResult) , seqProx_(SeqProxy::sequence(0)) , sequencesConsumed_(0) { - XRPL_ASSERT(!isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS"); + XRPL_ASSERT( + !isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS"); } TxConsequences::TxConsequences(STTx const& tx) @@ -292,9 +296,14 @@ invoke_apply(ApplyContext& ctx) } PreflightResult -preflight(Application& app, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j) +preflight( + ServiceRegistry& registry, + Rules const& rules, + STTx const& tx, + ApplyFlags flags, + beast::Journal j) { - PreflightContext const pfCtx(app, tx, rules, flags, j); + PreflightContext const pfCtx(registry, tx, rules, flags, j); try { return {pfCtx, invoke_preflight(pfCtx)}; @@ -308,14 +317,14 @@ preflight(Application& app, Rules const& rules, STTx const& tx, ApplyFlags flags PreflightResult preflight( - Application& app, + ServiceRegistry& registry, Rules const& rules, uint256 const& parentBatchId, STTx const& tx, ApplyFlags flags, beast::Journal j) { - PreflightContext const pfCtx(app, tx, parentBatchId, rules, flags, j); + PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j); try { return {pfCtx, invoke_preflight(pfCtx)}; @@ -328,26 +337,33 @@ preflight( } PreclaimResult -preclaim(PreflightResult const& preflightResult, Application& app, OpenView const& view) +preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view) { std::optional ctx; if (preflightResult.rules != view.rules()) { auto secondFlight = [&]() { if (preflightResult.parentBatchId) + { return preflight( - app, + registry, view.rules(), preflightResult.parentBatchId.value(), preflightResult.tx, preflightResult.flags, preflightResult.j); + } - return preflight(app, view.rules(), preflightResult.tx, preflightResult.flags, preflightResult.j); + return preflight( + registry, + view.rules(), + preflightResult.tx, + preflightResult.flags, + preflightResult.j); }(); ctx.emplace( - app, + registry, view, secondFlight.ter, secondFlight.tx, @@ -358,7 +374,7 @@ preclaim(PreflightResult const& preflightResult, Application& app, OpenView cons else { ctx.emplace( - app, + registry, view, preflightResult.ter, preflightResult.tx, @@ -369,7 +385,7 @@ preclaim(PreflightResult const& preflightResult, Application& app, OpenView cons try { - if (ctx->preflightResult != tesSUCCESS) + if (!isTesSuccess(ctx->preflightResult)) return {*ctx, ctx->preflightResult}; return {*ctx, invoke_preclaim(*ctx)}; } @@ -393,7 +409,7 @@ calculateDefaultBaseFee(ReadView const& view, STTx const& tx) } ApplyResult -doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) +doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view) { if (preclaimResult.view.seq() != view.seq()) { @@ -406,7 +422,7 @@ doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) if (!preclaimResult.likelyToClaimFee) return {preclaimResult.ter, false}; ApplyContext ctx( - app, + registry, view, preclaimResult.parentBatchId, preclaimResult.tx, diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp new file mode 100644 index 0000000000..b64be6192b --- /dev/null +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -0,0 +1,309 @@ +#include +// +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidAMM::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (isDelete) + return; + + if (after) + { + auto const type = after->getType(); + // AMM object changed + if (type == ltAMM) + { + ammAccount_ = after->getAccountID(sfAccount); + lptAMMBalanceAfter_ = after->getFieldAmount(sfLPTokenBalance); + } + // AMM pool changed + else if ( + (type == ltRIPPLE_STATE && after->getFlags() & lsfAMMNode) || + (type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID))) + { + ammPoolChanged_ = true; + } + } + + if (before) + { + // AMM object changed + if (before->getType() == ltAMM) + { + lptAMMBalanceBefore_ = before->getFieldAmount(sfLPTokenBalance); + } + } +} + +static bool +validBalances( + STAmount const& amount, + STAmount const& amount2, + STAmount const& lptAMMBalance, + ValidAMM::ZeroAllowed zeroAllowed) +{ + bool const positive = + amount > beast::zero && amount2 > beast::zero && lptAMMBalance > beast::zero; + if (zeroAllowed == ValidAMM::ZeroAllowed::Yes) + { + return positive || + (amount == beast::zero && amount2 == beast::zero && lptAMMBalance == beast::zero); + } + return positive; +} + +bool +ValidAMM::finalizeVote(bool enforce, beast::Journal const& j) const +{ + if (lptAMMBalanceAfter_ != lptAMMBalanceBefore_ || ammPoolChanged_) + { + // LPTokens and the pool can not change on vote + // LCOV_EXCL_START + JLOG(j.error()) << "AMMVote invariant failed: " << lptAMMBalanceBefore_.value_or(STAmount{}) + << " " << lptAMMBalanceAfter_.value_or(STAmount{}) << " " + << ammPoolChanged_; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + + return true; +} + +bool +ValidAMM::finalizeBid(bool enforce, beast::Journal const& j) const +{ + if (ammPoolChanged_) + { + // The pool can not change on bid + // LCOV_EXCL_START + JLOG(j.error()) << "AMMBid invariant failed: pool changed"; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + // LPTokens are burnt, therefore there should be fewer LPTokens + else if ( + lptAMMBalanceBefore_ && lptAMMBalanceAfter_ && + (*lptAMMBalanceAfter_ > *lptAMMBalanceBefore_ || *lptAMMBalanceAfter_ <= beast::zero)) + { + // LCOV_EXCL_START + JLOG(j.error()) << "AMMBid invariant failed: " << *lptAMMBalanceBefore_ << " " + << *lptAMMBalanceAfter_; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + + return true; +} + +bool +ValidAMM::finalizeCreate( + STTx const& tx, + ReadView const& view, + bool enforce, + beast::Journal const& j) const +{ + if (!ammAccount_) + { + // LCOV_EXCL_START + JLOG(j.error()) << "AMMCreate invariant failed: AMM object is not created"; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + else + { + auto const [amount, amount2] = ammPoolHolds( + view, + *ammAccount_, + tx[sfAmount].get(), + tx[sfAmount2].get(), + fhIGNORE_FREEZE, + j); + // Create invariant: + // sqrt(amount * amount2) == LPTokens + // all balances are greater than zero + if (!validBalances(amount, amount2, *lptAMMBalanceAfter_, ZeroAllowed::No) || + ammLPTokens(amount, amount2, lptAMMBalanceAfter_->issue()) != *lptAMMBalanceAfter_) + { + JLOG(j.error()) << "AMMCreate invariant failed: " << amount << " " << amount2 << " " + << *lptAMMBalanceAfter_; + if (enforce) + return false; + } + } + + return true; +} + +bool +ValidAMM::finalizeDelete(bool enforce, TER res, beast::Journal const& j) const +{ + if (ammAccount_) + { + // LCOV_EXCL_START + std::string const msg = (isTesSuccess(res)) ? "AMM object is not deleted on tesSUCCESS" + : "AMM object is changed on tecINCOMPLETE"; + JLOG(j.error()) << "AMMDelete invariant failed: " << msg; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + + return true; +} + +bool +ValidAMM::finalizeDEX(bool enforce, beast::Journal const& j) const +{ + if (ammAccount_) + { + // LCOV_EXCL_START + JLOG(j.error()) << "AMM swap invariant failed: AMM object changed"; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + + return true; +} + +bool +ValidAMM::generalInvariant( + xrpl::STTx const& tx, + xrpl::ReadView const& view, + ZeroAllowed zeroAllowed, + beast::Journal const& j) const +{ + auto const [amount, amount2] = ammPoolHolds( + view, + *ammAccount_, + tx[sfAsset].get(), + tx[sfAsset2].get(), + fhIGNORE_FREEZE, + j); + // Deposit and Withdrawal invariant: + // sqrt(amount * amount2) >= LPTokens + // all balances are greater than zero + // unless on last withdrawal + auto const poolProductMean = root2(amount * amount2); + bool const nonNegativeBalances = + validBalances(amount, amount2, *lptAMMBalanceAfter_, zeroAllowed); + bool const strongInvariantCheck = poolProductMean >= *lptAMMBalanceAfter_; + // Allow for a small relative error if strongInvariantCheck fails + auto weakInvariantCheck = [&]() { + return *lptAMMBalanceAfter_ != beast::zero && + withinRelativeDistance(poolProductMean, Number{*lptAMMBalanceAfter_}, Number{1, -11}); + }; + if (!nonNegativeBalances || (!strongInvariantCheck && !weakInvariantCheck())) + { + JLOG(j.error()) << "AMM " << tx.getTxnType() + << " invariant failed: " << tx.getHash(HashPrefix::transactionID) << " " + << ammPoolChanged_ << " " << amount << " " << amount2 << " " + << poolProductMean << " " << lptAMMBalanceAfter_->getText() << " " + << ((*lptAMMBalanceAfter_ == beast::zero) + ? Number{1} + : ((*lptAMMBalanceAfter_ - poolProductMean) / poolProductMean)); + return false; + } + + return true; +} + +bool +ValidAMM::finalizeDeposit( + xrpl::STTx const& tx, + xrpl::ReadView const& view, + bool enforce, + beast::Journal const& j) const +{ + if (!ammAccount_) + { + // LCOV_EXCL_START + JLOG(j.error()) << "AMMDeposit invariant failed: AMM object is deleted"; + if (enforce) + return false; + // LCOV_EXCL_STOP + } + else if (!generalInvariant(tx, view, ZeroAllowed::No, j) && enforce) + { + return false; + } + + return true; +} + +bool +ValidAMM::finalizeWithdraw( + xrpl::STTx const& tx, + xrpl::ReadView const& view, + bool enforce, + beast::Journal const& j) const +{ + if (!ammAccount_) + { + // Last Withdraw or Clawback deleted AMM + } + else if (!generalInvariant(tx, view, ZeroAllowed::Yes, j)) + { + if (enforce) + return false; + } + + return true; +} + +bool +ValidAMM::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + // Delete may return tecINCOMPLETE if there are too many + // trustlines to delete. + if (!isTesSuccess(result) && result != tecINCOMPLETE) + return true; + + bool const enforce = view.rules().enabled(fixAMMv1_3); + + switch (tx.getTxnType()) + { + case ttAMM_CREATE: + return finalizeCreate(tx, view, enforce, j); + case ttAMM_DEPOSIT: + return finalizeDeposit(tx, view, enforce, j); + case ttAMM_CLAWBACK: + case ttAMM_WITHDRAW: + return finalizeWithdraw(tx, view, enforce, j); + case ttAMM_BID: + return finalizeBid(enforce, j); + case ttAMM_VOTE: + return finalizeVote(enforce, j); + case ttAMM_DELETE: + return finalizeDelete(enforce, result, j); + case ttCHECK_CASH: + case ttOFFER_CREATE: + case ttPAYMENT: + return finalizeDEX(enforce, j); + default: + break; + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp new file mode 100644 index 0000000000..f38d260782 --- /dev/null +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -0,0 +1,282 @@ +#include +// +#include +#include +#include +#include +#include + +namespace xrpl { + +void +TransfersNotFrozen::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + /* + * A trust line freeze state alone doesn't determine if a transfer is + * frozen. The transfer must be examined "end-to-end" because both sides of + * the transfer may have different freeze states and freeze impact depends + * on the transfer direction. This is why first we need to track the + * transfers using IssuerChanges senders/receivers. + * + * Only in validateIssuerChanges, after we collected all changes can we + * determine if the transfer is valid. + */ + if (!isValidEntry(before, after)) + { + return; + } + + auto const balanceChange = calculateBalanceChange(before, after, isDelete); + if (balanceChange.signum() == 0) + { + return; + } + + recordBalanceChanges(after, balanceChange); +} + +bool +TransfersNotFrozen::finalize( + STTx const& tx, + TER const ter, + XRPAmount const fee, + ReadView const& view, + beast::Journal const& j) +{ + /* + * We check this invariant regardless of deep freeze amendment status, + * allowing for detection and logging of potential issues even when the + * amendment is disabled. + * + * If an exploit that allows moving frozen assets is discovered, + * we can alert operators who monitor fatal messages and trigger assert in + * debug builds for an early warning. + * + * In an unlikely event that an exploit is found, this early detection + * enables encouraging the UNL to expedite deep freeze amendment activation + * or deploy hotfixes via new amendments. In case of a new amendment, we'd + * only have to change this line setting 'enforce' variable. + * enforce = view.rules().enabled(featureDeepFreeze) || + * view.rules().enabled(fixFreezeExploit); + */ + [[maybe_unused]] bool const enforce = view.rules().enabled(featureDeepFreeze); + + for (auto const& [issue, changes] : balanceChanges_) + { + auto const issuerSle = findIssuer(issue.account, view); + // It should be impossible for the issuer to not be found, but check + // just in case so rippled doesn't crash in release. + if (!issuerSle) + { + // The comment above starting with "assert(enforce)" explains this + // assert. + XRPL_ASSERT( + enforce, + "xrpl::TransfersNotFrozen::finalize : enforce " + "invariant."); + if (enforce) + { + return false; + } + continue; + } + + if (!validateIssuerChanges(issuerSle, changes, tx, j, enforce)) + { + return false; + } + } + + return true; +} + +bool +TransfersNotFrozen::isValidEntry( + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + // `after` can never be null, even if the trust line is deleted. + XRPL_ASSERT(after, "xrpl::TransfersNotFrozen::isValidEntry : valid after."); + if (!after) + { + return false; + } + + if (after->getType() == ltACCOUNT_ROOT) + { + possibleIssuers_.emplace(after->at(sfAccount), after); + return false; + } + + /* While LedgerEntryTypesMatch invariant also checks types, all invariants + * are processed regardless of previous failures. + * + * This type check is still necessary here because it prevents potential + * issues in subsequent processing. + */ + return after->getType() == ltRIPPLE_STATE && (!before || before->getType() == ltRIPPLE_STATE); +} + +STAmount +TransfersNotFrozen::calculateBalanceChange( + std::shared_ptr const& before, + std::shared_ptr const& after, + bool isDelete) +{ + auto const getBalance = [](auto const& line, auto const& other, bool zero) { + STAmount amt = line ? line->at(sfBalance) : other->at(sfBalance).zeroed(); + return zero ? amt.zeroed() : amt; + }; + + /* Trust lines can be created dynamically by other transactions such as + * Payment and OfferCreate that cross offers. Such trust line won't be + * created frozen, but the sender might be, so the starting balance must be + * treated as zero. + */ + auto const balanceBefore = getBalance(before, after, false); + + /* Same as above, trust lines can be dynamically deleted, and for frozen + * trust lines, payments not involving the issuer must be blocked. This is + * achieved by treating the final balance as zero when isDelete=true to + * ensure frozen line restrictions are enforced even during deletion. + */ + auto const balanceAfter = getBalance(after, before, isDelete); + + return balanceAfter - balanceBefore; +} + +void +TransfersNotFrozen::recordBalance(Issue const& issue, BalanceChange change) +{ + XRPL_ASSERT( + change.balanceChangeSign, + "xrpl::TransfersNotFrozen::recordBalance : valid trustline " + "balance sign."); + auto& changes = balanceChanges_[issue]; + if (change.balanceChangeSign < 0) + { + changes.senders.emplace_back(std::move(change)); + } + else + { + changes.receivers.emplace_back(std::move(change)); + } +} + +void +TransfersNotFrozen::recordBalanceChanges( + std::shared_ptr const& after, + STAmount const& balanceChange) +{ + auto const balanceChangeSign = balanceChange.signum(); + auto const currency = after->at(sfBalance).getCurrency(); + + // Change from low account's perspective, which is trust line default + recordBalance({currency, after->at(sfHighLimit).getIssuer()}, {after, balanceChangeSign}); + + // Change from high account's perspective, which reverses the sign. + recordBalance({currency, after->at(sfLowLimit).getIssuer()}, {after, -balanceChangeSign}); +} + +std::shared_ptr +TransfersNotFrozen::findIssuer(AccountID const& issuerID, ReadView const& view) +{ + if (auto it = possibleIssuers_.find(issuerID); it != possibleIssuers_.end()) + { + return it->second; + } + + return view.read(keylet::account(issuerID)); +} + +bool +TransfersNotFrozen::validateIssuerChanges( + std::shared_ptr const& issuer, + IssuerChanges const& changes, + STTx const& tx, + beast::Journal const& j, + bool enforce) +{ + if (!issuer) + { + return false; + } + + bool const globalFreeze = issuer->isFlag(lsfGlobalFreeze); + if (changes.receivers.empty() || changes.senders.empty()) + { + /* If there are no receivers, then the holder(s) are returning + * their tokens to the issuer. Likewise, if there are no + * senders, then the issuer is issuing tokens to the holder(s). + * This is allowed regardless of the issuer's freeze flags. (The + * holder may have contradicting freeze flags, but that will be + * checked when the holder is treated as issuer.) + */ + return true; + } + + for (auto const& actors : {changes.senders, changes.receivers}) + { + for (auto const& change : actors) + { + bool const high = change.line->at(sfLowLimit).getIssuer() == issuer->at(sfAccount); + + if (!validateFrozenState(change, high, tx, j, enforce, globalFreeze)) + { + return false; + } + } + } + return true; +} + +bool +TransfersNotFrozen::validateFrozenState( + BalanceChange const& change, + bool high, + STTx const& tx, + beast::Journal const& j, + bool enforce, + bool globalFreeze) +{ + bool const freeze = + change.balanceChangeSign < 0 && change.line->isFlag(high ? lsfLowFreeze : lsfHighFreeze); + bool const deepFreeze = change.line->isFlag(high ? lsfLowDeepFreeze : lsfHighDeepFreeze); + bool const frozen = globalFreeze || deepFreeze || freeze; + + bool const isAMMLine = change.line->isFlag(lsfAMMNode); + + if (!frozen) + { + return true; + } + + // AMMClawbacks are allowed to override some freeze rules + if ((!isAMMLine || globalFreeze) && hasPrivilege(tx, overrideFreeze)) + { + JLOG(j.debug()) << "Invariant check allowing funds to be moved " + << (change.balanceChangeSign > 0 ? "to" : "from") + << " a frozen trustline for AMMClawback " << tx.getTransactionID(); + return true; + } + + JLOG(j.fatal()) << "Invariant failed: Attempting to move frozen funds for " + << tx.getTransactionID(); + // The comment above starting with "assert(enforce)" explains this assert. + XRPL_ASSERT( + enforce, + "xrpl::TransfersNotFrozen::validateFrozenState : enforce " + "invariant."); + + if (enforce) + { + return false; + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp new file mode 100644 index 0000000000..16108472aa --- /dev/null +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -0,0 +1,1018 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +#pragma push_macro("TRANSACTION") +#undef TRANSACTION + +#define TRANSACTION(tag, value, name, delegable, amendment, privileges, ...) \ + case tag: { \ + return (privileges) & priv; \ + } + +bool +hasPrivilege(STTx const& tx, Privilege priv) +{ + switch (tx.getTxnType()) + { +#include + + // Deprecated types + default: + return false; + } +}; + +#undef TRANSACTION +#pragma pop_macro("TRANSACTION") + +void +TransactionFeeCheck::visitEntry( + bool, + std::shared_ptr const&, + std::shared_ptr const&) +{ + // nothing to do +} + +bool +TransactionFeeCheck::finalize( + STTx const& tx, + TER const, + XRPAmount const fee, + ReadView const&, + beast::Journal const& j) +{ + // We should never charge a negative fee + if (fee.drops() < 0) + { + JLOG(j.fatal()) << "Invariant failed: fee paid was negative: " << fee.drops(); + return false; + } + + // We should never charge a fee that's greater than or equal to the + // entire XRP supply. + if (fee >= INITIAL_XRP) + { + JLOG(j.fatal()) << "Invariant failed: fee paid exceeds system limit: " << fee.drops(); + return false; + } + + // We should never charge more for a transaction than the transaction + // authorizes. It's possible to charge less in some circumstances. + if (fee > tx.getFieldAmount(sfFee).xrp()) + { + JLOG(j.fatal()) << "Invariant failed: fee paid is " << fee.drops() + << " exceeds fee specified in transaction."; + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +XRPNotCreated::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + /* We go through all modified ledger entries, looking only at account roots, + * escrow payments, and payment channels. We remove from the total any + * previous XRP values and add to the total any new XRP values. The net + * balance of a payment channel is computed from two fields (amount and + * balance) and deletions are ignored for paychan and escrow because the + * amount fields have not been adjusted for those in the case of deletion. + */ + if (before) + { + switch (before->getType()) + { + case ltACCOUNT_ROOT: + drops_ -= (*before)[sfBalance].xrp().drops(); + break; + case ltPAYCHAN: + drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops(); + break; + case ltESCROW: + if (isXRP((*before)[sfAmount])) + drops_ -= (*before)[sfAmount].xrp().drops(); + break; + default: + break; + } + } + + if (after) + { + switch (after->getType()) + { + case ltACCOUNT_ROOT: + drops_ += (*after)[sfBalance].xrp().drops(); + break; + case ltPAYCHAN: + if (!isDelete) + drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops(); + break; + case ltESCROW: + if (!isDelete && isXRP((*after)[sfAmount])) + drops_ += (*after)[sfAmount].xrp().drops(); + break; + default: + break; + } + } +} + +bool +XRPNotCreated::finalize( + STTx const& tx, + TER const, + XRPAmount const fee, + ReadView const&, + beast::Journal const& j) const +{ + // The net change should never be positive, as this would mean that the + // transaction created XRP out of thin air. That's not possible. + if (drops_ > 0) + { + JLOG(j.fatal()) << "Invariant failed: XRP net change was positive: " << drops_; + return false; + } + + // The negative of the net change should be equal to actual fee charged. + if (-drops_ != fee.drops()) + { + JLOG(j.fatal()) << "Invariant failed: XRP net change of " << drops_ << " doesn't match fee " + << fee.drops(); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +XRPBalanceChecks::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + auto isBad = [](STAmount const& balance) { + if (!balance.native()) + return true; + + auto const drops = balance.xrp(); + + // Can't have more than the number of drops instantiated + // in the genesis ledger. + if (drops > INITIAL_XRP) + return true; + + // Can't have a negative balance (0 is OK) + if (drops < XRPAmount{0}) + return true; + + return false; + }; + + if (before && before->getType() == ltACCOUNT_ROOT) + bad_ |= isBad((*before)[sfBalance]); + + if (after && after->getType() == ltACCOUNT_ROOT) + bad_ |= isBad((*after)[sfBalance]); +} + +bool +XRPBalanceChecks::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (bad_) + { + JLOG(j.fatal()) << "Invariant failed: incorrect account XRP balance"; + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +NoBadOffers::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + auto isBad = [](STAmount const& pays, STAmount const& gets) { + // An offer should never be negative + if (pays < beast::zero) + return true; + + if (gets < beast::zero) + return true; + + // Can't have an XRP to XRP offer: + return pays.native() && gets.native(); + }; + + if (before && before->getType() == ltOFFER) + bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]); + + if (after && after->getType() == ltOFFER) + bad_ |= isBad((*after)[sfTakerPays], (*after)[sfTakerGets]); +} + +bool +NoBadOffers::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (bad_) + { + JLOG(j.fatal()) << "Invariant failed: offer with a bad amount"; + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +NoZeroEscrow::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + auto isBad = [](STAmount const& amount) { + // XRP case + if (amount.native()) + { + if (amount.xrp() <= XRPAmount{0}) + return true; + + if (amount.xrp() >= INITIAL_XRP) + return true; + } + else + { + // IOU case + if (amount.holds()) + { + if (amount <= beast::zero) + return true; + + if (badCurrency() == amount.getCurrency()) + return true; + } + + // MPT case + if (amount.holds()) + { + if (amount <= beast::zero) + return true; + + if (amount.mpt() > MPTAmount{maxMPTokenAmount}) + return true; // LCOV_EXCL_LINE + } + } + return false; + }; + + if (before && before->getType() == ltESCROW) + bad_ |= isBad((*before)[sfAmount]); + + if (after && after->getType() == ltESCROW) + bad_ |= isBad((*after)[sfAmount]); + + auto checkAmount = [this](std::int64_t amount) { + if (amount > maxMPTokenAmount || amount < 0) + bad_ = true; + }; + + if (after && after->getType() == ltMPTOKEN_ISSUANCE) + { + auto const outstanding = (*after)[sfOutstandingAmount]; + checkAmount(outstanding); + if (auto const locked = (*after)[~sfLockedAmount]) + { + checkAmount(*locked); + bad_ = outstanding < *locked; + } + } + + if (after && after->getType() == ltMPTOKEN) + { + auto const mptAmount = (*after)[sfMPTAmount]; + checkAmount(mptAmount); + if (auto const locked = (*after)[~sfLockedAmount]) + { + checkAmount(*locked); + } + } +} + +bool +NoZeroEscrow::finalize( + STTx const& txn, + TER const, + XRPAmount const, + ReadView const& rv, + beast::Journal const& j) const +{ + if (bad_) + { + JLOG(j.fatal()) << "Invariant failed: escrow specifies invalid amount"; + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +AccountRootsNotDeleted::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const&) +{ + if (isDelete && before && before->getType() == ltACCOUNT_ROOT) + accountsDeleted_++; +} + +bool +AccountRootsNotDeleted::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + // AMM account root can be deleted as the result of AMM withdraw/delete + // transaction when the total AMM LP Tokens balance goes to 0. + // A successful AccountDelete or AMMDelete MUST delete exactly + // one account root. + if (hasPrivilege(tx, mustDeleteAcct) && isTesSuccess(result)) + { + if (accountsDeleted_ == 1) + return true; + + if (accountsDeleted_ == 0) + { + JLOG(j.fatal()) << "Invariant failed: account deletion " + "succeeded without deleting an account"; + } + else + JLOG(j.fatal()) << "Invariant failed: account deletion " + "succeeded but deleted multiple accounts!"; + return false; + } + + // A successful AMMWithdraw/AMMClawback MAY delete one account root + // when the total AMM LP Tokens balance goes to 0. Not every AMM withdraw + // deletes the AMM account, accountsDeleted_ is set if it is deleted. + if (hasPrivilege(tx, mayDeleteAcct) && isTesSuccess(result) && accountsDeleted_ == 1) + return true; + + if (accountsDeleted_ == 0) + return true; + + JLOG(j.fatal()) << "Invariant failed: an account root was deleted"; + return false; +} + +//------------------------------------------------------------------------------ + +void +AccountRootsDeletedClean::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (isDelete && before && before->getType() == ltACCOUNT_ROOT) + accountsDeleted_.emplace_back(before, after); +} + +bool +AccountRootsDeletedClean::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + // Always check for objects in the ledger, but to prevent differing + // transaction processing results, however unlikely, only fail if the + // feature is enabled. Enabled, or not, though, a fatal-level message will + // be logged + [[maybe_unused]] bool const enforce = view.rules().enabled(featureInvariantsV1_1) || + view.rules().enabled(featureSingleAssetVault) || + view.rules().enabled(featureLendingProtocol); + + auto const objectExists = [&view, enforce, &j](auto const& keylet) { + (void)enforce; + if (auto const sle = view.read(keylet)) + { + // Finding the object is bad + auto const typeName = [&sle]() { + auto item = LedgerFormats::getInstance().findByType(sle->getType()); + + if (item != nullptr) + return item->getName(); + return std::to_string(sle->getType()); + }(); + + JLOG(j.fatal()) << "Invariant failed: account deletion left behind a " << typeName + << " object"; + // The comment above starting with "assert(enforce)" explains this + // assert. + XRPL_ASSERT( + enforce, + "xrpl::AccountRootsDeletedClean::finalize::objectExists : " + "account deletion left no objects behind"); + return true; + } + return false; + }; + + for (auto const& [before, after] : accountsDeleted_) + { + auto const accountID = before->getAccountID(sfAccount); + // An account should not be deleted with a balance + if (after->at(sfBalance) != beast::zero) + { + JLOG(j.fatal()) << "Invariant failed: account deletion left " + "behind a non-zero balance"; + XRPL_ASSERT( + enforce, + "xrpl::AccountRootsDeletedClean::finalize : " + "deleted account has zero balance"); + if (enforce) + return false; + } + // An account should not be deleted with a non-zero owner count + if (after->at(sfOwnerCount) != 0) + { + JLOG(j.fatal()) << "Invariant failed: account deletion left " + "behind a non-zero owner count"; + XRPL_ASSERT( + enforce, + "xrpl::AccountRootsDeletedClean::finalize : " + "deleted account has zero owner count"); + if (enforce) + return false; + } + // Simple types + for (auto const& [keyletfunc, _1, _2] : directAccountKeylets) + { + // TODO: use '_' for both unused variables above once we are in C++26 + if (objectExists(std::invoke(keyletfunc, accountID)) && enforce) + return false; + } + + { + // NFT pages. nftpage_min and nftpage_max were already explicitly + // checked above as entries in directAccountKeylets. This uses + // view.succ() to check for any NFT pages in between the two + // endpoints. + Keylet const first = keylet::nftpage_min(accountID); + Keylet const last = keylet::nftpage_max(accountID); + + std::optional key = view.succ(first.key, last.key.next()); + + // current page + if (key && objectExists(Keylet{ltNFTOKEN_PAGE, *key}) && enforce) + return false; + } + + // If the account is a pseudo account, then the linked object must + // also be deleted. e.g. AMM, Vault, etc. + for (auto const& field : getPseudoAccountFields()) + { + if (before->isFieldPresent(*field)) + { + auto const key = before->getFieldH256(*field); + if (objectExists(keylet::unchecked(key)) && enforce) + return false; + } + } + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +LedgerEntryTypesMatch::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (before && after && before->getType() != after->getType()) + typeMismatch_ = true; + + if (after) + { +#pragma push_macro("LEDGER_ENTRY") +#undef LEDGER_ENTRY + +#define LEDGER_ENTRY(tag, ...) case tag: + + switch (after->getType()) + { +#include + + break; + default: + invalidTypeAdded_ = true; + break; + } + +#undef LEDGER_ENTRY +#pragma pop_macro("LEDGER_ENTRY") + } +} + +bool +LedgerEntryTypesMatch::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if ((!typeMismatch_) && (!invalidTypeAdded_)) + return true; + + if (typeMismatch_) + { + JLOG(j.fatal()) << "Invariant failed: ledger entry type mismatch"; + } + + if (invalidTypeAdded_) + { + JLOG(j.fatal()) << "Invariant failed: invalid ledger entry type added"; + } + + return false; +} + +//------------------------------------------------------------------------------ + +void +NoXRPTrustLines::visitEntry( + bool, + std::shared_ptr const&, + std::shared_ptr const& after) +{ + if (after && after->getType() == ltRIPPLE_STATE) + { + // checking the issue directly here instead of + // relying on .native() just in case native somehow + // were systematically incorrect + xrpTrustLine_ = after->getFieldAmount(sfLowLimit).issue() == xrpIssue() || + after->getFieldAmount(sfHighLimit).issue() == xrpIssue(); + } +} + +bool +NoXRPTrustLines::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (!xrpTrustLine_) + return true; + + JLOG(j.fatal()) << "Invariant failed: an XRP trust line was created"; + return false; +} + +//------------------------------------------------------------------------------ + +void +NoDeepFreezeTrustLinesWithoutFreeze::visitEntry( + bool, + std::shared_ptr const&, + std::shared_ptr const& after) +{ + if (after && after->getType() == ltRIPPLE_STATE) + { + std::uint32_t const uFlags = after->getFieldU32(sfFlags); + bool const lowFreeze = uFlags & lsfLowFreeze; + bool const lowDeepFreeze = uFlags & lsfLowDeepFreeze; + + bool const highFreeze = uFlags & lsfHighFreeze; + bool const highDeepFreeze = uFlags & lsfHighDeepFreeze; + + deepFreezeWithoutFreeze_ = (lowDeepFreeze && !lowFreeze) || (highDeepFreeze && !highFreeze); + } +} + +bool +NoDeepFreezeTrustLinesWithoutFreeze::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (!deepFreezeWithoutFreeze_) + return true; + + JLOG(j.fatal()) << "Invariant failed: a trust line with deep freeze flag " + "without normal freeze was created"; + return false; +} + +//------------------------------------------------------------------------------ + +void +ValidNewAccountRoot::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (!before && after->getType() == ltACCOUNT_ROOT) + { + accountsCreated_++; + accountSeq_ = (*after)[sfSequence]; + pseudoAccount_ = isPseudoAccount(after); + flags_ = after->getFlags(); + } +} + +bool +ValidNewAccountRoot::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) const +{ + if (accountsCreated_ == 0) + return true; + + if (accountsCreated_ > 1) + { + JLOG(j.fatal()) << "Invariant failed: multiple accounts " + "created in a single transaction"; + return false; + } + + // From this point on we know exactly one account was created. + if (hasPrivilege(tx, createAcct | createPseudoAcct) && isTesSuccess(result)) + { + bool const pseudoAccount = + (pseudoAccount_ && + (view.rules().enabled(featureSingleAssetVault) || + view.rules().enabled(featureLendingProtocol))); + + if (pseudoAccount && !hasPrivilege(tx, createPseudoAcct)) + { + JLOG(j.fatal()) << "Invariant failed: pseudo-account created by a " + "wrong transaction type"; + return false; + } + + std::uint32_t const startingSeq = pseudoAccount ? 0 : view.seq(); + + if (accountSeq_ != startingSeq) + { + JLOG(j.fatal()) << "Invariant failed: account created with " + "wrong starting sequence number"; + return false; + } + + if (pseudoAccount) + { + std::uint32_t const expected = (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + if (flags_ != expected) + { + JLOG(j.fatal()) << "Invariant failed: pseudo-account created with " + "wrong flags"; + return false; + } + } + + return true; + } + + JLOG(j.fatal()) << "Invariant failed: account root created illegally"; + return false; +} // namespace xrpl + +//------------------------------------------------------------------------------ + +void +ValidClawback::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const&) +{ + if (before && before->getType() == ltRIPPLE_STATE) + trustlinesChanged++; + + if (before && before->getType() == ltMPTOKEN) + mptokensChanged++; +} + +bool +ValidClawback::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) const +{ + if (tx.getTxnType() != ttCLAWBACK) + return true; + + if (isTesSuccess(result)) + { + if (trustlinesChanged > 1) + { + JLOG(j.fatal()) << "Invariant failed: more than one trustline changed."; + return false; + } + + if (mptokensChanged > 1) + { + JLOG(j.fatal()) << "Invariant failed: more than one mptokens changed."; + return false; + } + + if (trustlinesChanged == 1) + { + AccountID const issuer = tx.getAccountID(sfAccount); + STAmount const& amount = tx.getFieldAmount(sfAmount); + AccountID const& holder = amount.getIssuer(); + STAmount const holderBalance = + accountHolds(view, holder, amount.getCurrency(), issuer, fhIGNORE_FREEZE, j); + + if (holderBalance.signum() < 0) + { + JLOG(j.fatal()) << "Invariant failed: trustline balance is negative"; + return false; + } + } + } + else + { + if (trustlinesChanged != 0) + { + JLOG(j.fatal()) << "Invariant failed: some trustlines were changed " + "despite failure of the transaction."; + return false; + } + + if (mptokensChanged != 0) + { + JLOG(j.fatal()) << "Invariant failed: some mptokens were changed " + "despite failure of the transaction."; + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ + +void +ValidPseudoAccounts::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (isDelete) + { + // Deletion is ignored + return; + } + + if (after && after->getType() == ltACCOUNT_ROOT) + { + bool const isPseudo = [&]() { + // isPseudoAccount checks that any of the pseudo-account fields are + // set. + if (isPseudoAccount(after)) + return true; + // Not all pseudo-accounts have a zero sequence, but all accounts + // with a zero sequence had better be pseudo-accounts. + if (after->at(sfSequence) == 0) + return true; + + return false; + }(); + if (isPseudo) + { + // Pseudo accounts must have the following properties: + // 1. Exactly one of the pseudo-account fields is set. + // 2. The sequence number is not changed. + // 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth + // flags are set. + // 4. The RegularKey is not set. + { + std::vector const& fields = getPseudoAccountFields(); + + auto const numFields = + std::count_if(fields.begin(), fields.end(), [&after](SField const* sf) -> bool { + return after->isFieldPresent(*sf); + }); + if (numFields != 1) + { + std::stringstream error; + error << "pseudo-account has " << numFields << " pseudo-account fields set"; + errors_.emplace_back(error.str()); + } + } + if (before && before->at(sfSequence) != after->at(sfSequence)) + { + errors_.emplace_back("pseudo-account sequence changed"); + } + if (!after->isFlag(lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth)) + { + errors_.emplace_back("pseudo-account flags are not set"); + } + if (after->isFieldPresent(sfRegularKey)) + { + errors_.emplace_back("pseudo-account has a regular key"); + } + } + } +} + +bool +ValidPseudoAccounts::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + bool const enforce = view.rules().enabled(featureSingleAssetVault); + XRPL_ASSERT( + errors_.empty() || enforce, + "xrpl::ValidPseudoAccounts::finalize : no bad " + "changes or enforce invariant"); + if (!errors_.empty()) + { + for (auto const& error : errors_) + { + JLOG(j.fatal()) << "Invariant failed: " << error; + } + if (enforce) + return false; + } + return true; +} + +//------------------------------------------------------------------------------ + +void +NoModifiedUnmodifiableFields::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (isDelete || !before) + { + // Creation and deletion are ignored + return; + } + + changedEntries_.emplace(before, after); +} + +bool +NoModifiedUnmodifiableFields::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + static auto const fieldChanged = [](auto const& before, auto const& after, auto const& field) { + bool const beforeField = before->isFieldPresent(field); + bool const afterField = after->isFieldPresent(field); + return beforeField != afterField || (afterField && before->at(field) != after->at(field)); + }; + for (auto const& slePair : changedEntries_) + { + auto const& before = slePair.first; + auto const& after = slePair.second; + auto const type = after->getType(); + bool bad = false; + [[maybe_unused]] bool enforce = false; + switch (type) + { + case ltLOAN_BROKER: + /* + * We check this invariant regardless of lending protocol + * amendment status, allowing for detection and logging of + * potential issues even when the amendment is disabled. + */ + enforce = view.rules().enabled(featureLendingProtocol); + bad = fieldChanged(before, after, sfLedgerEntryType) || + fieldChanged(before, after, sfLedgerIndex) || + fieldChanged(before, after, sfSequence) || + fieldChanged(before, after, sfOwnerNode) || + fieldChanged(before, after, sfVaultNode) || + fieldChanged(before, after, sfVaultID) || + fieldChanged(before, after, sfAccount) || + fieldChanged(before, after, sfOwner) || + fieldChanged(before, after, sfManagementFeeRate) || + fieldChanged(before, after, sfCoverRateMinimum) || + fieldChanged(before, after, sfCoverRateLiquidation); + break; + case ltLOAN: + /* + * We check this invariant regardless of lending protocol + * amendment status, allowing for detection and logging of + * potential issues even when the amendment is disabled. + */ + enforce = view.rules().enabled(featureLendingProtocol); + bad = fieldChanged(before, after, sfLedgerEntryType) || + fieldChanged(before, after, sfLedgerIndex) || + fieldChanged(before, after, sfSequence) || + fieldChanged(before, after, sfOwnerNode) || + fieldChanged(before, after, sfLoanBrokerNode) || + fieldChanged(before, after, sfLoanBrokerID) || + fieldChanged(before, after, sfBorrower) || + fieldChanged(before, after, sfLoanOriginationFee) || + fieldChanged(before, after, sfLoanServiceFee) || + fieldChanged(before, after, sfLatePaymentFee) || + fieldChanged(before, after, sfClosePaymentFee) || + fieldChanged(before, after, sfOverpaymentFee) || + fieldChanged(before, after, sfInterestRate) || + fieldChanged(before, after, sfLateInterestRate) || + fieldChanged(before, after, sfCloseInterestRate) || + fieldChanged(before, after, sfOverpaymentInterestRate) || + fieldChanged(before, after, sfStartDate) || + fieldChanged(before, after, sfPaymentInterval) || + fieldChanged(before, after, sfGracePeriod) || + fieldChanged(before, after, sfLoanScale); + break; + default: + /* + * We check this invariant regardless of lending protocol + * amendment status, allowing for detection and logging of + * potential issues even when the amendment is disabled. + * + * We use the lending protocol as a gate, even though + * all transactions are affected because that's when it + * was added. + */ + enforce = view.rules().enabled(featureLendingProtocol); + bad = fieldChanged(before, after, sfLedgerEntryType) || + fieldChanged(before, after, sfLedgerIndex); + } + XRPL_ASSERT( + !bad || enforce, + "xrpl::NoModifiedUnmodifiableFields::finalize : no bad " + "changes or enforce invariant"); + if (bad) + { + JLOG(j.fatal()) << "Invariant failed: changed an unchangeable field for " + << tx.getTransactionID(); + if (enforce) + return false; + } + } + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/LoanInvariant.cpp b/src/libxrpl/tx/invariants/LoanInvariant.cpp new file mode 100644 index 0000000000..0df7e409d1 --- /dev/null +++ b/src/libxrpl/tx/invariants/LoanInvariant.cpp @@ -0,0 +1,279 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidLoanBroker::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (after) + { + if (after->getType() == ltLOAN_BROKER) + { + auto& broker = brokers_[after->key()]; + broker.brokerBefore = before; + broker.brokerAfter = after; + } + else if (after->getType() == ltACCOUNT_ROOT && after->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = after->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + else if (after->getType() == ltRIPPLE_STATE) + { + lines_.emplace_back(after); + } + else if (after->getType() == ltMPTOKEN) + { + mpts_.emplace_back(after); + } + } +} + +bool +ValidLoanBroker::goodZeroDirectory( + ReadView const& view, + SLE::const_ref dir, + beast::Journal const& j) const +{ + auto const next = dir->at(~sfIndexNext); + auto const prev = dir->at(~sfIndexPrevious); + if ((prev && *prev) || (next && *next)) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has multiple directory pages"; + return false; + } + auto indexes = dir->getFieldV256(sfIndexes); + if (indexes.size() > 1) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has multiple indexes in the Directory root"; + return false; + } + if (indexes.size() == 1) + { + auto const index = indexes.value().front(); + auto const sle = view.read(keylet::unchecked(index)); + if (!sle) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker directory corrupt"; + return false; + } + if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero " + "OwnerCount has an unexpected entry in the directory"; + return false; + } + } + + return true; +} + +bool +ValidLoanBroker::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + // Loan Brokers will not exist on ledger if the Lending Protocol amendment + // is not enabled, so there's no need to check it. + + for (auto const& line : lines_) + { + for (auto const& field : {&sfLowLimit, &sfHighLimit}) + { + auto const account = view.read(keylet::account(line->at(*field).getIssuer())); + // This Invariant doesn't know about the rules for Trust Lines, so + // if the account is missing, don't treat it as an error. This + // loop is only concerned with finding Broker pseudo-accounts + if (account && account->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = account->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + } + } + for (auto const& mpt : mpts_) + { + auto const account = view.read(keylet::account(mpt->at(sfAccount))); + // This Invariant doesn't know about the rules for MPTokens, so + // if the account is missing, don't treat is as an error. This + // loop is only concerned with finding Broker pseudo-accounts + if (account && account->isFieldPresent(sfLoanBrokerID)) + { + auto const& loanBrokerID = account->at(sfLoanBrokerID); + // create an entry if one doesn't already exist + brokers_.emplace(loanBrokerID, BrokerInfo{}); + } + } + + for (auto const& [brokerID, broker] : brokers_) + { + auto const& after = + broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID)); + + if (!after) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker missing"; + return false; + } + + auto const& before = broker.brokerBefore; + + // https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants + // If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most + // one node (the root), which will only hold entries for `RippleState` + // or `MPToken` objects. + if (after->at(sfOwnerCount) == 0) + { + auto const dir = view.read(keylet::ownerDir(after->at(sfAccount))); + if (dir) + { + if (!goodZeroDirectory(view, dir, j)) + { + return false; + } + } + } + if (before && before->at(sfLoanSequence) > after->at(sfLoanSequence)) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker sequence number " + "decreased"; + return false; + } + if (after->at(sfDebtTotal) < 0) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker debt total is negative"; + return false; + } + if (after->at(sfCoverAvailable) < 0) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is negative"; + return false; + } + auto const vault = view.read(keylet::vault(after->at(sfVaultID))); + if (!vault) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker vault ID is invalid"; + return false; + } + auto const& vaultAsset = vault->at(sfAsset); + if (after->at(sfCoverAvailable) < accountHolds( + view, + after->at(sfAccount), + vaultAsset, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j)) + { + JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available " + "is less than pseudo-account asset balance"; + return false; + } + } + return true; +} + +//------------------------------------------------------------------------------ + +void +ValidLoan::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (after && after->getType() == ltLOAN) + { + loans_.emplace_back(before, after); + } +} + +bool +ValidLoan::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + // Loans will not exist on ledger if the Lending Protocol amendment + // is not enabled, so there's no need to check it. + + for (auto const& [before, after] : loans_) + { + // https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3223-invariants + // If `Loan.PaymentRemaining = 0` then the loan MUST be fully paid off + if (after->at(sfPaymentRemaining) == 0 && + (after->at(sfTotalValueOutstanding) != beast::zero || + after->at(sfPrincipalOutstanding) != beast::zero || + after->at(sfManagementFeeOutstanding) != beast::zero)) + { + JLOG(j.fatal()) << "Invariant failed: Loan with zero payments " + "remaining has not been paid off"; + return false; + } + // If `Loan.PaymentRemaining != 0` then the loan MUST NOT be fully paid + // off + if (after->at(sfPaymentRemaining) != 0 && + after->at(sfTotalValueOutstanding) == beast::zero && + after->at(sfPrincipalOutstanding) == beast::zero && + after->at(sfManagementFeeOutstanding) == beast::zero) + { + JLOG(j.fatal()) << "Invariant failed: Loan with zero payments " + "remaining has not been paid off"; + return false; + } + if (before && (before->isFlag(lsfLoanOverpayment) != after->isFlag(lsfLoanOverpayment))) + { + JLOG(j.fatal()) << "Invariant failed: Loan Overpayment flag changed"; + return false; + } + // Must not be negative - STNumber + for (auto const field : + {&sfLoanServiceFee, + &sfLatePaymentFee, + &sfClosePaymentFee, + &sfPrincipalOutstanding, + &sfTotalValueOutstanding, + &sfManagementFeeOutstanding}) + { + if (after->at(*field) < 0) + { + JLOG(j.fatal()) << "Invariant failed: " << field->getName() << " is negative "; + return false; + } + } + // Must be positive - STNumber + for (auto const field : { + &sfPeriodicPayment, + }) + { + if (after->at(*field) <= 0) + { + JLOG(j.fatal()) << "Invariant failed: " << field->getName() + << " is zero or negative "; + return false; + } + } + } + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp new file mode 100644 index 0000000000..a3a5bf897d --- /dev/null +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -0,0 +1,197 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidMPTIssuance::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (after && after->getType() == ltMPTOKEN_ISSUANCE) + { + if (isDelete) + { + mptIssuancesDeleted_++; + } + else if (!before) + { + mptIssuancesCreated_++; + } + } + + if (after && after->getType() == ltMPTOKEN) + { + if (isDelete) + { + mptokensDeleted_++; + } + else if (!before) + { + mptokensCreated_++; + MPTIssue const mptIssue{after->at(sfMPTokenIssuanceID)}; + if (mptIssue.getIssuer() == after->at(sfAccount)) + mptCreatedByIssuer_ = true; + } + } +} + +bool +ValidMPTIssuance::finalize( + STTx const& tx, + TER const result, + XRPAmount const _fee, + ReadView const& view, + beast::Journal const& j) const +{ + if (isTesSuccess(result)) + { + auto const& rules = view.rules(); + [[maybe_unused]] + bool enforceCreatedByIssuer = + rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol); + if (mptCreatedByIssuer_) + { + JLOG(j.fatal()) << "Invariant failed: MPToken created for the MPT issuer"; + // The comment above starting with "assert(enforce)" explains this + // assert. + XRPL_ASSERT_PARTS( + enforceCreatedByIssuer, "xrpl::ValidMPTIssuance::finalize", "no issuer MPToken"); + if (enforceCreatedByIssuer) + return false; + } + + auto const txnType = tx.getTxnType(); + if (hasPrivilege(tx, createMPTIssuance)) + { + if (mptIssuancesCreated_ == 0) + { + JLOG(j.fatal()) << "Invariant failed: transaction " + "succeeded without creating a MPT issuance"; + } + else if (mptIssuancesDeleted_ != 0) + { + JLOG(j.fatal()) << "Invariant failed: transaction " + "succeeded while removing MPT issuances"; + } + else if (mptIssuancesCreated_ > 1) + { + JLOG(j.fatal()) << "Invariant failed: transaction " + "succeeded but created multiple issuances"; + } + + return mptIssuancesCreated_ == 1 && mptIssuancesDeleted_ == 0; + } + + if (hasPrivilege(tx, destroyMPTIssuance)) + { + if (mptIssuancesDeleted_ == 0) + { + JLOG(j.fatal()) << "Invariant failed: MPT issuance deletion " + "succeeded without removing a MPT issuance"; + } + else if (mptIssuancesCreated_ > 0) + { + JLOG(j.fatal()) << "Invariant failed: MPT issuance deletion " + "succeeded while creating MPT issuances"; + } + else if (mptIssuancesDeleted_ > 1) + { + JLOG(j.fatal()) << "Invariant failed: MPT issuance deletion " + "succeeded but deleted multiple issuances"; + } + + return mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 1; + } + + bool const lendingProtocolEnabled = view.rules().enabled(featureLendingProtocol); + // ttESCROW_FINISH may authorize an MPT, but it can't have the + // mayAuthorizeMPT privilege, because that may cause + // non-amendment-gated side effects. + bool const enforceEscrowFinish = (txnType == ttESCROW_FINISH) && + (view.rules().enabled(featureSingleAssetVault) || lendingProtocolEnabled); + if (hasPrivilege(tx, mustAuthorizeMPT | mayAuthorizeMPT) || enforceEscrowFinish) + { + bool const submittedByIssuer = tx.isFieldPresent(sfHolder); + + if (mptIssuancesCreated_ > 0) + { + JLOG(j.fatal()) << "Invariant failed: MPT authorize " + "succeeded but created MPT issuances"; + return false; + } + if (mptIssuancesDeleted_ > 0) + { + JLOG(j.fatal()) << "Invariant failed: MPT authorize " + "succeeded but deleted issuances"; + return false; + } + if (lendingProtocolEnabled && mptokensCreated_ + mptokensDeleted_ > 1) + { + JLOG(j.fatal()) << "Invariant failed: MPT authorize succeeded " + "but created/deleted bad number mptokens"; + return false; + } + if (submittedByIssuer && (mptokensCreated_ > 0 || mptokensDeleted_ > 0)) + { + JLOG(j.fatal()) << "Invariant failed: MPT authorize submitted by issuer " + "succeeded but created/deleted mptokens"; + return false; + } + if (!submittedByIssuer && hasPrivilege(tx, mustAuthorizeMPT) && + (mptokensCreated_ + mptokensDeleted_ != 1)) + { + // if the holder submitted this tx, then a mptoken must be + // either created or deleted. + JLOG(j.fatal()) << "Invariant failed: MPT authorize submitted by holder " + "succeeded but created/deleted bad number of mptokens"; + return false; + } + + return true; + } + if (txnType == ttESCROW_FINISH) + { + // ttESCROW_FINISH may authorize an MPT, but it can't have the + // mayAuthorizeMPT privilege, because that may cause + // non-amendment-gated side effects. + XRPL_ASSERT_PARTS( + !enforceEscrowFinish, "xrpl::ValidMPTIssuance::finalize", "not escrow finish tx"); + return true; + } + + if (hasPrivilege(tx, mayDeleteMPT) && mptokensDeleted_ == 1 && mptokensCreated_ == 0 && + mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0) + return true; + } + + if (mptIssuancesCreated_ != 0) + { + JLOG(j.fatal()) << "Invariant failed: a MPT issuance was created"; + } + else if (mptIssuancesDeleted_ != 0) + { + JLOG(j.fatal()) << "Invariant failed: a MPT issuance was deleted"; + } + else if (mptokensCreated_ != 0) + { + JLOG(j.fatal()) << "Invariant failed: a MPToken was created"; + } + else if (mptokensDeleted_ != 0) + { + JLOG(j.fatal()) << "Invariant failed: a MPToken was deleted"; + } + + return mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0 && mptokensCreated_ == 0 && + mptokensDeleted_ == 0; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp new file mode 100644 index 0000000000..cf00dc9290 --- /dev/null +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -0,0 +1,274 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidNFTokenPage::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + static constexpr uint256 const& pageBits = nft::pageMask; + static constexpr uint256 const accountBits = ~pageBits; + + if ((before && before->getType() != ltNFTOKEN_PAGE) || + (after && after->getType() != ltNFTOKEN_PAGE)) + return; + + auto check = [this, isDelete](std::shared_ptr const& sle) { + uint256 const account = sle->key() & accountBits; + uint256 const hiLimit = sle->key() & pageBits; + std::optional const prev = (*sle)[~sfPreviousPageMin]; + + // Make sure that any page links... + // 1. Are properly associated with the owning account and + // 2. The page is correctly ordered between links. + if (prev) + { + if (account != (*prev & accountBits)) + badLink_ = true; + + if (hiLimit <= (*prev & pageBits)) + badLink_ = true; + } + + if (auto const next = (*sle)[~sfNextPageMin]) + { + if (account != (*next & accountBits)) + badLink_ = true; + + if (hiLimit >= (*next & pageBits)) + badLink_ = true; + } + + { + auto const& nftokens = sle->getFieldArray(sfNFTokens); + + // An NFTokenPage should never contain too many tokens or be empty. + if (std::size_t const nftokenCount = nftokens.size(); + (!isDelete && nftokenCount == 0) || nftokenCount > dirMaxTokensPerPage) + invalidSize_ = true; + + // If prev is valid, use it to establish a lower bound for + // page entries. If prev is not valid the lower bound is zero. + uint256 const loLimit = prev ? *prev & pageBits : uint256(beast::zero); + + // Also verify that all NFTokenIDs in the page are sorted. + uint256 loCmp = loLimit; + for (auto const& obj : nftokens) + { + uint256 const tokenID = obj[sfNFTokenID]; + if (!nft::compareTokens(loCmp, tokenID)) + badSort_ = true; + loCmp = tokenID; + + // None of the NFTs on this page should belong on lower or + // higher pages. + if (uint256 const tokenPageBits = tokenID & pageBits; + tokenPageBits < loLimit || tokenPageBits >= hiLimit) + badEntry_ = true; + + if (auto uri = obj[~sfURI]; uri && uri->empty()) + badURI_ = true; + } + } + }; + + if (before) + { + check(before); + + // While an account's NFToken directory contains any NFTokens, the last + // NFTokenPage (with 96 bits of 1 in the low part of the index) should + // never be deleted. + if (isDelete && (before->key() & nft::pageMask) == nft::pageMask && + before->isFieldPresent(sfPreviousPageMin)) + { + deletedFinalPage_ = true; + } + } + + if (after) + check(after); + + if (!isDelete && before && after) + { + // If the NFTokenPage + // 1. Has a NextMinPage field in before, but loses it in after, and + // 2. This is not the last page in the directory + // Then we have identified a corruption in the links between the + // NFToken pages in the NFToken directory. + if ((before->key() & nft::pageMask) != nft::pageMask && + before->isFieldPresent(sfNextPageMin) && !after->isFieldPresent(sfNextPageMin)) + { + deletedLink_ = true; + } + } +} + +bool +ValidNFTokenPage::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) const +{ + if (badLink_) + { + JLOG(j.fatal()) << "Invariant failed: NFT page is improperly linked."; + return false; + } + + if (badEntry_) + { + JLOG(j.fatal()) << "Invariant failed: NFT found in incorrect page."; + return false; + } + + if (badSort_) + { + JLOG(j.fatal()) << "Invariant failed: NFTs on page are not sorted."; + return false; + } + + if (badURI_) + { + JLOG(j.fatal()) << "Invariant failed: NFT contains empty URI."; + return false; + } + + if (invalidSize_) + { + JLOG(j.fatal()) << "Invariant failed: NFT page has invalid size."; + return false; + } + + if (view.rules().enabled(fixNFTokenPageLinks)) + { + if (deletedFinalPage_) + { + JLOG(j.fatal()) << "Invariant failed: Last NFT page deleted with " + "non-empty directory."; + return false; + } + if (deletedLink_) + { + JLOG(j.fatal()) << "Invariant failed: Lost NextMinPage link."; + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ +void +NFTokenCountTracking::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (before && before->getType() == ltACCOUNT_ROOT) + { + beforeMintedTotal += (*before)[~sfMintedNFTokens].value_or(0); + beforeBurnedTotal += (*before)[~sfBurnedNFTokens].value_or(0); + } + + if (after && after->getType() == ltACCOUNT_ROOT) + { + afterMintedTotal += (*after)[~sfMintedNFTokens].value_or(0); + afterBurnedTotal += (*after)[~sfBurnedNFTokens].value_or(0); + } +} + +bool +NFTokenCountTracking::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) const +{ + if (!hasPrivilege(tx, changeNFTCounts)) + { + if (beforeMintedTotal != afterMintedTotal) + { + JLOG(j.fatal()) << "Invariant failed: the number of minted tokens " + "changed without a mint transaction!"; + return false; + } + + if (beforeBurnedTotal != afterBurnedTotal) + { + JLOG(j.fatal()) << "Invariant failed: the number of burned tokens " + "changed without a burn transaction!"; + return false; + } + + return true; + } + + if (tx.getTxnType() == ttNFTOKEN_MINT) + { + if (isTesSuccess(result) && beforeMintedTotal >= afterMintedTotal) + { + JLOG(j.fatal()) << "Invariant failed: successful minting didn't increase " + "the number of minted tokens."; + return false; + } + + if (!isTesSuccess(result) && beforeMintedTotal != afterMintedTotal) + { + JLOG(j.fatal()) << "Invariant failed: failed minting changed the " + "number of minted tokens."; + return false; + } + + if (beforeBurnedTotal != afterBurnedTotal) + { + JLOG(j.fatal()) << "Invariant failed: minting changed the number of " + "burned tokens."; + return false; + } + } + + if (tx.getTxnType() == ttNFTOKEN_BURN) + { + if (isTesSuccess(result)) + { + if (beforeBurnedTotal >= afterBurnedTotal) + { + JLOG(j.fatal()) << "Invariant failed: successful burning didn't increase " + "the number of burned tokens."; + return false; + } + } + + if (!isTesSuccess(result) && beforeBurnedTotal != afterBurnedTotal) + { + JLOG(j.fatal()) << "Invariant failed: failed burning changed the " + "number of burned tokens."; + return false; + } + + if (beforeMintedTotal != afterMintedTotal) + { + JLOG(j.fatal()) << "Invariant failed: burning changed the number of " + "minted tokens."; + return false; + } + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp new file mode 100644 index 0000000000..e932a6ba09 --- /dev/null +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -0,0 +1,97 @@ +#include +// +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidPermissionedDEX::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (after && after->getType() == ltDIR_NODE) + { + if (after->isFieldPresent(sfDomainID)) + domains_.insert(after->getFieldH256(sfDomainID)); + } + + if (after && after->getType() == ltOFFER) + { + if (after->isFieldPresent(sfDomainID)) + { + domains_.insert(after->getFieldH256(sfDomainID)); + } + else + { + regularOffers_ = true; + } + + // if a hybrid offer is missing domain or additional book, there's + // something wrong + if (after->isFlag(lsfHybrid) && + (!after->isFieldPresent(sfDomainID) || !after->isFieldPresent(sfAdditionalBooks) || + after->getFieldArray(sfAdditionalBooks).size() > 1)) + badHybrids_ = true; + } +} + +bool +ValidPermissionedDEX::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + auto const txType = tx.getTxnType(); + if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || !isTesSuccess(result)) + return true; + + // For each offercreate transaction, check if + // permissioned offers are valid + if (txType == ttOFFER_CREATE && badHybrids_) + { + JLOG(j.fatal()) << "Invariant failed: hybrid offer is malformed"; + return false; + } + + if (!tx.isFieldPresent(sfDomainID)) + return true; + + auto const domain = tx.getFieldH256(sfDomainID); + + if (!view.exists(keylet::permissionedDomain(domain))) + { + JLOG(j.fatal()) << "Invariant failed: domain doesn't exist"; + return false; + } + + // for both payment and offercreate, there shouldn't be another domain + // that's different from the domain specified + for (auto const& d : domains_) + { + if (d != domain) + { + JLOG(j.fatal()) << "Invariant failed: transaction" + " consumed wrong domains"; + return false; + } + } + + if (regularOffers_) + { + JLOG(j.fatal()) << "Invariant failed: domain transaction" + " affected regular offers"; + return false; + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp new file mode 100644 index 0000000000..02eaee0552 --- /dev/null +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -0,0 +1,163 @@ +#include +// +#include +#include +#include +#include +#include + +namespace xrpl { + +void +ValidPermissionedDomain::visitEntry( + bool isDel, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + if (before && before->getType() != ltPERMISSIONED_DOMAIN) + return; + if (after && after->getType() != ltPERMISSIONED_DOMAIN) + return; + + auto check = [isDel](std::vector& sleStatus, std::shared_ptr const& sle) { + auto const& credentials = sle->getFieldArray(sfAcceptedCredentials); + auto const sorted = credentials::makeSorted(credentials); + + SleStatus ss{credentials.size(), false, !sorted.empty(), isDel}; + + // If array have duplicates then all the other checks are invalid + if (ss.isUnique_) + { + unsigned i = 0; + for (auto const& cred : sorted) + { + auto const& credTx = credentials[i++]; + ss.isSorted_ = + (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]); + if (!ss.isSorted_) + break; + } + } + sleStatus.emplace_back(std::move(ss)); + }; + + if (after) + check(sleStatus_, after); +} + +bool +ValidPermissionedDomain::finalize( + STTx const& tx, + TER const result, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + auto check = [](SleStatus const& sleStatus, beast::Journal const& j) { + if (!sleStatus.credentialsSize_) + { + JLOG(j.fatal()) << "Invariant failed: permissioned domain with " + "no rules."; + return false; + } + + if (sleStatus.credentialsSize_ > maxPermissionedDomainCredentialsArraySize) + { + JLOG(j.fatal()) << "Invariant failed: permissioned domain bad " + "credentials size " + << sleStatus.credentialsSize_; + return false; + } + + if (!sleStatus.isUnique_) + { + JLOG(j.fatal()) << "Invariant failed: permissioned domain credentials " + "aren't unique"; + return false; + } + + if (!sleStatus.isSorted_) + { + JLOG(j.fatal()) << "Invariant failed: permissioned domain credentials " + "aren't sorted"; + return false; + } + + return true; + }; + + if (view.rules().enabled(fixPermissionedDomainInvariant)) + { + // No permissioned domains should be affected if the transaction failed + if (!isTesSuccess(result)) + { + // If nothing changed, all is good. If there were changes, that's bad. + return sleStatus_.empty(); + } + + if (sleStatus_.size() > 1) + { + JLOG(j.fatal()) << "Invariant failed: transaction affected more " + "than 1 permissioned domain entry."; + return false; + } + + switch (tx.getTxnType()) + { + case ttPERMISSIONED_DOMAIN_SET: { + if (sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: no domain objects affected by " + "PermissionedDomainSet"; + return false; + } + + auto const& sleStatus = sleStatus_[0]; + if (sleStatus.isDelete_) + { + JLOG(j.fatal()) << "Invariant failed: domain object " + "deleted by PermissionedDomainSet"; + return false; + } + return check(sleStatus, j); + } + case ttPERMISSIONED_DOMAIN_DELETE: { + if (sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: no domain objects affected by " + "PermissionedDomainDelete"; + return false; + } + + if (!sleStatus_[0].isDelete_) + { + JLOG(j.fatal()) << "Invariant failed: domain object " + "modified, but not deleted by " + "PermissionedDomainDelete"; + return false; + } + return true; + } + default: { + if (!sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: " << sleStatus_.size() + << " domain object(s) affected by an " + "unauthorized transaction. " + << tx.getTxnType(); + return false; + } + return true; + } + } + } + else + { + if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || !isTesSuccess(result) || + sleStatus_.empty()) + return true; + return check(sleStatus_[0], j); + } +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp new file mode 100644 index 0000000000..f0dd82c2f8 --- /dev/null +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -0,0 +1,927 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +ValidVault::Vault +ValidVault::Vault::make(SLE const& from) +{ + XRPL_ASSERT(from.getType() == ltVAULT, "ValidVault::Vault::make : from Vault object"); + + ValidVault::Vault self; + self.key = from.key(); + self.asset = from.at(sfAsset); + self.pseudoId = from.getAccountID(sfAccount); + self.owner = from.at(sfOwner); + self.shareMPTID = from.getFieldH192(sfShareMPTID); + self.assetsTotal = from.at(sfAssetsTotal); + self.assetsAvailable = from.at(sfAssetsAvailable); + self.assetsMaximum = from.at(sfAssetsMaximum); + self.lossUnrealized = from.at(sfLossUnrealized); + return self; +} + +ValidVault::Shares +ValidVault::Shares::make(SLE const& from) +{ + XRPL_ASSERT( + from.getType() == ltMPTOKEN_ISSUANCE, + "ValidVault::Shares::make : from MPTokenIssuance object"); + + ValidVault::Shares self; + self.share = MPTIssue(makeMptID(from.getFieldU32(sfSequence), from.getAccountID(sfIssuer))); + self.sharesTotal = from.at(sfOutstandingAmount); + self.sharesMaximum = from[~sfMaximumAmount].value_or(maxMPTokenAmount); + return self; +} + +void +ValidVault::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + // If `before` is empty, this means an object is being created, in which + // case `isDelete` must be false. Otherwise `before` and `after` are set and + // `isDelete` indicates whether an object is being deleted or modified. + XRPL_ASSERT( + after != nullptr && (before != nullptr || !isDelete), + "xrpl::ValidVault::visitEntry : some object is available"); + + // Number balanceDelta will capture the difference (delta) between "before" + // state (zero if created) and "after" state (zero if destroyed), so the + // invariants can validate that the change in account balances matches the + // change in vault balances, stored to deltas_ at the end of this function. + Number balanceDelta{}; + + std::int8_t sign = 0; + if (before) + { + switch (before->getType()) + { + case ltVAULT: + beforeVault_.push_back(Vault::make(*before)); + break; + case ltMPTOKEN_ISSUANCE: + // At this moment we have no way of telling if this object holds + // vault shares or something else. Save it for finalize. + beforeMPTs_.push_back(Shares::make(*before)); + balanceDelta = static_cast(before->getFieldU64(sfOutstandingAmount)); + sign = 1; + break; + case ltMPTOKEN: + balanceDelta = static_cast(before->getFieldU64(sfMPTAmount)); + sign = -1; + break; + case ltACCOUNT_ROOT: + case ltRIPPLE_STATE: + balanceDelta = before->getFieldAmount(sfBalance); + sign = -1; + break; + default:; + } + } + + if (!isDelete && after) + { + switch (after->getType()) + { + case ltVAULT: + afterVault_.push_back(Vault::make(*after)); + break; + case ltMPTOKEN_ISSUANCE: + // At this moment we have no way of telling if this object holds + // vault shares or something else. Save it for finalize. + afterMPTs_.push_back(Shares::make(*after)); + balanceDelta -= + Number(static_cast(after->getFieldU64(sfOutstandingAmount))); + sign = 1; + break; + case ltMPTOKEN: + balanceDelta -= Number(static_cast(after->getFieldU64(sfMPTAmount))); + sign = -1; + break; + case ltACCOUNT_ROOT: + case ltRIPPLE_STATE: + balanceDelta -= Number(after->getFieldAmount(sfBalance)); + sign = -1; + break; + default:; + } + } + + uint256 const key = (before ? before->key() : after->key()); + // Append to deltas if sign is non-zero, i.e. an object of an interesting + // type has been updated. A transaction may update an object even when + // its balance has not changed, e.g. transaction fee equals the amount + // transferred to the account. We intentionally do not compare balanceDelta + // against zero, to avoid missing such updates. + if (sign != 0) + deltas_[key] = balanceDelta * sign; +} + +bool +ValidVault::finalize( + STTx const& tx, + TER const ret, + XRPAmount const fee, + ReadView const& view, + beast::Journal const& j) +{ + bool const enforce = view.rules().enabled(featureSingleAssetVault); + + if (!isTesSuccess(ret)) + return true; // Do not perform checks + + if (afterVault_.empty() && beforeVault_.empty()) + { + if (hasPrivilege(tx, mustModifyVault)) + { + JLOG(j.fatal()) << // + "Invariant failed: vault operation succeeded without modifying " + "a vault"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : vault noop invariant"); + return !enforce; + } + + return true; // Not a vault operation + } + if (!(hasPrivilege(tx, mustModifyVault) || hasPrivilege(tx, mayModifyVault))) + { + JLOG(j.fatal()) << // + "Invariant failed: vault updated by a wrong transaction type"; + XRPL_ASSERT( + enforce, + "xrpl::ValidVault::finalize : illegal vault transaction " + "invariant"); + return !enforce; // Also not a vault operation + } + + if (beforeVault_.size() > 1 || afterVault_.size() > 1) + { + JLOG(j.fatal()) << // + "Invariant failed: vault operation updated more than single vault"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : single vault invariant"); + return !enforce; // That's all we can do here + } + + auto const txnType = tx.getTxnType(); + + // We do special handling for ttVAULT_DELETE first, because it's the only + // vault-modifying transaction without an "after" state of the vault + if (afterVault_.empty()) + { + if (txnType != ttVAULT_DELETE) + { + JLOG(j.fatal()) << // + "Invariant failed: vault deleted by a wrong transaction type"; + XRPL_ASSERT( + enforce, + "xrpl::ValidVault::finalize : illegal vault deletion " + "invariant"); + return !enforce; // That's all we can do here + } + + // Note, if afterVault_ is empty then we know that beforeVault_ is not + // empty, as enforced at the top of this function + auto const& beforeVault = beforeVault_[0]; + + // At this moment we only know a vault is being deleted and there + // might be some MPTokenIssuance objects which are deleted in the + // same transaction. Find the one matching this vault. + auto const deletedShares = [&]() -> std::optional { + for (auto const& e : beforeMPTs_) + { + if (e.share.getMptID() == beforeVault.shareMPTID) + return std::move(e); + } + return std::nullopt; + }(); + + if (!deletedShares) + { + JLOG(j.fatal()) << "Invariant failed: deleted vault must also " + "delete shares"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : shares deletion invariant"); + return !enforce; // That's all we can do here + } + + bool result = true; + if (deletedShares->sharesTotal != 0) + { + JLOG(j.fatal()) << "Invariant failed: deleted vault must have no " + "shares outstanding"; + result = false; + } + if (beforeVault.assetsTotal != zero) + { + JLOG(j.fatal()) << "Invariant failed: deleted vault must have no " + "assets outstanding"; + result = false; + } + if (beforeVault.assetsAvailable != zero) + { + JLOG(j.fatal()) << "Invariant failed: deleted vault must have no " + "assets available"; + result = false; + } + + return result; + } + if (txnType == ttVAULT_DELETE) + { + JLOG(j.fatal()) << "Invariant failed: vault deletion succeeded without " + "deleting a vault"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : vault deletion invariant"); + return !enforce; // That's all we can do here + } + + // Note, `afterVault_.empty()` is handled above + auto const& afterVault = afterVault_[0]; + XRPL_ASSERT( + beforeVault_.empty() || beforeVault_[0].key == afterVault.key, + "xrpl::ValidVault::finalize : single vault operation"); + + auto const updatedShares = [&]() -> std::optional { + // At this moment we only know that a vault is being updated and there + // might be some MPTokenIssuance objects which are also updated in the + // same transaction. Find the one matching the shares to this vault. + // Note, we expect updatedMPTs collection to be extremely small. For + // such collections linear search is faster than lookup. + for (auto const& e : afterMPTs_) + { + if (e.share.getMptID() == afterVault.shareMPTID) + return e; + } + + auto const sleShares = view.read(keylet::mptIssuance(afterVault.shareMPTID)); + + return sleShares ? std::optional(Shares::make(*sleShares)) : std::nullopt; + }(); + + bool result = true; + + // Universal transaction checks + if (!beforeVault_.empty()) + { + auto const& beforeVault = beforeVault_[0]; + if (afterVault.asset != beforeVault.asset || afterVault.pseudoId != beforeVault.pseudoId || + afterVault.shareMPTID != beforeVault.shareMPTID) + { + JLOG(j.fatal()) << "Invariant failed: violation of vault immutable data"; + result = false; + } + } + + if (!updatedShares) + { + JLOG(j.fatal()) << "Invariant failed: updated vault must have shares"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : vault has shares invariant"); + return !enforce; // That's all we can do here + } + + if (updatedShares->sharesTotal == 0) + { + if (afterVault.assetsTotal != zero) + { + JLOG(j.fatal()) << "Invariant failed: updated zero sized " + "vault must have no assets outstanding"; + result = false; + } + if (afterVault.assetsAvailable != zero) + { + JLOG(j.fatal()) << "Invariant failed: updated zero sized " + "vault must have no assets available"; + result = false; + } + } + else if (updatedShares->sharesTotal > updatedShares->sharesMaximum) + { + JLOG(j.fatal()) // + << "Invariant failed: updated shares must not exceed maximum " + << updatedShares->sharesMaximum; + result = false; + } + + if (afterVault.assetsAvailable < zero) + { + JLOG(j.fatal()) << "Invariant failed: assets available must be positive"; + result = false; + } + + if (afterVault.assetsAvailable > afterVault.assetsTotal) + { + JLOG(j.fatal()) << "Invariant failed: assets available must " + "not be greater than assets outstanding"; + result = false; + } + else if (afterVault.lossUnrealized > afterVault.assetsTotal - afterVault.assetsAvailable) + { + JLOG(j.fatal()) // + << "Invariant failed: loss unrealized must not exceed " + "the difference between assets outstanding and available"; + result = false; + } + + if (afterVault.assetsTotal < zero) + { + JLOG(j.fatal()) << "Invariant failed: assets outstanding must be positive"; + result = false; + } + + if (afterVault.assetsMaximum < zero) + { + JLOG(j.fatal()) << "Invariant failed: assets maximum must be positive"; + result = false; + } + + // Thanks to this check we can simply do `assert(!beforeVault_.empty()` when + // enforcing invariants on transaction types other than ttVAULT_CREATE + if (beforeVault_.empty() && txnType != ttVAULT_CREATE) + { + JLOG(j.fatal()) << // + "Invariant failed: vault created by a wrong transaction type"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : vault creation invariant"); + return !enforce; // That's all we can do here + } + + if (!beforeVault_.empty() && afterVault.lossUnrealized != beforeVault_[0].lossUnrealized && + txnType != ttLOAN_MANAGE && txnType != ttLOAN_PAY) + { + JLOG(j.fatal()) << // + "Invariant failed: vault transaction must not change loss " + "unrealized"; + result = false; + } + + auto const beforeShares = [&]() -> std::optional { + if (beforeVault_.empty()) + return std::nullopt; + auto const& beforeVault = beforeVault_[0]; + + for (auto const& e : beforeMPTs_) + { + if (e.share.getMptID() == beforeVault.shareMPTID) + return std::move(e); + } + return std::nullopt; + }(); + + if (!beforeShares && + (tx.getTxnType() == ttVAULT_DEPOSIT || // + tx.getTxnType() == ttVAULT_WITHDRAW || // + tx.getTxnType() == ttVAULT_CLAWBACK)) + { + JLOG(j.fatal()) << "Invariant failed: vault operation succeeded " + "without updating shares"; + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : shares noop invariant"); + return !enforce; // That's all we can do here + } + + auto const& vaultAsset = afterVault.asset; + auto const deltaAssets = [&](AccountID const& id) -> std::optional { + auto const get = // + [&](auto const& it, std::int8_t sign = 1) -> std::optional { + if (it == deltas_.end()) + return std::nullopt; + + return it->second * sign; + }; + + return std::visit( + [&](TIss const& issue) { + if constexpr (std::is_same_v) + { + if (isXRP(issue)) + return get(deltas_.find(keylet::account(id).key)); + return get( + deltas_.find(keylet::line(id, issue).key), id > issue.getIssuer() ? -1 : 1); + } + else if constexpr (std::is_same_v) + { + return get(deltas_.find(keylet::mptoken(issue.getMptID(), id).key)); + } + }, + vaultAsset.value()); + }; + auto const deltaAssetsTxAccount = [&]() -> std::optional { + auto ret = deltaAssets(tx[sfAccount]); + // Nothing returned or not XRP transaction + if (!ret.has_value() || !vaultAsset.native()) + return ret; + + // Delegated transaction; no need to compensate for fees + if (auto const delegate = tx[~sfDelegate]; + delegate.has_value() && *delegate != tx[sfAccount]) + return ret; + + *ret += fee.drops(); + if (*ret == zero) + return std::nullopt; + + return ret; + }; + auto const deltaShares = [&](AccountID const& id) -> std::optional { + auto const it = [&]() { + if (id == afterVault.pseudoId) + return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key); + return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key); + }(); + + return it != deltas_.end() ? std::optional(it->second) : std::nullopt; + }; + + auto const vaultHoldsNoAssets = [&](Vault const& vault) { + return vault.assetsAvailable == 0 && vault.assetsTotal == 0; + }; + + // Technically this does not need to be a lambda, but it's more + // convenient thanks to early "return false"; the not-so-nice + // alternatives are several layers of nested if/else or more complex + // (i.e. brittle) if statements. + result &= [&]() { + switch (txnType) + { + case ttVAULT_CREATE: { + bool result = true; + + if (!beforeVault_.empty()) + { + JLOG(j.fatal()) // + << "Invariant failed: create operation must not have " + "updated a vault"; + result = false; + } + + if (afterVault.assetsAvailable != zero || afterVault.assetsTotal != zero || + afterVault.lossUnrealized != zero || updatedShares->sharesTotal != 0) + { + JLOG(j.fatal()) // + << "Invariant failed: created vault must be empty"; + result = false; + } + + if (afterVault.pseudoId != updatedShares->share.getIssuer()) + { + JLOG(j.fatal()) // + << "Invariant failed: shares issuer and vault " + "pseudo-account must be the same"; + result = false; + } + + auto const sleSharesIssuer = + view.read(keylet::account(updatedShares->share.getIssuer())); + if (!sleSharesIssuer) + { + JLOG(j.fatal()) // + << "Invariant failed: shares issuer must exist"; + return false; + } + + if (!isPseudoAccount(sleSharesIssuer)) + { + JLOG(j.fatal()) // + << "Invariant failed: shares issuer must be a " + "pseudo-account"; + result = false; + } + + if (auto const vaultId = (*sleSharesIssuer)[~sfVaultID]; + !vaultId || *vaultId != afterVault.key) + { + JLOG(j.fatal()) // + << "Invariant failed: shares issuer pseudo-account " + "must point back to the vault"; + result = false; + } + + return result; + } + case ttVAULT_SET: { + bool result = true; + + XRPL_ASSERT( + !beforeVault_.empty(), "xrpl::ValidVault::finalize : set updated a vault"); + auto const& beforeVault = beforeVault_[0]; + + auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId); + if (vaultDeltaAssets) + { + JLOG(j.fatal()) << // + "Invariant failed: set must not change vault balance"; + result = false; + } + + if (beforeVault.assetsTotal != afterVault.assetsTotal) + { + JLOG(j.fatal()) << // + "Invariant failed: set must not change assets " + "outstanding"; + result = false; + } + + if (afterVault.assetsMaximum > zero && + afterVault.assetsTotal > afterVault.assetsMaximum) + { + JLOG(j.fatal()) << // + "Invariant failed: set assets outstanding must not " + "exceed assets maximum"; + result = false; + } + + if (beforeVault.assetsAvailable != afterVault.assetsAvailable) + { + JLOG(j.fatal()) << // + "Invariant failed: set must not change assets " + "available"; + result = false; + } + + if (beforeShares && updatedShares && + beforeShares->sharesTotal != updatedShares->sharesTotal) + { + JLOG(j.fatal()) << // + "Invariant failed: set must not change shares " + "outstanding"; + result = false; + } + + return result; + } + case ttVAULT_DEPOSIT: { + bool result = true; + + XRPL_ASSERT( + !beforeVault_.empty(), "xrpl::ValidVault::finalize : deposit updated a vault"); + auto const& beforeVault = beforeVault_[0]; + + auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId); + + if (!vaultDeltaAssets) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change vault balance"; + return false; // That's all we can do + } + + if (*vaultDeltaAssets > tx[sfAmount]) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must not change vault " + "balance by more than deposited amount"; + result = false; + } + + if (*vaultDeltaAssets <= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must increase vault balance"; + result = false; + } + + // Any payments (including deposits) made by the issuer + // do not change their balance, but create funds instead. + bool const issuerDeposit = [&]() -> bool { + if (vaultAsset.native()) + return false; + return tx[sfAccount] == vaultAsset.getIssuer(); + }(); + + if (!issuerDeposit) + { + auto const accountDeltaAssets = deltaAssetsTxAccount(); + if (!accountDeltaAssets) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change depositor " + "balance"; + return false; + } + + if (*accountDeltaAssets >= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must decrease depositor " + "balance"; + result = false; + } + + if (*accountDeltaAssets * -1 != *vaultDeltaAssets) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change vault and " + "depositor balance by equal amount"; + result = false; + } + } + + if (afterVault.assetsMaximum > zero && + afterVault.assetsTotal > afterVault.assetsMaximum) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit assets outstanding must not " + "exceed assets maximum"; + result = false; + } + + auto const accountDeltaShares = deltaShares(tx[sfAccount]); + if (!accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change depositor " + "shares"; + return false; // That's all we can do + } + + if (*accountDeltaShares <= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must increase depositor " + "shares"; + result = false; + } + + auto const vaultDeltaShares = deltaShares(afterVault.pseudoId); + if (!vaultDeltaShares || *vaultDeltaShares == zero) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change vault shares"; + return false; // That's all we can do + } + + if (*vaultDeltaShares * -1 != *accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: deposit must change depositor and " + "vault shares by equal amount"; + result = false; + } + + if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal) + { + JLOG(j.fatal()) << "Invariant failed: deposit and assets " + "outstanding must add up"; + result = false; + } + if (beforeVault.assetsAvailable + *vaultDeltaAssets != afterVault.assetsAvailable) + { + JLOG(j.fatal()) << "Invariant failed: deposit and assets " + "available must add up"; + result = false; + } + + return result; + } + case ttVAULT_WITHDRAW: { + bool result = true; + + XRPL_ASSERT( + !beforeVault_.empty(), + "xrpl::ValidVault::finalize : withdrawal updated a " + "vault"); + auto const& beforeVault = beforeVault_[0]; + + auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId); + + if (!vaultDeltaAssets) + { + JLOG(j.fatal()) << "Invariant failed: withdrawal must " + "change vault balance"; + return false; // That's all we can do + } + + if (*vaultDeltaAssets >= zero) + { + JLOG(j.fatal()) << "Invariant failed: withdrawal must " + "decrease vault balance"; + result = false; + } + + // Any payments (including withdrawal) going to the issuer + // do not change their balance, but destroy funds instead. + bool const issuerWithdrawal = [&]() -> bool { + if (vaultAsset.native()) + return false; + auto const destination = tx[~sfDestination].value_or(tx[sfAccount]); + return destination == vaultAsset.getIssuer(); + }(); + + if (!issuerWithdrawal) + { + auto const accountDeltaAssets = deltaAssetsTxAccount(); + auto const otherAccountDelta = [&]() -> std::optional { + if (auto const destination = tx[~sfDestination]; + destination && *destination != tx[sfAccount]) + return deltaAssets(*destination); + return std::nullopt; + }(); + + if (accountDeltaAssets.has_value() == otherAccountDelta.has_value()) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must change one " + "destination balance"; + return false; + } + + auto const destinationDelta = // + accountDeltaAssets ? *accountDeltaAssets : *otherAccountDelta; + + if (destinationDelta <= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must increase " + "destination balance"; + result = false; + } + + if (*vaultDeltaAssets * -1 != destinationDelta) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must change vault " + "and destination balance by equal amount"; + result = false; + } + } + + auto const accountDeltaShares = deltaShares(tx[sfAccount]); + if (!accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must change depositor " + "shares"; + return false; + } + + if (*accountDeltaShares >= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must decrease depositor " + "shares"; + result = false; + } + + auto const vaultDeltaShares = deltaShares(afterVault.pseudoId); + if (!vaultDeltaShares || *vaultDeltaShares == zero) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must change vault shares"; + return false; // That's all we can do + } + + if (*vaultDeltaShares * -1 != *accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: withdrawal must change depositor " + "and vault shares by equal amount"; + result = false; + } + + // Note, vaultBalance is negative (see check above) + if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal) + { + JLOG(j.fatal()) << "Invariant failed: withdrawal and " + "assets outstanding must add up"; + result = false; + } + + if (beforeVault.assetsAvailable + *vaultDeltaAssets != afterVault.assetsAvailable) + { + JLOG(j.fatal()) << "Invariant failed: withdrawal and " + "assets available must add up"; + result = false; + } + + return result; + } + case ttVAULT_CLAWBACK: { + bool result = true; + + XRPL_ASSERT( + !beforeVault_.empty(), "xrpl::ValidVault::finalize : clawback updated a vault"); + auto const& beforeVault = beforeVault_[0]; + + if (vaultAsset.native() || vaultAsset.getIssuer() != tx[sfAccount]) + { + // The owner can use clawback to force-burn shares when the + // vault is empty but there are outstanding shares + if (!(beforeShares && beforeShares->sharesTotal > 0 && + vaultHoldsNoAssets(beforeVault) && beforeVault.owner == tx[sfAccount])) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback may only be performed " + "by the asset issuer, or by the vault owner of an " + "empty vault"; + return false; // That's all we can do + } + } + + auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId); + if (vaultDeltaAssets) + { + if (*vaultDeltaAssets >= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must decrease vault " + "balance"; + result = false; + } + + if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback and assets outstanding " + "must add up"; + result = false; + } + + if (beforeVault.assetsAvailable + *vaultDeltaAssets != + afterVault.assetsAvailable) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback and assets available " + "must add up"; + result = false; + } + } + else if (!vaultHoldsNoAssets(beforeVault)) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must change vault balance"; + return false; // That's all we can do + } + + auto const accountDeltaShares = deltaShares(tx[sfHolder]); + if (!accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must change holder shares"; + return false; // That's all we can do + } + + if (*accountDeltaShares >= zero) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must decrease holder " + "shares"; + result = false; + } + + auto const vaultDeltaShares = deltaShares(afterVault.pseudoId); + if (!vaultDeltaShares || *vaultDeltaShares == zero) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must change vault shares"; + return false; // That's all we can do + } + + if (*vaultDeltaShares * -1 != *accountDeltaShares) + { + JLOG(j.fatal()) << // + "Invariant failed: clawback must change holder and " + "vault shares by equal amount"; + result = false; + } + + return result; + } + + case ttLOAN_SET: + case ttLOAN_MANAGE: + case ttLOAN_PAY: { + // TBD + return true; + } + + default: + // LCOV_EXCL_START + UNREACHABLE("xrpl::ValidVault::finalize : unknown transaction type"); + return false; + // LCOV_EXCL_STOP + } + }(); + + if (!result) + { + // The comment at the top of this file starting with "assert(enforce)" + // explains this assert. + XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : vault invariants"); + return !enforce; + } + + return true; +} + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/BookTip.cpp b/src/libxrpl/tx/paths/BookTip.cpp similarity index 91% rename from src/xrpld/app/tx/detail/BookTip.cpp rename to src/libxrpl/tx/paths/BookTip.cpp index 61010c0caa..5611a081c3 100644 --- a/src/xrpld/app/tx/detail/BookTip.cpp +++ b/src/libxrpl/tx/paths/BookTip.cpp @@ -1,4 +1,6 @@ -#include +#include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/paths/Flow.cpp b/src/libxrpl/tx/paths/Flow.cpp similarity index 91% rename from src/xrpld/app/paths/Flow.cpp rename to src/libxrpl/tx/paths/Flow.cpp index 1f6d29bfb9..5a706ea812 100644 --- a/src/xrpld/app/paths/Flow.cpp +++ b/src/libxrpl/tx/paths/Flow.cpp @@ -1,13 +1,12 @@ -#include -#include -#include -#include -#include - #include -#include +#include #include #include +#include +#include +#include +#include +#include namespace xrpl { @@ -16,10 +15,14 @@ static auto finishFlow(PaymentSandbox& sb, Issue const& srcIssue, Issue const& dstIssue, FlowResult&& f) { path::RippleCalc::Output result; - if (f.ter == tesSUCCESS) + if (isTesSuccess(f.ter)) + { f.sandbox->apply(sb); + } else + { result.removableOffers = std::move(f.removableOffers); + } result.setResult(f.ter); result.actualAmountIn = toSTAmount(f.in, srcIssue); @@ -79,7 +82,7 @@ flow( domainID, j); - if (toStrandsTer != tesSUCCESS) + if (!isTesSuccess(toStrandsTer)) { path::RippleCalc::Output result; result.setResult(toStrandsTer); @@ -90,7 +93,8 @@ flow( if (j.trace()) { - j.trace() << "\nsrc: " << src << "\ndst: " << dst << "\nsrcIssue: " << srcIssue << "\ndstIssue: " << dstIssue; + j.trace() << "\nsrc: " << src << "\ndst: " << dst << "\nsrcIssue: " << srcIssue + << "\ndstIssue: " << dstIssue; j.trace() << "\nNumStrands: " << strands.size(); for (auto const& curStrand : strands) { diff --git a/src/xrpld/app/tx/detail/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp similarity index 88% rename from src/xrpld/app/tx/detail/OfferStream.cpp rename to src/libxrpl/tx/paths/OfferStream.cpp index 9f013de607..b7dc431b23 100644 --- a/src/xrpld/app/tx/detail/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -1,10 +1,11 @@ -#include -#include - #include #include +#include +#include #include #include +#include +#include namespace xrpl { @@ -70,7 +71,8 @@ TOfferStreamBase::erase(ApplyView& view) p->setFieldV256(sfIndexes, v); view.update(p); - JLOG(j_.trace()) << "Missing offer " << tip_.index() << " removed from directory " << tip_.dir(); + JLOG(j_.trace()) << "Missing offer " << tip_.index() << " removed from directory " + << tip_.dir(); } static STAmount @@ -95,10 +97,13 @@ accountFundsHelper( beast::Journal j) { if (issue.account == id) + { // self funded return amtDefault; + } - return toAmount(accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); + return toAmount( + accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); } static XRPAmount @@ -110,7 +115,8 @@ accountFundsHelper( FreezeHandling freezeHandling, beast::Journal j) { - return toAmount(accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); + return toAmount( + accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); } template @@ -119,13 +125,16 @@ bool TOfferStreamBase::shouldRmSmallIncreasedQOffer() const { static_assert( - std::is_same_v || std::is_same_v, "STAmount is not supported"); + std::is_same_v || std::is_same_v, + "STAmount is not supported"); static_assert( - std::is_same_v || std::is_same_v, "STAmount is not supported"); + std::is_same_v || std::is_same_v, + "STAmount is not supported"); static_assert( - !std::is_same_v || !std::is_same_v, "Cannot have XRP/XRP offers"); + !std::is_same_v || !std::is_same_v, + "Cannot have XRP/XRP offers"); // Consider removing the offer if: // o `TakerPays` is XRP (because of XRP drops granularity) or @@ -231,8 +240,8 @@ TOfferStreamBase::step() continue; } - bool const deepFrozen = - isDeepFrozen(view_, offer_.owner(), offer_.issueIn().currency, offer_.issueIn().account); + bool const deepFrozen = isDeepFrozen( + view_, offer_.owner(), offer_.issueIn().currency, offer_.issueIn().account); if (deepFrozen) { JLOG(j_.trace()) << "Removing deep frozen unfunded offer " << entry->key(); @@ -242,7 +251,8 @@ TOfferStreamBase::step() } if (entry->isFieldPresent(sfDomainID) && - !permissioned_dex::offerInDomain(view_, entry->key(), entry->getFieldH256(sfDomainID), j_)) + !permissioned_dex::offerInDomain( + view_, entry->key(), entry->getFieldH256(sfDomainID), j_)) { JLOG(j_.trace()) << "Removing offer no longer in domain " << entry->key(); permRmOffer(entry->key()); @@ -251,7 +261,8 @@ TOfferStreamBase::step() } // Calculate owner funds - ownerFunds_ = accountFundsHelper(view_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); + ownerFunds_ = accountFundsHelper( + view_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); // Check for unfunded offer if (*ownerFunds_ <= beast::zero) @@ -259,8 +270,8 @@ TOfferStreamBase::step() // If the owner's balance in the pristine view is the same, // we haven't modified the balance and therefore the // offer is "found unfunded" versus "became unfunded" - auto const original_funds = - accountFundsHelper(cancelView_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); + auto const original_funds = accountFundsHelper( + cancelView_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); if (original_funds == *ownerFunds_) { @@ -312,8 +323,8 @@ TOfferStreamBase::step() if (rmSmallIncreasedQOffer) { - auto const original_funds = - accountFundsHelper(cancelView_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); + auto const original_funds = accountFundsHelper( + cancelView_, offer_.owner(), amount.out, offer_.issueOut(), fhZERO_IF_FROZEN, j_); if (original_funds == *ownerFunds_) { diff --git a/src/xrpld/app/paths/RippleCalc.cpp b/src/libxrpl/tx/paths/RippleCalc.cpp similarity index 84% rename from src/xrpld/app/paths/RippleCalc.cpp rename to src/libxrpl/tx/paths/RippleCalc.cpp index 0667e1971c..e87ecab90f 100644 --- a/src/xrpld/app/paths/RippleCalc.cpp +++ b/src/libxrpl/tx/paths/RippleCalc.cpp @@ -1,10 +1,9 @@ -#include -#include -#include - #include #include #include +#include +#include +#include namespace xrpl { namespace path { @@ -55,7 +54,8 @@ RippleCalc::rippleCalculate( }(); auto const sendMax = [&]() -> std::optional { - if (saMaxAmountReq >= beast::zero || saMaxAmountReq.getCurrency() != saDstAmountReq.getCurrency() || + if (saMaxAmountReq >= beast::zero || + saMaxAmountReq.getCurrency() != saDstAmountReq.getCurrency() || saMaxAmountReq.getIssuer() != uSrcAccountID) { return saMaxAmountReq; @@ -93,9 +93,9 @@ RippleCalc::rippleCalculate( } j.debug() << "RippleCalc Result> " - << " actualIn: " << flowOut.actualAmountIn << ", actualOut: " << flowOut.actualAmountOut - << ", result: " << flowOut.result() << ", dstAmtReq: " << saDstAmountReq - << ", sendMax: " << saMaxAmountReq; + << " actualIn: " << flowOut.actualAmountIn + << ", actualOut: " << flowOut.actualAmountOut << ", result: " << flowOut.result() + << ", dstAmtReq: " << saDstAmountReq << ", sendMax: " << saMaxAmountReq; flowSB.apply(view); return flowOut; diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp similarity index 80% rename from src/xrpld/app/tx/detail/DeleteAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountDelete.cpp index 18961469a3..fed265c125 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -1,26 +1,26 @@ -#include -#include -#include -#include -#include -#include -#include - #include -#include #include -#include #include +#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include namespace xrpl { bool -DeleteAccount::checkExtraFeatures(PreflightContext const& ctx) +AccountDelete::checkExtraFeatures(PreflightContext const& ctx) { if (ctx.tx.isFieldPresent(sfCredentialIDs) && !ctx.rules.enabled(featureCredentials)) return false; @@ -29,11 +29,13 @@ DeleteAccount::checkExtraFeatures(PreflightContext const& ctx) } NotTEC -DeleteAccount::preflight(PreflightContext const& ctx) +AccountDelete::preflight(PreflightContext const& ctx) { if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + { // An account cannot be deleted and give itself the resulting XRP. return temDST_IS_SRC; + } if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) return err; @@ -42,7 +44,7 @@ DeleteAccount::preflight(PreflightContext const& ctx) } XRPAmount -DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx) +AccountDelete::calculateBaseFee(ReadView const& view, STTx const& tx) { // The fee required for AccountDelete is one owner reserve. return calculateOwnerReserveFee(view, tx); @@ -51,7 +53,7 @@ DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx) namespace { // Define a function pointer type that can be used to delete ledger node types. using DeleterFuncPtr = TER (*)( - Application& app, + ServiceRegistry& registry, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -61,7 +63,7 @@ using DeleterFuncPtr = TER (*)( // Local function definitions that provides signature compatibility. TER offerDelete( - Application& app, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -73,19 +75,19 @@ offerDelete( TER removeSignersFromLedger( - Application& app, + ServiceRegistry& registry, ApplyView& view, AccountID const& account, uint256 const& delIndex, std::shared_ptr const& sleDel, beast::Journal j) { - return SetSignerList::removeFromLedger(app, view, account, j); + return SignerListSet::removeFromLedger(registry, view, account, j); } TER removeTicketFromLedger( - Application&, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -97,7 +99,7 @@ removeTicketFromLedger( TER removeDepositPreauthFromLedger( - Application&, + ServiceRegistry&, ApplyView& view, AccountID const&, uint256 const& delIndex, @@ -109,7 +111,7 @@ removeDepositPreauthFromLedger( TER removeNFTokenOfferFromLedger( - Application& app, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -124,7 +126,7 @@ removeNFTokenOfferFromLedger( TER removeDIDFromLedger( - Application& app, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -136,19 +138,19 @@ removeDIDFromLedger( TER removeOracleFromLedger( - Application&, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const&, std::shared_ptr const& sleDel, beast::Journal j) { - return DeleteOracle::deleteOracle(view, sleDel, account, j); + return OracleDelete::deleteOracle(view, sleDel, account, j); } TER removeCredentialFromLedger( - Application&, + ServiceRegistry&, ApplyView& view, AccountID const&, uint256 const&, @@ -160,7 +162,7 @@ removeCredentialFromLedger( TER removeDelegateFromLedger( - Application& app, + ServiceRegistry&, ApplyView& view, AccountID const& account, uint256 const& delIndex, @@ -204,7 +206,7 @@ nonObligationDeleter(LedgerEntryType t) } // namespace TER -DeleteAccount::preclaim(PreclaimContext const& ctx) +AccountDelete::preclaim(PreclaimContext const& ctx) { AccountID const account{ctx.tx[sfAccount]}; AccountID const dst{ctx.tx[sfDestination]}; @@ -234,7 +236,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) } auto sleAccount = ctx.view.read(keylet::account(account)); - XRPL_ASSERT(sleAccount, "xrpl::DeleteAccount::preclaim : non-null account"); + XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account"); if (!sleAccount) return terNO_ACCOUNT; @@ -247,7 +249,8 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) Keylet const first = keylet::nftpage_min(account); Keylet const last = keylet::nftpage_max(account); - auto const cp = ctx.view.read(Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key))); + auto const cp = ctx.view.read( + Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key))); if (cp) return tecHAS_OBLIGATIONS; @@ -272,7 +275,8 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) // their account and mints a NFToken, it is possible that the // NFTokenSequence of this NFToken is the same as the one that the // authorized minter minted in a previous ledger. - if ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) + (*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta > + if ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) + + (*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta > ctx.view.seq()) return tecTOO_SOON; @@ -301,7 +305,7 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) { // Directory node has an invalid index. Bail out. // LCOV_EXCL_START - JLOG(ctx.j.fatal()) << "DeleteAccount: directory node in ledger " << ctx.view.seq() + JLOG(ctx.j.fatal()) << "AccountDelete: directory node in ledger " << ctx.view.seq() << " has index to object that is missing: " << to_string(dirEntry); return tefBAD_LEDGER; // LCOV_EXCL_STOP @@ -323,21 +327,22 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) } TER -DeleteAccount::doApply() +AccountDelete::doApply() { auto src = view().peek(keylet::account(account_)); - XRPL_ASSERT(src, "xrpl::DeleteAccount::doApply : non-null source account"); + XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account"); auto const dstID = ctx_.tx[sfDestination]; auto dst = view().peek(keylet::account(dstID)); - XRPL_ASSERT(dst, "xrpl::DeleteAccount::doApply : non-null destination account"); + XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account"); if (!src || !dst) return tefBAD_LEDGER; // LCOV_EXCL_LINE if (ctx_.tx.isFieldPresent(sfCredentialIDs)) { - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal); + if (auto err = + verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal); !isTesSuccess(err)) return err; } @@ -351,41 +356,43 @@ DeleteAccount::doApply() std::shared_ptr& sleItem) -> std::pair { if (auto deleter = nonObligationDeleter(nodeType)) { - TER const result{deleter(ctx_.app, view(), account_, dirEntry, sleItem, j_)}; + TER const result{deleter(ctx_.registry, view(), account_, dirEntry, sleItem, j_)}; return {result, SkipEntry::No}; } // LCOV_EXCL_START UNREACHABLE( - "xrpl::DeleteAccount::doApply : undeletable item not found " + "xrpl::AccountDelete::doApply : undeletable item not found " "in preclaim"); - JLOG(j_.error()) << "DeleteAccount undeletable item not " + JLOG(j_.error()) << "AccountDelete undeletable item not " "found in preclaim."; return {tecHAS_OBLIGATIONS, SkipEntry::No}; // LCOV_EXCL_STOP }, ctx_.journal); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; // Transfer any XRP remaining after the fee is paid to the destination: - (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; - (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; - ctx_.deliver(mSourceBalance); + auto const remainingBalance = src->getFieldAmount(sfBalance).xrp(); + (*dst)[sfBalance] = (*dst)[sfBalance] + remainingBalance; + (*src)[sfBalance] = (*src)[sfBalance] - remainingBalance; + ctx_.deliver(remainingBalance); - XRPL_ASSERT((*src)[sfBalance] == XRPAmount(0), "xrpl::DeleteAccount::doApply : source balance is zero"); + XRPL_ASSERT( + (*src)[sfBalance] == XRPAmount(0), "xrpl::AccountDelete::doApply : source balance is zero"); // If there's still an owner directory associated with the source account // delete it. if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet)) { - JLOG(j_.error()) << "DeleteAccount cannot delete root dir node of " << toBase58(account_); + JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(account_); return tecHAS_OBLIGATIONS; } // Re-arm the password change fee if we can and need to. - if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) + if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) dst->clearFlag(lsfPasswordSpent); view().update(dst); diff --git a/src/xrpld/app/tx/detail/SetAccount.cpp b/src/libxrpl/tx/transactors/account/AccountSet.cpp similarity index 93% rename from src/xrpld/app/tx/detail/SetAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountSet.cpp index beca60c06b..33759c6ec4 100644 --- a/src/xrpld/app/tx/detail/SetAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountSet.cpp @@ -1,32 +1,34 @@ -#include -#include -#include - #include #include +#include #include #include #include #include #include +#include +#include namespace xrpl { TxConsequences -SetAccount::makeTxConsequences(PreflightContext const& ctx) +AccountSet::makeTxConsequences(PreflightContext const& ctx) { - // The SetAccount may be a blocker, but only if it sets or clears + // The AccountSet may be a blocker, but only if it sets or clears // specific account flags. auto getTxConsequencesCategory = [](STTx const& tx) { - if (std::uint32_t const uTxFlags = tx.getFlags(); uTxFlags & (tfRequireAuth | tfOptionalAuth)) + if (std::uint32_t const uTxFlags = tx.getFlags(); + uTxFlags & (tfRequireAuth | tfOptionalAuth)) return TxConsequences::blocker; - if (auto const uSetFlag = tx[~sfSetFlag]; - uSetFlag && (*uSetFlag == asfRequireAuth || *uSetFlag == asfDisableMaster || *uSetFlag == asfAccountTxnID)) + if (auto const uSetFlag = tx[~sfSetFlag]; uSetFlag && + (*uSetFlag == asfRequireAuth || *uSetFlag == asfDisableMaster || + *uSetFlag == asfAccountTxnID)) return TxConsequences::blocker; if (auto const uClearFlag = tx[~sfClearFlag]; uClearFlag && - (*uClearFlag == asfRequireAuth || *uClearFlag == asfDisableMaster || *uClearFlag == asfAccountTxnID)) + (*uClearFlag == asfRequireAuth || *uClearFlag == asfDisableMaster || + *uClearFlag == asfAccountTxnID)) return TxConsequences::blocker; return TxConsequences::normal; @@ -36,13 +38,13 @@ SetAccount::makeTxConsequences(PreflightContext const& ctx) } std::uint32_t -SetAccount::getFlagsMask(PreflightContext const& ctx) +AccountSet::getFlagsMask(PreflightContext const& ctx) { return tfAccountSetMask; } NotTEC -SetAccount::preflight(PreflightContext const& ctx) +AccountSet::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -125,7 +127,7 @@ SetAccount::preflight(PreflightContext const& ctx) if (auto const mk = tx[~sfMessageKey]) { - if (mk->size() && !publicKeyType({mk->data(), mk->size()})) + if (!mk->empty() && !publicKeyType({mk->data(), mk->size()})) { JLOG(j.trace()) << "Invalid message key specified."; return telBAD_PUBLIC_KEY; @@ -149,9 +151,9 @@ SetAccount::preflight(PreflightContext const& ctx) } NotTEC -SetAccount::checkPermission(ReadView const& view, STTx const& tx) +AccountSet::checkPermission(ReadView const& view, STTx const& tx) { - // SetAccount is prohibited to be granted on a transaction level, + // AccountSet is prohibited to be granted on a transaction level, // but some granular permissions are allowed. auto const delegate = tx[~sfDelegate]; if (!delegate) @@ -198,7 +200,7 @@ SetAccount::checkPermission(ReadView const& view, STTx const& tx) } TER -SetAccount::preclaim(PreclaimContext const& ctx) +AccountSet::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -261,7 +263,7 @@ SetAccount::preclaim(PreclaimContext const& ctx) } TER -SetAccount::doApply() +AccountSet::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -407,7 +409,8 @@ SetAccount::doApply() // If you have set NoFreeze, you may not clear GlobalFreeze // This prevents those who have set NoFreeze from using // GlobalFreeze strategically. - if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) && ((uFlagsOut & lsfNoFreeze) == 0)) + if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) && + ((uFlagsOut & lsfNoFreeze) == 0)) { JLOG(j_.trace()) << "Clear GlobalFreeze flag"; uFlagsOut &= ~lsfGlobalFreeze; @@ -489,7 +492,7 @@ SetAccount::doApply() if (messageKey.empty()) { - JLOG(j_.debug()) << "set message key"; + JLOG(j_.debug()) << "clear message key"; sle->makeFieldAbsent(sfMessageKey); } else @@ -563,32 +566,52 @@ SetAccount::doApply() sle->makeFieldAbsent(sfNFTokenMinter); if (uSetFlag == asfDisallowIncomingNFTokenOffer) + { uFlagsOut |= lsfDisallowIncomingNFTokenOffer; + } else if (uClearFlag == asfDisallowIncomingNFTokenOffer) + { uFlagsOut &= ~lsfDisallowIncomingNFTokenOffer; + } if (uSetFlag == asfDisallowIncomingCheck) + { uFlagsOut |= lsfDisallowIncomingCheck; + } else if (uClearFlag == asfDisallowIncomingCheck) + { uFlagsOut &= ~lsfDisallowIncomingCheck; + } if (uSetFlag == asfDisallowIncomingPayChan) + { uFlagsOut |= lsfDisallowIncomingPayChan; + } else if (uClearFlag == asfDisallowIncomingPayChan) + { uFlagsOut &= ~lsfDisallowIncomingPayChan; + } if (uSetFlag == asfDisallowIncomingTrustline) + { uFlagsOut |= lsfDisallowIncomingTrustline; + } else if (uClearFlag == asfDisallowIncomingTrustline) + { uFlagsOut &= ~lsfDisallowIncomingTrustline; + } // Set or clear flags for disallowing escrow if (ctx_.view().rules().enabled(featureTokenEscrow)) { if (uSetFlag == asfAllowTrustLineLocking) + { uFlagsOut |= lsfAllowTrustLineLocking; + } else if (uClearFlag == asfAllowTrustLineLocking) + { uFlagsOut &= ~lsfAllowTrustLineLocking; + } } // Set flag for clawback diff --git a/src/xrpld/app/tx/detail/SetRegularKey.cpp b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp similarity index 85% rename from src/xrpld/app/tx/detail/SetRegularKey.cpp rename to src/libxrpl/tx/transactors/account/SetRegularKey.cpp index 14302c67c4..08175b196f 100644 --- a/src/xrpld/app/tx/detail/SetRegularKey.cpp +++ b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp @@ -1,8 +1,7 @@ -#include - #include #include #include +#include namespace xrpl { @@ -32,7 +31,8 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx) NotTEC SetRegularKey::preflight(PreflightContext const& ctx) { - if (ctx.tx.isFieldPresent(sfRegularKey) && (ctx.tx.getAccountID(sfRegularKey) == ctx.tx.getAccountID(sfAccount))) + if (ctx.tx.isFieldPresent(sfRegularKey) && + (ctx.tx.getAccountID(sfRegularKey) == ctx.tx.getAccountID(sfAccount))) { return temBAD_REGKEY; } @@ -47,7 +47,7 @@ SetRegularKey::doApply() if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - if (!minimumFee(ctx_.app, ctx_.baseFee, view().fees(), view().flags())) + if (!minimumFee(ctx_.registry, ctx_.baseFee, view().fees(), view().flags())) sle->setFlag(lsfPasswordSpent); if (ctx_.tx.isFieldPresent(sfRegularKey)) diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp similarity index 80% rename from src/xrpld/app/tx/detail/SetSignerList.cpp rename to src/libxrpl/tx/transactors/account/SignerListSet.cpp index 1864e82b99..fe9e80d7e0 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -1,14 +1,14 @@ -#include -#include - #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -20,8 +20,8 @@ namespace xrpl { // setting the sfSignerListID to zero in all cases. static std::uint32_t const DEFAULT_SIGNER_LIST_ID = 0; -std::tuple, SetSignerList::Operation> -SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j) +std::tuple, SignerListSet::Operation> +SignerListSet::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j) { // Check the quorum. A non-zero quorum means we're creating or replacing // the list. A zero quorum means we're destroying the list. @@ -52,18 +52,18 @@ SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journ } std::uint32_t -SetSignerList::getFlagsMask(PreflightContext const& ctx) +SignerListSet::getFlagsMask(PreflightContext const& ctx) { // 0 means "Allow any flags" return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; } NotTEC -SetSignerList::preflight(PreflightContext const& ctx) +SignerListSet::preflight(PreflightContext const& ctx) { auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j); - if (std::get<0>(result) != tesSUCCESS) + if (!isTesSuccess(std::get<0>(result))) return std::get<0>(result); if (std::get<3>(result) == unknown) @@ -77,9 +77,9 @@ SetSignerList::preflight(PreflightContext const& ctx) { // Validate our settings. auto const account = ctx.tx.getAccountID(sfAccount); - NotTEC const ter = - validateQuorumAndSignerEntries(std::get<1>(result), std::get<2>(result), account, ctx.j, ctx.rules); - if (ter != tesSUCCESS) + NotTEC const ter = validateQuorumAndSignerEntries( + std::get<1>(result), std::get<2>(result), account, ctx.j, ctx.rules); + if (!isTesSuccess(ter)) { return ter; } @@ -89,7 +89,7 @@ SetSignerList::preflight(PreflightContext const& ctx) } TER -SetSignerList::doApply() +SignerListSet::doApply() { // Perform the operation preCompute() decided on. switch (do_) @@ -104,24 +104,28 @@ SetSignerList::doApply() break; } // LCOV_EXCL_START - UNREACHABLE("xrpl::SetSignerList::doApply : invalid operation"); + UNREACHABLE("xrpl::SignerListSet::doApply : invalid operation"); return temMALFORMED; // LCOV_EXCL_STOP } void -SetSignerList::preCompute() +SignerListSet::preCompute() { // Get the quorum and operation info. auto result = determineOperation(ctx_.tx, view().flags(), j_); - XRPL_ASSERT(std::get<0>(result) == tesSUCCESS, "xrpl::SetSignerList::preCompute : result is tesSUCCESS"); - XRPL_ASSERT(std::get<3>(result) != unknown, "xrpl::SetSignerList::preCompute : result is known operation"); + XRPL_ASSERT( + isTesSuccess(std::get<0>(result)), + "xrpl::SignerListSet::preCompute : result is tesSUCCESS"); + XRPL_ASSERT( + std::get<3>(result) != unknown, + "xrpl::SignerListSet::preCompute : result is known operation"); quorum_ = std::get<1>(result); signers_ = std::get<2>(result); do_ = std::get<3>(result); - return Transactor::preCompute(); + Transactor::preCompute(); } // The return type is signed so it is compatible with the 3rd argument @@ -144,14 +148,18 @@ signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules) // The static_cast should always be safe since entryCount should always // be in the range from 1 to 32. // We've got a lot of room to grow. - XRPL_ASSERT(entryCount >= STTx::minMultiSigners, "xrpl::signerCountBasedOwnerCountDelta : minimum signers"); - XRPL_ASSERT(entryCount <= STTx::maxMultiSigners, "xrpl::signerCountBasedOwnerCountDelta : maximum signers"); + XRPL_ASSERT( + entryCount >= STTx::minMultiSigners, + "xrpl::signerCountBasedOwnerCountDelta : minimum signers"); + XRPL_ASSERT( + entryCount <= STTx::maxMultiSigners, + "xrpl::signerCountBasedOwnerCountDelta : maximum signers"); return 2 + static_cast(entryCount); } static TER removeSignersFromLedger( - Application& app, + ServiceRegistry& registry, ApplyView& view, Keylet const& accountKeylet, Keylet const& ownerDirKeylet, @@ -173,7 +181,8 @@ removeSignersFromLedger( if ((signers->getFlags() & lsfOneOwnerCount) == 0) { STArray const& actualList = signers->getFieldArray(sfSignerEntries); - removeFromOwnerCount = signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) * -1; + removeFromOwnerCount = + signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) * -1; } // Remove the node from the account directory. @@ -187,7 +196,8 @@ removeSignersFromLedger( // LCOV_EXCL_STOP } - adjustOwnerCount(view, view.peek(accountKeylet), removeFromOwnerCount, app.journal("View")); + adjustOwnerCount( + view, view.peek(accountKeylet), removeFromOwnerCount, registry.journal("View")); view.erase(signers); @@ -195,17 +205,22 @@ removeSignersFromLedger( } TER -SetSignerList::removeFromLedger(Application& app, ApplyView& view, AccountID const& account, beast::Journal j) +SignerListSet::removeFromLedger( + ServiceRegistry& registry, + ApplyView& view, + AccountID const& account, + beast::Journal j) { auto const accountKeylet = keylet::account(account); auto const ownerDirKeylet = keylet::ownerDir(account); auto const signerListKeylet = keylet::signers(account); - return removeSignersFromLedger(app, view, accountKeylet, ownerDirKeylet, signerListKeylet, j); + return removeSignersFromLedger( + registry, view, accountKeylet, ownerDirKeylet, signerListKeylet, j); } NotTEC -SetSignerList::validateQuorumAndSignerEntries( +SignerListSet::validateQuorumAndSignerEntries( std::uint32_t quorum, std::vector const& signers, AccountID const& account, @@ -225,7 +240,7 @@ SetSignerList::validateQuorumAndSignerEntries( // Make sure there are no duplicate signers. XRPL_ASSERT( std::is_sorted(signers.begin(), signers.end()), - "xrpl::SetSignerList::validateQuorumAndSignerEntries : sorted " + "xrpl::SignerListSet::validateQuorumAndSignerEntries : sorted " "signers"); if (std::adjacent_find(signers.begin(), signers.end()) != signers.end()) { @@ -264,7 +279,7 @@ SetSignerList::validateQuorumAndSignerEntries( } TER -SetSignerList::replaceSignerList() +SignerListSet::replaceSignerList() { auto const accountKeylet = keylet::account(account_); auto const ownerDirKeylet = keylet::ownerDir(account_); @@ -273,7 +288,8 @@ SetSignerList::replaceSignerList() // This may be either a create or a replace. Preemptively remove any // old signer list. May reduce the reserve, so this is done before // checking the reserve. - if (TER const ter = removeSignersFromLedger(ctx_.app, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_)) + if (TER const ter = removeSignersFromLedger( + ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_)) return ter; auto const sle = view().peek(accountKeylet); @@ -290,8 +306,8 @@ SetSignerList::replaceSignerList() // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent - // with CreateTicket. - if (mPriorBalance < newReserve) + // with TicketCreate. + if (preFeeBalance_ < newReserve) return tecINSUFFICIENT_RESERVE; // Everything's ducky. Add the ltSIGNER_LIST to the ledger. @@ -299,9 +315,10 @@ SetSignerList::replaceSignerList() view().insert(signerList); writeSignersToSLE(signerList, flags); - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); // Add the signer list to the account's directory. - auto const page = ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_)); + auto const page = + ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_)); JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_) << ": " << (page ? "success" : "failure"); @@ -318,7 +335,7 @@ SetSignerList::replaceSignerList() } TER -SetSignerList::destroySignerList() +SignerListSet::destroySignerList() { auto const accountKeylet = keylet::account(account_); // Destroying the signer list is only allowed if either the master key @@ -332,11 +349,12 @@ SetSignerList::destroySignerList() auto const ownerDirKeylet = keylet::ownerDir(account_); auto const signerListKeylet = keylet::signers(account_); - return removeSignersFromLedger(ctx_.app, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_); + return removeSignersFromLedger( + ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_); } void -SetSignerList::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const +SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const { // Assign the quorum, default SignerListID, and flags. if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp similarity index 91% rename from src/xrpld/app/tx/detail/XChainBridge.cpp rename to src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index beeab41f67..9ac755cf4d 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1,8 +1,3 @@ -#include -#include -#include -#include - #include #include #include @@ -10,7 +5,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -23,6 +19,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -103,7 +103,8 @@ checkAttestationPublicKey( AccountID const accountFromPK = calcAccountID(pk); - if (auto const sleAttestationSigningAccount = view.read(keylet::account(attestationSignerAccount))) + if (auto const sleAttestationSigningAccount = + view.read(keylet::account(attestationSignerAccount))) { if (accountFromPK == attestationSignerAccount) { @@ -118,7 +119,8 @@ checkAttestationPublicKey( else { // regular key - if (std::optional regularKey = (*sleAttestationSigningAccount)[~sfRegularKey]; + if (std::optional regularKey = + (*sleAttestationSigningAccount)[~sfRegularKey]; regularKey != accountFromPK) { if (!regularKey) @@ -177,7 +179,8 @@ claimHelper( // part of the signers list, or their master key may have been disabled, // or their regular key may have changed attestations.erase_if([&](auto const& a) { - return checkAttestationPublicKey(view, signersList, a.keyAccount, a.publicKey, j) != tesSUCCESS; + return checkAttestationPublicKey(view, signersList, a.keyAccount, a.publicKey, j) != + tesSUCCESS; }); // Check if we have quorum for the amount specified on the new claimAtt @@ -265,8 +268,9 @@ onNewAttestations( bool changed = false; for (auto att = attBegin; att != attEnd; ++att) { - if (checkAttestationPublicKey(view, signersList, att->attestationSignerAccount, att->publicKey, j) != - tesSUCCESS) + auto const ter = checkAttestationPublicKey( + view, signersList, att->attestationSignerAccount, att->publicKey, j); + if (!isTesSuccess(ter)) { // The checkAttestationPublicKey is not strictly necessary here (it // should be checked in a preclaim step), but it would be bad to let @@ -295,7 +299,13 @@ onNewAttestations( } auto r = claimHelper( - attestations, view, typename TAttestation::MatchFields{*attBegin}, CheckDst::check, quorum, signersList, j); + attestations, + view, + typename TAttestation::MatchFields{*attBegin}, + CheckDst::check, + quorum, + signersList, + j); if (!r.has_value()) return {std::nullopt, changed}; @@ -329,7 +339,7 @@ enum class DepositAuthPolicy { normal, dstCanBypass }; struct TransferHelperSubmittingAccountInfo { AccountID account; - STAmount preFeeBalance; + STAmount preFeeBalance_; STAmount postFeeBalance; }; @@ -382,7 +392,8 @@ transferHelper( // If the destination is the claim owner, and this is a claim // transaction, that's the dst account sending funds to itself. It // can bypass deposit auth. - bool const canBypassDepositAuth = dst == claimOwner && depositAuthPolicy == DepositAuthPolicy::dstCanBypass; + bool const canBypassDepositAuth = + dst == claimOwner && depositAuthPolicy == DepositAuthPolicy::dstCanBypass; if (!canBypassDepositAuth && (sleDst->getFlags() & lsfDepositAuth) && !psb.exists(keylet::depositPreauth(dst, src))) @@ -414,7 +425,7 @@ transferHelper( if (!submittingAccountInfo || submittingAccountInfo->account != src || submittingAccountInfo->postFeeBalance != curBal) return curBal; - return submittingAccountInfo->preFeeBalance; + return submittingAccountInfo->preFeeBalance_; }(); if (availableBalance < amt + reserve) @@ -499,14 +510,15 @@ struct FinalizeClaimHelperResult bool isTesSuccess() const { - return mainFundsTer == tesSUCCESS && rewardTer == tesSUCCESS && (!rmSleTer || *rmSleTer == tesSUCCESS); + return (!mainFundsTer || xrpl::isTesSuccess(*mainFundsTer)) && + (!rewardTer || xrpl::isTesSuccess(*rewardTer)) && + (!rmSleTer || xrpl::isTesSuccess(*rmSleTer)); } TER ter() const { - if ((!mainFundsTer || *mainFundsTer == tesSUCCESS) && (!rewardTer || *rewardTer == tesSUCCESS) && - (!rmSleTer || *rmSleTer == tesSUCCESS)) + if (isTesSuccess()) return tesSUCCESS; // if any phase return a tecINTERNAL or a tef, prefer returning those @@ -520,11 +532,11 @@ struct FinalizeClaimHelperResult // Only after the tecINTERNAL and tef are checked, return the first // non-success error code. - if (mainFundsTer && mainFundsTer != tesSUCCESS) + if (mainFundsTer && !xrpl::isTesSuccess(*mainFundsTer)) return *mainFundsTer; - if (rewardTer && rewardTer != tesSUCCESS) + if (rewardTer && !xrpl::isTesSuccess(*rewardTer)) return *rewardTer; - if (rmSleTer && rmSleTer != tesSUCCESS) + if (rmSleTer && !xrpl::isTesSuccess(*rmSleTer)) return *rmSleTer; return tesSUCCESS; } @@ -556,7 +568,7 @@ struct FinalizeClaimHelperResult the fields mean. The individual ters need to be returned instead of an overall ter because the caller needs this information if the attestation list changed or not. - */ +*/ FinalizeClaimHelperResult finalizeClaimHelper( @@ -846,7 +858,8 @@ applyClaimAttestations( { STXChainBridge::ChainType const dstChain = STXChainBridge::otherChain(srcChain); - STXChainBridge::ChainType const attDstChain = STXChainBridge::dstChain(attBegin->wasLockingChainSend); + STXChainBridge::ChainType const attDstChain = + STXChainBridge::dstChain(attBegin->wasLockingChainSend); if (attDstChain != dstChain) { @@ -856,14 +869,21 @@ applyClaimAttestations( XChainClaimAttestations curAtts{sleClaimID->getFieldArray(sfXChainClaimAttestations)}; - auto const newAttResult = - onNewAttestations(curAtts, view, &atts[0], &atts[0] + atts.size(), quorum, signersList, j); + auto const newAttResult = onNewAttestations( + curAtts, + view, + &atts[0], + &atts[0] + atts.size(), // NOLINT(bugprone-pointer-arithmetic-on-polymorphic-object) + quorum, + signersList, + j); // update the claim id sleClaimID->setFieldArray(sfXChainClaimAttestations, curAtts.toSTArray()); psb.update(sleClaimID); - return ScopeResult{newAttResult, (*sleClaimID)[sfSignatureReward], (*sleClaimID)[sfAccount]}; + return ScopeResult{ + newAttResult, (*sleClaimID)[sfSignatureReward], (*sleClaimID)[sfAccount]}; }(); if (!scopeResult.has_value()) @@ -891,7 +911,8 @@ applyClaimAttestations( auto const rTer = r.ter(); - if (!isTesSuccess(rTer) && (!attListChanged || rTer == tecINTERNAL || rTer == tefBAD_LEDGER)) + if (!isTesSuccess(rTer) && + (!attListChanged || rTer == tecINTERNAL || rTer == tefBAD_LEDGER)) return rTer; } @@ -947,7 +968,8 @@ applyCreateAccountAttestations( { STXChainBridge::ChainType const dstChain = STXChainBridge::otherChain(srcChain); - STXChainBridge::ChainType const attDstChain = STXChainBridge::dstChain(attBegin->wasLockingChainSend); + STXChainBridge::ChainType const attDstChain = + STXChainBridge::dstChain(attBegin->wasLockingChainSend); if (attDstChain != dstChain) { @@ -955,12 +977,13 @@ applyCreateAccountAttestations( } } - auto const claimIDKeylet = keylet::xChainCreateAccountClaimID(bridgeSpec, attBegin->createCount); + auto const claimIDKeylet = + keylet::xChainCreateAccountClaimID(bridgeSpec, attBegin->createCount); struct ScopeResult { OnNewAttestationResult newAttResult; - bool createCID; + bool createCID{}; XChainCreateAccountAttestations curAtts; }; @@ -1005,12 +1028,21 @@ applyCreateAccountAttestations( XChainCreateAccountAttestations curAtts = [&] { if (sleClaimID) - return XChainCreateAccountAttestations{sleClaimID->getFieldArray(sfXChainCreateAccountAttestations)}; + { + return XChainCreateAccountAttestations{ + sleClaimID->getFieldArray(sfXChainCreateAccountAttestations)}; + } return XChainCreateAccountAttestations{}; }(); - auto const newAttResult = - onNewAttestations(curAtts, view, &atts[0], &atts[0] + atts.size(), quorum, signersList, j); + auto const newAttResult = onNewAttestations( + curAtts, + view, + &atts[0], + &atts[0] + atts.size(), // NOLINT(bugprone-pointer-arithmetic-on-polymorphic-object) + quorum, + signersList, + j); if (!createCID) { @@ -1073,7 +1105,8 @@ applyCreateAccountAttestations( createdSleClaimID->setFieldArray(sfXChainCreateAccountAttestations, curAtts.toSTArray()); // Add to owner directory of the door account - auto const page = psb.dirInsert(keylet::ownerDir(doorAccount), claimIDKeylet, describeOwnerDir(doorAccount)); + auto const page = psb.dirInsert( + keylet::ownerDir(doorAccount), claimIDKeylet, describeOwnerDir(doorAccount)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE (*createdSleClaimID)[sfOwnerNode] = *page; @@ -1109,8 +1142,8 @@ toClaim(STTx const& tx) } catch (...) { + return std::nullopt; } - return std::nullopt; } template @@ -1173,16 +1206,18 @@ attestationDoApply(ApplyContext& ctx) { auto const att = toClaim(ctx.tx); if (!att) + { // Should already be checked in preflight return tecINTERNAL; // LCOV_EXCL_LINE + } STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; struct ScopeResult { - STXChainBridge::ChainType srcChain; + STXChainBridge::ChainType srcChain = STXChainBridge::ChainType::locking; std::unordered_map signersList; - std::uint32_t quorum; + std::uint32_t quorum{}; AccountID thisDoor; Keylet bridgeK; }; @@ -1203,16 +1238,23 @@ attestationDoApply(ApplyContext& ctx) STXChainBridge::ChainType dstChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { dstChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { dstChain = STXChainBridge::ChainType::issuing; + } else + { return Unexpected(tecINTERNAL); + } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); // signersList is a map from account id to weights - auto [signersList, quorum, slTer] = getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal); + auto [signersList, quorum, slTer] = + getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal); if (!isTesSuccess(slTer)) return Unexpected(slTer); @@ -1232,7 +1274,15 @@ attestationDoApply(ApplyContext& ctx) if constexpr (std::is_same_v) { return applyClaimAttestations( - ctx.view(), ctx.rawView(), &*att, &*att + 1, bridgeSpec, srcChain, signersList, quorum, ctx.journal); + ctx.view(), + ctx.rawView(), + &*att, + &*att + 1, + bridgeSpec, + srcChain, + signersList, + quorum, + ctx.journal); } else if constexpr (std::is_same_v) { @@ -1286,8 +1336,8 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) } if (minAccountCreate && - ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) || !isXRP(bridgeSpec.lockingChainIssue()) || - !isXRP(bridgeSpec.issuingChainIssue()))) + ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) || + !isXRP(bridgeSpec.lockingChainIssue()) || !isXRP(bridgeSpec.issuingChainIssue()))) { return temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT; } @@ -1297,8 +1347,8 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) // Issuing account must be the root account for XRP (which presumably // owns all the XRP). This is done so the issuing account can't "run // out" of wrapped tokens. - static auto const rootAccount = - calcAccountID(generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")).first); + static auto const rootAccount = calcAccountID( + generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")).first); if (bridgeSpec.issuingChainDoor() != rootAccount) { return temXCHAIN_BRIDGE_BAD_ISSUES; @@ -1329,14 +1379,16 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) { auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; - STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); { auto hasBridge = [&](STXChainBridge::ChainType ct) -> bool { return ctx.view.exists(keylet::bridge(bridgeSpec, ct)); }; - if (hasBridge(STXChainBridge::ChainType::issuing) || hasBridge(STXChainBridge::ChainType::locking)) + if (hasBridge(STXChainBridge::ChainType::issuing) || + hasBridge(STXChainBridge::ChainType::locking)) { return tecDUPLICATE; } @@ -1383,7 +1435,8 @@ XChainCreateBridge::doApply() if (!sleAcct) return tecINTERNAL; // LCOV_EXCL_LINE - STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); Keylet const bridgeKeylet = keylet::bridge(bridgeSpec, chainType); auto const sleBridge = std::make_shared(bridgeKeylet); @@ -1399,7 +1452,8 @@ XChainCreateBridge::doApply() // Add to owner directory { - auto const page = ctx_.view().dirInsert(keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account)); + auto const page = ctx_.view().dirInsert( + keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE (*sleBridge)[sfOwnerNode] = *page; @@ -1418,7 +1472,7 @@ XChainCreateBridge::doApply() std::uint32_t BridgeModify::getFlagsMask(PreflightContext const& ctx) { - return tfBridgeModifyMask; + return tfXChainModifyBridgeMask; } NotTEC @@ -1453,8 +1507,8 @@ BridgeModify::preflight(PreflightContext const& ctx) } if (minAccountCreate && - ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) || !isXRP(bridgeSpec.lockingChainIssue()) || - !isXRP(bridgeSpec.issuingChainIssue()))) + ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) || + !isXRP(bridgeSpec.lockingChainIssue()) || !isXRP(bridgeSpec.issuingChainIssue()))) { return temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT; } @@ -1468,7 +1522,8 @@ BridgeModify::preclaim(PreclaimContext const& ctx) auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; - STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); if (!ctx.view.read(keylet::bridge(bridgeSpec, chainType))) { @@ -1491,7 +1546,8 @@ BridgeModify::doApply() if (!sleAcct) return tecINTERNAL; // LCOV_EXCL_LINE - STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); auto const sleBridge = ctx_.view().peek(keylet::bridge(bridgeSpec, chainType)); if (!sleBridge) @@ -1521,7 +1577,8 @@ XChainClaim::preflight(PreflightContext const& ctx) auto const amount = ctx.tx[sfAmount]; if (amount.signum() <= 0 || - (amount.issue() != bridgeSpec.lockingChainIssue() && amount.issue() != bridgeSpec.issuingChainIssue())) + (amount.issue() != bridgeSpec.lockingChainIssue() && + amount.issue() != bridgeSpec.issuingChainIssue())) { return temBAD_AMOUNT; } @@ -1552,11 +1609,17 @@ XChainClaim::preclaim(PreclaimContext const& ctx) bool isLockingChain = false; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { isLockingChain = true; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { isLockingChain = false; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } { @@ -1585,9 +1648,13 @@ XChainClaim::preclaim(PreclaimContext const& ctx) auto const otherChainAmount = [&]() -> STAmount { STAmount r(thisChainAmount); if (isLockingChain) + { r.setIssue(bridgeSpec.issuingChainIssue()); + } else + { r.setIssue(bridgeSpec.lockingChainIssue()); + } return r; }(); @@ -1650,11 +1717,17 @@ XChainClaim::doApply() STXChainBridge::ChainType dstChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { dstChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { dstChain = STXChainBridge::ChainType::issuing; + } else + { return Unexpected(tecINTERNAL); + } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); @@ -1664,7 +1737,8 @@ XChainClaim::doApply() return r; }(); - auto const [signersList, quorum, slTer] = getSignersListAndQuorum(ctx_.view(), *sleBridge, ctx_.journal); + auto const [signersList, quorum, slTer] = + getSignersListAndQuorum(ctx_.view(), *sleBridge, ctx_.journal); if (!isTesSuccess(slTer)) return Unexpected(slTer); @@ -1694,7 +1768,8 @@ XChainClaim::doApply() if (!scopeResult.has_value()) return scopeResult.error(); - auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] = scopeResult.value(); + auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] = + scopeResult.value(); std::optional const dstTag = ctx_.tx[~sfDestinationTag]; auto const r = finalizeClaimHelper( @@ -1744,7 +1819,8 @@ XChainCommit::preflight(PreflightContext const& ctx) if (amount.signum() <= 0 || !isLegalNet(amount)) return temBAD_AMOUNT; - if (amount.issue() != bridgeSpec.lockingChainIssue() && amount.issue() != bridgeSpec.issuingChainIssue()) + if (amount.issue() != bridgeSpec.lockingChainIssue() && + amount.issue() != bridgeSpec.issuingChainIssue()) return temBAD_ISSUER; return tesSUCCESS; @@ -1774,11 +1850,17 @@ XChainCommit::preclaim(PreclaimContext const& ctx) bool isLockingChain = false; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { isLockingChain = true; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { isLockingChain = false; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } if (isLockingChain) @@ -1804,7 +1886,8 @@ XChainCommit::doApply() auto const amount = ctx_.tx[sfAmount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; - if (!psb.read(keylet::account(account))) + auto const sleAccount = psb.read(keylet::account(account)); + if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE auto const sleBridge = readBridge(psb, bridgeSpec); @@ -1814,7 +1897,8 @@ XChainCommit::doApply() auto const dst = (*sleBridge)[sfAccount]; // Support dipping into reserves to pay the fee - TransferHelperSubmittingAccountInfo submittingAccountInfo{account_, mPriorBalance, mSourceBalance}; + TransferHelperSubmittingAccountInfo submittingAccountInfo{ + account_, preFeeBalance_, (*sleAccount)[sfBalance]}; auto const thTer = transferHelper( psb, @@ -1928,7 +2012,8 @@ XChainCreateClaimID::doApply() // Add to owner directory { - auto const page = ctx_.view().dirInsert(keylet::ownerDir(account), claimIDKeylet, describeOwnerDir(account)); + auto const page = ctx_.view().dirInsert( + keylet::ownerDir(account), claimIDKeylet, describeOwnerDir(account)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE (*sleClaimID)[sfOwnerNode] = *page; @@ -2043,11 +2128,17 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) STXChainBridge::ChainType srcChain = STXChainBridge::ChainType::locking; { if (thisDoor == bridgeSpec.lockingChainDoor()) + { srcChain = STXChainBridge::ChainType::locking; + } else if (thisDoor == bridgeSpec.issuingChainDoor()) + { srcChain = STXChainBridge::ChainType::issuing; + } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } STXChainBridge::ChainType const dstChain = STXChainBridge::otherChain(srcChain); @@ -2081,7 +2172,8 @@ XChainCreateAccountCommit::doApply() auto const dst = (*sleBridge)[sfAccount]; // Support dipping into reserves to pay the fee - TransferHelperSubmittingAccountInfo submittingAccountInfo{account_, mPriorBalance, mSourceBalance}; + TransferHelperSubmittingAccountInfo submittingAccountInfo{ + account_, preFeeBalance_, (*sle)[sfBalance]}; STAmount const toTransfer = amount + reward; auto const thTer = transferHelper( psb, diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp similarity index 84% rename from src/xrpld/app/tx/detail/CancelCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCancel.cpp index 4ac04e9ffb..2d08b2f1b0 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -1,23 +1,23 @@ -#include -#include - #include #include +#include +#include #include #include #include #include +#include namespace xrpl { NotTEC -CancelCheck::preflight(PreflightContext const& ctx) +CheckCancel::preflight(PreflightContext const& ctx) { return tesSUCCESS; } TER -CancelCheck::preclaim(PreclaimContext const& ctx) +CheckCancel::preclaim(PreclaimContext const& ctx) { auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID])); if (!sleCheck) @@ -26,15 +26,11 @@ CancelCheck::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - using duration = NetClock::duration; - using timepoint = NetClock::time_point; - auto const optExpiry = (*sleCheck)[~sfExpiration]; - // Expiration is defined in terms of the close time of the parent // ledger, because we definitively know the time that it closed but // we do not know the closing time of the ledger that is under // construction. - if (!optExpiry || (ctx.view.parentCloseTime() < timepoint{duration{*optExpiry}})) + if (!hasExpired(ctx.view, (*sleCheck)[~sfExpiration])) { // If the check is not yet expired, then only the creator or the // destination may cancel the check. @@ -50,7 +46,7 @@ CancelCheck::preclaim(PreclaimContext const& ctx) } TER -CancelCheck::doApply() +CheckCancel::doApply() { auto const sleCheck = view().peek(keylet::check(ctx_.tx[sfCheckID])); if (!sleCheck) @@ -62,7 +58,7 @@ CancelCheck::doApply() AccountID const srcId{sleCheck->getAccountID(sfAccount)}; AccountID const dstId{sleCheck->getAccountID(sfDestination)}; - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); // If the check is not written to self (and it shouldn't be), remove the // check from the destination account root. diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp similarity index 91% rename from src/xrpld/app/tx/detail/CashCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCash.cpp index 9deee006d9..7fa53c7352 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -1,20 +1,21 @@ -#include -#include -#include - #include #include +#include +#include +#include #include #include #include #include +#include +#include #include namespace xrpl { NotTEC -CashCheck::preflight(PreflightContext const& ctx) +CheckCash::preflight(PreflightContext const& ctx) { // Exactly one of Amount or DeliverMin must be present. auto const optAmount = ctx.tx[~sfAmount]; @@ -45,7 +46,7 @@ CashCheck::preflight(PreflightContext const& ctx) } TER -CashCheck::preclaim(PreclaimContext const& ctx) +CheckCash::preclaim(PreclaimContext const& ctx) { auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID])); if (!sleCheck) @@ -126,7 +127,8 @@ CashCheck::preclaim(PreclaimContext const& ctx) // Make sure the check owner holds at least value. If they have // less than value the check cannot be cashed. { - STAmount availableFunds{accountFunds(ctx.view, sleCheck->at(sfAccount), value, fhZERO_IF_FROZEN, ctx.j)}; + STAmount availableFunds{ + accountFunds(ctx.view, sleCheck->at(sfAccount), value, fhZERO_IF_FROZEN, ctx.j)}; // Note that src will have one reserve's worth of additional XRP // once the check is cashed, since the check's reserve will no @@ -148,7 +150,8 @@ CashCheck::preclaim(PreclaimContext const& ctx) auto const sleIssuer = ctx.view.read(keylet::account(issuerId)); if (!sleIssuer) { - JLOG(ctx.j.warn()) << "Can't receive IOUs from non-existent issuer: " << to_string(issuerId); + JLOG(ctx.j.warn()) + << "Can't receive IOUs from non-existent issuer: " << to_string(issuerId); return tecNO_ISSUER; } @@ -168,7 +171,8 @@ CashCheck::preclaim(PreclaimContext const& ctx) // weak ordering. Determine which entry we need to access. bool const canonical_gt(dstId > issuerId); - bool const is_authorized(sleTrustLine->at(sfFlags) & (canonical_gt ? lsfLowAuth : lsfHighAuth)); + bool const is_authorized( + sleTrustLine->at(sfFlags) & (canonical_gt ? lsfLowAuth : lsfHighAuth)); if (!is_authorized) { @@ -194,7 +198,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) } TER -CashCheck::doApply() +CheckCash::doApply() { // Flow requires that we operate on a PaymentSandbox, rather than // directly on a View. @@ -228,7 +232,7 @@ CashCheck::doApply() // // If it is not a check to self (as should be the case), then there's // work to do... - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); auto const optDeliverMin = ctx_.tx[~sfDeliverMin]; if (srcId != account_) @@ -257,17 +261,20 @@ CashCheck::doApply() { // Vote no. However the transaction might succeed if applied // in a different order. - JLOG(j_.trace()) << "Cash Check: Insufficient XRP: " << srcLiquid.getFullText() << " < " - << xrpDeliver.getFullText(); + JLOG(j_.trace()) << "Cash Check: Insufficient XRP: " << srcLiquid.getFullText() + << " < " << xrpDeliver.getFullText(); return tecUNFUNDED_PAYMENT; } if (optDeliverMin) + { // Set the DeliveredAmount metadata. ctx_.deliver(xrpDeliver); + } // The source account has enough XRP so make the ledger change. - if (TER const ter{transferXRP(psb, srcId, account_, xrpDeliver, viewJ)}; ter != tesSUCCESS) + if (TER const ter{transferXRP(psb, srcId, account_, xrpDeliver, viewJ)}; + !isTesSuccess(ter)) { // The transfer failed. Return the error code. return ter; @@ -281,8 +288,10 @@ CashCheck::doApply() // transfer rate to account for. Since the transfer rate cannot // exceed 200%, we use 1/2 maxValue as our limit. STAmount const flowDeliver{ - optDeliverMin ? STAmount(optDeliverMin->issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset) - : ctx_.tx.getFieldAmount(sfAmount)}; + optDeliverMin + ? STAmount( + optDeliverMin->issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset) + : ctx_.tx.getFieldAmount(sfAmount)}; // If a trust line does not exist yet create one. Issue const& trustLineIssue = flowDeliver.issue(); @@ -304,7 +313,7 @@ CashCheck::doApply() // Can the account cover the trust line's reserve? if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)}; - mPriorBalance < psb.fees().accountReserve(ownerCount + 1)) + preFeeBalance_ < psb.fees().accountReserve(ownerCount + 1)) { JLOG(j_.trace()) << "Trust line does not exist. " "Insufficent reserve to create line."; @@ -384,7 +393,7 @@ CashCheck::doApply() std::nullopt, // check does not support domain viewJ); - if (result.result() != tesSUCCESS) + if (!isTesSuccess(result.result())) { JLOG(ctx_.journal.warn()) << "flow failed when cashing check."; return result.result(); @@ -411,7 +420,8 @@ CashCheck::doApply() // Check was cashed. If not a self send (and it shouldn't be), remove // check link from destination directory. if (srcId != account_ && - !psb.dirRemove(keylet::ownerDir(account_), sleCheck->at(sfDestinationNode), sleCheck->key(), true)) + !psb.dirRemove( + keylet::ownerDir(account_), sleCheck->at(sfDestinationNode), sleCheck->key(), true)) { // LCOV_EXCL_START JLOG(j_.fatal()) << "Unable to delete check from destination."; diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp similarity index 84% rename from src/xrpld/app/tx/detail/CreateCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCreate.cpp index d10863df46..50554a1beb 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -1,16 +1,18 @@ -#include - #include #include +#include +#include +#include #include #include #include #include +#include namespace xrpl { NotTEC -CreateCheck::preflight(PreflightContext const& ctx) +CheckCreate::preflight(PreflightContext const& ctx) { if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) { @@ -23,7 +25,8 @@ CreateCheck::preflight(PreflightContext const& ctx) STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)}; if (!isLegalNet(sendMax) || sendMax.signum() <= 0) { - JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: " << sendMax.getFullText(); + JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: " + << sendMax.getFullText(); return temBAD_AMOUNT; } @@ -47,7 +50,7 @@ CreateCheck::preflight(PreflightContext const& ctx) } TER -CreateCheck::preclaim(PreclaimContext const& ctx) +CheckCreate::preclaim(PreclaimContext const& ctx) { AccountID const dstId{ctx.tx[sfDestination]}; auto const sleDst = ctx.view.read(keylet::account(dstId)); @@ -98,7 +101,8 @@ CreateCheck::preclaim(PreclaimContext const& ctx) if (issuerId != srcId) { // Check if the issuer froze the line - auto const sleTrust = ctx.view.read(keylet::line(srcId, issuerId, sendMax.getCurrency())); + auto const sleTrust = + ctx.view.read(keylet::line(srcId, issuerId, sendMax.getCurrency())); if (sleTrust && sleTrust->isFlag((issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze)) { JLOG(ctx.j.warn()) << "Creating a check for frozen trustline."; @@ -108,7 +112,8 @@ CreateCheck::preclaim(PreclaimContext const& ctx) if (issuerId != dstId) { // Check if dst froze the line. - auto const sleTrust = ctx.view.read(keylet::line(issuerId, dstId, sendMax.getCurrency())); + auto const sleTrust = + ctx.view.read(keylet::line(issuerId, dstId, sendMax.getCurrency())); if (sleTrust && sleTrust->isFlag((dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze)) { JLOG(ctx.j.warn()) << "Creating a check for destination frozen trustline."; @@ -126,7 +131,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) } TER -CreateCheck::doApply() +CheckCreate::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -138,7 +143,7 @@ CreateCheck::doApply() { STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -164,15 +169,16 @@ CreateCheck::doApply() view().insert(sleCheck); - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); // If it's not a self-send (and it shouldn't be), add Check to the // destination's owner directory. if (dstAccountId != account_) { - auto const page = view().dirInsert(keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId)); + auto const page = view().dirInsert( + keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId)); - JLOG(j_.trace()) << "Adding Check to destination directory " << to_string(checkKeylet.key) << ": " - << (page ? "success" : "failure"); + JLOG(j_.trace()) << "Adding Check to destination directory " << to_string(checkKeylet.key) + << ": " << (page ? "success" : "failure"); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE @@ -181,7 +187,8 @@ CreateCheck::doApply() } { - auto const page = view().dirInsert(keylet::ownerDir(account_), checkKeylet, describeOwnerDir(account_)); + auto const page = + view().dirInsert(keylet::ownerDir(account_), checkKeylet, describeOwnerDir(account_)); JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": " << (page ? "success" : "failure"); diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp new file mode 100644 index 0000000000..c4842b54e4 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +using namespace credentials; + +std::uint32_t +CredentialAccept::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialAccept::preflight(PreflightContext const& ctx) +{ + if (!ctx.tx[sfIssuer]) + { + JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed."; + return temINVALID_ACCOUNT_ID; + } + + auto const credType = ctx.tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialAccept::preclaim(PreclaimContext const& ctx) +{ + AccountID const subject = ctx.tx[sfAccount]; + AccountID const issuer = ctx.tx[sfIssuer]; + auto const credType(ctx.tx[sfCredentialType]); + + if (!ctx.view.exists(keylet::account(issuer))) + { + JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer); + return tecNO_ISSUER; + } + + auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType)); + if (!sleCred) + { + JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer) + << ", " << credType; + return tecNO_ENTRY; + } + + if (sleCred->getFieldU32(sfFlags) & lsfAccepted) + { + JLOG(ctx.j.warn()) << "Credential already accepted: " << to_string(subject) << ", " + << to_string(issuer) << ", " << credType; + return tecDUPLICATE; + } + + return tesSUCCESS; +} + +TER +CredentialAccept::doApply() +{ + AccountID const issuer{ctx_.tx[sfIssuer]}; + + // Both exist as credential object exist itself (checked in preclaim) + auto const sleSubject = view().peek(keylet::account(account_)); + auto const sleIssuer = view().peek(keylet::account(issuer)); + + if (!sleSubject || !sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + STAmount const reserve{ + view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)}; + if (preFeeBalance_ < reserve) + return tecINSUFFICIENT_RESERVE; + } + + auto const credType(ctx_.tx[sfCredentialType]); + Keylet const credentialKey = keylet::credential(account_, issuer, credType); + auto const sleCred = view().peek(credentialKey); // Checked in preclaim() + + if (checkExpired(sleCred, view().header().parentCloseTime)) + { + JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText(); + // delete expired credentials even if the transaction failed + auto const err = credentials::deleteSLE(view(), sleCred, j_); + return isTesSuccess(err) ? tecEXPIRED : err; + } + + sleCred->setFieldU32(sfFlags, lsfAccepted); + view().update(sleCred); + + adjustOwnerCount(view(), sleIssuer, -1, j_); + adjustOwnerCount(view(), sleSubject, 1, j_); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp new file mode 100644 index 0000000000..f8f0f01b63 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +/* + Credentials + ====== + + A verifiable credentials (VC + https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C + specification (https://www.w3.org/TR/vc-data-model-2.0/), is a + secure and tamper-evident way to represent information about a subject, such + as an individual, organization, or even an IoT device. These credentials are + issued by a trusted entity and can be verified by third parties without + directly involving the issuer at all. +*/ + +using namespace credentials; + +std::uint32_t +CredentialCreate::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialCreate::preflight(PreflightContext const& ctx) +{ + auto const& tx = ctx.tx; + auto& j = ctx.j; + + if (!tx[sfSubject]) + { + JLOG(j.trace()) << "Malformed transaction: Invalid Subject"; + return temMALFORMED; + } + + auto const uri = tx[~sfURI]; + if (uri && (uri->empty() || (uri->size() > maxCredentialURILength))) + { + JLOG(j.trace()) << "Malformed transaction: invalid size of URI."; + return temMALFORMED; + } + + auto const credType = tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialCreate::preclaim(PreclaimContext const& ctx) +{ + auto const credType(ctx.tx[sfCredentialType]); + auto const subject = ctx.tx[sfSubject]; + + if (!ctx.view.exists(keylet::account(subject))) + { + JLOG(ctx.j.trace()) << "Subject doesn't exist."; + return tecNO_TARGET; + } + + if (ctx.view.exists(keylet::credential(subject, ctx.tx[sfAccount], credType))) + { + JLOG(ctx.j.trace()) << "Credential already exists."; + return tecDUPLICATE; + } + + return tesSUCCESS; +} + +TER +CredentialCreate::doApply() +{ + auto const subject = ctx_.tx[sfSubject]; + auto const credType(ctx_.tx[sfCredentialType]); + Keylet const credentialKey = keylet::credential(subject, account_, credType); + + auto const sleCred = std::make_shared(credentialKey); + if (!sleCred) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const optExp = ctx_.tx[~sfExpiration]; + if (optExp) + { + std::uint32_t const closeTime = + ctx_.view().header().parentCloseTime.time_since_epoch().count(); + + if (closeTime > *optExp) + { + JLOG(j_.trace()) << "Malformed transaction: " + "Expiration time is in the past."; + return tecEXPIRED; + } + + sleCred->setFieldU32(sfExpiration, *optExp); + } + + auto const sleIssuer = view().peek(keylet::account(account_)); + if (!sleIssuer) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + STAmount const reserve{ + view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)}; + if (preFeeBalance_ < reserve) + return tecINSUFFICIENT_RESERVE; + } + + sleCred->setAccountID(sfSubject, subject); + sleCred->setAccountID(sfIssuer, account_); + sleCred->setFieldVL(sfCredentialType, credType); + + if (ctx_.tx.isFieldPresent(sfURI)) + sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI)); + + { + auto const page = + view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_)); + JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key) + << ": " << (page ? "success" : "failure"); + if (!page) + return tecDIR_FULL; + sleCred->setFieldU64(sfIssuerNode, *page); + + adjustOwnerCount(view(), sleIssuer, 1, j_); + } + + if (subject == account_) + { + sleCred->setFieldU32(sfFlags, lsfAccepted); + } + else + { + // Added to both dirs, owned only by issuer. CredentialAccept will transfer ownership to + // subject. CredentialDelete will remove from both dirs and decrement 1 ownerCount. + auto const page = + view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject)); + JLOG(j_.trace()) << "Adding Credential to subject directory " + << to_string(credentialKey.key) << ": " << (page ? "success" : "failure"); + if (!page) + return tecDIR_FULL; + sleCred->setFieldU64(sfSubjectNode, *page); + } + + view().insert(sleCred); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp new file mode 100644 index 0000000000..5f78ba6757 --- /dev/null +++ b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +using namespace credentials; + +std::uint32_t +CredentialDelete::getFlagsMask(PreflightContext const& ctx) +{ + // 0 means "Allow any flags" + return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0; +} + +NotTEC +CredentialDelete::preflight(PreflightContext const& ctx) +{ + auto const subject = ctx.tx[~sfSubject]; + auto const issuer = ctx.tx[~sfIssuer]; + + if (!subject && !issuer) + { + // Neither field is present, the transaction is malformed. + JLOG(ctx.j.trace()) << "Malformed transaction: " + "No Subject or Issuer fields."; + return temMALFORMED; + } + + // Make sure that the passed account is valid. + if ((subject && subject->isZero()) || (issuer && issuer->isZero())) + { + JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer " + "field zeroed."; + return temINVALID_ACCOUNT_ID; + } + + auto const credType = ctx.tx[sfCredentialType]; + if (credType.empty() || (credType.size() > maxCredentialTypeLength)) + { + JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType."; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +CredentialDelete::preclaim(PreclaimContext const& ctx) +{ + AccountID const account{ctx.tx[sfAccount]}; + auto const subject = ctx.tx[~sfSubject].value_or(account); + auto const issuer = ctx.tx[~sfIssuer].value_or(account); + auto const credType(ctx.tx[sfCredentialType]); + + if (!ctx.view.exists(keylet::credential(subject, issuer, credType))) + return tecNO_ENTRY; + + return tesSUCCESS; +} + +TER +CredentialDelete::doApply() +{ + auto const subject = ctx_.tx[~sfSubject].value_or(account_); + auto const issuer = ctx_.tx[~sfIssuer].value_or(account_); + + auto const credType(ctx_.tx[sfCredentialType]); + auto const sleCred = view().peek(keylet::credential(subject, issuer, credType)); + if (!sleCred) + return tefINTERNAL; // LCOV_EXCL_LINE + + if ((subject != account_) && (issuer != account_) && + !checkExpired(sleCred, ctx_.view().header().parentCloseTime)) + { + JLOG(j_.trace()) << "Can't delete non-expired credential."; + return tecNO_PERMISSION; + } + + return deleteSLE(view(), sleCred, j_); +} + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp similarity index 70% rename from src/xrpld/app/tx/detail/DelegateSet.cpp rename to src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 8dba3ef72e..9fb17c4d1f 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -1,10 +1,11 @@ -#include - #include #include +#include +#include #include #include #include +#include namespace xrpl { @@ -42,6 +43,13 @@ DelegateSet::preclaim(PreclaimContext const& ctx) if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize]))) return tecNO_TARGET; + // Deleting the delegate object is invalid if it doesn’t exist. + if (ctx.tx.getFieldArray(sfPermissions).empty() && + !ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize]))) + { + return tecNO_ENTRY; + } + return tesSUCCESS; } @@ -60,36 +68,40 @@ DelegateSet::doApply() { auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (permissions.empty()) + { // if permissions array is empty, delete the ledger object. return deleteDelegate(view(), sle, account_, j_); + } sle->setFieldArray(sfPermissions, permissions); ctx_.view().update(sle); return tesSUCCESS; } - STAmount const reserve{ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; + auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + if (permissions.empty()) + return tecINTERNAL; // LCOV_EXCL_LINE - if (mPriorBalance < reserve) + STAmount const reserve{ + ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; + + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; - auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); - if (!permissions.empty()) - { - sle = std::make_shared(delegateKey); - sle->setAccountID(sfAccount, account_); - sle->setAccountID(sfAuthorize, authAccount); + sle = std::make_shared(delegateKey); + sle->setAccountID(sfAccount, account_); + sle->setAccountID(sfAuthorize, authAccount); - sle->setFieldArray(sfPermissions, permissions); - auto const page = ctx_.view().dirInsert(keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_)); + sle->setFieldArray(sfPermissions, permissions); + auto const page = + ctx_.view().dirInsert(keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_)); - if (!page) - return tecDIR_FULL; // LCOV_EXCL_LINE + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE - (*sle)[sfOwnerNode] = *page; - ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); - } + (*sle)[sfOwnerNode] = *page; + ctx_.view().insert(sle); + adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); return tesSUCCESS; } diff --git a/src/xrpld/app/misc/detail/DelegateUtils.cpp b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp similarity index 95% rename from src/xrpld/app/misc/detail/DelegateUtils.cpp rename to src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp index 74ec55cdee..d9d74a1212 100644 --- a/src/xrpld/app/misc/detail/DelegateUtils.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp @@ -1,6 +1,5 @@ -#include - #include +#include namespace xrpl { NotTEC diff --git a/src/xrpld/app/tx/detail/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp similarity index 85% rename from src/xrpld/app/tx/detail/AMMBid.cpp rename to src/libxrpl/tx/transactors/dex/AMMBid.cpp index 1d0c30c1f0..a1ace702dc 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -1,13 +1,12 @@ -#include -#include -#include - #include #include #include #include #include #include +#include +#include +#include namespace xrpl { @@ -20,7 +19,8 @@ AMMBid::checkExtraFeatures(PreflightContext const& ctx) NotTEC AMMBid::preflight(PreflightContext const& ctx) { - if (auto const res = invalidAMMAssetPair(ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) + if (auto const res = + invalidAMMAssetPair(ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) { JLOG(ctx.j.debug()) << "AMM Bid: Invalid asset pair."; return res; @@ -46,13 +46,13 @@ AMMBid::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfAuthAccounts)) { - if (auto const authAccounts = ctx.tx.getFieldArray(sfAuthAccounts); - authAccounts.size() > AUCTION_SLOT_MAX_AUTH_ACCOUNTS) + auto const authAccounts = ctx.tx.getFieldArray(sfAuthAccounts); + if (authAccounts.size() > AUCTION_SLOT_MAX_AUTH_ACCOUNTS) { JLOG(ctx.j.debug()) << "AMM Bid: Invalid number of AuthAccounts."; return temMALFORMED; } - else if (ctx.rules.enabled(fixAMMv1_3)) + if (ctx.rules.enabled(fixAMMv1_3)) { AccountID account = ctx.tx[sfAccount]; std::set unique; @@ -168,7 +168,8 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour return {tecINTERNAL, false}; } auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot); - auto const current = duration_cast(ctx_.view().header().parentCloseTime.time_since_epoch()).count(); + auto const current = + duration_cast(ctx_.view().header().parentCloseTime.time_since_epoch()).count(); // Auction slot discounted fee auto const discountedFee = (*ammSle)[sfTradingFee] / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION; auto const tradingFee = getFee((*ammSle)[sfTradingFee]); @@ -192,26 +193,36 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour auctionSlot.setAccountID(sfAccount, account_); auctionSlot.setFieldU32(sfExpiration, current + TOTAL_TIME_SLOT_SECS); if (fee != 0) + { auctionSlot.setFieldU16(sfDiscountedFee, fee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); + } auctionSlot.setFieldAmount(sfPrice, toSTAmount(lpTokens.issue(), minPrice)); if (ctx_.tx.isFieldPresent(sfAuthAccounts)) + { auctionSlot.setFieldArray(sfAuthAccounts, ctx_.tx.getFieldArray(sfAuthAccounts)); + } else + { auctionSlot.makeFieldAbsent(sfAuthAccounts); + } // Burn the remaining bid amount - auto const saBurn = adjustLPTokens(lptAMMBalance, toSTAmount(lptAMMBalance.issue(), burn), IsDeposit::No); + auto const saBurn = + adjustLPTokens(lptAMMBalance, toSTAmount(lptAMMBalance.issue(), burn), IsDeposit::No); if (saBurn >= lptAMMBalance) { // This error case should never occur. // LCOV_EXCL_START - JLOG(ctx_.journal.fatal()) << "AMM Bid: LP Token burn exceeds AMM balance " << burn << " " << lptAMMBalance; + JLOG(ctx_.journal.fatal()) + << "AMM Bid: LP Token burn exceeds AMM balance " << burn << " " << lptAMMBalance; return tecINTERNAL; // LCOV_EXCL_STOP } auto res = redeemIOU(sb, account_, saBurn, lpTokens.issue(), ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Bid: failed to redeem."; return res; @@ -233,8 +244,8 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour { if (computedPrice <= *bidMax) return std::max(computedPrice, Number(*bidMin)); - JLOG(ctx_.journal.debug()) - << "AMM Bid: not in range " << computedPrice << " " << *bidMin << " " << *bidMax; + JLOG(ctx_.journal.debug()) << "AMM Bid: not in range " << computedPrice << " " + << *bidMin << " " << *bidMax; return std::nullopt; } // Bidder pays max(bidPrice, computedPrice) @@ -242,30 +253,38 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour { return std::max(computedPrice, Number(*bidMin)); } - else if (bidMax) + if (bidMax) { if (computedPrice <= *bidMax) return computedPrice; - JLOG(ctx_.journal.debug()) << "AMM Bid: not in range " << computedPrice << " " << *bidMax; + JLOG(ctx_.journal.debug()) + << "AMM Bid: not in range " << computedPrice << " " << *bidMax; return std::nullopt; } - else - return computedPrice; + + return computedPrice; }(); if (!payPrice) + { return Unexpected(tecAMM_FAILED); - else if (payPrice > lpTokens) + } + if (payPrice > lpTokens) + { return Unexpected(tecAMM_INVALID_TOKENS); + } return *payPrice; }; // No one owns the slot or expired slot. if (auto const acct = auctionSlot[~sfAccount]; !acct || !validOwner(*acct)) { - if (auto const payPrice = getPayPrice(minSlotPrice); !payPrice) + auto const payPrice = getPayPrice(minSlotPrice); + if (!payPrice) + { return {payPrice.error(), false}; - else - res = updateSlot(discountedFee, *payPrice, *payPrice); + } + + res = updateSlot(discountedFee, *payPrice, *payPrice); } else { @@ -294,11 +313,17 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour if (refund > *payPrice) { // This error case should never occur. - JLOG(ctx_.journal.fatal()) << "AMM Bid: refund exceeds payPrice " << refund << " " << *payPrice; + JLOG(ctx_.journal.fatal()) + << "AMM Bid: refund exceeds payPrice " << refund << " " << *payPrice; return {tecINTERNAL, false}; } - res = accountSend(sb, account_, auctionSlot[sfAccount], toSTAmount(lpTokens.issue(), refund), ctx_.journal); - if (res != tesSUCCESS) + res = accountSend( + sb, + account_, + auctionSlot[sfAccount], + toSTAmount(lpTokens.issue(), refund), + ctx_.journal); + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Bid: failed to refund."; return {res, false}; @@ -308,7 +333,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour res = updateSlot(discountedFee, *payPrice, burn); } - return {res, res == tesSUCCESS}; + return {res, isTesSuccess(res)}; } TER diff --git a/src/xrpld/app/tx/detail/AMMClawback.cpp b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp similarity index 83% rename from src/xrpld/app/tx/detail/AMMClawback.cpp rename to src/libxrpl/tx/transactors/dex/AMMClawback.cpp index 6da899c578..a5fda15033 100644 --- a/src/xrpld/app/tx/detail/AMMClawback.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp @@ -1,14 +1,13 @@ -#include -#include -#include -#include - #include #include #include #include #include #include +#include +#include +#include +#include #include @@ -103,7 +102,7 @@ AMMClawback::doApply() Sandbox sb(&ctx_.view()); auto const ter = applyGuts(sb); - if (ter == tesSUCCESS) + if (isTesSuccess(ter)) sb.apply(ctx_.rawView()); return ter; @@ -129,17 +128,17 @@ AMMClawback::applyGuts(Sandbox& sb) if (sb.rules().enabled(fixAMMClawbackRounding)) { - // retrieve LP token balance inside the amendment gate to avoid - // inconsistent error behavior + // retrieve LP token balance inside the amendment gate to avoid inconsistent error behavior auto const lpTokenBalance = ammLPHolds(sb, *ammSle, holder, j_); if (lpTokenBalance == beast::zero) return tecAMM_BALANCE; - if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokenBalance, ammSle, holder); !res) + if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokenBalance, ammSle, holder); + !res) return res.error(); // LCOV_EXCL_LINE } - - auto const expected = ammHolds(sb, *ammSle, asset, asset2, FreezeHandling::fhIGNORE_FREEZE, ctx_.journal); + auto const expected = + ammHolds(sb, *ammSle, asset, asset2, FreezeHandling::fhIGNORE_FREEZE, ctx_.journal); if (!expected) return expected.error(); // LCOV_EXCL_LINE @@ -150,36 +149,53 @@ AMMClawback::applyGuts(Sandbox& sb) STAmount amountWithdraw; std::optional amount2Withdraw; + // calling a second time on purpose since `verifyAndAdjustLPTokenBalance` rounds and may adjust + // the balance auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_); if (holdLPtokens == beast::zero) return tecAMM_BALANCE; if (!clawAmount) + { // Because we are doing a two-asset withdrawal, // tfee is actually not used, so pass tfee as 0. - std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = AMMWithdraw::equalWithdrawTokens( - sb, - *ammSle, - holder, - ammAccount, - amountBalance, - amount2Balance, - lptAMMBalance, - holdLPtokens, - holdLPtokens, - 0, - FreezeHandling::fhIGNORE_FREEZE, - WithdrawAll::Yes, - mPriorBalance, - ctx_.journal); + std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = + AMMWithdraw::equalWithdrawTokens( + sb, + *ammSle, + holder, + ammAccount, + amountBalance, + amount2Balance, + lptAMMBalance, + holdLPtokens, + holdLPtokens, + 0, + FreezeHandling::fhIGNORE_FREEZE, + WithdrawAll::Yes, + preFeeBalance_, + ctx_.journal); + } else - std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = equalWithdrawMatchingOneAmount( - sb, *ammSle, holder, ammAccount, amountBalance, amount2Balance, lptAMMBalance, holdLPtokens, *clawAmount); + { + std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = + equalWithdrawMatchingOneAmount( + sb, + *ammSle, + holder, + ammAccount, + amountBalance, + amount2Balance, + lptAMMBalance, + holdLPtokens, + *clawAmount); + } - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; // LCOV_EXCL_LINE - auto const res = AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammSle, newLPTokenBalance, asset, asset2, j_); + auto const res = + AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammSle, newLPTokenBalance, asset, asset2, j_); if (!res.second) return res.first; // LCOV_EXCL_LINE @@ -188,7 +204,7 @@ AMMClawback::applyGuts(Sandbox& sb) << " old balance: " << to_string(lptAMMBalance.iou()); auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; // LCOV_EXCL_LINE // if the issuer issues both assets and sets flag tfClawTwoAssets, we @@ -223,6 +239,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( auto const lpTokensWithdraw = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac); if (lpTokensWithdraw > holdLPtokens) + { // if lptoken balance less than what the issuer intended to clawback, // clawback all the tokens. Because we are doing a two-asset withdrawal, // tfee is actually not used, so pass tfee as 0. @@ -239,8 +256,9 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::Yes, - mPriorBalance, + preFeeBalance_, ctx_.journal); + } auto const& rules = sb.rules(); if (rules.enabled(fixAMMClawbackRounding)) @@ -270,7 +288,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } @@ -289,7 +307,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp similarity index 83% rename from src/xrpld/app/tx/detail/AMMCreate.cpp rename to src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 4634ac79e0..dd717948b3 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -1,14 +1,14 @@ -#include -#include -#include -#include - +#include #include #include +#include #include #include #include #include +#include +#include +#include namespace xrpl { @@ -66,26 +66,28 @@ AMMCreate::preclaim(PreclaimContext const& ctx) auto const amount2 = ctx.tx[sfAmount2]; // Check if AMM already exists for the token pair - if (auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); ctx.view.read(ammKeylet)) + if (auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue()); + ctx.view.read(ammKeylet)) { JLOG(ctx.j.debug()) << "AMM Instance: ltAMM already exists."; return tecDUPLICATE; } - if (auto const ter = requireAuth(ctx.view, amount.issue(), accountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(ctx.view, amount.issue(), accountID); !isTesSuccess(ter)) { JLOG(ctx.j.debug()) << "AMM Instance: account is not authorized, " << amount.issue(); return ter; } - if (auto const ter = requireAuth(ctx.view, amount2.issue(), accountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(ctx.view, amount2.issue(), accountID); !isTesSuccess(ter)) { JLOG(ctx.j.debug()) << "AMM Instance: account is not authorized, " << amount2.issue(); return ter; } // Globally or individually frozen - if (isFrozen(ctx.view, accountID, amount.issue()) || isFrozen(ctx.view, accountID, amount2.issue())) + if (isFrozen(ctx.view, accountID, amount.issue()) || + isFrozen(ctx.view, accountID, amount2.issue())) { JLOG(ctx.j.debug()) << "AMM Instance: involves frozen asset."; return tecFROZEN; @@ -120,7 +122,9 @@ AMMCreate::preclaim(PreclaimContext const& ctx) if (isXRP(asset)) return xrpBalance < asset; return accountID != asset.issue().account && - accountHolds(ctx.view, accountID, asset.issue(), FreezeHandling::fhZERO_IF_FROZEN, ctx.j) < asset; + accountHolds( + ctx.view, accountID, asset.issue(), FreezeHandling::fhZERO_IF_FROZEN, ctx.j) < + asset; }; if (insufficientBalance(amount) || insufficientBalance(amount2)) @@ -137,13 +141,15 @@ AMMCreate::preclaim(PreclaimContext const& ctx) if (isLPToken(amount) || isLPToken(amount2)) { - JLOG(ctx.j.debug()) << "AMM Instance: can't create with LPTokens " << amount << " " << amount2; + JLOG(ctx.j.debug()) << "AMM Instance: can't create with LPTokens " << amount << " " + << amount2; return tecAMM_INVALID_TOKENS; } if (ctx.view.rules().enabled(featureSingleAssetVault)) { - if (auto const accountId = pseudoAccountAddress(ctx.view, keylet::amm(amount.issue(), amount2.issue()).key); + if (auto const accountId = + pseudoAccountAddress(ctx.view, keylet::amm(amount.issue(), amount2.issue()).key); accountId == beast::zero) return terADDRESS_COLLISION; } @@ -158,14 +164,15 @@ AMMCreate::preclaim(PreclaimContext const& ctx) auto clawbackDisabled = [&](Issue const& issue) -> TER { if (isXRP(issue)) return tesSUCCESS; - if (auto const sle = ctx.view.read(keylet::account(issue.account)); !sle) + auto const sle = ctx.view.read(keylet::account(issue.account)); + if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE - else if (sle->getFlags() & lsfAllowTrustLineClawback) + if (sle->getFlags() & lsfAllowTrustLineClawback) return tecNO_PERMISSION; return tesSUCCESS; }; - if (auto const ter = clawbackDisabled(amount.issue()); ter != tesSUCCESS) + if (auto const ter = clawbackDisabled(amount.issue()); !isTesSuccess(ter)) return ter; return clawbackDisabled(amount2.issue()); } @@ -226,33 +233,35 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // Send LPT to LP. auto res = accountSend(sb, accountId, account_, lpTokens, ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens; return {res, false}; } auto sendAndTrustSet = [&](STAmount const& amount) -> TER { - if (auto const res = accountSend(sb, account_, accountId, amount, ctx_.journal, WaiveTransferFee::Yes)) + if (auto const res = + accountSend(sb, account_, accountId, amount, ctx_.journal, WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline if (!isXRP(amount)) { - if (SLE::pointer sleRippleState = sb.peek(keylet::line(accountId, amount.issue())); !sleRippleState) - return tecINTERNAL; // LCOV_EXCL_LINE - else + SLE::pointer sleRippleState = sb.peek(keylet::line(accountId, amount.issue())); + if (!sleRippleState) { - auto const flags = sleRippleState->getFlags(); - sleRippleState->setFieldU32(sfFlags, flags | lsfAMMNode); - sb.update(sleRippleState); + return tecINTERNAL; // LCOV_EXCL_LINE } + + auto const flags = sleRippleState->getFlags(); + sleRippleState->setFieldU32(sfFlags, flags | lsfAMMNode); + sb.update(sleRippleState); } return tesSUCCESS; }; // Send asset1. res = sendAndTrustSet(amount); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send " << amount; return {res, false}; @@ -260,24 +269,24 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // Send asset2. res = sendAndTrustSet(amount2); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(j_.debug()) << "AMM Instance: failed to send " << amount2; return {res, false}; } - JLOG(j_.debug()) << "AMM Instance: success " << accountId << " " << ammKeylet.key << " " << lpTokens << " " - << amount << " " << amount2; + JLOG(j_.debug()) << "AMM Instance: success " << accountId << " " << ammKeylet.key << " " + << lpTokens << " " << amount << " " << amount2; auto addOrderBook = [&](Issue const& issueIn, Issue const& issueOut, std::uint64_t uRate) { Book const book{issueIn, issueOut, std::nullopt}; auto const dir = keylet::quality(keylet::book(book), uRate); if (auto const bookExisted = static_cast(sb.read(dir)); !bookExisted) - ctx_.app.getOrderBookDB().addOrderBook(book); + ctx_.registry.getOrderBookDB().addOrderBook(book); }; addOrderBook(amount.issue(), amount2.issue(), getRate(amount2, amount)); addOrderBook(amount2.issue(), amount.issue(), getRate(amount, amount2)); - return {res, res == tesSUCCESS}; + return {res, isTesSuccess(res)}; } TER diff --git a/src/xrpld/app/tx/detail/AMMDelete.cpp b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp similarity index 79% rename from src/xrpld/app/tx/detail/AMMDelete.cpp rename to src/libxrpl/tx/transactors/dex/AMMDelete.cpp index f810f57758..0fbaa4e23e 100644 --- a/src/xrpld/app/tx/detail/AMMDelete.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp @@ -1,10 +1,9 @@ -#include -#include - #include #include #include #include +#include +#include namespace xrpl { @@ -44,8 +43,9 @@ AMMDelete::doApply() // as we go on processing transactions. Sandbox sb(&ctx_.view()); - auto const ter = deleteAMMAccount(sb, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); - if (ter == tesSUCCESS || ter == tecINCOMPLETE) + auto const ter = + deleteAMMAccount(sb, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); + if (isTesSuccess(ter) || ter == tecINCOMPLETE) sb.apply(ctx_.rawView()); return ter; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp similarity index 82% rename from src/xrpld/app/tx/detail/AMMDeposit.cpp rename to src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index af4ce51894..cae83ccef1 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -1,12 +1,12 @@ -#include -#include -#include - #include #include +#include #include #include #include +#include +#include +#include namespace xrpl { @@ -18,9 +18,8 @@ AMMDeposit::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMDeposit::getFlagsMask(PreflightContext const& ctx) - { - return tfDepositMask; + return tfAMMDepositMask; } NotTEC @@ -89,7 +88,8 @@ AMMDeposit::preflight(PreflightContext const& ctx) if (amount && amount2 && amount->issue() == amount2->issue()) { - JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue." << amount->issue() << " " << amount2->issue(); + JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue." << amount->issue() << " " + << amount2->issue(); return temBAD_AMM_TOKENS; } @@ -101,8 +101,8 @@ AMMDeposit::preflight(PreflightContext const& ctx) if (amount) { - if (auto const res = - invalidAMMAmount(*amount, std::make_optional(std::make_pair(asset, asset2)), ePrice.has_value())) + if (auto const res = invalidAMMAmount( + *amount, std::make_optional(std::make_pair(asset, asset2)), ePrice.has_value())) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount"; return res; @@ -111,7 +111,8 @@ AMMDeposit::preflight(PreflightContext const& ctx) if (amount2) { - if (auto const res = invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2)))) + if (auto const res = + invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2)))) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount2"; return res; @@ -121,8 +122,8 @@ AMMDeposit::preflight(PreflightContext const& ctx) // must be amount issue if (amount && ePrice) { - if (auto const res = - invalidAMMAmount(*ePrice, std::make_optional(std::make_pair(amount->issue(), amount->issue())))) + if (auto const res = invalidAMMAmount( + *ePrice, std::make_optional(std::make_pair(amount->issue(), amount->issue())))) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid EPrice"; return res; @@ -150,8 +151,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) return terNO_AMM; } - auto const expected = - ammHolds(ctx.view, *ammSle, std::nullopt, std::nullopt, FreezeHandling::fhIGNORE_FREEZE, ctx.j); + auto const expected = ammHolds( + ctx.view, *ammSle, std::nullopt, std::nullopt, FreezeHandling::fhIGNORE_FREEZE, ctx.j); if (!expected) return expected.error(); // LCOV_EXCL_LINE auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected; @@ -171,7 +172,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) { if (lptAMMBalance == beast::zero) return tecAMM_EMPTY; - if (amountBalance <= beast::zero || amount2Balance <= beast::zero || lptAMMBalance < beast::zero) + if (amountBalance <= beast::zero || amount2Balance <= beast::zero || + lptAMMBalance < beast::zero) { // LCOV_EXCL_START JLOG(ctx.j.debug()) << "AMM Deposit: reserves or tokens balance is zero."; @@ -190,7 +192,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) { auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue(); // Adjust the reserve if LP doesn't have LPToken trustline - auto const sle = ctx.view.read(keylet::line(accountID, lpIssue.account, lpIssue.currency)); + auto const sle = + ctx.view.read(keylet::line(accountID, lpIssue.account, lpIssue.currency)); if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit) return TER(tesSUCCESS); if (sle) @@ -198,7 +201,9 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) return tecINSUF_RESERVE_LINE; } return (accountID == deposit.issue().account || - accountHolds(ctx.view, accountID, deposit.issue(), FreezeHandling::fhIGNORE_FREEZE, ctx.j) >= deposit) + accountHolds( + ctx.view, accountID, deposit.issue(), FreezeHandling::fhIGNORE_FREEZE, ctx.j) >= + deposit) ? TER(tesSUCCESS) : tecUNFUNDED_AMM; }; @@ -216,8 +221,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) if (isFrozen(ctx.view, accountID, asset)) { - JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen, " << to_string(accountID) << " " - << to_string(asset.currency); + JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen, " + << to_string(accountID) << " " << to_string(asset.currency); return tecFROZEN; } @@ -245,28 +250,31 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) if (auto const ter = requireAuth(ctx.view, amount->issue(), accountID)) { // LCOV_EXCL_START - JLOG(ctx.j.debug()) << "AMM Deposit: account is not authorized, " << amount->issue(); + JLOG(ctx.j.debug()) + << "AMM Deposit: account is not authorized, " << amount->issue(); return ter; // LCOV_EXCL_STOP } // AMM account or currency frozen if (isFrozen(ctx.view, ammAccountID, amount->issue())) { - JLOG(ctx.j.debug()) << "AMM Deposit: AMM account or currency is frozen, " << to_string(accountID); + JLOG(ctx.j.debug()) + << "AMM Deposit: AMM account or currency is frozen, " << to_string(accountID); return tecFROZEN; } // Account frozen if (isIndividualFrozen(ctx.view, accountID, amount->issue())) { - JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, " << to_string(accountID) << " " - << to_string(amount->issue().currency); + JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, " << to_string(accountID) + << " " << to_string(amount->issue().currency); return tecFROZEN; } if (checkBalance) { if (auto const ter = balance(*amount)) { - JLOG(ctx.j.debug()) << "AMM Deposit: account has insufficient funds, " << *amount; + JLOG(ctx.j.debug()) + << "AMM Deposit: account has insufficient funds, " << *amount; return ter; } } @@ -292,7 +300,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) } // Equal deposit lp tokens - if (auto const lpTokens = ctx.tx[~sfLPTokenOut]; lpTokens && lpTokens->issue() != lptAMMBalance.issue()) + if (auto const lpTokens = ctx.tx[~sfLPTokenOut]; + lpTokens && lpTokens->issue() != lptAMMBalance.issue()) { JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens."; return temBAD_AMM_TOKENS; @@ -336,16 +345,19 @@ AMMDeposit::applyGuts(Sandbox& sb) if (!expected) return {expected.error(), false}; // LCOV_EXCL_LINE auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected; - auto const tfee = (lptAMMBalance == beast::zero) ? ctx_.tx[~sfTradingFee].value_or(0) - : getTradingFee(ctx_.view(), *ammSle, account_); + auto const tfee = (lptAMMBalance == beast::zero) + ? ctx_.tx[~sfTradingFee].value_or(0) + : getTradingFee(ctx_.view(), *ammSle, account_); auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx; auto const [result, newLPTokenBalance] = [&, &amountBalance = amountBalance, &amount2Balance = amount2Balance, - &lptAMMBalance = lptAMMBalance]() -> std::pair { + &lptAMMBalance = + lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalDepositLimit( sb, ammAccountID, @@ -356,13 +368,24 @@ AMMDeposit::applyGuts(Sandbox& sb) *amount2, lpTokensDeposit, tfee); + } if (subTxType & tfOneAssetLPToken) - return singleDepositTokens(sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee); + { + return singleDepositTokens( + sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee); + } if (subTxType & tfLimitLPToken) - return singleDepositEPrice(sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee); + { + return singleDepositEPrice( + sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee); + } if (subTxType & tfSingleAsset) - return singleDeposit(sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee); + { + return singleDeposit( + sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee); + } if (subTxType & tfLPToken) + { return equalDepositTokens( sb, ammAccountID, @@ -373,8 +396,12 @@ AMMDeposit::applyGuts(Sandbox& sb) amount, amount2, tfee); + } if (subTxType & tfTwoAssetIfEmpty) - return equalDepositInEmptyState(sb, ammAccountID, *amount, *amount2, lptAMMBalance.issue(), tfee); + { + return equalDepositInEmptyState( + sb, ammAccountID, *amount, *amount2, lptAMMBalance.issue(), tfee); + } // should not happen. // LCOV_EXCL_START JLOG(j_.error()) << "AMM Deposit: invalid options."; @@ -382,9 +409,11 @@ AMMDeposit::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result == tesSUCCESS) + if (isTesSuccess(result)) { - XRPL_ASSERT(newLPTokenBalance > beast::zero, "xrpl::AMMDeposit::applyGuts : valid new LP token balance"); + XRPL_ASSERT( + newLPTokenBalance > beast::zero, + "xrpl::AMMDeposit::applyGuts : valid new LP token balance"); ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance); // LP depositing into AMM empty state gets the auction slot // and the voting @@ -394,7 +423,7 @@ AMMDeposit::applyGuts(Sandbox& sb) sb.update(ammSle); } - return {result, result == tesSUCCESS}; + return {result, isTesSuccess(result)}; } TER @@ -440,14 +469,27 @@ AMMDeposit::deposit( } else if ( account_ == depositAmount.issue().account || - accountHolds(view, account_, depositAmount.issue(), FreezeHandling::fhIGNORE_FREEZE, ctx_.journal) >= - depositAmount) + accountHolds( + view, + account_, + depositAmount.issue(), + FreezeHandling::fhIGNORE_FREEZE, + ctx_.journal) >= depositAmount) + { return tesSUCCESS; + } return tecUNFUNDED_AMM; }; - auto const [amountDepositActual, amount2DepositActual, lpTokensDepositActual] = adjustAmountsByLPTokens( - amountBalance, amountDeposit, amount2Deposit, lptAMMBalance, lpTokensDeposit, tfee, IsDeposit::Yes); + auto const [amountDepositActual, amount2DepositActual, lpTokensDepositActual] = + adjustAmountsByLPTokens( + amountBalance, + amountDeposit, + amount2Deposit, + lptAMMBalance, + lpTokensDeposit, + tfee, + IsDeposit::Yes); if (lpTokensDepositActual <= beast::zero) { @@ -458,11 +500,11 @@ AMMDeposit::deposit( if (amountDepositActual < depositMin || amount2DepositActual < deposit2Min || lpTokensDepositActual < lpTokensDepositMin) { - JLOG(ctx_.journal.debug()) << "AMM Deposit: min deposit fails " << amountDepositActual << " " - << depositMin.value_or(STAmount{}) << " " - << amount2DepositActual.value_or(STAmount{}) << " " - << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual << " " - << lpTokensDepositMin.value_or(STAmount{}); + JLOG(ctx_.journal.debug()) + << "AMM Deposit: min deposit fails " << amountDepositActual << " " + << depositMin.value_or(STAmount{}) << " " << amount2DepositActual.value_or(STAmount{}) + << " " << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual << " " + << lpTokensDepositMin.value_or(STAmount{}); return {tecAMM_FAILED, STAmount{}}; } @@ -475,8 +517,9 @@ AMMDeposit::deposit( return {ter, STAmount{}}; } - auto res = accountSend(view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + auto res = accountSend( + view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes); + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual; return {res, STAmount{}}; @@ -493,17 +536,19 @@ AMMDeposit::deposit( return {ter, STAmount{}}; } - res = accountSend(view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + res = accountSend( + view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes); + if (!isTesSuccess(res)) { - JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << *amount2DepositActual; + JLOG(ctx_.journal.debug()) + << "AMM Deposit: failed to deposit " << *amount2DepositActual; return {res, STAmount{}}; } } // Deposit LP tokens res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; return {res, STAmount{}}; @@ -513,7 +558,10 @@ AMMDeposit::deposit( } static STAmount -adjustLPTokensOut(Rules const& rules, STAmount const& lptAMMBalance, STAmount const& lpTokensDeposit) +adjustLPTokensOut( + Rules const& rules, + STAmount const& lptAMMBalance, + STAmount const& lpTokensDeposit) { if (!rules.enabled(fixAMMv1_3)) return lpTokensDeposit; @@ -542,8 +590,10 @@ AMMDeposit::equalDepositTokens( return {tecAMM_INVALID_TOKENS, STAmount{}}; auto const frac = divide(tokensAdj, lptAMMBalance, lptAMMBalance.issue()); // amounts factor in the adjusted tokens - auto const amountDeposit = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes); - auto const amount2Deposit = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes); + auto const amountDeposit = + getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes); + auto const amount2Deposit = + getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes); return deposit( view, ammAccount, @@ -611,14 +661,17 @@ AMMDeposit::equalDepositLimit( if (tokensAdj == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac); auto const amount2Deposit = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes); if (amount2Deposit <= amount2) + { return deposit( view, ammAccount, @@ -631,19 +684,23 @@ AMMDeposit::equalDepositLimit( std::nullopt, lpTokensDepositMin, tfee); + } frac = Number{amount2} / amount2Balance; tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes); if (tokensAdj == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE } // factor in the adjusted tokens frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac); auto const amountDeposit = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes); if (amountDeposit <= amount) + { return deposit( view, ammAccount, @@ -656,6 +713,7 @@ AMMDeposit::equalDepositLimit( std::nullopt, lpTokensDepositMin, tfee); + } return {tecAMM_FAILED, STAmount{}}; } @@ -677,14 +735,16 @@ AMMDeposit::singleDeposit( std::optional const& lpTokensDepositMin, std::uint16_t tfee) { - auto const tokens = - adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee)); + auto const tokens = adjustLPTokensOut( + view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee)); if (tokens == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountDepositAdj] = @@ -780,14 +840,16 @@ AMMDeposit::singleDepositEPrice( { if (amount != beast::zero) { - auto const tokens = - adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee)); + auto const tokens = adjustLPTokensOut( + view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee)); if (tokens <= beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountDepositAdj] = @@ -796,6 +858,7 @@ AMMDeposit::singleDepositEPrice( return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE auto const ep = Number{amountDepositAdj} / tokensAdj; if (ep <= ePrice) + { return deposit( view, ammAccount, @@ -808,6 +871,7 @@ AMMDeposit::singleDepositEPrice( std::nullopt, std::nullopt, tfee); + } } // LPTokens is asset out => E = b / t @@ -836,15 +900,17 @@ AMMDeposit::singleDepositEPrice( auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2; auto amtNoRoundCb = [&] { return f1 * amountBalance * solveQuadraticEq(a1, b1, c1); }; auto amtProdCb = [&] { return f1 * solveQuadraticEq(a1, b1, c1); }; - auto const amountDeposit = getRoundedAsset(view.rules(), amtNoRoundCb, amountBalance, amtProdCb, IsDeposit::Yes); + auto const amountDeposit = + getRoundedAsset(view.rules(), amtNoRoundCb, amountBalance, amtProdCb, IsDeposit::Yes); if (amountDeposit <= beast::zero) return {tecAMM_FAILED, STAmount{}}; auto tokNoRoundCb = [&] { return amountDeposit / ePrice; }; auto tokProdCb = [&] { return amountDeposit / ePrice; }; - auto const tokens = getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::Yes); + auto const tokens = + getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::Yes); // factor in the adjusted tokens - auto const [tokensAdj, amountDepositAdj] = - adjustAssetInByTokens(view.rules(), amountBalance, amountDeposit, lptAMMBalance, tokens, tfee); + auto const [tokensAdj, amountDepositAdj] = adjustAssetInByTokens( + view.rules(), amountBalance, amountDeposit, lptAMMBalance, tokens, tfee); if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero) return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/misc/detail/AMMHelpers.cpp b/src/libxrpl/tx/transactors/dex/AMMHelpers.cpp similarity index 82% rename from src/xrpld/app/misc/detail/AMMHelpers.cpp rename to src/libxrpl/tx/transactors/dex/AMMHelpers.cpp index ff3474f33d..20ffab52ca 100644 --- a/src/xrpld/app/misc/detail/AMMHelpers.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMHelpers.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { @@ -34,12 +34,10 @@ lpTokensOut( auto const t = lptAMMBalance * (r - c) / (1 + c); return toSTAmount(lptAMMBalance.issue(), t); } - else - { - // minimize tokens out - auto const frac = (r - c) / (1 + c); - return multiply(lptAMMBalance, frac, Number::downward); - } + + // minimize tokens out + auto const frac = (r - c) / (1 + c); + return multiply(lptAMMBalance, frac, Number::downward); } /* Equation 4 solves equation 3 for b: @@ -54,7 +52,11 @@ lpTokensOut( * (R/t2)**2 + R*(2*d/t2 - 1/f1) + d**2 - f2**2 = 0 */ STAmount -ammAssetIn(STAmount const& asset1Balance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee) +ammAssetIn( + STAmount const& asset1Balance, + STAmount const& lptAMMBalance, + STAmount const& lpTokens, + std::uint16_t tfee) { auto const f1 = feeMult(tfee); auto const f2 = feeMultHalf(tfee) / f1; @@ -68,12 +70,10 @@ ammAssetIn(STAmount const& asset1Balance, STAmount const& lptAMMBalance, STAmoun { return toSTAmount(asset1Balance.issue(), asset1Balance * solveQuadraticEq(a, b, c)); } - else - { - // maximize deposit - auto const frac = solveQuadraticEq(a, b, c); - return multiply(asset1Balance, frac, Number::upward); - } + + // maximize deposit + auto const frac = solveQuadraticEq(a, b, c); + return multiply(asset1Balance, frac, Number::upward); } /* Equation 7: @@ -95,12 +95,10 @@ lpTokensIn( auto const t = lptAMMBalance * (c - root2(c * c - 4 * fr)) / 2; return toSTAmount(lptAMMBalance.issue(), t); } - else - { - // maximize tokens in - auto const frac = (c - root2(c * c - 4 * fr)) / 2; - return multiply(lptAMMBalance, frac, Number::upward); - } + + // maximize tokens in + auto const frac = (c - root2(c * c - 4 * fr)) / 2; + return multiply(lptAMMBalance, frac, Number::upward); } /* Equation 8 solves equation 7 for b: @@ -114,7 +112,11 @@ lpTokensIn( * R = (t1**2 + t1*(f - 2)) / (t1*f - 1) */ STAmount -ammAssetOut(STAmount const& assetBalance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee) +ammAssetOut( + STAmount const& assetBalance, + STAmount const& lptAMMBalance, + STAmount const& lpTokens, + std::uint16_t tfee) { auto const f = getFee(tfee); Number const t1 = lpTokens / lptAMMBalance; @@ -123,12 +125,10 @@ ammAssetOut(STAmount const& assetBalance, STAmount const& lptAMMBalance, STAmoun auto const b = assetBalance * (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); return toSTAmount(assetBalance.issue(), b); } - else - { - // minimize withdraw - auto const frac = (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); - return multiply(assetBalance, frac, Number::downward); - } + + // minimize withdraw + auto const frac = (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1); + return multiply(assetBalance, frac, Number::downward); } Number @@ -173,7 +173,8 @@ adjustAmountsByLPTokens( if (lpTokensActual < lpTokens) { bool const ammRoundingEnabled = [&]() { - if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixAMMv1_1)) + if (auto const& rules = getCurrentTransactionRules(); + rules && rules->enabled(fixAMMv1_1)) return true; return false; }(); @@ -185,31 +186,41 @@ adjustAmountsByLPTokens( auto const amountActual = toSTAmount(amount.issue(), fr * amount); auto const amount2Actual = toSTAmount(amount2->issue(), fr * *amount2); if (!ammRoundingEnabled) + { return std::make_tuple( amountActual < amount ? amountActual : amount, amount2Actual < amount2 ? amount2Actual : amount2, lpTokensActual); - else - return std::make_tuple(amountActual, amount2Actual, lpTokensActual); + } + + return std::make_tuple(amountActual, amount2Actual, lpTokensActual); } // Single trade auto const amountActual = [&]() { if (isDeposit == IsDeposit::Yes) + { return ammAssetIn(amountBalance, lptAMMBalance, lpTokensActual, tfee); - else if (!ammRoundingEnabled) + } + if (!ammRoundingEnabled) + { return ammAssetOut(amountBalance, lptAMMBalance, lpTokens, tfee); - else - return ammAssetOut(amountBalance, lptAMMBalance, lpTokensActual, tfee); + } + + return ammAssetOut(amountBalance, lptAMMBalance, lpTokensActual, tfee); }(); if (!ammRoundingEnabled) - return amountActual < amount ? std::make_tuple(amountActual, std::nullopt, lpTokensActual) - : std::make_tuple(amount, std::nullopt, lpTokensActual); - else - return std::make_tuple(amountActual, std::nullopt, lpTokensActual); + { + return amountActual < amount + ? std::make_tuple(amountActual, std::nullopt, lpTokensActual) + : std::make_tuple(amount, std::nullopt, lpTokensActual); + } + + return std::make_tuple(amountActual, std::nullopt, lpTokensActual); } - XRPL_ASSERT(lpTokensActual == lpTokens, "xrpl::adjustAmountsByLPTokens : LP tokens match actual"); + XRPL_ASSERT( + lpTokensActual == lpTokens, "xrpl::adjustAmountsByLPTokens : LP tokens match actual"); return {amount, amount2, lpTokensActual}; } @@ -230,9 +241,11 @@ solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c) // use numerically stable citardauq formula for quadratic equation solution // https://people.csail.mit.edu/bkph/articles/Quadratics.pdf if (b > 0) + { return (2 * c) / (-b - root2(d)); - else - return (2 * c) / (-b + root2(d)); + } + + return (2 * c) / (-b + root2(d)); } STAmount @@ -246,9 +259,9 @@ multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm) STAmount getRoundedAsset( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& balance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit) { if (!rules.enabled(fixAMMv1_3)) @@ -262,7 +275,11 @@ getRoundedAsset( } STAmount -getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& frac, IsDeposit isDeposit) +getRoundedLPTokens( + Rules const& rules, + STAmount const& balance, + Number const& frac, + IsDeposit isDeposit) { if (!rules.enabled(fixAMMv1_3)) return toSTAmount(balance.issue(), balance * frac); @@ -275,9 +292,9 @@ getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& fr STAmount getRoundedLPTokens( Rules const& rules, - std::function&& noRoundCb, + std::function const& noRoundCb, STAmount const& lptAMMBalance, - std::function&& productCb, + std::function const& productCb, IsDeposit isDeposit) { if (!rules.enabled(fixAMMv1_3)) @@ -348,7 +365,11 @@ adjustAssetOutByTokens( } Number -adjustFracByTokens(Rules const& rules, STAmount const& lptAMMBalance, STAmount const& tokens, Number const& frac) +adjustFracByTokens( + Rules const& rules, + STAmount const& lptAMMBalance, + STAmount const& tokens, + Number const& frac) { if (!rules.enabled(fixAMMv1_3)) return frac; diff --git a/src/xrpld/app/misc/detail/AMMUtils.cpp b/src/libxrpl/tx/transactors/dex/AMMUtils.cpp similarity index 82% rename from src/xrpld/app/misc/detail/AMMUtils.cpp rename to src/libxrpl/tx/transactors/dex/AMMUtils.cpp index 36a40f1709..91891ce86f 100644 --- a/src/xrpld/app/misc/detail/AMMUtils.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMUtils.cpp @@ -1,10 +1,10 @@ -#include -#include - #include +#include #include #include #include +#include +#include namespace xrpl { @@ -36,22 +36,29 @@ ammHolds( auto const issue2 = ammSle[sfAsset2].get(); if (optIssue1 && optIssue2) { - if (invalidAMMAssetPair(*optIssue1, *optIssue2, std::make_optional(std::make_pair(issue1, issue2)))) + if (invalidAMMAssetPair( + *optIssue1, *optIssue2, std::make_optional(std::make_pair(issue1, issue2)))) { // This error can only be hit if the AMM is corrupted // LCOV_EXCL_START - JLOG(j.debug()) << "ammHolds: Invalid optIssue1 or optIssue2 " << *optIssue1 << " " << *optIssue2; + JLOG(j.debug()) << "ammHolds: Invalid optIssue1 or optIssue2 " << *optIssue1 << " " + << *optIssue2; return std::nullopt; // LCOV_EXCL_STOP } return std::make_optional(std::make_pair(*optIssue1, *optIssue2)); } auto const singleIssue = [&issue1, &issue2, &j]( - Issue checkIssue, char const* label) -> std::optional> { + Issue checkIssue, + char const* label) -> std::optional> { if (checkIssue == issue1) + { return std::make_optional(std::make_pair(issue1, issue2)); - else if (checkIssue == issue2) + } + if (checkIssue == issue2) + { return std::make_optional(std::make_pair(issue2, issue1)); + } // Unreachable unless AMM corrupted. // LCOV_EXCL_START JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue; @@ -62,7 +69,7 @@ ammHolds( { return singleIssue(*optIssue1, "optIssue1"); } - else if (optIssue2) + if (optIssue2) { // Cannot have Amount2 without Amount. return singleIssue(*optIssue2, "optIssue2"); // LCOV_EXCL_LINE @@ -71,8 +78,8 @@ ammHolds( }(); if (!issues) return Unexpected(tecAMM_INVALID_TOKENS); - auto const [asset1, asset2] = - ammPoolHolds(view, ammSle.getAccountID(sfAccount), issues->first, issues->second, freezeHandling, j); + auto const [asset1, asset2] = ammPoolHolds( + view, ammSle.getAccountID(sfAccount), issues->first, issues->second, freezeHandling, j); return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]); } @@ -98,13 +105,15 @@ ammLPHolds( { amount.clear(Issue{currency, ammAccount}); JLOG(j.trace()) << "ammLPHolds: no SLE " - << " lpAccount=" << to_string(lpAccount) << " amount=" << amount.getFullText(); + << " lpAccount=" << to_string(lpAccount) + << " amount=" << amount.getFullText(); } else if (isFrozen(view, lpAccount, currency, ammAccount)) { amount.clear(Issue{currency, ammAccount}); JLOG(j.trace()) << "ammLPHolds: frozen currency " - << " lpAccount=" << to_string(lpAccount) << " amount=" << amount.getFullText(); + << " lpAccount=" << to_string(lpAccount) + << " amount=" << amount.getFullText(); } else { @@ -117,14 +126,19 @@ ammLPHolds( amount.setIssuer(ammAccount); JLOG(j.trace()) << "ammLPHolds:" - << " lpAccount=" << to_string(lpAccount) << " amount=" << amount.getFullText(); + << " lpAccount=" << to_string(lpAccount) + << " amount=" << amount.getFullText(); } return view.balanceHook(lpAccount, ammAccount, amount); } STAmount -ammLPHolds(ReadView const& view, SLE const& ammSle, AccountID const& lpAccount, beast::Journal const j) +ammLPHolds( + ReadView const& view, + SLE const& ammSle, + AccountID const& lpAccount, + beast::Journal const j) { return ammLPHolds( view, @@ -144,18 +158,21 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account) "xrpl::getTradingFee : auction present"); if (ammSle.isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(ammSle.peekAtField(sfAuctionSlot)); + auto const& auctionSlot = safe_downcast(ammSle.peekAtField(sfAuctionSlot)); // Not expired if (auto const expiration = auctionSlot[~sfExpiration]; - duration_cast(view.header().parentCloseTime.time_since_epoch()).count() < expiration) + duration_cast(view.header().parentCloseTime.time_since_epoch()).count() < + expiration) { if (auctionSlot[~sfAccount] == account) return auctionSlot[sfDiscountedFee]; if (auctionSlot.isFieldPresent(sfAuthAccounts)) { for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts)) + { if (acct[~sfAccount] == account) return auctionSlot[sfDiscountedFee]; + } } } } @@ -170,8 +187,9 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const if (auto const sle = view.read(keylet::account(ammAccountID))) return (*sle)[sfBalance]; } - else if (auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency)); - sle && !isFrozen(view, ammAccountID, issue.currency, issue.account)) + else if ( + auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency)); + sle && !isFrozen(view, ammAccountID, issue.currency, issue.account)) { auto amount = (*sle)[sfBalance]; if (ammAccountID > issue.account) @@ -184,12 +202,18 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const } static TER -deleteAMMTrustLines(Sandbox& sb, AccountID const& ammAccountID, std::uint16_t maxTrustlinesToDelete, beast::Journal j) +deleteAMMTrustLines( + Sandbox& sb, + AccountID const& ammAccountID, + std::uint16_t maxTrustlinesToDelete, + beast::Journal j) { return cleanupOnAccountDelete( sb, keylet::ownerDir(ammAccountID), - [&](LedgerEntryType nodeType, uint256 const&, std::shared_ptr& sleItem) -> std::pair { + [&](LedgerEntryType nodeType, + uint256 const&, + std::shared_ptr& sleItem) -> std::pair { // Skip AMM if (nodeType == LedgerEntryType::ltAMM) return {tesSUCCESS, SkipEntry::Yes}; @@ -235,12 +259,14 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo if (!sleAMMRoot) { // LCOV_EXCL_START - JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist " << to_string(ammAccountID); + JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist " + << to_string(ammAccountID); return tecINTERNAL; // LCOV_EXCL_STOP } - if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j); ter != tesSUCCESS) + if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j); + !isTesSuccess(ter)) return ter; auto const ownerDirKeylet = keylet::ownerDir(ammAccountID); @@ -254,7 +280,8 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet)) { // LCOV_EXCL_START - JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of " << toBase58(ammAccountID); + JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of " + << toBase58(ammAccountID); return tecINTERNAL; // LCOV_EXCL_STOP } @@ -294,20 +321,29 @@ initializeFeeAuctionVote( STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot); auctionSlot.setAccountID(sfAccount, account); // current + sec in 24h - auto const expiration = - std::chrono::duration_cast(view.header().parentCloseTime.time_since_epoch()).count() + + auto const expiration = std::chrono::duration_cast( + view.header().parentCloseTime.time_since_epoch()) + .count() + TOTAL_TIME_SLOT_SECS; auctionSlot.setFieldU32(sfExpiration, expiration); auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0}); // Set the fee if (tfee != 0) + { ammSle->setFieldU16(sfTradingFee, tfee); + } else if (ammSle->isFieldPresent(sfTradingFee)) + { ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE + } if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION) + { auctionSlot.setFieldU16(sfDiscountedFee, dfee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE + } } Expected @@ -353,8 +389,10 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE auto const lowLimit = sle->getFieldAmount(sfLowLimit); auto const highLimit = sle->getFieldAmount(sfHighLimit); - auto const isLPTrustline = lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount; - auto const isLPTokenTrustline = lowLimit.issue() == ammIssue || highLimit.issue() == ammIssue; + auto const isLPTrustline = + lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount; + auto const isLPTokenTrustline = + lowLimit.issue() == ammIssue || highLimit.issue() == ammIssue; // Liquidity Provider trustline if (isLPTrustline) @@ -366,13 +404,19 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE } else if (++nIOUTrustLines > 2) + { return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + } } // Another Liquidity Provider LPToken trustline else if (isLPTokenTrustline) + { return false; + } else if (++nIOUTrustLines > 2) + { return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + } } auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); if (uNodeNext == 0) @@ -393,11 +437,16 @@ verifyAndAdjustLPTokenBalance( std::shared_ptr& ammSle, AccountID const& account) { - if (auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); !res) - return Unexpected(res.error()); - else if (res.value()) + auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); + if (!res.has_value()) { - if (withinRelativeDistance(lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3})) + return Unexpected(res.error()); + } + + if (res.value()) + { + if (withinRelativeDistance( + lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3})) { ammSle->setFieldAmount(sfLPTokenBalance, lpTokens); sb.update(ammSle); diff --git a/src/xrpld/app/tx/detail/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp similarity index 84% rename from src/xrpld/app/tx/detail/AMMVote.cpp rename to src/libxrpl/tx/transactors/dex/AMMVote.cpp index 05b23233a4..2096eca0f0 100644 --- a/src/xrpld/app/tx/detail/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -1,10 +1,9 @@ -#include -#include - #include #include #include #include +#include +#include namespace xrpl { @@ -17,7 +16,8 @@ AMMVote::checkExtraFeatures(PreflightContext const& ctx) NotTEC AMMVote::preflight(PreflightContext const& ctx) { - if (auto const res = invalidAMMAssetPair(ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) + if (auto const res = + invalidAMMAssetPair(ctx.tx[sfAsset].get(), ctx.tx[sfAsset2].get())) { JLOG(ctx.j.debug()) << "AMM Vote: invalid asset pair."; return res; @@ -35,15 +35,18 @@ AMMVote::preflight(PreflightContext const& ctx) TER AMMVote::preclaim(PreclaimContext const& ctx) { - if (auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); !ammSle) + auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); + if (!ammSle) { JLOG(ctx.j.debug()) << "AMM Vote: Invalid asset pair."; return terNO_AMM; } - else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) + if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) + { return tecAMM_EMPTY; - else if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); - lpTokensNew == beast::zero) + } + if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); + lpTokensNew == beast::zero) { JLOG(ctx.j.debug()) << "AMM Vote: account is not LP."; return tecAMM_INVALID_TOKENS; @@ -100,13 +103,15 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou if (feeVal != 0) newEntry.setFieldU16(sfTradingFee, feeVal); newEntry.setFieldU32( - sfVoteWeight, static_cast(Number(lpTokens) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance)); + sfVoteWeight, + static_cast(Number(lpTokens) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance)); // Find an entry with the least tokens/fee. Make the order deterministic // if the tokens/fees are equal. if (!minTokens || (lpTokens < *minTokens || - (lpTokens == *minTokens && (feeVal < minFee || (feeVal == minFee && account < minAccount))))) + (lpTokens == *minTokens && + (feeVal < minFee || (feeVal == minFee && account < minAccount))))) { minTokens = lpTokens; minPos = updatedVoteSlots.size(); @@ -125,21 +130,28 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou newEntry.setFieldU16(sfTradingFee, feeNew); newEntry.setFieldU32( sfVoteWeight, - static_cast(Number(lpTokensNew) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance)); + static_cast( + Number(lpTokensNew) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance)); newEntry.setAccountID(sfAccount, account_); num += feeNew * lpTokensNew; den += lpTokensNew; if (minPos) + { *(updatedVoteSlots.begin() + *minPos) = std::move(newEntry); + } else + { updatedVoteSlots.push_back(std::move(newEntry)); + } }; // Add new entry if the number of the vote entries // is less than Max. if (updatedVoteSlots.size() < VOTE_MAX_SLOTS) + { update(); - // Add the entry if the account has more tokens than - // the least token holder or same tokens and higher fee. + // Add the entry if the account has more tokens than + // the least token holder or same tokens and higher fee. + } else if (lpTokensNew > *minTokens || (lpTokensNew == *minTokens && feeNew > minFee)) { auto const entry = updatedVoteSlots.begin() + minPos; @@ -170,9 +182,13 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou { auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot); if (auto const discountedFee = fee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION) + { auctionSlot.setFieldU16(sfDiscountedFee, discountedFee); + } else if (auctionSlot.isFieldPresent(sfDiscountedFee)) + { auctionSlot.makeFieldAbsent(sfDiscountedFee); + } } } else diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp similarity index 82% rename from src/xrpld/app/tx/detail/AMMWithdraw.cpp rename to src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 58d1e066d6..b822ff6b84 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -1,11 +1,10 @@ -#include -#include -#include - #include #include #include #include +#include +#include +#include namespace xrpl { @@ -18,7 +17,7 @@ AMMWithdraw::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMWithdraw::getFlagsMask(PreflightContext const& ctx) { - return tfWithdrawMask; + return tfAMMWithdrawMask; } NotTEC @@ -114,7 +113,8 @@ AMMWithdraw::preflight(PreflightContext const& ctx) if (amount2) { - if (auto const res = invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2)))) + if (auto const res = + invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2)))) { JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset2OutAmount"; return res; @@ -134,7 +134,10 @@ AMMWithdraw::preflight(PreflightContext const& ctx) } static std::optional -tokensWithdraw(STAmount const& lpTokens, std::optional const& tokensIn, std::uint32_t flags) +tokensWithdraw( + STAmount const& lpTokens, + std::optional const& tokensIn, + std::uint32_t flags) { if (flags & (tfWithdrawAll | tfOneAssetWithdrawAll)) return lpTokens; @@ -168,7 +171,8 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected; if (lptAMMBalance == beast::zero) return tecAMM_EMPTY; - if (amountBalance <= beast::zero || amount2Balance <= beast::zero || lptAMMBalance < beast::zero) + if (amountBalance <= beast::zero || amount2Balance <= beast::zero || + lptAMMBalance < beast::zero) { // LCOV_EXCL_START JLOG(ctx.j.debug()) << "AMM Withdraw: reserves or tokens balance is zero."; @@ -183,25 +187,28 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) { if (amount > balance) { - JLOG(ctx.j.debug()) << "AMM Withdraw: withdrawing more than the balance, " << *amount; + JLOG(ctx.j.debug()) + << "AMM Withdraw: withdrawing more than the balance, " << *amount; return tecAMM_BALANCE; } if (auto const ter = requireAuth(ctx.view, amount->issue(), accountID)) { - JLOG(ctx.j.debug()) << "AMM Withdraw: account is not authorized, " << amount->issue(); + JLOG(ctx.j.debug()) + << "AMM Withdraw: account is not authorized, " << amount->issue(); return ter; } // AMM account or currency frozen if (isFrozen(ctx.view, ammAccountID, amount->issue())) { - JLOG(ctx.j.debug()) << "AMM Withdraw: AMM account or currency is frozen, " << to_string(accountID); + JLOG(ctx.j.debug()) + << "AMM Withdraw: AMM account or currency is frozen, " << to_string(accountID); return tecFROZEN; } // Account frozen if (isIndividualFrozen(ctx.view, accountID, amount->issue())) { - JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, " << to_string(accountID) << " " - << to_string(amount->issue().currency); + JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, " << to_string(accountID) + << " " << to_string(amount->issue().currency); return tecFROZEN; } } @@ -266,7 +273,8 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (!accountSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); - auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); + auto const lpTokensWithdraw = + tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); // Due to rounding, the LPTokenBalance of the last LP // might not match the LP's trustline balance @@ -294,18 +302,43 @@ AMMWithdraw::applyGuts(Sandbox& sb) auto const [result, newLPTokenBalance] = [&, &amountBalance = amountBalance, &amount2Balance = amount2Balance, - &lptAMMBalance = lptAMMBalance]() -> std::pair { + &lptAMMBalance = + lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalWithdrawLimit( - sb, *ammSle, ammAccountID, amountBalance, amount2Balance, lptAMMBalance, *amount, *amount2, tfee); + sb, + *ammSle, + ammAccountID, + amountBalance, + amount2Balance, + lptAMMBalance, + *amount, + *amount2, + tfee); + } if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll) + { return singleWithdrawTokens( - sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *lpTokensWithdraw, tfee); + sb, + *ammSle, + ammAccountID, + amountBalance, + lptAMMBalance, + *amount, + *lpTokensWithdraw, + tfee); + } if (subTxType & tfLimitLPToken) + { return singleWithdrawEPrice( sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee); + } if (subTxType & tfSingleAsset) - return singleWithdraw(sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee); + { + return singleWithdraw( + sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee); + } if (subTxType & tfLPToken || subTxType & tfWithdrawAll) { return equalWithdrawTokens( @@ -326,18 +359,24 @@ AMMWithdraw::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return {result, false}; auto const res = deleteAMMAccountIfEmpty( - sb, ammSle, newLPTokenBalance, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); + sb, + ammSle, + newLPTokenBalance, + ctx_.tx[sfAsset].get(), + ctx_.tx[sfAsset2].get(), + j_); // LCOV_EXCL_START if (!res.second) return {res.first, false}; // LCOV_EXCL_STOP - JLOG(ctx_.journal.trace()) << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou()) << " " - << to_string(lpTokens.iou()) << " " << to_string(lptAMMBalance.iou()); + JLOG(ctx_.journal.trace()) << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou()) + << " " << to_string(lpTokens.iou()) << " " + << to_string(lptAMMBalance.iou()); return {tesSUCCESS, true}; } @@ -383,7 +422,7 @@ AMMWithdraw::withdraw( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, j_); return {ter, newLPTokenBalance}; } @@ -406,7 +445,8 @@ AMMWithdraw::withdraw( beast::Journal const& journal) { auto const lpTokens = ammLPHolds(view, ammSle, account, journal); - auto const expected = ammHolds(view, ammSle, amountWithdraw.issue(), std::nullopt, freezeHandling, journal); + auto const expected = + ammHolds(view, ammSle, amountWithdraw.issue(), std::nullopt, freezeHandling, journal); // LCOV_EXCL_START if (!expected) return {expected.error(), STAmount{}, STAmount{}, STAmount{}}; @@ -417,6 +457,7 @@ AMMWithdraw::withdraw( auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] = [&]() -> std::tuple, STAmount> { if (withdrawAll == WithdrawAll::No) + { return adjustAmountsByLPTokens( amountBalance, amountWithdraw, @@ -425,13 +466,15 @@ AMMWithdraw::withdraw( lpTokensWithdraw, tfee, IsDeposit::No); + } return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw); }(); if (lpTokensWithdrawActual <= beast::zero || lpTokensWithdrawActual > lpTokens) { - JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, invalid LP tokens: " << lpTokensWithdrawActual - << " " << lpTokens << " " << lpTokensAMMBalance; + JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, invalid LP tokens: " + << lpTokensWithdrawActual << " " << lpTokens << " " + << lpTokensAMMBalance; return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, STAmount{}}; } @@ -440,8 +483,9 @@ AMMWithdraw::withdraw( if (view.rules().enabled(fixAMMv1_1) && lpTokensWithdrawActual > lpTokensAMMBalance) { // LCOV_EXCL_START - JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, unexpected LP tokens: " << lpTokensWithdrawActual - << " " << lpTokens << " " << lpTokensAMMBalance; + JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, unexpected LP tokens: " + << lpTokensWithdrawActual << " " << lpTokens << " " + << lpTokensAMMBalance; return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}}; // LCOV_EXCL_STOP } @@ -452,7 +496,8 @@ AMMWithdraw::withdraw( { JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw one side of the pool " << " curBalance: " << curBalance << " " << amountWithdrawActual - << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " << lpTokensAMMBalance; + << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " + << lpTokensAMMBalance; return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}}; } @@ -463,7 +508,8 @@ AMMWithdraw::withdraw( JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw all tokens " << " curBalance: " << curBalance << " " << amountWithdrawActual << " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0}) - << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " << lpTokensAMMBalance; + << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " + << lpTokensAMMBalance; return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}}; } @@ -474,7 +520,8 @@ AMMWithdraw::withdraw( << " curBalance: " << curBalance << " " << amountWithdrawActual << " curBalance2: " << curBalance2 << " " << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{}) - << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " << lpTokensAMMBalance; + << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance " + << lpTokensAMMBalance; return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}}; } @@ -491,9 +538,10 @@ AMMWithdraw::withdraw( auto const balance = (*sleAccount)[sfBalance].xrp(); std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount); - // See also SetTrust::doApply() + // See also TrustSet::doApply() XRPAmount const reserve( - (ownerCount < 2) ? XRPAmount(beast::zero) : view.fees().accountReserve(ownerCount + 1)); + (ownerCount < 2) ? XRPAmount(beast::zero) + : view.fees().accountReserve(ownerCount + 1)); if (std::max(priorBalance, balance) < reserve) return tecINSUFFICIENT_RESERVE; @@ -505,8 +553,9 @@ AMMWithdraw::withdraw( return {err, STAmount{}, STAmount{}, STAmount{}}; // Withdraw amountWithdraw - auto res = accountSend(view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + auto res = accountSend( + view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes); + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual; @@ -517,11 +566,12 @@ AMMWithdraw::withdraw( // Withdraw amount2Withdraw if (amount2WithdrawActual) { - if (auto const err = sufficientReserve(amount2WithdrawActual->issue()); err != tesSUCCESS) + if (auto const err = sufficientReserve(amount2WithdrawActual->issue()); !isTesSuccess(err)) return {err, STAmount{}, STAmount{}, STAmount{}}; - res = accountSend(view, ammAccount, account, *amount2WithdrawActual, journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + res = accountSend( + view, ammAccount, account, *amount2WithdrawActual, journal, WaiveTransferFee::Yes); + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual; @@ -532,7 +582,7 @@ AMMWithdraw::withdraw( // Withdraw LP tokens res = redeemIOU(view, account, lpTokensWithdrawActual, lpTokensWithdrawActual.issue(), journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens"; @@ -541,7 +591,10 @@ AMMWithdraw::withdraw( } return std::make_tuple( - tesSUCCESS, lpTokensAMMBalance - lpTokensWithdrawActual, amountWithdrawActual, amount2WithdrawActual); + tesSUCCESS, + lpTokensAMMBalance - lpTokensWithdrawActual, + amountWithdrawActual, + amount2WithdrawActual); } static STAmount @@ -585,7 +638,7 @@ AMMWithdraw::equalWithdrawTokens( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, ctx_.journal); return {ter, newLPTokenBalance}; } @@ -604,10 +657,10 @@ AMMWithdraw::deleteAMMAccountIfEmpty( if (lpTokenBalance == beast::zero) { ter = deleteAMMAccount(sb, issue1, issue2, journal); - if (ter != tesSUCCESS && ter != tecINCOMPLETE) + if (!isTesSuccess(ter) && ter != tecINCOMPLETE) return {ter, false}; // LCOV_EXCL_LINE - else - updateBalance = (ter == tecINCOMPLETE); + + updateBalance = (ter == tecINCOMPLETE); } if (updateBalance) @@ -633,7 +686,7 @@ AMMWithdraw::equalWithdrawTokens( STAmount const& lpTokens, STAmount const& lpTokensWithdraw, std::uint16_t tfee, - FreezeHandling freezeHanding, + FreezeHandling freezeHandling, WithdrawAll withdrawAll, XRPAmount const& priorBalance, beast::Journal const& journal) @@ -654,19 +707,22 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, lpTokensWithdraw, tfee, - freezeHanding, + freezeHandling, WithdrawAll::Yes, priorBalance, journal); } - auto const tokensAdj = adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll); + auto const tokensAdj = + adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll); if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero) return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, std::nullopt}; // the adjusted tokens are factored in auto const frac = divide(tokensAdj, lptAMMBalance, noIssue()); - auto const amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No); - auto const amount2Withdraw = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No); + auto const amountWithdraw = + getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No); + auto const amount2Withdraw = + getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No); // LP is making equal withdrawal by tokens but the requested amount // of LP tokens is likely too small and results in one-sided pool // withdrawal due to round off. Fail so the user withdraws @@ -685,7 +741,7 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, tokensAdj, tfee, - freezeHanding, + freezeHandling, withdrawAll, priorBalance, journal); @@ -747,7 +803,15 @@ AMMWithdraw::equalWithdrawLimit( if (amount2Withdraw <= amount2) { return withdraw( - view, ammSle, ammAccount, amountBalance, amount, amount2Withdraw, lptAMMBalance, tokensAdj, tfee); + view, + ammSle, + ammAccount, + amountBalance, + amount, + amount2Withdraw, + lptAMMBalance, + tokensAdj, + tfee); } frac = Number{amount2} / amount2Balance; @@ -761,12 +825,25 @@ AMMWithdraw::equalWithdrawLimit( if (!view.rules().enabled(fixAMMv1_3)) { // LCOV_EXCL_START - XRPL_ASSERT(amountWithdraw <= amount, "xrpl::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw"); + XRPL_ASSERT( + amountWithdraw <= amount, + "xrpl::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw"); // LCOV_EXCL_STOP } else if (amountWithdraw > amount) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - return withdraw(view, ammSle, ammAccount, amountBalance, amountWithdraw, amount2, lptAMMBalance, tokensAdj, tfee); + } + return withdraw( + view, + ammSle, + ammAccount, + amountBalance, + amountWithdraw, + amount2, + lptAMMBalance, + tokensAdj, + tfee); } /** Withdraw single asset equivalent to the amount specified in Asset1Out. @@ -785,13 +862,18 @@ AMMWithdraw::singleWithdraw( std::uint16_t tfee) { auto const tokens = adjustLPTokensIn( - view.rules(), lptAMMBalance, lpTokensIn(amountBalance, amount, lptAMMBalance, tfee), isWithdrawAll(ctx_.tx)); + view.rules(), + lptAMMBalance, + lpTokensIn(amountBalance, amount, lptAMMBalance, tfee), + isWithdrawAll(ctx_.tx)); if (tokens == beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } // factor in the adjusted tokens auto const [tokensAdj, amountWithdrawAdj] = @@ -799,7 +881,15 @@ AMMWithdraw::singleWithdraw( if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero) return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE return withdraw( - view, ammSle, ammAccount, amountBalance, amountWithdrawAdj, std::nullopt, lptAMMBalance, tokensAdj, tfee); + view, + ammSle, + ammAccount, + amountBalance, + amountWithdrawAdj, + std::nullopt, + lptAMMBalance, + tokensAdj, + tfee); } /** withdrawal of single asset specified in Asset1Out proportional @@ -823,7 +913,8 @@ AMMWithdraw::singleWithdrawTokens( STAmount const& lpTokensWithdraw, std::uint16_t tfee) { - auto const tokensAdj = adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, isWithdrawAll(ctx_.tx)); + auto const tokensAdj = + adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, isWithdrawAll(ctx_.tx)); if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero) return {tecAMM_INVALID_TOKENS, STAmount{}}; // the adjusted tokens are factored in @@ -831,7 +922,15 @@ AMMWithdraw::singleWithdrawTokens( if (amount == beast::zero || amountWithdraw >= amount) { return withdraw( - view, ammSle, ammAccount, amountBalance, amountWithdraw, std::nullopt, lptAMMBalance, tokensAdj, tfee); + view, + ammSle, + ammAccount, + amountBalance, + amountWithdraw, + std::nullopt, + lptAMMBalance, + tokensAdj, + tfee); } return {tecAMM_FAILED, STAmount{}}; @@ -879,24 +978,38 @@ AMMWithdraw::singleWithdrawEPrice( // t = T*(T + A*E*(f - 2))/(T*f - A*E) Number const ae = amountBalance * ePrice; auto const f = getFee(tfee); - auto tokNoRoundCb = [&] { return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae); }; + auto tokNoRoundCb = [&] { + return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae); + }; auto tokProdCb = [&] { return (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae); }; - auto const tokensAdj = getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::No); + auto const tokensAdj = + getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::No); if (tokensAdj <= beast::zero) { if (!view.rules().enabled(fixAMMv1_3)) + { return {tecAMM_FAILED, STAmount{}}; - else - return {tecAMM_INVALID_TOKENS, STAmount{}}; + } + + return {tecAMM_INVALID_TOKENS, STAmount{}}; } auto amtNoRoundCb = [&] { return tokensAdj / ePrice; }; auto amtProdCb = [&] { return tokensAdj / ePrice; }; // the adjusted tokens are factored in - auto const amountWithdraw = getRoundedAsset(view.rules(), amtNoRoundCb, amount, amtProdCb, IsDeposit::No); + auto const amountWithdraw = + getRoundedAsset(view.rules(), amtNoRoundCb, amount, amtProdCb, IsDeposit::No); if (amount == beast::zero || amountWithdraw >= amount) { return withdraw( - view, ammSle, ammAccount, amountBalance, amountWithdraw, std::nullopt, lptAMMBalance, tokensAdj, tfee); + view, + ammSle, + ammAccount, + amountBalance, + amountWithdraw, + std::nullopt, + lptAMMBalance, + tokensAdj, + tfee); } return {tecAMM_FAILED, STAmount{}}; diff --git a/src/xrpld/app/tx/detail/CancelOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp similarity index 78% rename from src/xrpld/app/tx/detail/CancelOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCancel.cpp index 1dc9ad0bde..9d60347778 100644 --- a/src/xrpld/app/tx/detail/CancelOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp @@ -1,17 +1,17 @@ -#include - #include #include +#include #include +#include namespace xrpl { NotTEC -CancelOffer::preflight(PreflightContext const& ctx) +OfferCancel::preflight(PreflightContext const& ctx) { if (!ctx.tx[sfOfferSequence]) { - JLOG(ctx.j.trace()) << "CancelOffer::preflight: missing sequence"; + JLOG(ctx.j.trace()) << "OfferCancel::preflight: missing sequence"; return temBAD_SEQUENCE; } @@ -21,7 +21,7 @@ CancelOffer::preflight(PreflightContext const& ctx) //------------------------------------------------------------------------------ TER -CancelOffer::preclaim(PreclaimContext const& ctx) +OfferCancel::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; auto const offerSequence = ctx.tx[sfOfferSequence]; @@ -43,7 +43,7 @@ CancelOffer::preclaim(PreclaimContext const& ctx) //------------------------------------------------------------------------------ TER -CancelOffer::doApply() +OfferCancel::doApply() { auto const offerSequence = ctx_.tx[sfOfferSequence]; @@ -54,7 +54,7 @@ CancelOffer::doApply() if (auto sleOffer = view().peek(keylet::offer(account_, offerSequence))) { JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence; - return offerDelete(view(), sleOffer, ctx_.app.journal("View")); + return offerDelete(view(), sleOffer, ctx_.registry.journal("View")); } JLOG(j_.debug()) << "Offer #" << offerSequence << " can't be found."; diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp similarity index 88% rename from src/xrpld/app/tx/detail/CreateOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCreate.cpp index fab406189b..d52fa94877 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -1,20 +1,23 @@ -#include -#include -#include -#include - #include #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include namespace xrpl { TxConsequences -CreateOffer::makeTxConsequences(PreflightContext const& ctx) +OfferCreate::makeTxConsequences(PreflightContext const& ctx) { auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount { auto const& amount{tx[sfTakerGets]}; @@ -25,7 +28,7 @@ CreateOffer::makeTxConsequences(PreflightContext const& ctx) } bool -CreateOffer::checkExtraFeatures(PreflightContext const& ctx) +OfferCreate::checkExtraFeatures(PreflightContext const& ctx) { if (ctx.tx.isFieldPresent(sfDomainID) && !ctx.rules.enabled(featurePermissionedDEX)) return false; @@ -34,7 +37,7 @@ CreateOffer::checkExtraFeatures(PreflightContext const& ctx) } std::uint32_t -CreateOffer::getFlagsMask(PreflightContext const& ctx) +OfferCreate::getFlagsMask(PreflightContext const& ctx) { // The tfOfferCreateMask is built assuming that PermissionedDEX is // enabled @@ -46,7 +49,7 @@ CreateOffer::getFlagsMask(PreflightContext const& ctx) } NotTEC -CreateOffer::preflight(PreflightContext const& ctx) +OfferCreate::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -124,7 +127,7 @@ CreateOffer::preflight(PreflightContext const& ctx) } TER -CreateOffer::preclaim(PreclaimContext const& ctx) +OfferCreate::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -144,7 +147,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence); - auto viewJ = ctx.app.journal("View"); + auto viewJ = ctx.registry.journal("View"); if (isGlobalFrozen(ctx.view, uPaysIssuerID) || isGlobalFrozen(ctx.view, uGetsIssuerID)) { @@ -162,7 +165,8 @@ CreateOffer::preclaim(PreclaimContext const& ctx) // before the transaction sequence number. if (cancelSequence && (uAccountSequence <= *cancelSequence)) { - JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence << " uOfferSequence=" << *cancelSequence; + JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence + << " uOfferSequence=" << *cancelSequence; return temBAD_SEQUENCE; } @@ -176,8 +180,9 @@ CreateOffer::preclaim(PreclaimContext const& ctx) // Make sure that we are authorized to hold what the taker will pay us. if (!saTakerPays.native()) { - auto result = checkAcceptAsset(ctx.view, ctx.flags, id, ctx.j, Issue(uPaysCurrency, uPaysIssuerID)); - if (result != tesSUCCESS) + auto result = + checkAcceptAsset(ctx.view, ctx.flags, id, ctx.j, Issue(uPaysCurrency, uPaysIssuerID)); + if (!isTesSuccess(result)) return result; } @@ -193,7 +198,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) } TER -CreateOffer::checkAcceptAsset( +OfferCreate::checkAcceptAsset( ReadView const& view, ApplyFlags const flags, AccountID const id, @@ -201,19 +206,22 @@ CreateOffer::checkAcceptAsset( Issue const& issue) { // Only valid for custom currencies - XRPL_ASSERT(!isXRP(issue.currency), "xrpl::CreateOffer::checkAcceptAsset : input is not XRP"); + XRPL_ASSERT(!isXRP(issue.currency), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP"); auto const issuerAccount = view.read(keylet::account(issue.account)); if (!issuerAccount) { - JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: " << to_string(issue.account); + JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: " + << to_string(issue.account); return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER}; } + // An account cannot create a trustline to itself, so no line can exist + // to be frozen. Additionally, an issuer can always accept its own + // issuance. if (issue.account == id) - // An account can always accept its own issuance. return tesSUCCESS; if ((*issuerAccount)[sfFlags] & lsfRequireAuth) @@ -240,14 +248,6 @@ CreateOffer::checkAcceptAsset( } } - // An account can not create a trustline to itself, so no line can exist - // to be frozen. Additionally, an issuer can always accept its own - // issuance. - if (issue.account == id) - { - return tesSUCCESS; - } - auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency)); if (!trustLine) @@ -268,7 +268,7 @@ CreateOffer::checkAcceptAsset( } std::pair -CreateOffer::flowCross( +OfferCreate::flowCross( PaymentSandbox& psb, PaymentSandbox& psbCancel, Amounts const& takerAmount, @@ -282,7 +282,8 @@ CreateOffer::flowCross( // We check this in preclaim, but when selling XRP charged fees can // cause a user's available balance to go to 0 (by causing it to dip // below the reserve) so we check this case again. - STAmount const inStartBalance = accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_); + STAmount const inStartBalance = + accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_); if (inStartBalance <= beast::zero) { // The account balance can't cover even part of the offer. @@ -300,7 +301,8 @@ CreateOffer::flowCross( gatewayXferRate = transferRate(psb, sendMax.getIssuer()); if (gatewayXferRate.value != QUALITY_ONE) { - sendMax = multiplyRound(takerAmount.in, gatewayXferRate, takerAmount.in.issue(), true); + sendMax = + multiplyRound(takerAmount.in, gatewayXferRate, takerAmount.in.issue(), true); } } @@ -339,13 +341,18 @@ CreateOffer::flowCross( // specified. Since we don't know how much they might offer, // we allow delivery of the largest possible amount. if (deliver.native()) + { deliver = STAmount{STAmount::cMaxNative}; + } else + { // We can't use the maximum possible currency here because // there might be a gateway transfer rate to account for. // Since the transfer rate cannot exceed 200%, we use 1/2 // maxValue for our limit. - deliver = STAmount{takerAmount.out.issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset}; + deliver = STAmount{ + takerAmount.out.issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset}; + } } // Call the payment engine's flow() to do the actual work. @@ -377,7 +384,8 @@ CreateOffer::flowCross( auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged if (isTesSuccess(result.result())) { - STAmount const takerInBalance = accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_); + STAmount const takerInBalance = + accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_); if (takerInBalance <= beast::zero) { @@ -402,19 +410,24 @@ CreateOffer::flowCross( // gateway's transfer rate. STAmount nonGatewayAmountIn = result.actualAmountIn; if (gatewayXferRate.value != QUALITY_ONE) - nonGatewayAmountIn = - divideRound(result.actualAmountIn, gatewayXferRate, takerAmount.in.issue(), true); + { + nonGatewayAmountIn = divideRound( + result.actualAmountIn, gatewayXferRate, takerAmount.in.issue(), true); + } afterCross.in -= nonGatewayAmountIn; // It's possible that the divRound will cause our subtract // to go slightly negative. So limit afterCross.in to zero. if (afterCross.in < beast::zero) + { // We should verify that the difference *is* small, but // what is a good threshold to check? afterCross.in.clear(); + } - afterCross.out = divRoundStrict(afterCross.in, rate, takerAmount.out.issue(), false); + afterCross.out = + divRoundStrict(afterCross.in, rate, takerAmount.out.issue(), false); } else { @@ -422,7 +435,9 @@ CreateOffer::flowCross( // remaining output. This too preserves the offer // Quality. afterCross.out -= result.actualAmountOut; - XRPL_ASSERT(afterCross.out >= beast::zero, "xrpl::CreateOffer::flowCross : minimum offer"); + XRPL_ASSERT( + afterCross.out >= beast::zero, + "xrpl::OfferCreate::flowCross : minimum offer"); if (afterCross.out < beast::zero) afterCross.out.clear(); afterCross.in = mulRound(afterCross.out, rate, takerAmount.in.issue(), true); @@ -441,7 +456,7 @@ CreateOffer::flowCross( } std::string -CreateOffer::format_amount(STAmount const& amount) +OfferCreate::format_amount(STAmount const& amount) { std::string txt = amount.getText(); txt += "/"; @@ -450,7 +465,7 @@ CreateOffer::format_amount(STAmount const& amount) } TER -CreateOffer::applyHybrid( +OfferCreate::applyHybrid( Sandbox& sb, std::shared_ptr sleOffer, Keylet const& offerKey, @@ -489,14 +504,14 @@ CreateOffer::applyHybrid( bookArr.push_back(std::move(bookInfo)); if (!bookExists) - ctx_.app.getOrderBookDB().addOrderBook(book); + ctx_.registry.getOrderBookDB().addOrderBook(book); sleOffer->setFieldArray(sfAdditionalBooks, bookArr); return tesSUCCESS; } std::pair -CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) +OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) { using beast::zero; @@ -523,7 +538,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // end up on the books. auto uRate = getRate(saTakerGets, saTakerPays); - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); TER result = tesSUCCESS; @@ -554,7 +569,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) bool const bOpenLedger = sb.open(); bool crossed = false; - if (result == tesSUCCESS) + if (isTesSuccess(result)) { // If a tick size applies, round the offer to the tick size auto const& uPaysIssuerID = saTakerPays.getIssuer(); @@ -626,8 +641,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // We expect the implementation of cross to succeed // or give a tec. XRPL_ASSERT( - result == tesSUCCESS || isTecClaim(result), - "xrpl::CreateOffer::applyGuts : result is tesSUCCESS or " + isTesSuccess(result) || isTecClaim(result), + "xrpl::OfferCreate::applyGuts : result is tesSUCCESS or " "tecCLAIM"); if (auto stream = j_.trace()) @@ -640,16 +655,18 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) if (result == tecFAILED_PROCESSING && bOpenLedger) result = telFAILED_PROCESSING; - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); return {result, true}; } XRPL_ASSERT( - saTakerGets.issue() == place_offer.in.issue(), "xrpl::CreateOffer::applyGuts : taker gets issue match"); + saTakerGets.issue() == place_offer.in.issue(), + "xrpl::OfferCreate::applyGuts : taker gets issue match"); XRPL_ASSERT( - saTakerPays.issue() == place_offer.out.issue(), "xrpl::CreateOffer::applyGuts : taker pays issue match"); + saTakerPays.issue() == place_offer.out.issue(), + "xrpl::OfferCreate::applyGuts : taker pays issue match"); if (takerAmount != place_offer) crossed = true; @@ -678,9 +695,10 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) } XRPL_ASSERT( - saTakerPays > zero && saTakerGets > zero, "xrpl::CreateOffer::applyGuts : taker pays and gets positive"); + saTakerPays > zero && saTakerGets > zero, + "xrpl::OfferCreate::applyGuts : taker pays and gets positive"); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); return {result, true}; @@ -707,10 +725,12 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { JLOG(j_.trace()) << "Immediate or cancel: offer canceled"; if (!crossed) + { // Any ImmediateOrCancel offer that transfers absolutely no funds // returns tecKILLED rather than tesSUCCESS. Motivation for the // change is here: https://github.com/ripple/rippled/issues/4115 return {tecKILLED, false}; + } return {tesSUCCESS, true}; } @@ -721,7 +741,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { XRPAmount reserve = sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1); - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) { // If we are here, the signing account had an insufficient reserve // *prior* to our processing. If something actually crossed, then @@ -729,7 +749,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) if (!crossed) result = tecINSUF_RESERVE_OFFER; - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); } @@ -742,7 +762,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) auto const offer_index = keylet::offer(account_, offerSequence); // Add offer to owner's directory. - auto const ownerNode = sb.dirInsert(keylet::ownerDir(account_), offer_index, describeOwnerDir(account_)); + auto const ownerNode = + sb.dirInsert(keylet::ownerDir(account_), offer_index, describeOwnerDir(account_)); if (!ownerNode) { @@ -755,7 +776,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // Update owner count. adjustOwnerCount(sb, sleCreator, 1, viewJ); - JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : " << to_string(saTakerGets.issue()) + JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : " + << to_string(saTakerGets.issue()) << (domainID ? (" : " + to_string(*domainID)) : ""); Book const book{saTakerPays.issue(), saTakerGets.issue(), domainID}; @@ -817,15 +839,16 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // if it's a hybrid offer, set hybrid flag, and create an open dir if (bHybrid) { - auto const res = applyHybrid(sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir); - if (res != tesSUCCESS) + auto const res = + applyHybrid(sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir); + if (!isTesSuccess(res)) return {res, true}; // LCOV_EXCL_LINE } sb.insert(sleOffer); if (!bookExisted) - ctx_.app.getOrderBookDB().addOrderBook(book); + ctx_.registry.getOrderBookDB().addOrderBook(book); JLOG(j_.debug()) << "final result: success"; @@ -833,7 +856,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) } TER -CreateOffer::doApply() +OfferCreate::doApply() { // This is the ledger view that we work against. Transactions are applied // as we go on processing transactions. @@ -846,9 +869,13 @@ CreateOffer::doApply() auto const result = applyGuts(sb, sbCancel); if (result.second) + { sb.apply(ctx_.rawView()); + } else + { sbCancel.apply(ctx_.rawView()); + } return result.first; } diff --git a/src/xrpld/app/misc/PermissionedDEXHelpers.cpp b/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp similarity index 66% rename from src/xrpld/app/misc/PermissionedDEXHelpers.cpp rename to src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp index f88c2ab9d4..d857795e39 100644 --- a/src/xrpld/app/misc/PermissionedDEXHelpers.cpp +++ b/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp @@ -1,6 +1,5 @@ -#include - -#include +#include +#include namespace xrpl { namespace permissioned_dex { @@ -18,19 +17,25 @@ accountInDomain(ReadView const& view, AccountID const& account, Domain const& do auto const& credentials = sleDomain->getFieldArray(sfAcceptedCredentials); - bool const inDomain = std::any_of(credentials.begin(), credentials.end(), [&](auto const& credential) { - auto const sleCred = view.read(keylet::credential(account, credential[sfIssuer], credential[sfCredentialType])); - if (!sleCred || !sleCred->isFlag(lsfAccepted)) - return false; + bool const inDomain = + std::any_of(credentials.begin(), credentials.end(), [&](auto const& credential) { + auto const sleCred = view.read( + keylet::credential(account, credential[sfIssuer], credential[sfCredentialType])); + if (!sleCred || !sleCred->isFlag(lsfAccepted)) + return false; - return !credentials::checkExpired(sleCred, view.header().parentCloseTime); - }); + return !credentials::checkExpired(sleCred, view.header().parentCloseTime); + }); return inDomain; } bool -offerInDomain(ReadView const& view, uint256 const& offerID, Domain const& domainID, beast::Journal j) +offerInDomain( + ReadView const& view, + uint256 const& offerID, + Domain const& domainID, + beast::Journal j) { auto const sleOffer = view.read(keylet::offer(offerID)); diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp new file mode 100644 index 0000000000..0d5b63635f --- /dev/null +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +namespace xrpl { + +NotTEC +DIDDelete::preflight(PreflightContext const& ctx) +{ + return tesSUCCESS; +} + +TER +DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) +{ + auto const sle = ctx.view().peek(sleKeylet); + if (!sle) + return tecNO_ENTRY; + + return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal); +} + +TER +DIDDelete::deleteSLE( + ApplyView& view, + std::shared_ptr sle, + AccountID const owner, + beast::Journal j) +{ + // Remove object from owner directory + if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete DID from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + + auto const sleOwner = view.peek(keylet::account(owner)); + if (!sleOwner) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleOwner, -1, j); + view.update(sleOwner); + + // Remove object from ledger + view.erase(sle); + return tesSUCCESS; +} + +TER +DIDDelete::doApply() +{ + return deleteSLE(ctx_, keylet::did(account_), account_); +} + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp similarity index 69% rename from src/xrpld/app/tx/detail/DID.cpp rename to src/libxrpl/tx/transactors/did/DIDSet.cpp index 7219dcf60e..cd5c9bbc96 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -1,11 +1,11 @@ -#include - #include #include -#include +#include +#include #include #include #include +#include namespace xrpl { @@ -26,11 +26,13 @@ namespace xrpl { NotTEC DIDSet::preflight(PreflightContext const& ctx) { - if (!ctx.tx.isFieldPresent(sfURI) && !ctx.tx.isFieldPresent(sfDIDDocument) && !ctx.tx.isFieldPresent(sfData)) + if (!ctx.tx.isFieldPresent(sfURI) && !ctx.tx.isFieldPresent(sfDIDDocument) && + !ctx.tx.isFieldPresent(sfData)) return temEMPTY_DID; - if (ctx.tx.isFieldPresent(sfURI) && ctx.tx[sfURI].empty() && ctx.tx.isFieldPresent(sfDIDDocument) && - ctx.tx[sfDIDDocument].empty() && ctx.tx.isFieldPresent(sfData) && ctx.tx[sfData].empty()) + if (ctx.tx.isFieldPresent(sfURI) && ctx.tx[sfURI].empty() && + ctx.tx.isFieldPresent(sfDIDDocument) && ctx.tx[sfDIDDocument].empty() && + ctx.tx.isFieldPresent(sfData) && ctx.tx[sfData].empty()) return temEMPTY_DID; auto isTooLong = [&](auto const& sField, std::size_t length) -> bool { @@ -40,13 +42,13 @@ DIDSet::preflight(PreflightContext const& ctx) }; if (isTooLong(sfURI, maxDIDURILength) || isTooLong(sfDIDDocument, maxDIDDocumentLength) || - isTooLong(sfData, maxDIDAttestationLength)) + isTooLong(sfData, maxDIDDataLength)) return temMALFORMED; return tesSUCCESS; } -TER +static TER addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owner) { auto const sleAccount = ctx.view().peek(keylet::account(owner)); @@ -67,7 +69,8 @@ addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owne // Add ledger object to owner's page { - auto page = ctx.view().dirInsert(keylet::ownerDir(owner), sle->key(), describeOwnerDir(owner)); + auto page = + ctx.view().dirInsert(keylet::ownerDir(owner), sle->key(), describeOwnerDir(owner)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; @@ -102,7 +105,8 @@ DIDSet::doApply() update(sfDIDDocument); update(sfData); - if (!sleDID->isFieldPresent(sfURI) && !sleDID->isFieldPresent(sfDIDDocument) && !sleDID->isFieldPresent(sfData)) + if (!sleDID->isFieldPresent(sfURI) && !sleDID->isFieldPresent(sfDIDDocument) && + !sleDID->isFieldPresent(sfData)) { return tecEMPTY_DID; } @@ -131,50 +135,4 @@ DIDSet::doApply() return addSLE(ctx_, sleDID, account_); } -NotTEC -DIDDelete::preflight(PreflightContext const& ctx) -{ - return tesSUCCESS; -} - -TER -DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) -{ - auto const sle = ctx.view().peek(sleKeylet); - if (!sle) - return tecNO_ENTRY; - - return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal); -} - -TER -DIDDelete::deleteSLE(ApplyView& view, std::shared_ptr sle, AccountID const owner, beast::Journal j) -{ - // Remove object from owner directory - if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) - { - // LCOV_EXCL_START - JLOG(j.fatal()) << "Unable to delete DID Token from owner."; - return tefBAD_LEDGER; - // LCOV_EXCL_STOP - } - - auto const sleOwner = view.peek(keylet::account(owner)); - if (!sleOwner) - return tecINTERNAL; // LCOV_EXCL_LINE - - adjustOwnerCount(view, sleOwner, -1, j); - view.update(sleOwner); - - // Remove object from ledger - view.erase(sle); - return tesSUCCESS; -} - -TER -DIDDelete::doApply() -{ - return deleteSLE(ctx_, keylet::did(account_), account_); -} - } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp new file mode 100644 index 0000000000..ff0f933a2b --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +NotTEC +EscrowCancel::preflight(PreflightContext const& ctx) +{ + return tesSUCCESS; +} + +template +static TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount); + +template <> +TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecINTERNAL + if (issuer == account) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the issuer has requireAuth set, check if the account is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), account); !isTesSuccess(ter)) + return ter; + + return tesSUCCESS; +} + +template <> +TER +escrowCancelPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecINTERNAL + if (issuer == account) + return tecINTERNAL; // LCOV_EXCL_LINE + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the account is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + return tesSUCCESS; +} + +TER +EscrowCancel::preclaim(PreclaimContext const& ctx) +{ + if (ctx.view.rules().enabled(featureTokenEscrow)) + { + auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); + auto const slep = ctx.view.read(k); + if (!slep) + return tecNO_TARGET; + + AccountID const account = (*slep)[sfAccount]; + STAmount const amount = (*slep)[sfAmount]; + + if (!isXRP(amount)) + { + if (auto const ret = std::visit( + [&](T const&) { + return escrowCancelPreclaimHelper(ctx, account, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + } + return tesSUCCESS; +} + +TER +EscrowCancel::doApply() +{ + auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + auto const slep = ctx_.view().peek(k); + if (!slep) + { + if (ctx_.view().rules().enabled(featureTokenEscrow)) + return tecINTERNAL; // LCOV_EXCL_LINE + + return tecNO_TARGET; + } + + auto const now = ctx_.view().header().parentCloseTime; + + // No cancel time specified: can't execute at all. + if (!(*slep)[~sfCancelAfter]) + return tecNO_PERMISSION; + + // Too soon: can't execute before the cancel time. + if (!after(now, (*slep)[sfCancelAfter])) + return tecNO_PERMISSION; + + AccountID const account = (*slep)[sfAccount]; + + // Remove escrow from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove escrow from recipient's owner directory, if present. + if (auto const optPage = (*slep)[~sfDestinationNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + auto const sle = ctx_.view().peek(keylet::account(account)); + STAmount const amount = slep->getFieldAmount(sfAmount); + + // Transfer amount back to the owner + if (isXRP(amount)) + { + (*sle)[sfBalance] = (*sle)[sfBalance] + amount; + } + else + { + if (!ctx_.view().rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + auto const issuer = amount.getIssuer(); + bool const createAsset = account == account_; + if (auto const ret = std::visit( + [&](T const&) { + return escrowUnlockApplyHelper( + ctx_.view(), + parityRate, + slep, + preFeeBalance_, + amount, + issuer, + account, // sender and receiver are the same + account, + createAsset, + j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; // LCOV_EXCL_LINE + + // Remove escrow from issuers owner directory, if present. + if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + } + + auto const reserveToSubtract = calculateAdditionalReserve((*slep)[~sfFinishFunction]); + adjustOwnerCount(ctx_.view(), sle, -1 * reserveToSubtract, ctx_.journal); + ctx_.view().update(sle); + + // Remove escrow from ledger + ctx_.view().erase(slep); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp new file mode 100644 index 0000000000..e111114e7e --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -0,0 +1,604 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +/* + Escrow + ====== + + Escrow is a feature of the XRP Ledger that allows you to send conditional + XRP payments. These conditional payments, called escrows, set aside XRP and + deliver it later when certain conditions are met. Conditions to successfully + finish an escrow include time-based unlocks and crypto-conditions. Escrows + can also be set to expire if not finished in time. + + The XRP set aside in an escrow is locked up. No one can use or destroy the + XRP until the escrow has been successfully finished or canceled. Before the + expiration time, only the intended receiver can get the XRP. After the + expiration time, the XRP can only be returned to the sender. + + For more details on escrow, including examples, diagrams and more please + visit https://xrpl.org/escrow.html + + For details on specific transactions, including fields and validation rules + please see: + + `EscrowCreate` + -------------- + See: https://xrpl.org/escrowcreate.html + + `EscrowFinish` + -------------- + See: https://xrpl.org/escrowfinish.html + + `EscrowCancel` + -------------- + See: https://xrpl.org/escrowcancel.html +*/ + +//------------------------------------------------------------------------------ + +TxConsequences +EscrowCreate::makeTxConsequences(PreflightContext const& ctx) +{ + auto const amount = ctx.tx[sfAmount]; + return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::zero}; +} + +template +static NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx); + +template <> +NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx) +{ + STAmount const amount = ctx.tx[sfAmount]; + if (amount.native() || amount <= beast::zero) + return temBAD_AMOUNT; + + if (badCurrency() == amount.getCurrency()) + return temBAD_CURRENCY; + + return tesSUCCESS; +} + +template <> +NotTEC +escrowCreatePreflightHelper(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureMPTokensV1)) + return temDISABLED; + + auto const amount = ctx.tx[sfAmount]; + if (amount.native() || amount.mpt() > MPTAmount{maxMPTokenAmount} || amount <= beast::zero) + return temBAD_AMOUNT; + + return tesSUCCESS; +} + +XRPAmount +EscrowCreate::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + XRPAmount txnFees{Transactor::calculateBaseFee(view, tx)}; + if (tx.isFieldPresent(sfFinishFunction)) + { + // 10 base fees for the transaction (1 is in + // `Transactor::calculateBaseFee`), plus 5 drops per byte + txnFees += 9 * view.fees().base + 5 * tx[sfFinishFunction].size(); + } + return txnFees; +} + +bool +EscrowCreate::checkExtraFeatures(PreflightContext const& ctx) +{ + if ((ctx.tx.isFieldPresent(sfFinishFunction) || ctx.tx.isFieldPresent(sfData)) && + !ctx.rules.enabled(featureSmartEscrow)) + return false; + + return true; +} + +NotTEC +EscrowCreate::preflight(PreflightContext const& ctx) +{ + STAmount const amount{ctx.tx[sfAmount]}; + if (!isXRP(amount)) + { + if (!ctx.rules.enabled(featureTokenEscrow)) + return temBAD_AMOUNT; + + if (auto const ret = std::visit( + [&](T const&) { return escrowCreatePreflightHelper(ctx); }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + else + { + if (amount <= beast::zero) + return temBAD_AMOUNT; + } + + // We must specify at least one timeout value + if (!ctx.tx[~sfCancelAfter] && !ctx.tx[~sfFinishAfter]) + return temBAD_EXPIRATION; + + // If both finish and cancel times are specified then the cancel time must + // be strictly after the finish time. + if (ctx.tx[~sfCancelAfter] && ctx.tx[~sfFinishAfter] && + ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter]) + return temBAD_EXPIRATION; + + if (ctx.tx.isFieldPresent(sfFinishFunction) && !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] && !ctx.tx[~sfFinishFunction]) + { + JLOG(ctx.j.debug()) << "Must have at least one of FinishAfter, " + "Condition, or FinishFunction."; + return temMALFORMED; + } + + if (auto const cb = ctx.tx[~sfCondition]) + { + using namespace xrpl::cryptoconditions; + + std::error_code ec; + + auto condition = Condition::deserialize(*cb, ec); + if (!condition) + { + JLOG(ctx.j.debug()) << "Malformed condition during escrow creation: " << ec.message(); + return temMALFORMED; + } + } + + if (ctx.tx.isFieldPresent(sfData)) + { + if (!ctx.tx.isFieldPresent(sfFinishFunction)) + { + JLOG(ctx.j.debug()) << "EscrowCreate with Data requires FinishFunction"; + return temMALFORMED; + } + auto const data = ctx.tx.getFieldVL(sfData); + if (data.size() > maxWasmDataLength) + { + JLOG(ctx.j.debug()) << "EscrowCreate.Data bad size " << data.size(); + return temMALFORMED; + } + } + + if (ctx.tx.isFieldPresent(sfFinishFunction)) + { + auto const fees(ctx.registry.getFees()); + if (fees.extensionSizeLimit == 0 || fees.extensionComputeLimit == 0) + { + JLOG(ctx.j.debug()) << "WASM runtime deactivated by fee voting"; + return temTEMP_DISABLED; + } + + auto const code = ctx.tx.getFieldVL(sfFinishFunction); + if (code.size() == 0 || code.size() > fees.extensionSizeLimit) + { + JLOG(ctx.j.debug()) << "EscrowCreate.FinishFunction 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(sfFinishFunction)) + { + auto const code = ctx.tx.getFieldVL(sfFinishFunction); + // basic checks happen in `preflight` + + HostFunctions mock(ctx.j); + auto const re = preflightEscrowWasm(code, mock, ESCROW_FUNCTION_NAME); + if (!isTesSuccess(re)) + { + JLOG(ctx.j.debug()) << "EscrowCreate.FinishFunction bad WASM"; + return re; + } + } + + return tesSUCCESS; +} + +template +static TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount); + +template <> +TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecNO_PERMISSION + if (issuer == account) + return tecNO_PERMISSION; + + // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION + auto const sleIssuer = ctx.view.read(keylet::account(issuer)); + if (!sleIssuer) + return tecNO_ISSUER; + if (!sleIssuer->isFlag(lsfAllowTrustLineLocking)) + return tecNO_PERMISSION; + + // If the account does not have a trustline to the issuer, return tecNO_LINE + auto const sleRippleState = ctx.view.read(keylet::line(account, issuer, amount.getCurrency())); + if (!sleRippleState) + return tecNO_LINE; + + STAmount const balance = (*sleRippleState)[sfBalance]; + + // If balance is positive, issuer must have higher address than account + if (balance > beast::zero && issuer < account) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If balance is negative, issuer must have lower address than account + if (balance < beast::zero && issuer > account) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If the issuer has requireAuth set, check if the account is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), account); !isTesSuccess(ter)) + return ter; + + // If the issuer has requireAuth set, check if the destination is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the account, return tecFROZEN + if (isFrozen(ctx.view, account, amount.issue())) + return tecFROZEN; + + // If the issuer has frozen the destination, return tecFROZEN + if (isFrozen(ctx.view, dest, amount.issue())) + return tecFROZEN; + + STAmount const spendableAmount = + accountHolds(ctx.view, account, amount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j); + + // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS + if (spendableAmount <= beast::zero) + return tecINSUFFICIENT_FUNDS; + + // If the spendable amount is less than the amount, return + // tecINSUFFICIENT_FUNDS + if (spendableAmount < amount) + return tecINSUFFICIENT_FUNDS; + + // If the amount is not addable to the balance, return tecPRECISION_LOSS + if (!canAdd(spendableAmount, amount)) + return tecPRECISION_LOSS; + + return tesSUCCESS; +} + +template <> +TER +escrowCreatePreclaimHelper( + PreclaimContext const& ctx, + AccountID const& account, + AccountID const& dest, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tecNO_PERMISSION + if (issuer == account) + return tecNO_PERMISSION; + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION + if (!sleIssuance->isFlag(lsfMPTCanEscrow)) + return tecNO_PERMISSION; + + // If the issuer is not the same as the issuer of the mpt, return + // tecNO_PERMISSION + if (sleIssuance->getAccountID(sfIssuer) != issuer) + return tecNO_PERMISSION; // LCOV_EXCL_LINE + + // If the account does not have the mpt, return tecOBJECT_NOT_FOUND + if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account))) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the account is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has requireAuth set, check if the destination is + // authorized + if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the account, return tecLOCKED + if (isFrozen(ctx.view, account, mptIssue)) + return tecLOCKED; + + // If the issuer has frozen the destination, return tecLOCKED + if (isFrozen(ctx.view, dest, mptIssue)) + return tecLOCKED; + + // If the mpt cannot be transferred, return tecNO_AUTH + if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest); !isTesSuccess(ter)) + return ter; + + STAmount const spendableAmount = accountHolds( + ctx.view, account, amount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j); + + // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS + if (spendableAmount <= beast::zero) + return tecINSUFFICIENT_FUNDS; + + // If the spendable amount is less than the amount, return + // tecINSUFFICIENT_FUNDS + if (spendableAmount < amount) + return tecINSUFFICIENT_FUNDS; + + return tesSUCCESS; +} + +TER +EscrowCreate::preclaim(PreclaimContext const& ctx) +{ + STAmount const amount{ctx.tx[sfAmount]}; + AccountID const account{ctx.tx[sfAccount]}; + AccountID const dest{ctx.tx[sfDestination]}; + + auto const sled = ctx.view.read(keylet::account(dest)); + if (!sled) + return tecNO_DST; + + // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated + // because all writes to pseudo-account discriminator fields **are** + // amendment gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) + return tecNO_PERMISSION; + + if (!isXRP(amount)) + { + if (!ctx.view.rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + if (auto const ret = std::visit( + [&](T const&) { + return escrowCreatePreclaimHelper(ctx, account, dest, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + return tesSUCCESS; +} + +template +static TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal); + +template <> +TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal) +{ + // Defensive: Issuer cannot create an escrow + if (issuer == sender) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const ter = rippleCredit( + view, sender, issuer, amount, amount.holds() ? false : true, journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + return tesSUCCESS; +} + +template <> +TER +escrowLockApplyHelper( + ApplyView& view, + AccountID const& issuer, + AccountID const& sender, + STAmount const& amount, + beast::Journal journal) +{ + // Defensive: Issuer cannot create an escrow + if (issuer == sender) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const ter = rippleLockEscrowMPT(view, sender, amount, journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + return tesSUCCESS; +} + +template +static uint32_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); +} + +TER +EscrowCreate::doApply() +{ + auto const closeTime = ctx_.view().header().parentCloseTime; + + if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) + return tecNO_PERMISSION; + + if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter])) + return tecNO_PERMISSION; + + auto const sle = ctx_.view().peek(keylet::account(account_)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // Check reserve and funds availability + STAmount const amount{ctx_.tx[sfAmount]}; + auto const reserveToAdd = calculateAdditionalReserve(ctx_.tx[~sfFinishFunction]); + + auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + reserveToAdd); + + auto const balance = sle->getFieldAmount(sfBalance).xrp(); + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + // Check reserve and funds availability + if (isXRP(amount)) + { + if (balance < reserve + STAmount(amount).xrp()) + return tecUNFUNDED; + } + + // Check destination account + { + auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination])); + if (!sled) + return tecNO_DST; // LCOV_EXCL_LINE + if (((*sled)[sfFlags] & lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag]) + return tecDST_TAG_NEEDED; + } + + // Create escrow in ledger. Note that we we use the value from the + // sequence or ticket. For more explanation see comments in SeqProxy.h. + Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue()); + auto const slep = std::make_shared(escrowKeylet); + (*slep)[sfAmount] = amount; + (*slep)[sfAccount] = account_; + (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; + (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; + (*slep)[sfDestination] = ctx_.tx[sfDestination]; + (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; + (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; + (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + (*slep)[~sfFinishFunction] = ctx_.tx[~sfFinishFunction]; + (*slep)[~sfData] = ctx_.tx[~sfData]; + + if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) + { + (*slep)[sfSequence] = ctx_.tx.getSeqValue(); + } + + if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount)) + { + auto const xferRate = transferRate(ctx_.view(), amount); + if (xferRate != parityRate) + (*slep)[sfTransferRate] = xferRate.value; + } + + ctx_.view().insert(slep); + + // Add escrow to sender's owner directory + { + auto page = ctx_.view().dirInsert( + keylet::ownerDir(account_), escrowKeylet, describeOwnerDir(account_)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfOwnerNode] = *page; + } + + // If it's not a self-send, add escrow to recipient's owner directory. + AccountID const dest = ctx_.tx[sfDestination]; + if (dest != account_) + { + auto page = + ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfDestinationNode] = *page; + } + + // IOU escrow objects are added to the issuer's owner directory to help + // track the total locked balance. For MPT, this isn't necessary because the + // locked balance is already stored directly in the MPTokenIssuance object. + AccountID const issuer = amount.getIssuer(); + if (!isXRP(amount) && issuer != account_ && issuer != dest && !amount.holds()) + { + auto page = + ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfIssuerNode] = *page; + } + + // Deduct owner's balance + if (isXRP(amount)) + { + (*sle)[sfBalance] = (*sle)[sfBalance] - amount; + } + else + { + if (auto const ret = std::visit( + [&](T const&) { + return escrowLockApplyHelper(ctx_.view(), issuer, account_, amount, j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + { + return ret; // LCOV_EXCL_LINE + } + } + + // increment owner count + adjustOwnerCount(ctx_.view(), sle, reserveToAdd, ctx_.journal); + ctx_.view().update(sle); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp new file mode 100644 index 0000000000..7a8112b41d --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +// During an EscrowFinish, the transaction must specify both +// a condition and a fulfillment. We track whether that +// fulfillment matches and validates the condition. +constexpr HashRouterFlags SF_CF_INVALID = HashRouterFlags::PRIVATE5; +constexpr HashRouterFlags SF_CF_VALID = HashRouterFlags::PRIVATE6; + +//------------------------------------------------------------------------------ + +static bool +checkCondition(Slice f, Slice c) +{ + using namespace xrpl::cryptoconditions; + + std::error_code ec; + + auto condition = Condition::deserialize(c, ec); + if (!condition) + return false; + + auto fulfillment = Fulfillment::deserialize(f, ec); + if (!fulfillment) + return false; + + return validate(*fulfillment, *condition); +} + +bool +EscrowFinish::checkExtraFeatures(PreflightContext const& ctx) +{ + if (ctx.tx.isFieldPresent(sfCredentialIDs) && !ctx.rules.enabled(featureCredentials)) + return false; + + if (ctx.tx.isFieldPresent(sfComputationAllowance) && !ctx.rules.enabled(featureSmartEscrow)) + { + return false; + } + return true; +} + +NotTEC +EscrowFinish::preflight(PreflightContext const& ctx) +{ + auto const cb = ctx.tx[~sfCondition]; + auto const fb = ctx.tx[~sfFulfillment]; + + // If you specify a condition, then you must also specify + // a fulfillment. + if (static_cast(cb) != static_cast(fb)) + { + JLOG(ctx.j.debug()) << "Condition != Fulfillment"; + return temMALFORMED; + } + + if (auto const allowance = ctx.tx[~sfComputationAllowance]; allowance) + { + auto const fees(ctx.registry.getFees()); + if (fees.extensionComputeLimit == 0) + { + JLOG(ctx.j.debug()) << "WASM runtime deactivated by fee voting"; + return temTEMP_DISABLED; + } + if (*allowance == 0) + { + return temBAD_LIMIT; + } + if (*allowance > fees.extensionComputeLimit) + { + JLOG(ctx.j.debug()) << "ComputationAllowance too large: " << *allowance; + return temBAD_LIMIT; + } + } + + if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +NotTEC +EscrowFinish::preflightSigValidated(PreflightContext const& ctx) +{ + auto const cb = ctx.tx[~sfCondition]; + auto const fb = ctx.tx[~sfFulfillment]; + + if (cb && fb) + { + auto& router = ctx.registry.getHashRouter(); + + auto const id = ctx.tx.getTransactionID(); + auto const flags = router.getFlags(id); + + // If we haven't checked the condition, check it + // now. Whether it passes or not isn't important + // in preflight. + if (!any(flags & (SF_CF_INVALID | SF_CF_VALID))) + { + if (checkCondition(*fb, *cb)) + { + router.setFlags(id, SF_CF_VALID); + } + else + { + router.setFlags(id, SF_CF_INVALID); + } + } + } + + return tesSUCCESS; +} + +XRPAmount +EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + XRPAmount extraFee{0}; + + if (auto const fb = tx[~sfFulfillment]) + { + extraFee += view.fees().base * (32 + (fb->size() / 16)); + } + if (std::optional const allowance = tx[~sfComputationAllowance]; 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) / MICRO_DROPS_PER_DROP + 1; + extraFee += allowanceFee; + } + return Transactor::calculateBaseFee(view, tx) + extraFee; +} + +template +static TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount); + +template <> +TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the account, return tesSUCCESS + if (issuer == dest) + return tesSUCCESS; + + // If the issuer has requireAuth set, check if the destination is authorized + if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); !isTesSuccess(ter)) + return ter; + + // If the issuer has deep frozen the destination, return tecFROZEN + if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer())) + return tecFROZEN; + + return tesSUCCESS; +} + +template <> +TER +escrowFinishPreclaimHelper( + PreclaimContext const& ctx, + AccountID const& dest, + STAmount const& amount) +{ + AccountID issuer = amount.getIssuer(); + // If the issuer is the same as the dest, return tesSUCCESS + if (issuer == dest) + return tesSUCCESS; + + // If the mpt does not exist, return tecOBJECT_NOT_FOUND + auto const issuanceKey = keylet::mptIssuance(amount.get().getMptID()); + auto const sleIssuance = ctx.view.read(issuanceKey); + if (!sleIssuance) + return tecOBJECT_NOT_FOUND; + + // If the issuer has requireAuth set, check if the destination is + // authorized + auto const& mptIssue = amount.get(); + if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth); + !isTesSuccess(ter)) + return ter; + + // If the issuer has frozen the destination, return tecLOCKED + if (isFrozen(ctx.view, dest, mptIssue)) + return tecLOCKED; + + return tesSUCCESS; +} + +TER +EscrowFinish::preclaim(PreclaimContext const& ctx) +{ + if (ctx.view.rules().enabled(featureCredentials)) + { + if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); + !isTesSuccess(err)) + return err; + } + + if (ctx.view.rules().enabled(featureTokenEscrow) || + ctx.view.rules().enabled(featureSmartEscrow)) + { + // this check is done in doApply before this amendment is enabled + auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); + auto const slep = ctx.view.read(k); + if (!slep) + return tecNO_TARGET; + + if (ctx.view.rules().enabled(featureSmartEscrow)) + { + if (slep->isFieldPresent(sfFinishFunction)) + { + if (!ctx.tx.isFieldPresent(sfComputationAllowance)) + { + JLOG(ctx.j.debug()) << "FinishFunction requires ComputationAllowance"; + return tefWASM_FIELD_NOT_INCLUDED; + } + } + else + { + if (ctx.tx.isFieldPresent(sfComputationAllowance)) + { + JLOG(ctx.j.debug()) << "FinishFunction not present, " + "ComputationAllowance present"; + return tefNO_WASM; + } + } + } + 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( + [&](T const&) { + return escrowFinishPreclaimHelper(ctx, dest, amount); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + } + } + } + return tesSUCCESS; +} + +TER +EscrowFinish::doApply() +{ + auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + auto const slep = ctx_.view().peek(k); + if (!slep) + { + if (ctx_.view().rules().enabled(featureTokenEscrow) || + ctx_.view().rules().enabled(featureSmartEscrow)) + return tecINTERNAL; // LCOV_EXCL_LINE + + return tecNO_TARGET; + } + + // If a cancel time is present, a finish operation should only succeed prior + // to that time. + auto const now = ctx_.view().header().parentCloseTime; + + // Too soon: can't execute before the finish time + if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) + return tecNO_PERMISSION; + + // Too late: can't execute after the cancel time + 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(), account_, destID, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + } + + // Check cryptocondition fulfillment + { + auto const id = ctx_.tx.getTransactionID(); + auto flags = ctx_.registry.getHashRouter().getFlags(id); + + auto const cb = ctx_.tx[~sfCondition]; + + // It's unlikely that the results of the check will + // expire from the hash router, but if it happens, + // simply re-run the check. + if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID))) + { + // LCOV_EXCL_START + auto const fb = ctx_.tx[~sfFulfillment]; + + if (!fb) + return tecINTERNAL; + + if (checkCondition(*fb, *cb)) + { + flags = SF_CF_VALID; + } + else + { + flags = SF_CF_INVALID; + } + + ctx_.registry.getHashRouter().setFlags(id, flags); + // LCOV_EXCL_STOP + } + + // If the check failed, then simply return an error + // and don't look at anything else. + if (any(flags & SF_CF_INVALID)) + return tecCRYPTOCONDITION_ERROR; + + // Check against condition in the ledger entry: + auto const cond = (*slep)[~sfCondition]; + + // If a condition wasn't specified during creation, + // one shouldn't be included now. + if (!cond && cb) + return tecCRYPTOCONDITION_ERROR; + + // If a condition was specified during creation of + // the suspended payment, the identical condition + // must be presented again. We don't check if the + // fulfillment matches the condition since we did + // that in preflight. + if (cond && (cond != cb)) + return tecCRYPTOCONDITION_ERROR; + } + + 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(), account_, destID, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + } + + // Execute custom release function + if ((*slep)[~sfFinishFunction]) + { + JLOG(j_.trace()) << "The escrow has a finish function, running WASM code..."; + // WASM execution + auto const wasmStr = slep->getFieldVL(sfFinishFunction); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + + WasmHostFunctionsImpl ledgerDataProvider(ctx_, k); + + if (!ctx_.tx.isFieldPresent(sfComputationAllowance)) + { + // already checked above, this check is just in case + return tecINTERNAL; + } + std::uint32_t const allowance = ctx_.tx[sfComputationAllowance]; + auto re = runEscrowWasm(wasm, ledgerDataProvider, allowance, ESCROW_FUNCTION_NAME); + JLOG(j_.trace()) << "Escrow WASM ran"; + + if (auto const& data = ledgerDataProvider.getData(); data.has_value()) + { + if (data->size() > maxWasmDataLength) + { + // should already be checked in the updateData host function + return tecINTERNAL; // LCOV_EXCL_LINE + } + slep->setFieldVL(sfData, makeSlice(*data)); + ctx_.view().update(slep); + } + + if (re.has_value()) + { + auto const reValue = re.value().result; + auto const reCost = re.value().cost; + JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) << ", cost: " << reCost; + + ctx_.setWasmReturnCode(reValue); + + if (reCost < 0 || reCost > std::numeric_limits::max()) + return tecINTERNAL; // LCOV_EXCL_LINE + ctx_.setGasUsed(static_cast(reCost)); + + if (reValue <= 0) + { + return tecWASM_REJECTED; + } + } + else + { + JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error()); + return re.error(); + } + } + + AccountID const account = (*slep)[sfAccount]; + + // Remove escrow from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove escrow from recipient's owner directory, if present. + if (auto const optPage = (*slep)[~sfDestinationNode]) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + STAmount const amount = slep->getFieldAmount(sfAmount); + // Transfer amount to destination + if (isXRP(amount)) + { + (*sled)[sfBalance] = (*sled)[sfBalance] + amount; + } + else + { + if (!ctx_.view().rules().enabled(featureTokenEscrow)) + return temDISABLED; // LCOV_EXCL_LINE + + Rate lockedRate = slep->isFieldPresent(sfTransferRate) + ? xrpl::Rate(slep->getFieldU32(sfTransferRate)) + : parityRate; + auto const issuer = amount.getIssuer(); + bool const createAsset = destID == account_; + if (auto const ret = std::visit( + [&](T const&) { + return escrowUnlockApplyHelper( + ctx_.view(), + lockedRate, + sled, + preFeeBalance_, + amount, + issuer, + account, + destID, + createAsset, + j_); + }, + amount.asset().value()); + !isTesSuccess(ret)) + return ret; + + // Remove escrow from issuers owner directory, if present. + if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) + { + if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + } + + ctx_.view().update(sled); + + auto const reserveToSubtract = calculateAdditionalReserve((*slep)[~sfFinishFunction]); + + // Adjust source owner count + auto const sle = ctx_.view().peek(keylet::account(account)); + adjustOwnerCount(ctx_.view(), sle, -1 * reserveToSubtract, ctx_.journal); + ctx_.view().update(sle); + + // Remove escrow from ledger + ctx_.view().erase(slep); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h new file mode 100644 index 0000000000..5fdc43c359 --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h @@ -0,0 +1,232 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal); + +template <> +inline TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal) +{ + Keylet const trustLineKey = keylet::line(receiver, amount.issue()); + bool const recvLow = issuer > receiver; + bool const senderIssuer = issuer == sender; + bool const receiverIssuer = issuer == receiver; + + if (senderIssuer) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (receiverIssuer) + return tesSUCCESS; + + if (!view.exists(trustLineKey) && createAsset) + { + // Can the account cover the trust line's reserve? + if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; + xrpBalance < view.fees().accountReserve(ownerCount + 1)) + { + JLOG(journal.trace()) << "Trust line does not exist. " + "Insufficient reserve to create line."; + + return tecNO_LINE_INSUF_RESERVE; + } + + Currency const currency = amount.getCurrency(); + STAmount initialBalance(amount.issue()); + initialBalance.setIssuer(noAccount()); + + // clang-format off + if (TER const ter = trustCreate( + view, // payment sandbox + recvLow, // is dest low? + issuer, // source + receiver, // destination + trustLineKey.key, // ledger index + sleDest, // Account to add to + false, // authorize account + (sleDest->getFlags() & lsfDefaultRipple) == 0, + false, // freeze trust line + false, // deep freeze trust line + initialBalance, // zero initial balance + Issue(currency, receiver), // limit of zero + 0, // quality in + 0, // quality out + journal); // journal + !isTesSuccess(ter)) + { + return ter; // LCOV_EXCL_LINE + } + // clang-format on + + view.update(sleDest); + } + + if (!view.exists(trustLineKey) && !receiverIssuer) + return tecNO_LINE; + + auto const xferRate = transferRate(view, amount); + // update if issuer rate is less than locked rate + if (xferRate < lockedRate) + lockedRate = xferRate; + + // Transfer Rate only applies when: + // 1. Issuer is not involved in the transfer (senderIssuer or + // receiverIssuer) + // 2. The locked rate is different from the parity rate + + // NOTE: Transfer fee in escrow works a bit differently from a normal + // payment. In escrow, the fee is deducted from the locked/sending amount, + // whereas in a normal payment, the transfer fee is taken on top of the + // sending amount. + auto finalAmt = amount; + if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) + { + // compute transfer fee, if any + auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.issue(), true); + // compute balance to transfer + finalAmt = amount.value() - xferFee; + } + + // validate the line limit if the account submitting txn is not the receiver + // of the funds + if (!createAsset) + { + auto const sleRippleState = view.peek(trustLineKey); + if (!sleRippleState) + return tecINTERNAL; // LCOV_EXCL_LINE + + // if the issuer is the high, then we use the low limit + // otherwise we use the high limit + STAmount const lineLimit = + sleRippleState->getFieldAmount(recvLow ? sfLowLimit : sfHighLimit); + + STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance); + + // flip the sign of the line balance if the issuer is not high + if (!recvLow) + lineBalance.negate(); + + // add the final amount to the line balance + lineBalance += finalAmt; + + // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED + if (lineLimit < lineBalance) + return tecLIMIT_EXCEEDED; + } + + // if destination is not the issuer then transfer funds + if (!receiverIssuer) + { + auto const ter = rippleCredit(view, issuer, receiver, finalAmt, true, journal); + if (!isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + return tesSUCCESS; +} + +template <> +inline TER +escrowUnlockApplyHelper( + ApplyView& view, + Rate lockedRate, + std::shared_ptr const& sleDest, + STAmount const& xrpBalance, + STAmount const& amount, + AccountID const& issuer, + AccountID const& sender, + AccountID const& receiver, + bool createAsset, + beast::Journal journal) +{ + bool const senderIssuer = issuer == sender; + bool const receiverIssuer = issuer == receiver; + + auto const mptID = amount.get().getMptID(); + auto const issuanceKey = keylet::mptIssuance(mptID); + if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) + { + if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; + xrpBalance < view.fees().accountReserve(ownerCount + 1)) + { + return tecINSUFFICIENT_RESERVE; + } + + if (auto const ter = MPTokenAuthorize::createMPToken(view, mptID, receiver, 0); + !isTesSuccess(ter)) + { + return ter; // LCOV_EXCL_LINE + } + + // update owner count. + adjustOwnerCount(view, sleDest, 1, journal); + } + + if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) + return tecNO_PERMISSION; + + auto const xferRate = transferRate(view, amount); + // update if issuer rate is less than locked rate + if (xferRate < lockedRate) + lockedRate = xferRate; + + // Transfer Rate only applies when: + // 1. Issuer is not involved in the transfer (senderIssuer or + // receiverIssuer) + // 2. The locked rate is different from the parity rate + + // NOTE: Transfer fee in escrow works a bit differently from a normal + // payment. In escrow, the fee is deducted from the locked/sending amount, + // whereas in a normal payment, the transfer fee is taken on top of the + // sending amount. + auto finalAmt = amount; + if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate) + { + // compute transfer fee, if any + auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.asset(), true); + // compute balance to transfer + finalAmt = amount.value() - xferFee; + } + return rippleUnlockEscrowMPT( + view, + sender, + receiver, + finalAmt, + view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, + journal); +} + +} // namespace xrpl diff --git a/src/xrpld/app/misc/detail/LendingHelpers.cpp b/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp similarity index 87% rename from src/xrpld/app/misc/detail/LendingHelpers.cpp rename to src/libxrpl/tx/transactors/lending/LendingHelpers.cpp index 106eeb9301..ad4cd8440d 100644 --- a/src/xrpld/app/misc/detail/LendingHelpers.cpp +++ b/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp @@ -1,6 +1,6 @@ -#include +#include // DO NOT REMOVE forces header file include to sort first -#include +#include namespace xrpl { @@ -60,7 +60,8 @@ loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval) bool isRounded(Asset const& asset, Number const& value, std::int32_t scale) { - return roundToAsset(asset, value, scale, Number::downward) == roundToAsset(asset, value, scale, Number::upward); + return roundToAsset(asset, value, scale, Number::downward) == + roundToAsset(asset, value, scale, Number::upward); } namespace detail { @@ -112,7 +113,10 @@ computePaymentFactor(Number const& periodicRate, std::uint32_t paymentsRemaining * Equation (7) from XLS-66 spec, Section A-2 Equation Glossary */ Number -loanPeriodicPayment(Number const& principalOutstanding, Number const& periodicRate, std::uint32_t paymentsRemaining) +loanPeriodicPayment( + Number const& principalOutstanding, + Number const& periodicRate, + std::uint32_t paymentsRemaining) { if (principalOutstanding == 0 || paymentsRemaining == 0) return 0; @@ -261,7 +265,9 @@ doPayment( "xrpl::detail::doPayment", "Full principal payment"); XRPL_ASSERT_PARTS( - totalValueOutstandingProxy == payment.trackedValueDelta, "xrpl::detail::doPayment", "Full value payment"); + totalValueOutstandingProxy == payment.trackedValueDelta, + "xrpl::detail::doPayment", + "Full value payment"); XRPL_ASSERT_PARTS( managementFeeOutstandingProxy == payment.trackedManagementFeeDelta, "xrpl::detail::doPayment", @@ -299,7 +305,9 @@ doPayment( "xrpl::detail::doPayment", "Partial principal payment"); XRPL_ASSERT_PARTS( - totalValueOutstandingProxy > payment.trackedValueDelta, "xrpl::detail::doPayment", "Partial value payment"); + totalValueOutstandingProxy > payment.trackedValueDelta, + "xrpl::detail::doPayment", + "Partial value payment"); // Management fees are expected to be relatively small, and could get to // zero before the loan is paid off XRPL_ASSERT_PARTS( @@ -317,7 +325,8 @@ doPayment( XRPL_ASSERT_PARTS( // Use an explicit cast because the template parameter can be // ValueProxy or Number - static_cast(principalOutstandingProxy) <= static_cast(totalValueOutstandingProxy), + static_cast(principalOutstandingProxy) <= + static_cast(totalValueOutstandingProxy), "xrpl::detail::doPayment", "principal does not exceed total"); @@ -328,23 +337,24 @@ doPayment( "xrpl::detail::doPayment", "fee outstanding stays valid"); - return LoanPaymentParts{// Principal paid is straightforward - it's the tracked delta - .principalPaid = payment.trackedPrincipalDelta, + return LoanPaymentParts{ + // Principal paid is straightforward - it's the tracked delta + .principalPaid = payment.trackedPrincipalDelta, - // Interest paid combines: - // 1. Tracked interest from the amortization schedule - // (derived from the tracked deltas) - // 2. Untracked interest (e.g., late payment penalties) - .interestPaid = payment.trackedInterestPart() + payment.untrackedInterest, + // Interest paid combines: + // 1. Tracked interest from the amortization schedule + // (derived from the tracked deltas) + // 2. Untracked interest (e.g., late payment penalties) + .interestPaid = payment.trackedInterestPart() + payment.untrackedInterest, - // Value change represents how the loan's total value changed beyond - // normal amortization. - .valueChange = payment.untrackedInterest, + // Value change represents how the loan's total value changed beyond + // normal amortization. + .valueChange = payment.untrackedInterest, - // Fee paid combines: - // 1. Tracked management fees from the amortization schedule - // 2. Untracked fees (e.g., late payment fees, service fees) - .feePaid = payment.trackedManagementFeeDelta + payment.untrackedManagementFee}; + // Fee paid combines: + // 1. Tracked management fees from the amortization schedule + // 2. Untracked fees (e.g., late payment fees, service fees) + .feePaid = payment.trackedManagementFeeDelta + payment.untrackedManagementFee}; } /* Simulates an overpayment to validate it won't break the loan's amortization. @@ -370,8 +380,8 @@ tryOverpayment( beast::Journal j) { // Calculate what the loan state SHOULD be theoretically (at full precision) - auto const theoreticalState = - computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining, managementFeeRate); + auto const theoreticalState = computeTheoreticalLoanState( + periodicPayment, periodicRate, paymentRemaining, managementFeeRate); // Calculate the accumulated rounding errors. These need to be preserved // across the re-amortization to maintain consistency with the loan's @@ -381,14 +391,20 @@ tryOverpayment( // Compute the new principal by applying the overpayment to the theoretical // principal. Use max with 0 to ensure we never go negative. - auto const newTheoreticalPrincipal = - std::max(theoreticalState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta, Number{0}); + auto const newTheoreticalPrincipal = std::max( + theoreticalState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta, + Number{0}); // Compute new loan properties based on the reduced principal. This // recalculates the periodic payment, total value, and management fees // for the remaining payment schedule. auto newLoanProperties = computeLoanProperties( - asset, newTheoreticalPrincipal, periodicRate, paymentRemaining, managementFeeRate, loanScale); + asset, + newTheoreticalPrincipal, + periodicRate, + paymentRemaining, + managementFeeRate, + loanScale); JLOG(j.debug()) << "new periodic payment: " << newLoanProperties.periodicPayment << ", new total value: " << newLoanProperties.loanState.valueOutstanding @@ -414,7 +430,10 @@ tryOverpayment( roundedOldState.principalOutstanding); auto const totalValueOutstanding = std::clamp( roundToAsset( - asset, principalOutstanding + newTheoreticalState.interestOutstanding(), loanScale, Number::upward), + asset, + principalOutstanding + newTheoreticalState.interestOutstanding(), + loanScale, + Number::upward), numZero, roundedOldState.valueOutstanding); auto const managementFeeOutstanding = std::clamp( @@ -454,7 +473,8 @@ tryOverpayment( // Validate that all computed properties are reasonable. These checks should // never fail under normal circumstances, but we validate defensively. - if (newLoanProperties.periodicPayment <= 0 || newLoanProperties.loanState.valueOutstanding <= 0 || + if (newLoanProperties.periodicPayment <= 0 || + newLoanProperties.loanState.valueOutstanding <= 0 || newLoanProperties.loanState.managementFeeDue < 0) { // LCOV_EXCL_START @@ -463,7 +483,8 @@ tryOverpayment( "not compute. TotalValueOutstanding: " << newLoanProperties.loanState.valueOutstanding << ", PeriodicPayment : " << newLoanProperties.periodicPayment - << ", ManagementFeeOwedToBroker: " << newLoanProperties.loanState.managementFeeDue; + << ", ManagementFeeOwedToBroker: " + << newLoanProperties.loanState.managementFeeDue; return Unexpected(tesSUCCESS); // LCOV_EXCL_STOP } @@ -504,7 +525,8 @@ tryOverpayment( .valueChange = valueChange + overpaymentComponents.untrackedInterest, // Fee paid includes both the reduction in tracked management fees // and any untracked fees on the overpayment itself - .feePaid = overpaymentComponents.untrackedManagementFee + overpaymentComponents.trackedManagementFeeDelta, + .feePaid = overpaymentComponents.untrackedManagementFee + + overpaymentComponents.trackedManagementFeeDelta, }, newLoanProperties); } @@ -535,8 +557,8 @@ doOverpayment( TenthBips16 const managementFeeRate, beast::Journal j) { - auto const loanState = - constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy); + auto const loanState = constructLoanState( + totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy); auto const periodicPayment = periodicPaymentProxy; JLOG(j.debug()) << "overpayment components:" << ", totalValue before: " << *totalValueOutstandingProxy @@ -545,7 +567,8 @@ doOverpayment( << ", managementFeeDelta: " << overpaymentComponents.trackedManagementFeeDelta << ", interestPart: " << overpaymentComponents.trackedInterestPart() << ", untrackedInterest: " << overpaymentComponents.untrackedInterest - << ", totalDue: " << overpaymentComponents.totalDue << ", payments remaining :" << paymentRemaining; + << ", totalDue: " << overpaymentComponents.totalDue + << ", payments remaining :" << paymentRemaining; // Attempt to re-amortize the loan with the overpayment applied. // This modifies the temporary copies, leaving the proxies unchanged. @@ -594,9 +617,11 @@ doOverpayment( JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange << ", totalValue before: " << *totalValueOutstandingProxy << ", totalValue after: " << newRoundedLoanState.valueOutstanding - << ", totalValue delta: " << (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding) + << ", totalValue delta: " + << (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding) << ", principalDelta: " << overpaymentComponents.trackedPrincipalDelta - << ", principalPaid: " << loanPaymentParts.principalPaid << ", Computed difference: " + << ", principalPaid: " << loanPaymentParts.principalPaid + << ", Computed difference: " << overpaymentComponents.trackedPrincipalDelta - (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding); @@ -656,8 +681,8 @@ computeLatePayment( return Unexpected(tecTOO_SOON); // Calculate the penalty interest based on how long the payment is overdue. - auto const latePaymentInterest = - loanLatePaymentInterest(principalOutstanding, lateInterestRate, view.parentCloseTime(), nextDueDate); + auto const latePaymentInterest = loanLatePaymentInterest( + principalOutstanding, lateInterestRate, view.parentCloseTime(), nextDueDate); // Round the late interest and split it between the vault (net interest) // and the broker (management fee portion). This lambda ensures we @@ -694,14 +719,17 @@ computeLatePayment( periodic.untrackedInterest + roundedLateInterest}; XRPL_ASSERT_PARTS( - isRounded(asset, late.totalDue, loanScale), "xrpl::detail::computeLatePayment", "total due is rounded"); + isRounded(asset, late.totalDue, loanScale), + "xrpl::detail::computeLatePayment", + "total due is rounded"); // Check that the borrower provided enough funds to cover the late payment. // The late payment is more expensive than a regular payment due to the // penalties. if (amount < late.totalDue) { - JLOG(j.warn()) << "Late loan payment amount is insufficient. Due: " << late.totalDue << ", paid: " << amount; + JLOG(j.warn()) << "Late loan payment amount is insufficient. Due: " << late.totalDue + << ", paid: " << amount; return Unexpected(tecINSUFFICIENT_PAYMENT); } @@ -784,7 +812,8 @@ computeFullPayment( // Pay off all tracked outstanding balances: principal, interest, // and fees. // This marks the loan as complete (final payment). - .trackedValueDelta = principalOutstanding + totalInterestOutstanding + managementFeeOutstanding, + .trackedValueDelta = + principalOutstanding + totalInterestOutstanding + managementFeeOutstanding, .trackedPrincipalDelta = principalOutstanding, // All outstanding management fees are paid. This zeroes out the @@ -813,10 +842,13 @@ computeFullPayment( }; XRPL_ASSERT_PARTS( - isRounded(asset, full.totalDue, loanScale), "xrpl::detail::computeFullPayment", "total due is rounded"); + isRounded(asset, full.totalDue, loanScale), + "xrpl::detail::computeFullPayment", + "total due is rounded"); JLOG(j.trace()) << "computeFullPayment result: periodicPayment: " << periodicPayment - << ", periodicRate: " << periodicRate << ", paymentRemaining: " << paymentRemaining + << ", periodicRate: " << periodicRate + << ", paymentRemaining: " << paymentRemaining << ", theoreticalPrincipalOutstanding: " << theoreticalPrincipalOutstanding << ", fullPaymentInterest: " << fullPaymentInterest << ", roundedFullInterest: " << roundedFullInterest @@ -824,9 +856,11 @@ computeFullPayment( << ", untrackedInterest: " << full.untrackedInterest; if (amount < full.totalDue) + { // If the payment is less than the full payment amount, it's not // sufficient to be a full payment. return Unexpected(tecINSUFFICIENT_PAYMENT); + } return full; } @@ -868,11 +902,13 @@ computePaymentComponents( TenthBips16 managementFeeRate) { XRPL_ASSERT_PARTS( - isRounded(asset, totalValueOutstanding, scale) && isRounded(asset, principalOutstanding, scale) && + isRounded(asset, totalValueOutstanding, scale) && + isRounded(asset, principalOutstanding, scale) && isRounded(asset, managementFeeOutstanding, scale), "xrpl::detail::computePaymentComponents", "Outstanding values are rounded"); - XRPL_ASSERT_PARTS(paymentRemaining > 0, "xrpl::detail::computePaymentComponents", "some payments remaining"); + XRPL_ASSERT_PARTS( + paymentRemaining > 0, "xrpl::detail::computePaymentComponents", "some payments remaining"); auto const roundedPeriodicPayment = roundPeriodicPayment(asset, periodicPayment, scale); @@ -891,8 +927,8 @@ computePaymentComponents( // Calculate what the loan state SHOULD be after this payment (the target). // This is computed at full precision using the theoretical amortization. - LoanState const trueTarget = - computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate); + LoanState const trueTarget = computeTheoreticalLoanState( + periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate); // Round the target to the loan's scale to match how actual loan values // are stored. @@ -958,7 +994,8 @@ computePaymentComponents( component -= part; excess -= part; } - XRPL_ASSERT_PARTS(excess >= beast::zero, "xrpl::detail::computePaymentComponents", "excess non-negative"); + XRPL_ASSERT_PARTS( + excess >= beast::zero, "xrpl::detail::computePaymentComponents", "excess non-negative"); }; // Helper to reduce deltas when they collectively exceed a limit. // Order matters: we prefer to reduce interest first (most flexible), @@ -988,7 +1025,9 @@ computePaymentComponents( Number shortage = roundedPeriodicPayment - deltas.total(); XRPL_ASSERT_PARTS( - isRounded(asset, shortage, scale), "xrpl::detail::computePaymentComponents", "shortage is rounded"); + isRounded(asset, shortage, scale), + "xrpl::detail::computePaymentComponents", + "shortage is rounded"); if (shortage < beast::zero) { @@ -1001,7 +1040,8 @@ computePaymentComponents( // At this point, shortage >= 0 means we're paying less than the full // periodic payment (due to rounding or component caps). // shortage < 0 would mean we're trying to pay more than allowed (bug). - XRPL_ASSERT_PARTS(shortage >= beast::zero, "xrpl::detail::computePaymentComponents", "no shortage or excess"); + XRPL_ASSERT_PARTS( + shortage >= beast::zero, "xrpl::detail::computePaymentComponents", "no shortage or excess"); // Final validation that all components are valid XRPL_ASSERT_PARTS( @@ -1010,7 +1050,8 @@ computePaymentComponents( "total value adds up"); XRPL_ASSERT_PARTS( - deltas.principal >= beast::zero && deltas.principal <= currentLedgerState.principalOutstanding, + deltas.principal >= beast::zero && + deltas.principal <= currentLedgerState.principalOutstanding, "xrpl::detail::computePaymentComponents", "valid principal result"); XRPL_ASSERT_PARTS( @@ -1018,7 +1059,8 @@ computePaymentComponents( "xrpl::detail::computePaymentComponents", "valid interest result"); XRPL_ASSERT_PARTS( - deltas.managementFee >= beast::zero && deltas.managementFee <= currentLedgerState.managementFeeDue, + deltas.managementFee >= beast::zero && + deltas.managementFee <= currentLedgerState.managementFeeDue, "xrpl::detail::computePaymentComponents", "valid fee result"); @@ -1029,9 +1071,12 @@ computePaymentComponents( // Final safety clamp to ensure no value exceeds its outstanding balance return PaymentComponents{ - .trackedValueDelta = std::clamp(deltas.total(), numZero, currentLedgerState.valueOutstanding), - .trackedPrincipalDelta = std::clamp(deltas.principal, numZero, currentLedgerState.principalOutstanding), - .trackedManagementFeeDelta = std::clamp(deltas.managementFee, numZero, currentLedgerState.managementFeeDue), + .trackedValueDelta = + std::clamp(deltas.total(), numZero, currentLedgerState.valueOutstanding), + .trackedPrincipalDelta = + std::clamp(deltas.principal, numZero, currentLedgerState.principalOutstanding), + .trackedManagementFeeDelta = + std::clamp(deltas.managementFee, numZero, currentLedgerState.managementFeeDue), }; } @@ -1070,14 +1115,16 @@ computeOverpaymentComponents( // First, deduct the fixed overpayment fee from the total amount. // This reduces the effective payment that will be applied to the loan. // Equation (22) from XLS-66 spec, Section A-2 Equation Glossary - Number const overpaymentFee = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentFeeRate), loanScale); + Number const overpaymentFee = + roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentFeeRate), loanScale); // Calculate the penalty interest on the effective payment amount. // This interest doesn't follow the normal amortization schedule - it's // a one-time charge for paying early. // Equation (20) and (21) from XLS-66 spec, Section A-2 Equation Glossary auto const [roundedOverpaymentInterest, roundedOverpaymentManagementFee] = [&]() { - auto const interest = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentInterestRate), loanScale); + auto const interest = + roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentInterestRate), loanScale); return detail::computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale); }(); @@ -1087,8 +1134,8 @@ computeOverpaymentComponents( // reduction. detail::PaymentComponents{ .trackedValueDelta = overpayment - overpaymentFee, - .trackedPrincipalDelta = - overpayment - roundedOverpaymentInterest - roundedOverpaymentManagementFee - overpaymentFee, + .trackedPrincipalDelta = overpayment - roundedOverpaymentInterest - + roundedOverpaymentManagementFee - overpaymentFee, .trackedManagementFeeDelta = roundedOverpaymentManagementFee, .specialCase = detail::PaymentSpecialCase::extra}, // Untracked management fee is the fixed overpayment fee @@ -1155,7 +1202,8 @@ checkLoanGuards( LoanProperties const& properties, beast::Journal j) { - auto const totalInterestOutstanding = properties.loanState.valueOutstanding - principalRequested; + auto const totalInterestOutstanding = + properties.loanState.valueOutstanding - principalRequested; // Guard 1: if there is no computed total interest over the life of the // loan for a non-zero interest rate, we cannot properly amortize the // loan @@ -1192,10 +1240,12 @@ checkLoanGuards( // Guard 3: If the periodic payment is so small that it can't even be // rounded to a representable value, then the loan can't be paid. Also, // avoids dividing by 0. - auto const roundedPayment = roundPeriodicPayment(vaultAsset, properties.periodicPayment, properties.loanScale); + auto const roundedPayment = + roundPeriodicPayment(vaultAsset, properties.periodicPayment, properties.loanScale); if (roundedPayment == beast::zero) { - JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounds to 0. "; + JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment + << ") rounds to 0. "; return tecPRECISION_LOSS; } @@ -1205,11 +1255,13 @@ checkLoanGuards( { NumberRoundModeGuard mg(Number::upward); - if (std::int64_t const computedPayments{properties.loanState.valueOutstanding / roundedPayment}; + if (std::int64_t const computedPayments{ + properties.loanState.valueOutstanding / roundedPayment}; computedPayments != paymentTotal) { - JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounding (" - << roundedPayment << ") on a total value of " << properties.loanState.valueOutstanding + JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment + << ") rounding (" << roundedPayment << ") on a total value of " + << properties.loanState.valueOutstanding << " can not complete the loan in the specified " "number of payments (" << computedPayments << " != " << paymentTotal << ")"; @@ -1236,7 +1288,12 @@ computeFullPaymentInterest( TenthBips32 closeInterestRate) { auto const accruedInterest = detail::loanAccruedInterest( - theoreticalPrincipalOutstanding, periodicRate, parentCloseTime, startDate, prevPaymentDate, paymentInterval); + theoreticalPrincipalOutstanding, + periodicRate, + parentCloseTime, + startDate, + prevPaymentDate, + paymentInterval); XRPL_ASSERT( accruedInterest >= 0, "xrpl::detail::computeFullPaymentInterest : valid accrued " @@ -1287,7 +1344,11 @@ computeTheoreticalLoanState( { if (paymentRemaining == 0) { - return LoanState{.valueOutstanding = 0, .principalOutstanding = 0, .interestDue = 0, .managementFeeDue = 0}; + return LoanState{ + .valueOutstanding = 0, + .principalOutstanding = 0, + .interestDue = 0, + .managementFeeDue = 0}; } // Equation (30) from XLS-66 spec, Section A-2 Equation Glossary @@ -1300,7 +1361,8 @@ computeTheoreticalLoanState( Number const interestOutstandingGross = totalValueOutstanding - principalOutstanding; // Equation (32) from XLS-66 spec, Section A-2 Equation Glossary - Number const managementFeeOutstanding = tenthBipsOfValue(interestOutstandingGross, managementFeeRate); + Number const managementFeeOutstanding = + tenthBipsOfValue(interestOutstandingGross, managementFeeRate); // Equation (33) from XLS-66 spec, Section A-2 Equation Glossary Number const interestOutstandingNet = interestOutstandingGross - managementFeeOutstanding; @@ -1352,7 +1414,9 @@ LoanState constructRoundedLoanState(SLE::const_ref loan) { return constructLoanState( - loan->at(sfTotalValueOutstanding), loan->at(sfPrincipalOutstanding), loan->at(sfManagementFeeOutstanding)); + loan->at(sfTotalValueOutstanding), + loan->at(sfPrincipalOutstanding), + loan->at(sfManagementFeeOutstanding)); } /* @@ -1362,7 +1426,11 @@ constructRoundedLoanState(SLE::const_ref loan) * Equation (32) from XLS-66 spec, Section A-2 Equation Glossary */ Number -computeManagementFee(Asset const& asset, Number const& value, TenthBips32 managementFeeRate, std::int32_t scale) +computeManagementFee( + Asset const& asset, + Number const& value, + TenthBips32 managementFeeRate, + std::int32_t scale) { return roundToAsset(asset, tenthBipsOfValue(value, managementFeeRate), scale, Number::downward); } @@ -1388,7 +1456,12 @@ computeLoanProperties( auto const periodicRate = loanPeriodicRate(interestRate, paymentInterval); XRPL_ASSERT(interestRate == 0 || periodicRate > 0, "xrpl::computeLoanProperties : valid rate"); return computeLoanProperties( - asset, principalOutstanding, periodicRate, paymentsRemaining, managementFeeRate, minimumScale); + asset, + principalOutstanding, + periodicRate, + paymentsRemaining, + managementFeeRate, + minimumScale); } /* @@ -1408,7 +1481,8 @@ computeLoanProperties( TenthBips32 managementFeeRate, std::int32_t minimumScale) { - auto const periodicPayment = detail::loanPeriodicPayment(principalOutstanding, periodicRate, paymentsRemaining); + auto const periodicPayment = + detail::loanPeriodicPayment(principalOutstanding, periodicRate, paymentsRemaining); auto const [totalValueOutstanding, loanScale] = [&]() { // only round up if there should be interest @@ -1440,11 +1514,13 @@ computeLoanProperties( // Since we just figured out the loan scale, we haven't been able to // validate that the principal fits in it, so to allow this function to // succeed, round it here, and let the caller do the validation. - auto const roundedPrincipalOutstanding = roundToAsset(asset, principalOutstanding, loanScale, Number::to_nearest); + auto const roundedPrincipalOutstanding = + roundToAsset(asset, principalOutstanding, loanScale, Number::to_nearest); // Equation (31) from XLS-66 spec, Section A-2 Equation Glossary auto const totalInterestOutstanding = totalValueOutstanding - roundedPrincipalOutstanding; - auto const feeOwedToBroker = computeManagementFee(asset, totalInterestOutstanding, managementFeeRate, loanScale); + auto const feeOwedToBroker = + computeManagementFee(asset, totalInterestOutstanding, managementFeeRate, loanScale); // Compute the principal part of the first payment. This is needed // because the principal part may be rounded down to zero, which @@ -1452,11 +1528,11 @@ computeLoanProperties( auto const firstPaymentPrincipal = [&]() { // Compute the parts for the first payment. Ensure that the // principal payment will actually change the principal. - auto const startingState = - computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining, managementFeeRate); + auto const startingState = computeTheoreticalLoanState( + periodicPayment, periodicRate, paymentsRemaining, managementFeeRate); - auto const firstPaymentState = - computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining - 1, managementFeeRate); + auto const firstPaymentState = computeTheoreticalLoanState( + periodicPayment, periodicRate, paymentsRemaining - 1, managementFeeRate); // The unrounded principal part needs to be large enough to affect // the principal. What to do if not is left to the caller @@ -1465,7 +1541,8 @@ computeLoanProperties( return LoanProperties{ .periodicPayment = periodicPayment, - .loanState = constructLoanState(totalValueOutstanding, roundedPrincipalOutstanding, feeOwedToBroker), + .loanState = + constructLoanState(totalValueOutstanding, roundedPrincipalOutstanding, feeOwedToBroker), .loanScale = loanScale, .firstPaymentPrincipal = firstPaymentPrincipal, }; @@ -1557,27 +1634,29 @@ loanMakePayment( TenthBips32 const closeInterestRate{loan->at(sfCloseInterestRate)}; Number const closePaymentFee = roundToAsset(asset, loan->at(sfClosePaymentFee), loanScale); - LoanState const roundedLoanState = - constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy); + LoanState const roundedLoanState = constructLoanState( + totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy); - if (auto const fullPaymentComponents = detail::computeFullPayment( - asset, - view, - principalOutstandingProxy, - managementFeeOutstandingProxy, - periodicPayment, - paymentRemainingProxy, - prevPaymentDateProxy, - startDate, - paymentInterval, - closeInterestRate, - loanScale, - roundedLoanState.interestDue, - periodicRate, - closePaymentFee, - amount, - managementFeeRate, - j)) + auto const fullPaymentComponents = detail::computeFullPayment( + asset, + view, + principalOutstandingProxy, + managementFeeOutstandingProxy, + periodicPayment, + paymentRemainingProxy, + prevPaymentDateProxy, + startDate, + paymentInterval, + closeInterestRate, + loanScale, + roundedLoanState.interestDue, + periodicRate, + closePaymentFee, + amount, + managementFeeRate, + j); + + if (fullPaymentComponents.has_value()) { return doPayment( *fullPaymentComponents, @@ -1589,11 +1668,14 @@ loanMakePayment( nextDueDateProxy, paymentInterval); } - else if (fullPaymentComponents.error()) + + if (fullPaymentComponents.error()) + { // error() will be the TER returned if a payment is not made. It // will only evaluate to true if it's unsuccessful. Otherwise, // tesSUCCESS means nothing was done, so continue. return Unexpected(fullPaymentComponents.error()); + } // LCOV_EXCL_START UNREACHABLE("xrpl::loanMakePayment : invalid full payment result"); @@ -1617,7 +1699,10 @@ loanMakePayment( paymentRemainingProxy, managementFeeRate), serviceFee}; - XRPL_ASSERT_PARTS(periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "regular payment valid principal"); + XRPL_ASSERT_PARTS( + periodic.trackedPrincipalDelta >= 0, + "xrpl::loanMakePayment", + "regular payment valid principal"); // ------------------------------------------------------------- // late payment handling @@ -1626,18 +1711,20 @@ loanMakePayment( TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)}; Number const latePaymentFee = loan->at(sfLatePaymentFee); - if (auto const latePaymentComponents = detail::computeLatePayment( - asset, - view, - principalOutstandingProxy, - nextDueDateProxy, - periodic, - lateInterestRate, - loanScale, - latePaymentFee, - amount, - managementFeeRate, - j)) + auto const latePaymentComponents = detail::computeLatePayment( + asset, + view, + principalOutstandingProxy, + nextDueDateProxy, + periodic, + lateInterestRate, + loanScale, + latePaymentFee, + amount, + managementFeeRate, + j); + + if (latePaymentComponents.has_value()) { return doPayment( *latePaymentComponents, @@ -1649,7 +1736,8 @@ loanMakePayment( nextDueDateProxy, paymentInterval); } - else if (latePaymentComponents.error()) + + if (latePaymentComponents.error()) { // error() will be the TER returned if a payment is not made. It // will only evaluate to true if it's unsuccessful. @@ -1681,7 +1769,9 @@ loanMakePayment( { // Try to make more payments XRPL_ASSERT_PARTS( - periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "payment pays non-negative principal"); + periodic.trackedPrincipalDelta >= 0, + "xrpl::loanMakePayment", + "payment pays non-negative principal"); totalPaid += periodic.totalDue; totalParts += detail::doPayment( @@ -1696,7 +1786,8 @@ loanMakePayment( ++numPayments; XRPL_ASSERT_PARTS( - (periodic.specialCase == detail::PaymentSpecialCase::final) == (paymentRemainingProxy == 0), + (periodic.specialCase == detail::PaymentSpecialCase::final) == + (paymentRemainingProxy == 0), "xrpl::loanMakePayment", "final payment is the final payment"); @@ -1733,8 +1824,9 @@ loanMakePayment( // ------------------------------------------------------------- // overpayment handling - if (paymentType == LoanPaymentType::overpayment && loan->isFlag(lsfLoanOverpayment) && paymentRemainingProxy > 0 && - totalPaid < amount && numPayments < loanMaximumPaymentsPerTransaction) + if (paymentType == LoanPaymentType::overpayment && loan->isFlag(lsfLoanOverpayment) && + paymentRemainingProxy > 0 && totalPaid < amount && + numPayments < loanMaximumPaymentsPerTransaction) { TenthBips32 const overpaymentInterestRate{loan->at(sfOverpaymentInterestRate)}; TenthBips32 const overpaymentFeeRate{loan->at(sfOverpaymentFee)}; @@ -1744,8 +1836,14 @@ loanMakePayment( // another normal payment. But cap it just in case. Number const overpayment = std::min(amount - totalPaid, *totalValueOutstandingProxy); - detail::ExtendedPaymentComponents const overpaymentComponents = detail::computeOverpaymentComponents( - asset, loanScale, overpayment, overpaymentInterestRate, overpaymentFeeRate, managementFeeRate); + detail::ExtendedPaymentComponents const overpaymentComponents = + detail::computeOverpaymentComponents( + asset, + loanScale, + overpayment, + overpaymentInterestRate, + overpaymentFeeRate, + managementFeeRate); // Don't process an overpayment if the whole amount (or more!) // gets eaten by fees and interest. @@ -1770,26 +1868,33 @@ loanMakePayment( paymentRemainingProxy, managementFeeRate, j)) + { totalParts += *overResult; + } else if (overResult.error()) + { // error() will be the TER returned if a payment is not // made. It will only evaluate to true if it's unsuccessful. // Otherwise, tesSUCCESS means nothing was done, so // continue. return Unexpected(overResult.error()); + } } } // Check the final results are rounded, to double-check that the // intermediate steps were rounded. XRPL_ASSERT( - isRounded(asset, totalParts.principalPaid, loanScale) && totalParts.principalPaid >= beast::zero, + isRounded(asset, totalParts.principalPaid, loanScale) && + totalParts.principalPaid >= beast::zero, "xrpl::loanMakePayment : total principal paid is valid"); XRPL_ASSERT( - isRounded(asset, totalParts.interestPaid, loanScale) && totalParts.interestPaid >= beast::zero, + isRounded(asset, totalParts.interestPaid, loanScale) && + totalParts.interestPaid >= beast::zero, "xrpl::loanMakePayment : total interest paid is valid"); XRPL_ASSERT( - isRounded(asset, totalParts.valueChange, loanScale), "xrpl::loanMakePayment : loan value change is valid"); + isRounded(asset, totalParts.valueChange, loanScale), + "xrpl::loanMakePayment : loan value change is valid"); XRPL_ASSERT( isRounded(asset, totalParts.feePaid, loanScale) && totalParts.feePaid >= beast::zero, "xrpl::loanMakePayment : fee paid is valid"); diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp similarity index 93% rename from src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 7038752f8b..24aa3f8bf2 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -1,8 +1,8 @@ -#include +#include // -#include - +#include #include +#include namespace xrpl { @@ -119,18 +119,21 @@ determineAsset( { return amount.asset(); } - else if (holder == brokerPseudoAccountID) + if (holder == brokerPseudoAccountID) { // We want the asset to match the vault asset, so use the account as the // issuer return Issue{amount.getCurrency(), account}; } - else - return Unexpected(tecWRONG_ASSET); + + return Unexpected(tecWRONG_ASSET); } Expected -determineClawAmount(SLE const& sleBroker, Asset const& vaultAsset, std::optional const& amount) +determineClawAmount( + SLE const& sleBroker, + Asset const& vaultAsset, + std::optional const& amount) { auto const maxClawAmount = [&]() { // Always round the minimum required up @@ -173,7 +176,10 @@ preclaimHelper(PreclaimContext const& ctx, SLE const& sleIssuer, STAmount template <> TER -preclaimHelper(PreclaimContext const& ctx, SLE const& sleIssuer, STAmount const& clawAmount) +preclaimHelper( + PreclaimContext const& ctx, + SLE const& sleIssuer, + STAmount const& clawAmount) { auto const issuanceKey = keylet::mptIssuance(clawAmount.get().getMptID()); auto const sleIssuance = ctx.view.read(issuanceKey); @@ -261,7 +267,9 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx) // Explicitly check the balance of the trust line / MPT to make sure the // balance is actually there. It should always match `sfCoverAvailable`, so // if there isn't, this is an internal error. - if (accountHolds(ctx.view, brokerPseudoAccountID, vaultAsset, fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) < clawAmount) + if (accountHolds( + ctx.view, brokerPseudoAccountID, vaultAsset, fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) < + clawAmount) return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE // Check if the vault asset issuer has the correct flags @@ -275,7 +283,8 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx) } return std::visit( - [&](T const&) { return preclaimHelper(ctx, *sleIssuer, clawAmount); }, vaultAsset.value()); + [&](T const&) { return preclaimHelper(ctx, *sleIssuer, clawAmount); }, + vaultAsset.value()); } TER diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp similarity index 95% rename from src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index 3dd513414e..4630e6a360 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -1,8 +1,8 @@ -#include +#include // -#include - +#include #include +#include namespace xrpl { diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp similarity index 93% rename from src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index f2c8f28a84..6946992376 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -1,10 +1,11 @@ -#include +#include // -#include -#include - -#include +#include +#include +#include #include +#include +#include namespace xrpl { @@ -168,7 +169,7 @@ LoanBrokerCoverWithdraw::doApply() associateAsset(*broker, vaultAsset); - return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, mPriorBalance, amount, j_); + return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_); } //------------------------------------------------------------------------------ diff --git a/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp similarity index 88% rename from src/xrpld/app/tx/detail/LoanBrokerDelete.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 7c8b6c1f64..a755db7942 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -1,8 +1,9 @@ -#include +#include // -#include - +#include +#include #include +#include namespace xrpl { @@ -71,7 +72,8 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx) if (rounded != beast::zero) { // LCOV_EXCL_START - JLOG(ctx.j.warn()) << "LoanBrokerDelete: Debt total is " << debtTotal << ", which rounds to " << rounded; + JLOG(ctx.j.warn()) << "LoanBrokerDelete: Debt total is " << debtTotal + << ", which rounds to " << rounded; return tecHAS_OBLIGATIONS; // LCOV_EXCL_STOP } @@ -112,18 +114,21 @@ LoanBrokerDelete::doApply() auto const brokerPseudoID = broker->at(sfAccount); - if (!view().dirRemove(keylet::ownerDir(account_), broker->at(sfOwnerNode), broker->key(), false)) + if (!view().dirRemove( + keylet::ownerDir(account_), broker->at(sfOwnerNode), broker->key(), false)) { return tefBAD_LEDGER; // LCOV_EXCL_LINE } - if (!view().dirRemove(keylet::ownerDir(vaultPseudoID), broker->at(sfVaultNode), broker->key(), false)) + if (!view().dirRemove( + keylet::ownerDir(vaultPseudoID), broker->at(sfVaultNode), broker->key(), false)) { return tefBAD_LEDGER; // LCOV_EXCL_LINE } { auto const coverAvailable = STAmount{vaultAsset, broker->at(sfCoverAvailable)}; - if (auto const ter = accountSend(view(), brokerPseudoID, account_, coverAvailable, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), brokerPseudoID, account_, coverAvailable, j_, WaiveTransferFee::Yes)) return ter; } diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp similarity index 92% rename from src/xrpld/app/tx/detail/LoanBrokerSet.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 9553402f15..f8813ddbef 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -1,8 +1,9 @@ -#include +#include // -#include - +#include +#include #include +#include namespace xrpl { @@ -18,7 +19,8 @@ LoanBrokerSet::preflight(PreflightContext const& ctx) using namespace Lending; auto const& tx = ctx.tx; - if (auto const data = tx[~sfData]; data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength)) + if (auto const data = tx[~sfData]; + data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength)) return temINVALID; if (!validNumericRange(tx[~sfManagementFeeRate], maxManagementFeeRate)) return temINVALID; @@ -140,8 +142,8 @@ LoanBrokerSet::preclaim(PreclaimContext const& ctx) { if (auto const value = tx[field]; value && STAmount{asset, *value} != *value) { - JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value << ") can not be represented as a(n) " - << to_string(asset) << "."; + JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value + << ") can not be represented as a(n) " << to_string(asset) << "."; return tecPRECISION_LOSS; } } @@ -220,7 +222,7 @@ LoanBrokerSet::doApply() // one for the pseudo-account. adjustOwnerCount(view, owner, 2, j_); auto const ownerCount = owner->at(sfOwnerCount); - if (mPriorBalance < view.fees().accountReserve(ownerCount)) + if (preFeeBalance_ < view.fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); @@ -229,7 +231,7 @@ LoanBrokerSet::doApply() auto& pseudo = *maybePseudo; auto pseudoId = pseudo->at(sfAccount); - if (auto ter = addEmptyHolding(view, pseudoId, mPriorBalance, sleVault->at(sfAsset), j_)) + if (auto ter = addEmptyHolding(view, pseudoId, preFeeBalance_, sleVault->at(sfAsset), j_)) return ter; // Initialize data fields: diff --git a/src/xrpld/app/tx/detail/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp similarity index 88% rename from src/xrpld/app/tx/detail/LoanDelete.cpp rename to src/libxrpl/tx/transactors/lending/LoanDelete.cpp index d975834d29..39b28f5110 100644 --- a/src/xrpld/app/tx/detail/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -1,8 +1,8 @@ -#include +#include // -#include - +#include #include +#include namespace xrpl { @@ -84,7 +84,8 @@ LoanDelete::doApply() auto const vaultAsset = vaultSle->at(sfAsset); // Remove LoanID from Directory of the LoanBroker pseudo-account. - if (!view.dirRemove(keylet::ownerDir(brokerPseudoAccount), loanSle->at(sfLoanBrokerNode), loanID, false)) + if (!view.dirRemove( + keylet::ownerDir(brokerPseudoAccount), loanSle->at(sfLoanBrokerNode), loanID, false)) return tefBAD_LEDGER; // LCOV_EXCL_LINE // Remove LoanID from Directory of the Borrower. if (!view.dirRemove(keylet::ownerDir(borrower), loanSle->at(sfOwnerNode), loanID, false)) @@ -106,8 +107,10 @@ LoanDelete::doApply() { XRPL_ASSERT_PARTS( roundToAsset( - vaultSle->at(sfAsset), debtTotalProxy, getAssetsTotalScale(vaultSle), Number::towards_zero) == - beast::zero, + vaultSle->at(sfAsset), + debtTotalProxy, + getAssetsTotalScale(vaultSle), + Number::towards_zero) == beast::zero, "xrpl::LoanDelete::doApply", "last loan, remaining debt rounds to zero"); debtTotalProxy = 0; diff --git a/src/xrpld/app/tx/detail/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp similarity index 93% rename from src/xrpld/app/tx/detail/LoanManage.cpp rename to src/libxrpl/tx/transactors/lending/LoanManage.cpp index 8d0a79686c..d507ba5499 100644 --- a/src/xrpld/app/tx/detail/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -1,9 +1,9 @@ -#include +#include // -#include - +#include #include #include +#include namespace xrpl { @@ -70,7 +70,8 @@ LoanManage::preclaim(PreclaimContext const& ctx) JLOG(ctx.j.warn()) << "Loan is impaired. A loan can not be impaired twice."; return tecNO_PERMISSION; } - if (!(loanSle->isFlag(lsfLoanImpaired) || loanSle->isFlag(lsfLoanDefault)) && (tx.isFlag(tfLoanUnimpair))) + if (!(loanSle->isFlag(lsfLoanImpaired) || loanSle->isFlag(lsfLoanDefault)) && + (tx.isFlag(tfLoanUnimpair))) { JLOG(ctx.j.warn()) << "Loan is unimpaired. Can not be unimpaired again."; return tecNO_PERMISSION; @@ -182,7 +183,8 @@ LoanManage::defaultLoan( // LCOV_EXCL_STOP } - auto const vaultDefaultRounded = roundToAsset(vaultAsset, vaultDefaultAmount, vaultScale, Number::downward); + auto const vaultDefaultRounded = + roundToAsset(vaultAsset, vaultDefaultAmount, vaultScale, Number::downward); vaultTotalProxy -= vaultDefaultRounded; // Increase the Asset Available of the Vault by liquidated First-Loss // Capital and any unclaimed funds amount: @@ -191,9 +193,10 @@ LoanManage::defaultLoan( { auto const difference = vaultAvailableProxy - vaultTotalProxy; JLOG(j.debug()) << "Vault assets available: " << *vaultAvailableProxy << "(" - << vaultAvailableProxy.value().exponent() << "), Total: " << *vaultTotalProxy << "(" - << vaultTotalProxy.value().exponent() << "), Difference: " << difference << "(" - << difference.exponent() << ")"; + << vaultAvailableProxy.value().exponent() + << "), Total: " << *vaultTotalProxy << "(" + << vaultTotalProxy.value().exponent() << "), Difference: " << difference + << "(" << difference.exponent() << ")"; if (vaultAvailableProxy.value().exponent() - difference.exponent() > 13) { // If the difference is dust, bring the total up to match @@ -224,7 +227,8 @@ LoanManage::defaultLoan( return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustImpreciseNumber(vaultLossUnrealizedProxy, -totalDefaultAmount, vaultAsset, vaultScale); + adjustImpreciseNumber( + vaultLossUnrealizedProxy, -totalDefaultAmount, vaultAsset, vaultScale); } view.update(vaultSle); } @@ -271,7 +275,12 @@ LoanManage::defaultLoan( } TER -LoanManage::impairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j) +LoanManage::impairLoan( + ApplyView& view, + SLE::ref loanSle, + SLE::ref vaultSle, + Asset const& vaultAsset, + beast::Journal j) { Number const lossUnrealized = owedToVault(loanSle); @@ -348,7 +357,8 @@ LoanManage::unimpairLoan( else { // loan was unimpaired after the original payment due date - loanSle->at(sfNextPaymentDueDate) = view.parentCloseTime().time_since_epoch().count() + paymentInterval; + loanSle->at(sfNextPaymentDueDate) = + view.parentCloseTime().time_since_epoch().count() + paymentInterval; } view.update(loanSle); diff --git a/src/xrpld/app/tx/detail/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp similarity index 83% rename from src/xrpld/app/tx/detail/LoanPay.cpp rename to src/libxrpl/tx/transactors/lending/LoanPay.cpp index 744e81d67d..8739cb645a 100644 --- a/src/xrpld/app/tx/detail/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -1,12 +1,12 @@ -#include +#include // -#include -#include - #include +#include #include #include #include +#include +#include #include @@ -35,11 +35,14 @@ LoanPay::preflight(PreflightContext const& ctx) // The loan payment flags are all mutually exclusive. If more than one is // set, the tx is malformed. - static_assert((tfLoanLatePayment | tfLoanFullPayment | tfLoanOverpayment) == ~(tfLoanPayMask | tfUniversal)); + static_assert( + (tfLoanLatePayment | tfLoanFullPayment | tfLoanOverpayment) == + ~(tfLoanPayMask | tfUniversal)); auto const flagsSet = ctx.tx.getFlags() & ~(tfLoanPayMask | tfUniversal); if (std::popcount(flagsSet) > 1) { - JLOG(ctx.j.warn()) << "Only one LoanPay flag can be set per tx. " << flagsSet << " is too many."; + JLOG(ctx.j.warn()) << "Only one LoanPay flag can be set per tx. " << flagsSet + << " is too many."; return temINVALID_FLAG; } @@ -54,9 +57,11 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) auto const normalCost = Transactor::calculateBaseFee(view, tx); if (tx.isFlag(tfLoanFullPayment) || tx.isFlag(tfLoanLatePayment)) + { // The loan will be making one set of calculations for one full or late // payment return normalCost; + } // The fee is based on the potential number of payments, unless the loan is // being fully paid off. @@ -65,8 +70,10 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) auto const loanSle = view.read(keylet::loan(loanID)); if (!loanSle) + { // Let preclaim worry about the error for this return normalCost; + } if (loanSle->at(sfPaymentRemaining) <= loanPaymentsPerFeeIncrement) { @@ -76,29 +83,37 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) } if (hasExpired(view, loanSle->at(sfNextPaymentDueDate))) + { // If the payment is late, and the late payment flag is not set, it'll // fail return normalCost; + } auto const brokerSle = view.read(keylet::loanbroker(loanSle->at(sfLoanBrokerID))); if (!brokerSle) + { // Let preclaim worry about the error for this return normalCost; + } auto const vaultSle = view.read(keylet::vault(brokerSle->at(sfVaultID))); if (!vaultSle) + { // Let preclaim worry about the error for this return normalCost; + } auto const asset = vaultSle->at(sfAsset); if (asset != amount.asset()) + { // Let preclaim worry about the error for this return normalCost; + } auto const scale = loanSle->at(sfLoanScale); - auto const regularPayment = - roundPeriodicPayment(asset, loanSle->at(sfPeriodicPayment), scale) + loanSle->at(sfLoanServiceFee); + auto const regularPayment = roundPeriodicPayment(asset, loanSle->at(sfPeriodicPayment), scale) + + loanSle->at(sfLoanServiceFee); // If making an overpayment, count it as a full payment because it will do // about the same amount of work, if not more. @@ -108,8 +123,9 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx) // Charge one base fee per paymentsPerFeeIncrement payments, rounding up. Number::setround(Number::upward); - auto const feeIncrements = - std::max(std::int64_t(1), static_cast(numPaymentEstimate / loanPaymentsPerFeeIncrement)); + auto const feeIncrements = std::max( + std::int64_t(1), + static_cast(numPaymentEstimate / loanPaymentsPerFeeIncrement)); return feeIncrements * normalCost; } @@ -263,8 +279,10 @@ LoanPay::doApply() // the broker's solvency. NumberRoundModeGuard mg(Number::upward); return coverAvailableProxy >= - roundToAsset(asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) && - !isDeepFrozen(view, brokerOwner, asset) && !requireAuth(view, asset, brokerOwner, AuthType::StrongAuth); + roundToAsset( + asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) && + !isDeepFrozen(view, brokerOwner, asset) && + !requireAuth(view, asset, brokerOwner, AuthType::StrongAuth); }(); auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount; @@ -312,7 +330,8 @@ LoanPay::doApply() if (!paymentParts) { - XRPL_ASSERT_PARTS(paymentParts.error(), "xrpl::LoanPay::doApply", "payment error is an error"); + XRPL_ASSERT_PARTS( + paymentParts.error(), "xrpl::LoanPay::doApply", "payment error is an error"); return paymentParts.error(); } @@ -337,7 +356,8 @@ LoanPay::doApply() "valid total paid"); XRPL_ASSERT_PARTS(paymentParts->feePaid >= 0, "xrpl::LoanPay::doApply", "valid fee paid"); - if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 || paymentParts->feePaid < 0) + if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 || + paymentParts->feePaid < 0) { // LCOV_EXCL_START JLOG(j_.fatal()) << "Loan payment computation returned invalid values."; @@ -346,7 +366,8 @@ LoanPay::doApply() } JLOG(j_.debug()) << "Loan Pay: principal paid: " << paymentParts->principalPaid - << ", interest paid: " << paymentParts->interestPaid << ", fee paid: " << paymentParts->feePaid + << ", interest paid: " << paymentParts->interestPaid + << ", fee paid: " << paymentParts->feePaid << ", value change: " << paymentParts->valueChange; //------------------------------------------------------ @@ -361,7 +382,8 @@ LoanPay::doApply() auto const vaultScale = getAssetsTotalScale(vaultSle); auto const totalPaidToVaultRaw = paymentParts->principalPaid + paymentParts->interestPaid; - auto const totalPaidToVaultRounded = roundToAsset(asset, totalPaidToVaultRaw, vaultScale, Number::downward); + auto const totalPaidToVaultRounded = + roundToAsset(asset, totalPaidToVaultRaw, vaultScale, Number::downward); XRPL_ASSERT_PARTS( !asset.integral() || totalPaidToVaultRaw == totalPaidToVaultRounded, "xrpl::LoanPay::doApply", @@ -401,7 +423,12 @@ LoanPay::doApply() { Number const assetsAvailableBefore = *assetsAvailableProxy; Number const pseudoAccountBalanceBefore = accountHolds( - view, vaultPseudoAccount, asset, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_); + view, + vaultPseudoAccount, + asset, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); XRPL_ASSERT_PARTS( assetsAvailableBefore == pseudoAccountBalanceBefore, @@ -430,11 +457,14 @@ LoanPay::doApply() JLOG(j_.debug()) << "total paid to vault raw: " << totalPaidToVaultRaw << ", total paid to vault rounded: " << totalPaidToVaultRounded - << ", total paid to broker: " << totalPaidToBroker << ", amount from transaction: " << amount; + << ", total paid to broker: " << totalPaidToBroker + << ", amount from transaction: " << amount; // Move funds XRPL_ASSERT_PARTS( - totalPaidToVaultRounded + totalPaidToBroker <= amount, "xrpl::LoanPay::doApply", "amount is sufficient"); + totalPaidToVaultRounded + totalPaidToBroker <= amount, + "xrpl::LoanPay::doApply", + "amount is sufficient"); if (!sendBrokerFeeToOwner) { @@ -456,15 +486,34 @@ LoanPay::doApply() "assets available must not be greater than assets outstanding"); #if !NDEBUG - auto const accountBalanceBefore = - accountHolds(view, account_, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + auto const accountBalanceBefore = accountHolds( + view, + account_, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); auto const vaultBalanceBefore = account_ == vaultPseudoAccount ? STAmount{asset, 0} : accountHolds( - view, vaultPseudoAccount, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + view, + vaultPseudoAccount, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); auto const brokerBalanceBefore = account_ == brokerPayee ? STAmount{asset, 0} - : accountHolds(view, brokerPayee, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + : accountHolds( + view, + brokerPayee, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); #endif if (totalPaidToVaultRounded != beast::zero) @@ -478,12 +527,14 @@ LoanPay::doApply() if (brokerPayee == account_) { // The broker may have deleted their holding. Recreate it if needed - if (auto const ter = - addEmptyHolding(view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_); + if (auto const ter = addEmptyHolding( + view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, // and is fine here return ter; + } } if (auto const ter = requireAuth(view, asset, brokerPayee, AuthType::StrongAuth)) return ter; @@ -500,29 +551,54 @@ LoanPay::doApply() #if !NDEBUG Number const assetsAvailableAfter = *assetsAvailableProxy; - Number const pseudoAccountBalanceAfter = - accountHolds(view, vaultPseudoAccount, asset, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_); + Number const pseudoAccountBalanceAfter = accountHolds( + view, + vaultPseudoAccount, + asset, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); XRPL_ASSERT_PARTS( assetsAvailableAfter == pseudoAccountBalanceAfter, "xrpl::LoanPay::doApply", "vault pseudo balance agrees after"); - auto const accountBalanceAfter = - accountHolds(view, account_, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + auto const accountBalanceAfter = accountHolds( + view, + account_, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); auto const vaultBalanceAfter = account_ == vaultPseudoAccount ? STAmount{asset, 0} : accountHolds( - view, vaultPseudoAccount, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + view, + vaultPseudoAccount, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); auto const brokerBalanceAfter = account_ == brokerPayee ? STAmount{asset, 0} - : accountHolds(view, brokerPayee, asset, fhIGNORE_FREEZE, ahIGNORE_AUTH, j_, SpendableHandling::shFULL_BALANCE); + : accountHolds( + view, + brokerPayee, + asset, + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + j_, + SpendableHandling::shFULL_BALANCE); XRPL_ASSERT_PARTS( accountBalanceBefore + vaultBalanceBefore + brokerBalanceBefore == accountBalanceAfter + vaultBalanceAfter + brokerBalanceAfter, "xrpl::LoanPay::doApply", "funds are conserved (with rounding)"); - XRPL_ASSERT_PARTS(accountBalanceAfter >= beast::zero, "xrpl::LoanPay::doApply", "positive account balance"); + XRPL_ASSERT_PARTS( + accountBalanceAfter >= beast::zero, "xrpl::LoanPay::doApply", "positive account balance"); XRPL_ASSERT_PARTS( accountBalanceAfter < accountBalanceBefore || account_ == asset.getIssuer(), "xrpl::LoanPay::doApply", @@ -532,9 +608,13 @@ LoanPay::doApply() "xrpl::LoanPay::doApply", "positive vault and broker balances"); XRPL_ASSERT_PARTS( - vaultBalanceAfter >= vaultBalanceBefore, "xrpl::LoanPay::doApply", "vault balance did not decrease"); + vaultBalanceAfter >= vaultBalanceBefore, + "xrpl::LoanPay::doApply", + "vault balance did not decrease"); XRPL_ASSERT_PARTS( - brokerBalanceAfter >= brokerBalanceBefore, "xrpl::LoanPay::doApply", "broker balance did not decrease"); + brokerBalanceAfter >= brokerBalanceBefore, + "xrpl::LoanPay::doApply", + "broker balance did not decrease"); XRPL_ASSERT_PARTS( vaultBalanceAfter > vaultBalanceBefore || brokerBalanceAfter > brokerBalanceBefore, "xrpl::LoanPay::doApply", diff --git a/src/xrpld/app/tx/detail/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp similarity index 86% rename from src/xrpld/app/tx/detail/LoanSet.cpp rename to src/libxrpl/tx/transactors/lending/LoanSet.cpp index b7105f5df8..f046a24961 100644 --- a/src/xrpld/app/tx/detail/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -1,9 +1,10 @@ -#include +#include // -#include - +#include +#include #include #include +#include namespace xrpl { @@ -27,7 +28,8 @@ LoanSet::preflight(PreflightContext const& ctx) auto const& tx = ctx.tx; // Special case for Batch inner transactions - if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatch) && !tx.isFieldPresent(sfCounterparty)) + if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatch) && + !tx.isFieldPresent(sfCounterparty)) { auto const parentBatchId = ctx.parentBatchId.value_or(uint256{0}); JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " @@ -53,7 +55,8 @@ LoanSet::preflight(PreflightContext const& ctx) return ret; } - if (auto const data = tx[~sfData]; data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength)) + if (auto const data = tx[~sfData]; + data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength)) return temINVALID; for (auto const& field : {&sfLoanServiceFee, &sfLatePaymentFee, &sfClosePaymentFee}) { @@ -61,9 +64,10 @@ LoanSet::preflight(PreflightContext const& ctx) return temINVALID; } // Principal Requested is required - if (auto const p = tx[sfPrincipalRequested]; p <= 0) + auto const p = tx[sfPrincipalRequested]; + if (p <= 0) return temINVALID; - else if (!validNumericRange(tx[~sfLoanOriginationFee], p)) + if (!validNumericRange(tx[~sfLoanOriginationFee], p)) return temINVALID; if (!validNumericRange(tx[~sfInterestRate], maxInterestRate)) return temINVALID; @@ -79,19 +83,22 @@ LoanSet::preflight(PreflightContext const& ctx) if (auto const paymentTotal = tx[~sfPaymentTotal]; paymentTotal && *paymentTotal <= 0) return temINVALID; - if (auto const paymentInterval = tx[~sfPaymentInterval]; - !validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval)) - return temINVALID; - // Grace period is between min default value and payment interval - else if (auto const gracePeriod = tx[~sfGracePeriod]; // - !validNumericRange( - gracePeriod, paymentInterval.value_or(LoanSet::defaultPaymentInterval), defaultGracePeriod)) + auto const paymentInterval = tx[~sfPaymentInterval]; + if (!validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval)) + return temINVALID; // Grace period is between min default value and payment interval + if (auto const gracePeriod = tx[~sfGracePeriod]; !validNumericRange( + gracePeriod, + paymentInterval.value_or(LoanSet::defaultPaymentInterval), + defaultGracePeriod)) + { return temINVALID; + } // Copied from preflight2 if (counterPartySig) { - if (auto const ret = xrpl::detail::preflightCheckSimulateKeys(ctx.flags, *counterPartySig, ctx.j)) + if (auto const ret = + xrpl::detail::preflightCheckSimulateKeys(ctx.flags, *counterPartySig, ctx.j)) return *ret; } @@ -125,7 +132,8 @@ LoanSet::checkSign(PreclaimContext const& ctx) if (!ctx.tx.isFieldPresent(sfCounterpartySignature)) return tesSUCCESS; auto const counterSig = ctx.tx.getFieldObject(sfCounterpartySignature); - return Transactor::checkSign(ctx.view, ctx.flags, ctx.parentBatchId, *counterSigner, counterSig, ctx.j); + return Transactor::checkSign( + ctx.view, ctx.flags, ctx.parentBatchId, *counterSigner, counterSig, ctx.j); } XRPAmount @@ -144,11 +152,12 @@ LoanSet::calculateBaseFee(ReadView const& view, STTx const& tx) // for the transaction. Note that unlike the base class, the single signer // is counted if present. It will only be absent in a batch inner // transaction. - std::size_t const signerCount = [&counterSig]() { - // Compute defensively. Assure that "tx" cannot be accessed and cause - // confusion or miscalculations. - return counterSig.isFieldPresent(sfSigners) ? counterSig.getFieldArray(sfSigners).size() - : (counterSig.isFieldPresent(sfTxnSignature) ? 1 : 0); + std::size_t const signerCount = [&counterSig]() -> int { + // Compute defensively. + // Assure that "tx" cannot be accessed and cause confusion or miscalculations. + if (counterSig.isFieldPresent(sfSigners)) + return counterSig.getFieldArray(sfSigners).size(); + return counterSig.isFieldPresent(sfTxnSignature) ? 1 : 0; }(); return normalCost + (signerCount * baseFee); @@ -158,7 +167,11 @@ std::vector> const& LoanSet::getValueFields() { static std::vector> const valueFields{ - ~sfPrincipalRequested, ~sfLoanOriginationFee, ~sfLoanServiceFee, ~sfLatePaymentFee, ~sfClosePaymentFee + ~sfPrincipalRequested, + ~sfLoanOriginationFee, + ~sfLoanServiceFee, + ~sfLatePaymentFee, + ~sfClosePaymentFee // Overpayment fee is really a rate. Don't check it here. }; @@ -255,8 +268,10 @@ LoanSet::preclaim(PreclaimContext const& ctx) auto const vault = ctx.view.read(keylet::vault(brokerSle->at(sfVaultID))); if (!vault) + { // Should be impossible return tefBAD_LEDGER; // LCOV_EXCL_LINE + } if (vault->at(sfAssetsMaximum) != 0 && vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum)) { @@ -276,8 +291,8 @@ LoanSet::preclaim(PreclaimContext const& ctx) { if (auto const value = tx[field]; value && STAmount{asset, *value} != *value) { - JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value << ") can not be represented as a(n) " - << to_string(asset) << "."; + JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value + << ") can not be represented as a(n) " << to_string(asset) << "."; return tecPRECISION_LOSS; } } @@ -383,11 +398,15 @@ LoanSet::doApply() vaultScale); LoanState const state = constructLoanState( - properties.loanState.valueOutstanding, principalRequested, properties.loanState.managementFeeDue); + properties.loanState.valueOutstanding, + principalRequested, + properties.loanState.managementFeeDue); auto const vaultMaximum = *vaultSle->at(sfAssetsMaximum); XRPL_ASSERT_PARTS( - vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy, "xrpl::LoanSet::doApply", "Vault is below maximum limit"); + vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy, + "xrpl::LoanSet::doApply", + "Vault is below maximum limit"); if (vaultMaximum != 0 && state.interestDue > vaultMaximum - vaultTotalProxy) { JLOG(j_.warn()) << "Loan would exceed the maximum assets of the vault"; @@ -397,16 +416,24 @@ LoanSet::doApply() // relevant for IOU assets. for (auto const& field : getValueFields()) { - if (auto const value = tx[field]; value && !isRounded(vaultAsset, *value, properties.loanScale)) + if (auto const value = tx[field]; + value && !isRounded(vaultAsset, *value, properties.loanScale)) { - JLOG(j_.warn()) << field.f->getName() << " (" << *value << ") has too much precision. Total loan value is " - << properties.loanState.valueOutstanding << " with a scale of " << properties.loanScale; + JLOG(j_.warn()) << field.f->getName() << " (" << *value + << ") has too much precision. Total loan value is " + << properties.loanState.valueOutstanding << " with a scale of " + << properties.loanScale; return tecPRECISION_LOSS; } } - if (auto const ret = - checkLoanGuards(vaultAsset, principalRequested, interestRate != beast::zero, paymentTotal, properties, j_)) + if (auto const ret = checkLoanGuards( + vaultAsset, + principalRequested, + interestRate != beast::zero, + paymentTotal, + properties, + j_)) return ret; // Check that the other computed values are valid @@ -428,7 +455,8 @@ LoanSet::doApply() auto const newDebtDelta = principalRequested + state.interestDue; auto const newDebtTotal = brokerSle->at(sfDebtTotal) + newDebtDelta; - if (auto const debtMaximum = brokerSle->at(sfDebtMaximum); debtMaximum != 0 && debtMaximum < newDebtTotal) + if (auto const debtMaximum = brokerSle->at(sfDebtMaximum); + debtMaximum != 0 && debtMaximum < newDebtTotal) { JLOG(j_.warn()) << "Loan would exceed the maximum debt limit of the LoanBroker."; return tecLIMIT_EXCEEDED; @@ -449,7 +477,8 @@ LoanSet::doApply() adjustOwnerCount(view, borrowerSle, 1, j_); { auto const ownerCount = borrowerSle->at(sfOwnerCount); - auto const balance = account_ == borrower ? mPriorBalance : borrowerSle->at(sfBalance).value().xrp(); + auto const balance = + account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); if (balance < view.fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; } @@ -461,12 +490,17 @@ LoanSet::doApply() // Create a holding for the borrower if one does not already exist. XRPL_ASSERT_PARTS( - borrower == account_ || borrower == counterparty, "xrpl::LoanSet::doApply", "borrower signed transaction"); - if (auto const ter = addEmptyHolding(view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_); + borrower == account_ || borrower == counterparty, + "xrpl::LoanSet::doApply", + "borrower signed transaction"); + if (auto const ter = addEmptyHolding( + view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, and // is fine here return ter; + } if (auto const ter = requireAuth(view, vaultAsset, borrower, AuthType::StrongAuth)) return ter; @@ -482,12 +516,14 @@ LoanSet::doApply() "xrpl::LoanSet::doApply", "broker owner signed transaction"); - if (auto const ter = - addEmptyHolding(view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_); + if (auto const ter = addEmptyHolding( + view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_); ter && ter != tecDUPLICATE) + { // ignore tecDUPLICATE. That means the holding already exists, // and is fine here return ter; + } } if (auto const ter = requireAuth(view, vaultAsset, brokerOwner, AuthType::StrongAuth)) diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp similarity index 85% rename from src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 349dc2c1ea..15a745b8dd 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -1,19 +1,13 @@ -#include -#include - #include +#include #include #include #include +#include +#include namespace xrpl { -std::uint32_t -NFTokenAcceptOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenAcceptOfferMask; -} - NotTEC NFTokenAcceptOffer::preflight(PreflightContext const& ctx) { @@ -41,7 +35,8 @@ NFTokenAcceptOffer::preflight(PreflightContext const& ctx) TER NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { - auto const checkOffer = [&ctx](std::optional id) -> std::pair, TER> { + auto const checkOffer = + [&ctx](std::optional id) -> std::pair, TER> { if (id) { if (id->isZero()) @@ -133,14 +128,14 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // Check if broker is allowed to receive the fee with these IOUs. if (!brokerFee->native() && ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2)) { - auto res = - nft::checkTrustlineAuthorized(ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + auto res = nft::checkTrustlineAuthorized( + ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); + if (!isTesSuccess(res)) return res; - res = - nft::checkTrustlineDeepFrozen(ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + res = nft::checkTrustlineDeepFrozen( + ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); + if (!isTesSuccess(res)) return res; } } @@ -164,7 +159,8 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { // If the offer has a Destination field, the acceptor must be the // Destination. - if (auto const dest = bo->at(~sfDestination); dest.has_value() && *dest != ctx.tx[sfAccount]) + if (auto const dest = bo->at(~sfDestination); + dest.has_value() && *dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } @@ -183,18 +179,21 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // created by the broker. if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2) && !needed.native()) { - auto res = nft::checkTrustlineAuthorized(ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + auto res = nft::checkTrustlineAuthorized( + ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; if (!so) { - res = nft::checkTrustlineAuthorized(ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + res = nft::checkTrustlineAuthorized( + ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; - res = nft::checkTrustlineDeepFrozen(ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + res = nft::checkTrustlineDeepFrozen( + ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; } } @@ -218,7 +217,8 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { // If the offer has a Destination field, the acceptor must be the // Destination. - if (auto const dest = so->at(~sfDestination); dest.has_value() && *dest != ctx.tx[sfAccount]) + if (auto const dest = so->at(~sfDestination); + dest.has_value() && *dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } @@ -247,22 +247,23 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2)) { - auto res = nft::checkTrustlineAuthorized(ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + auto res = nft::checkTrustlineAuthorized( + ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; if (!bo) { - res = - nft::checkTrustlineAuthorized(ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + res = nft::checkTrustlineAuthorized( + ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; } } - auto const res = - nft::checkTrustlineDeepFrozen(ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + auto const res = nft::checkTrustlineDeepFrozen( + ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); + if (!isTesSuccess(res)) return res; } } @@ -271,8 +272,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // this nftoken auto const& offer = bo ? bo : so; if (!offer) + { // Purely defensive, should be caught in preflight. return tecINTERNAL; // LCOV_EXCL_LINE + } auto const& tokenID = offer->at(sfNFTokenID); auto const& amount = offer->at(sfAmount); @@ -284,19 +287,22 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // give the NFToken issuer an undesired trust line. // Issuer doesn't need a trust line to accept their own currency. if (ctx.view.rules().enabled(fixEnforceNFTokenTrustline) && - (nft::getFlags(tokenID) & nft::flagCreateTrustLines) == 0 && nftMinter != amount.getIssuer() && + (nft::getFlags(tokenID) & nft::flagCreateTrustLines) == 0 && + nftMinter != amount.getIssuer() && !ctx.view.read(keylet::line(nftMinter, amount.issue()))) return tecNO_LINE; // Check that the issuer is allowed to receive IOUs. if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2)) { - auto res = nft::checkTrustlineAuthorized(ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + auto res = nft::checkTrustlineAuthorized( + ctx.view, nftMinter, ctx.j, amount.asset().get()); + if (!isTesSuccess(res)) return res; - res = nft::checkTrustlineDeepFrozen(ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + res = nft::checkTrustlineDeepFrozen( + ctx.view, nftMinter, ctx.j, amount.asset().get()); + if (!isTesSuccess(res)) return res; } } @@ -318,7 +324,7 @@ NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount con // we know that something went wrong. This was originally found in the // context of IOU transfer fees. Since there are several payouts in this tx, // just confirm that the end state is OK. - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; if (accountFunds(view(), from, amount, fhZERO_IF_FROZEN, j_).signum() < 0) return tecINSUFFICIENT_FUNDS; @@ -328,14 +334,18 @@ NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount con } TER -NFTokenAcceptOffer::transferNFToken(AccountID const& buyer, AccountID const& seller, uint256 const& nftokenID) +NFTokenAcceptOffer::transferNFToken( + AccountID const& buyer, + AccountID const& seller, + uint256 const& nftokenID) { auto tokenAndPage = nft::findTokenAndPage(view(), seller, nftokenID); if (!tokenAndPage) return tecINTERNAL; // LCOV_EXCL_LINE - if (auto const ret = nft::removeToken(view(), seller, nftokenID, std::move(tokenAndPage->page)); !isTesSuccess(ret)) + if (auto const ret = nft::removeToken(view(), seller, nftokenID, std::move(tokenAndPage->page)); + !isTesSuccess(ret)) return ret; auto const sleBuyer = view().read(keylet::account(buyer)); @@ -354,7 +364,7 @@ NFTokenAcceptOffer::transferNFToken(AccountID const& buyer, AccountID const& sel // NFTs free of reserve. if (view().rules().enabled(fixNFTokenReserve)) { - // To check if there is sufficient reserve, we cannot use mPriorBalance + // To check if there is sufficient reserve, we cannot use preFeeBalance_ // because NFT is sold for a price. So we must use the balance after // the deduction of the potential offer price. A small caveat here is // that the balance has already deducted the transaction fee, meaning @@ -364,7 +374,8 @@ NFTokenAcceptOffer::transferNFToken(AccountID const& buyer, AccountID const& sel auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) { - if (auto const reserve = view().fees().accountReserve(buyerOwnerCountAfter); buyerBalance < reserve) + if (auto const reserve = view().fees().accountReserve(buyerOwnerCountAfter); + buyerBalance < reserve) return tecINSUFFICIENT_RESERVE; } } @@ -427,14 +438,16 @@ NFTokenAcceptOffer::doApply() { bool foundExpired = false; - auto const deleteOfferIfExpired = [this, &foundExpired](std::shared_ptr const& offer) -> TER { + auto const deleteOfferIfExpired = + [this, &foundExpired](std::shared_ptr const& offer) -> TER { if (offer && hasExpired(view(), (*offer)[~sfExpiration])) { JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key(); if (!nft::deleteTokenOffer(view(), offer)) { // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete expired offer '" << offer->key() << "': ignoring"; + JLOG(j_.fatal()) + << "Unable to delete expired offer '" << offer->key() << "': ignoring"; return tecINTERNAL; // LCOV_EXCL_STOP } @@ -464,7 +477,8 @@ NFTokenAcceptOffer::doApply() if (so && !nft::deleteTokenOffer(view(), so)) { // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete sell offer '" << to_string(so->key()) << "': ignoring"; + JLOG(j_.fatal()) << "Unable to delete sell offer '" << to_string(so->key()) + << "': ignoring"; return tecINTERNAL; // LCOV_EXCL_STOP } diff --git a/src/xrpld/app/tx/detail/NFTokenBurn.cpp b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp similarity index 81% rename from src/xrpld/app/tx/detail/NFTokenBurn.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp index a6146dcc4e..6d9ee56c51 100644 --- a/src/xrpld/app/tx/detail/NFTokenBurn.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp @@ -1,9 +1,8 @@ -#include -#include - #include #include #include +#include +#include namespace xrpl { @@ -52,7 +51,8 @@ NFTokenBurn::doApply() // Remove the token, effectively burning it: auto const ret = nft::removeToken( view(), - ctx_.tx.isFieldPresent(sfOwner) ? ctx_.tx.getAccountID(sfOwner) : ctx_.tx.getAccountID(sfAccount), + ctx_.tx.isFieldPresent(sfOwner) ? ctx_.tx.getAccountID(sfOwner) + : ctx_.tx.getAccountID(sfAccount), ctx_.tx[sfNFTokenID]); // Should never happen since preclaim() verified the token is present. @@ -69,13 +69,15 @@ NFTokenBurn::doApply() // Because the number of sell offers is likely to be less than // the number of buy offers, we prioritize the deletion of sell // offers in order to clean up sell offer directory - std::size_t const deletedSellOffers = - nft::removeTokenOffersWithLimit(view(), keylet::nft_sells(ctx_.tx[sfNFTokenID]), maxDeletableTokenOfferEntries); + std::size_t const deletedSellOffers = nft::removeTokenOffersWithLimit( + view(), keylet::nft_sells(ctx_.tx[sfNFTokenID]), maxDeletableTokenOfferEntries); if (maxDeletableTokenOfferEntries > deletedSellOffers) { nft::removeTokenOffersWithLimit( - view(), keylet::nft_buys(ctx_.tx[sfNFTokenID]), maxDeletableTokenOfferEntries - deletedSellOffers); + view(), + keylet::nft_buys(ctx_.tx[sfNFTokenID]), + maxDeletableTokenOfferEntries - deletedSellOffers); } return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp similarity index 81% rename from src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index f691a1b816..df0561e076 100644 --- a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -1,24 +1,18 @@ -#include -#include - #include #include #include +#include +#include #include namespace xrpl { -std::uint32_t -NFTokenCancelOffer::getFlagsMask(PreflightContext const& ctx) -{ - return tfNFTokenCancelOfferMask; -} - NotTEC NFTokenCancelOffer::preflight(PreflightContext const& ctx) { - if (auto const& ids = ctx.tx[sfNFTokenOffers]; ids.empty() || (ids.size() > maxTokenOfferCancelCount)) + if (auto const& ids = ctx.tx[sfNFTokenOffers]; + ids.empty() || (ids.size() > maxTokenOfferCancelCount)) return temMALFORMED; // In order to prevent unnecessarily overlarge transactions, we @@ -77,10 +71,12 @@ NFTokenCancelOffer::doApply() { for (auto const& id : ctx_.tx[sfNFTokenOffers]) { - if (auto offer = view().peek(keylet::nftoffer(id)); offer && !nft::deleteTokenOffer(view(), offer)) + if (auto offer = view().peek(keylet::nftoffer(id)); + offer && !nft::deleteTokenOffer(view(), offer)) { // LCOV_EXCL_START - JLOG(j_.fatal()) << "Unable to delete token offer " << id << " (ledger " << view().seq() << ")"; + JLOG(j_.fatal()) << "Unable to delete token offer " << id << " (ledger " << view().seq() + << ")"; return tefBAD_LEDGER; // LCOV_EXCL_STOP } diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp similarity index 85% rename from src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp index fdf2444ebc..eaca8b3380 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp @@ -1,9 +1,8 @@ -#include -#include - #include #include #include +#include +#include namespace xrpl { @@ -43,9 +42,10 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx) return tecEXPIRED; uint256 const nftokenID = ctx.tx[sfNFTokenID]; - std::uint32_t const txFlags = {ctx.tx.getFlags()}; + std::uint32_t const txFlags = ctx.tx.getFlags(); - if (!nft::findToken(ctx.view, ctx.tx[(txFlags & tfSellNFToken) ? sfAccount : sfOwner], nftokenID)) + if (!nft::findToken( + ctx.view, ctx.tx[(txFlags & tfSellNFToken) ? sfAccount : sfOwner], nftokenID)) return tecNO_ENTRY; // Use implementation shared with NFTokenMint @@ -74,7 +74,7 @@ NFTokenCreateOffer::doApply() ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), ctx_.tx[sfNFTokenID], - mPriorBalance, + preFeeBalance_, j_, ctx_.tx.getFlags()); } diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp similarity index 88% rename from src/xrpld/app/tx/detail/NFTokenMint.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index b00001e8fc..7bebbd0070 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -1,11 +1,10 @@ -#include - #include #include #include #include #include #include +#include #include @@ -48,11 +47,16 @@ NFTokenMint::getFlagsMask(PreflightContext const& ctx) // The fixRemoveNFTokenAutoTrustLine amendment disables minting with the // tfTrustLine flag as a way to prevent the attack. But until the // amendment passes we still need to keep the old behavior available. - std::uint32_t const nfTokenMintMask = ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine) - // if featureDynamicNFT enabled then new flag allowing mutable URI - // available - ? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMaskWithMutable : tfNFTokenMintMask - : ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable : tfNFTokenMintOldMask; + std::uint32_t const nfTokenMintMask = [&]() -> std::uint32_t { + if (ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine)) + { + // if featureDynamicNFT enabled then new flag allowing mutable URI available + return ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask + : tfNFTokenMintMaskWithoutMutable; + } + return ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable + : tfNFTokenMintOldMask; + }(); return nfTokenMintMask; } @@ -77,7 +81,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) if (auto uri = ctx.tx[~sfURI]) { - if (uri->length() == 0 || uri->length() > maxTokenURILength) + if (uri->empty() || uri->length() > maxTokenURILength) return temMALFORMED; } @@ -203,8 +207,10 @@ NFTokenMint::doApply() auto const tokenSeq = [this, &issuer]() -> Expected { auto const root = view().peek(keylet::account(issuer)); if (root == nullptr) + { // Should not happen. Checked in preclaim. return Unexpected(tecNO_ISSUER); + } // If the issuer hasn't minted an NFToken before we must add a // FirstNFTokenSequence field to the issuer's AccountRoot. The @@ -226,7 +232,8 @@ NFTokenMint::doApply() std::uint32_t const acctSeq = root->at(sfSequence); root->at(sfFirstNFTokenSequence) = - ctx_.tx.isFieldPresent(sfIssuer) || ctx_.tx.getSeqProxy().isTicket() ? acctSeq : acctSeq - 1; + ctx_.tx.isFieldPresent(sfIssuer) || ctx_.tx.getSeqProxy().isTicket() ? acctSeq + : acctSeq - 1; } std::uint32_t const mintedNftCnt = (*root)[~sfMintedNFTokens].value_or(0u); @@ -251,14 +258,18 @@ NFTokenMint::doApply() if (!tokenSeq.has_value()) return (tokenSeq.error()); - std::uint32_t const ownerCountBefore = view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); + std::uint32_t const ownerCountBefore = + view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); // Assemble the new NFToken. - SOTemplate const* nfTokenTemplate = InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); + SOTemplate const* nfTokenTemplate = + InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); if (nfTokenTemplate == nullptr) + { // Should never happen. return tecINTERNAL; // LCOV_EXCL_LINE + } auto const nftokenID = createNFTokenID( extractNFTokenFlagsFromTxFlags(ctx_.tx.getFlags()), @@ -274,7 +285,8 @@ NFTokenMint::doApply() object.setFieldVL(sfURI, *uri); }); - if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken)); ret != tesSUCCESS) + if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken)); + !isTesSuccess(ret)) return ret; if (ctx_.tx.isFieldPresent(sfAmount)) @@ -290,7 +302,7 @@ NFTokenMint::doApply() ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), nftokenID, - mPriorBalance, + preFeeBalance_, j_); !isTesSuccess(ter)) return ter; @@ -300,10 +312,12 @@ NFTokenMint::doApply() // allows NFTs to be added to the page (and burn fees) without // requiring the reserve to be met each time. The reserve is // only managed when a new NFT page or sell offer is added. - if (auto const ownerCountAfter = view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); + if (auto const ownerCountAfter = + view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); ownerCountAfter > ownerCountBefore) { - if (auto const reserve = view().fees().accountReserve(ownerCountAfter); mPriorBalance < reserve) + if (auto const reserve = view().fees().accountReserve(ownerCountAfter); + preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/NFTokenModify.cpp b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp similarity index 89% rename from src/xrpld/app/tx/detail/NFTokenModify.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenModify.cpp index 15be2d196a..1b2c9c2ab0 100644 --- a/src/xrpld/app/tx/detail/NFTokenModify.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp @@ -1,8 +1,7 @@ -#include -#include - #include #include +#include +#include namespace xrpl { @@ -14,7 +13,7 @@ NFTokenModify::preflight(PreflightContext const& ctx) if (auto uri = ctx.tx[~sfURI]) { - if (uri->length() == 0 || uri->length() > maxTokenURILength) + if (uri->empty() || uri->length() > maxTokenURILength) return temMALFORMED; } diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp similarity index 87% rename from src/xrpld/app/tx/detail/NFTokenUtils.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp index 5581182204..f7c12b5488 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp @@ -1,12 +1,15 @@ -#include - #include #include #include +#include +#include +#include +#include #include #include #include #include +#include #include #include @@ -24,7 +27,8 @@ locatePage(ReadView const& view, AccountID const& owner, uint256 const& id) // This NFT can only be found in the first page with a key that's strictly // greater than `first`, so look for that, up until the maximum possible // page. - return view.read(Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); + return view.read( + Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } static std::shared_ptr @@ -36,7 +40,8 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) // This NFT can only be found in the first page with a key that's strictly // greater than `first`, so look for that, up until the maximum possible // page. - return view.peek(Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); + return view.peek( + Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } static std::shared_ptr @@ -53,7 +58,8 @@ getPageForToken( // This NFT can only be found in the first page with a key that's strictly // greater than `first`, so look for that, up until the maximum possible // page. - auto cp = view.peek(Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); + auto cp = + view.peek(Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); // A suitable page doesn't exist; we'll have to create one. if (!cp) @@ -85,12 +91,13 @@ getPageForToken( // We prefer to keep equivalent NFTs on a page boundary. That gives // any additional equivalent NFTs maximum room for expansion. // Round up the boundary until there's a non-equivalent entry. - uint256 const cmp = narr[(dirMaxTokensPerPage / 2) - 1].getFieldH256(sfNFTokenID) & nft::pageMask; + uint256 const cmp = + narr[(dirMaxTokensPerPage / 2) - 1].getFieldH256(sfNFTokenID) & nft::pageMask; // Note that the calls to find_if_not() and (later) find_if() // rely on the fact that narr is kept in sorted order. - auto splitIter = - std::find_if_not(narr.begin() + (dirMaxTokensPerPage / 2), narr.end(), [&cmp](STObject const& obj) { + auto splitIter = std::find_if_not( + narr.begin() + (dirMaxTokensPerPage / 2), narr.end(), [&cmp](STObject const& obj) { return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp; }); @@ -98,9 +105,11 @@ getPageForToken( // equivalent NFTokens then check the front of the page for a // place to make the split. if (splitIter == narr.end()) + { splitIter = std::find_if(narr.begin(), narr.end(), [&cmp](STObject const& obj) { return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp; }); + } // There should be no circumstance when splitIter == end(), but if it // were to happen we should bail out because something is confused. @@ -192,7 +201,11 @@ compareTokens(uint256 const& a, uint256 const& b) } TER -changeTokenURI(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, std::optional const& uri) +changeTokenURI( + ApplyView& view, + AccountID const& owner, + uint256 const& nftokenID, + std::optional const& uri) { std::shared_ptr const page = locatePage(view, owner, nftokenID); @@ -203,16 +216,21 @@ changeTokenURI(ApplyView& view, AccountID const& owner, uint256 const& nftokenID // Locate the NFT in the page STArray& arr = page->peekFieldArray(sfNFTokens); - auto const nftIter = std::find_if( - arr.begin(), arr.end(), [&nftokenID](STObject const& obj) { return (obj[sfNFTokenID] == nftokenID); }); + auto const nftIter = std::find_if(arr.begin(), arr.end(), [&nftokenID](STObject const& obj) { + return (obj[sfNFTokenID] == nftokenID); + }); if (nftIter == arr.end()) return tecINTERNAL; // LCOV_EXCL_LINE if (uri) + { nftIter->setFieldVL(sfURI, *uri); + } else if (nftIter->isFieldPresent(sfURI)) + { nftIter->makeFieldAbsent(sfURI); + } view.update(page); return tesSUCCESS; @@ -229,7 +247,11 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) // the NFT. std::shared_ptr page = getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { - adjustOwnerCount(view, view.peek(keylet::account(owner)), 1, beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCount( + view, + view.peek(keylet::account(owner)), + 1, + beast::Journal{beast::Journal::getNullSink()}); }); if (!page) @@ -327,14 +349,19 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) /** Remove the token from the owner's token directory. */ TER -removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, std::shared_ptr&& curr) +removeToken( + ApplyView& view, + AccountID const& owner, + uint256 const& nftokenID, + std::shared_ptr const& curr) { // We found a page, but the given NFT may not be in it. auto arr = curr->getFieldArray(sfNFTokens); { - auto x = std::find_if( - arr.begin(), arr.end(), [&nftokenID](STObject const& obj) { return (obj[sfNFTokenID] == nftokenID); }); + auto x = std::find_if(arr.begin(), arr.end(), [&nftokenID](STObject const& obj) { + return (obj[sfNFTokenID] == nftokenID); + }); if (x == arr.end()) return tecNO_ENTRY; @@ -351,9 +378,11 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s page2 = view.peek(Keylet(ltNFTOKEN_PAGE, *id)); if (!page2) + { Throw( - "page " + to_string(page1->key()) + " has a broken " + field.getName() + " field pointing to " + - to_string(*id)); + "page " + to_string(page1->key()) + " has a broken " + field.getName() + + " field pointing to " + to_string(*id)); + } } return page2; @@ -379,8 +408,13 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s cnt--; if (cnt != 0) + { adjustOwnerCount( - view, view.peek(keylet::account(owner)), cnt, beast::Journal{beast::Journal::getNullSink()}); + view, + view.peek(keylet::account(owner)), + cnt, + beast::Journal{beast::Journal::getNullSink()}); + } return tesSUCCESS; } @@ -394,7 +428,8 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s // 2. Fix up the link from prev's previous page. // 3. Fix up the owner count. // 4. Erase the previous page. - if (view.rules().enabled(fixNFTokenPageLinks) && ((curr->key() & nft::pageMask) == pageMask)) + if (view.rules().enabled(fixNFTokenPageLinks) && + ((curr->key() & nft::pageMask) == pageMask)) { // Copy all relevant information from prev to curr. curr->peekFieldArray(sfNFTokens) = prev->peekFieldArray(sfNFTokens); @@ -414,7 +449,10 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s } adjustOwnerCount( - view, view.peek(keylet::account(owner)), -1, beast::Journal{beast::Journal::getNullSink()}); + view, + view.peek(keylet::account(owner)), + -1, + beast::Journal{beast::Journal::getNullSink()}); view.update(curr); view.erase(prev); @@ -424,9 +462,13 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s // The page is empty and not the last page, so we can just unlink it // and then remove it. if (next) + { prev->setFieldH256(sfNextPageMin, next->key()); + } else + { prev->makeFieldAbsent(sfNextPageMin); + } view.update(prev); } @@ -435,9 +477,13 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s { // Make our next page point to our previous page: if (prev) + { next->setFieldH256(sfPreviousPageMin, prev->key()); + } else + { next->makeFieldAbsent(sfPreviousPageMin); + } view.update(next); } @@ -456,10 +502,16 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s // just in case. if (prev && next && mergePages( - view, view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) + view, + view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), + view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) cnt++; - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1 * cnt, beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCount( + view, + view.peek(keylet::account(owner)), + -1 * cnt, + beast::Journal{beast::Journal::getNullSink()}); return tesSUCCESS; } @@ -496,8 +548,10 @@ findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftoken for (auto const& t : page->getFieldArray(sfNFTokens)) { if (t[sfNFTokenID] == nftokenID) + { // This std::optional constructor is explicit, so it is spelled out. return std::optional(std::in_place, t, std::move(page)); + } } return std::nullopt; } @@ -534,9 +588,14 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t if (auto const offer = view.peek(keylet::nftoffer(offerIndexes[i]))) { if (deleteTokenOffer(view, offer)) + { ++deletedOffersCount; + } else - Throw("Offer " + to_string(offerIndexes[i]) + " cannot be deleted!"); + { + Throw( + "Offer " + to_string(offerIndexes[i]) + " cannot be deleted!"); + } } if (maxDeletableOffers == deletedOffersCount) @@ -588,13 +647,15 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) auto const nftokenID = (*offer)[sfNFTokenID]; if (!view.dirRemove( - ((*offer)[sfFlags] & tfSellNFToken) ? keylet::nft_sells(nftokenID) : keylet::nft_buys(nftokenID), + ((*offer)[sfFlags] & lsfSellNFToken) ? keylet::nft_sells(nftokenID) + : keylet::nft_buys(nftokenID), (*offer)[sfNFTokenOfferNode], offer->key(), false)) return false; - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCount( + view, view.peek(keylet::account(owner)), -1, beast::Journal{beast::Journal::getNullSink()}); view.erase(offer); return true; @@ -607,8 +668,9 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) auto const last = keylet::nftpage_max(owner); - std::shared_ptr page = view.peek( - Keylet(ltNFTOKEN_PAGE, view.succ(keylet::nftpage_min(owner).key, last.key.next()).value_or(last.key))); + std::shared_ptr page = view.peek(Keylet( + ltNFTOKEN_PAGE, + view.succ(keylet::nftpage_min(owner).key, last.key.next()).value_or(last.key))); if (!page) return didRepair; @@ -642,17 +704,19 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) std::shared_ptr nextPage; while ( - (nextPage = - view.peek(Keylet(ltNFTOKEN_PAGE, view.succ(page->key().next(), last.key.next()).value_or(last.key))))) + (nextPage = view.peek(Keylet( + ltNFTOKEN_PAGE, view.succ(page->key().next(), last.key.next()).value_or(last.key))))) { - if (!page->isFieldPresent(sfNextPageMin) || page->getFieldH256(sfNextPageMin) != nextPage->key()) + if (!page->isFieldPresent(sfNextPageMin) || + page->getFieldH256(sfNextPageMin) != nextPage->key()) { didRepair = true; page->setFieldH256(sfNextPageMin, nextPage->key()); view.update(page); } - if (!nextPage->isFieldPresent(sfPreviousPageMin) || nextPage->getFieldH256(sfPreviousPageMin) != page->key()) + if (!nextPage->isFieldPresent(sfPreviousPageMin) || + nextPage->getFieldH256(sfPreviousPageMin) != page->key()) { didRepair = true; nextPage->setFieldH256(sfPreviousPageMin, page->key()); @@ -660,8 +724,10 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) } if (nextPage->key() == last.key) + { // We need special handling for the last page. break; + } page = nextPage; } @@ -691,8 +757,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) // Also fix up the NextPageMin link in the new Previous. auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink)); if (!newPrev) + { Throw( - "NFTokenPage directory for " + to_string(owner) + " cannot be repaired. Unexpected link problem."); + "NFTokenPage directory for " + to_string(owner) + + " cannot be repaired. Unexpected link problem."); + } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); } @@ -723,8 +792,10 @@ tokenOfferCreatePreflight( std::uint32_t txFlags) { if (amount.negative()) + { // An offer for a negative amount makes no sense. return temBAD_AMOUNT; + } if (!isXRP(amount)) { @@ -782,7 +853,8 @@ tokenOfferCreatePreclaim( // issuer does not need a trust line to accept their fee. if (view.rules().enabled(featureNFTokenMintOffer)) { - if (nftIssuer != amount.getIssuer() && !view.read(keylet::line(nftIssuer, amount.issue()))) + if (nftIssuer != amount.getIssuer() && + !view.read(keylet::line(nftIssuer, amount.issue()))) return tecNO_LINE; } else if (!view.exists(keylet::line(nftIssuer, amount.issue()))) @@ -851,8 +923,9 @@ tokenOfferCreatePreclaim( // is authorized, even though we previously checked it's balance via // accountHolds. This is due to a possibility of existence of // unauthorized trustlines with balance - auto const res = nft::checkTrustlineAuthorized(view, acctID, j, amount.asset().get()); - if (res != tesSUCCESS) + auto const res = + nft::checkTrustlineAuthorized(view, acctID, j, amount.asset().get()); + if (!isTesSuccess(res)) return res; } return tesSUCCESS; @@ -872,7 +945,8 @@ tokenOfferCreateApply( std::uint32_t txFlags) { Keylet const acctKeylet = keylet::account(acctID); - if (auto const acct = view.read(acctKeylet); priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) + if (auto const acct = view.read(acctKeylet); + priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) return tecINSUFFICIENT_RESERVE; auto const offerID = keylet::nftoffer(acctID, seqProxy.value()); @@ -880,7 +954,8 @@ tokenOfferCreateApply( // Create the offer: { // Token offers are always added to the owner's owner directory: - auto const ownerNode = view.dirInsert(keylet::ownerDir(acctID), offerID, describeOwnerDir(acctID)); + auto const ownerNode = + view.dirInsert(keylet::ownerDir(acctID), offerID, describeOwnerDir(acctID)); if (!ownerNode) return tecDIR_FULL; // LCOV_EXCL_LINE @@ -929,7 +1004,11 @@ tokenOfferCreateApply( } TER -checkTrustlineAuthorized(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue) +checkTrustlineAuthorized( + ReadView const& view, + AccountID const id, + beast::Journal const j, + Issue const& issue) { // Only valid for custom currencies XRPL_ASSERT(!isXRP(issue.currency), "xrpl::nft::checkTrustlineAuthorized : valid to check."); @@ -977,7 +1056,11 @@ checkTrustlineAuthorized(ReadView const& view, AccountID const id, beast::Journa } TER -checkTrustlineDeepFrozen(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue) +checkTrustlineDeepFrozen( + ReadView const& view, + AccountID const id, + beast::Journal const j, + Issue const& issue) { // Only valid for custom currencies XRPL_ASSERT(!isXRP(issue.currency), "xrpl::nft::checkTrustlineDeepFrozen : valid to check."); diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp similarity index 72% rename from src/xrpld/app/tx/detail/DeleteOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 9b62bffb5f..bde403f821 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -1,31 +1,33 @@ -#include - #include +#include #include #include #include +#include namespace xrpl { NotTEC -DeleteOracle::preflight(PreflightContext const& ctx) +OracleDelete::preflight(PreflightContext const& ctx) { return tesSUCCESS; } TER -DeleteOracle::preclaim(PreclaimContext const& ctx) +OracleDelete::preclaim(PreclaimContext const& ctx) { if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount)))) return terNO_ACCOUNT; // LCOV_EXCL_LINE - if (auto const sle = ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); - !sle) + auto const sle = + ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); + if (!sle) { JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist."; return tecNO_ENTRY; } - else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) + + if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) { // this can't happen because of the above check // LCOV_EXCL_START @@ -37,7 +39,11 @@ DeleteOracle::preclaim(PreclaimContext const& ctx) } TER -DeleteOracle::deleteOracle(ApplyView& view, std::shared_ptr const& sle, AccountID const& account, beast::Journal j) +OracleDelete::deleteOracle( + ApplyView& view, + std::shared_ptr const& sle, + AccountID const& account, + beast::Journal j) { if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -64,7 +70,7 @@ DeleteOracle::deleteOracle(ApplyView& view, std::shared_ptr const& sle, Acc } TER -DeleteOracle::doApply() +OracleDelete::doApply() { if (auto sle = ctx_.view().peek(keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]))) return deleteOracle(ctx_.view(), sle, account_, j_); diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp similarity index 87% rename from src/xrpld/app/tx/detail/SetOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 73ef1c7ae2..061dd16b0c 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -1,11 +1,12 @@ -#include - #include #include +#include +#include #include #include #include #include +#include namespace xrpl { @@ -13,11 +14,12 @@ static inline std::pair tokenPairKey(STObject const& pair) { return std::make_pair( - pair.getFieldCurrency(sfBaseAsset).currency(), pair.getFieldCurrency(sfQuoteAsset).currency()); + pair.getFieldCurrency(sfBaseAsset).currency(), + pair.getFieldCurrency(sfQuoteAsset).currency()); } NotTEC -SetOracle::preflight(PreflightContext const& ctx) +OracleSet::preflight(PreflightContext const& ctx) { auto const& dataSeries = ctx.tx.getFieldArray(sfPriceDataSeries); if (dataSeries.empty()) @@ -26,7 +28,8 @@ SetOracle::preflight(PreflightContext const& ctx) return temARRAY_TOO_LARGE; auto isInvalidLength = [&](auto const& sField, std::size_t length) { - return ctx.tx.isFieldPresent(sField) && (ctx.tx[sField].length() == 0 || ctx.tx[sField].length() > length); + return ctx.tx.isFieldPresent(sField) && + (ctx.tx[sField].length() == 0 || ctx.tx[sField].length() > length); }; if (isInvalidLength(sfProvider, maxOracleProvider) || isInvalidLength(sfURI, maxOracleURI) || @@ -37,7 +40,7 @@ SetOracle::preflight(PreflightContext const& ctx) } TER -SetOracle::preclaim(PreclaimContext const& ctx) +OracleSet::preclaim(PreclaimContext const& ctx) { auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); if (!sleSetter) @@ -46,7 +49,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) // lastUpdateTime must be within maxLastUpdateTimeDelta seconds // of the last closed ledger using namespace std::chrono; - std::size_t const closeTime = duration_cast(ctx.view.header().closeTime.time_since_epoch()).count(); + std::size_t const closeTime = + duration_cast(ctx.view.header().closeTime.time_since_epoch()).count(); std::size_t const lastUpdateTime = ctx.tx[sfLastUpdateTime]; if (lastUpdateTime < epoch_offset.count()) return tecINVALID_UPDATE_TIME; @@ -57,7 +61,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) lastUpdateTimeEpoch > (closeTime + maxLastUpdateTimeDelta)) return tecINVALID_UPDATE_TIME; - auto const sle = ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); + auto const sle = + ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); // token pairs to add/update std::set> pairs; @@ -74,11 +79,17 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (entry[~sfScale] > maxPriceScale) return temMALFORMED; if (entry.isFieldPresent(sfAssetPrice)) + { pairs.emplace(key); + } else if (sle) + { pairsDel.emplace(key); + } else + { return temMALFORMED; + } } // Lambda is used to check if the value of a field, passed @@ -89,7 +100,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) return !v || *v == (*sle)[field]; }; - std::uint32_t adjustReserve = 0; + std::int8_t adjustReserve = 0; if (sle) { // update @@ -108,9 +119,13 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (!pairs.contains(key)) { if (pairsDel.contains(key)) + { pairsDel.erase(key); + } else + { pairs.emplace(key); + } } } if (!pairsDel.empty()) @@ -134,7 +149,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (pairs.size() > maxOracleDataSeries) return tecARRAY_TOO_LARGE; - auto const reserve = ctx.view.fees().accountReserve(sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); + auto const reserve = + ctx.view.fees().accountReserve(sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); auto const& balance = sleSetter->getFieldAmount(sfBalance); if (balance < reserve) @@ -158,12 +174,13 @@ adjustOwnerCount(ApplyContext& ctx, int count) static void setPriceDataInnerObjTemplate(STObject& obj) { - if (SOTemplate const* elements = InnerObjectFormats::getInstance().findSOTemplateBySField(sfPriceData)) + if (SOTemplate const* elements = + InnerObjectFormats::getInstance().findSOTemplateBySField(sfPriceData)) obj.set(*elements); } TER -SetOracle::doApply() +OracleSet::doApply() { auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]); @@ -224,7 +241,8 @@ SetOracle::doApply() if (ctx_.tx.isFieldPresent(sfURI)) sle->setFieldVL(sfURI, ctx_.tx[sfURI]); sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]); - if (!sle->isFieldPresent(sfOracleDocumentID) && ctx_.view().rules().enabled(fixIncludeKeyletFields)) + if (!sle->isFieldPresent(sfOracleDocumentID) && + ctx_.view().rules().enabled(fixIncludeKeyletFields)) { (*sle)[sfOracleDocumentID] = ctx_.tx[sfOracleDocumentID]; } @@ -273,7 +291,8 @@ SetOracle::doApply() sle->setFieldVL(sfAssetClass, ctx_.tx[sfAssetClass]); sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]); - auto page = ctx_.view().dirInsert(keylet::ownerDir(account_), sle->key(), describeOwnerDir(account_)); + auto page = ctx_.view().dirInsert( + keylet::ownerDir(account_), sle->key(), describeOwnerDir(account_)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp similarity index 81% rename from src/xrpld/app/tx/detail/DepositPreauth.cpp rename to src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 4636bdb359..60b8dcd823 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -1,11 +1,12 @@ -#include - #include -#include #include +#include +#include +#include #include #include #include +#include #include @@ -29,11 +30,13 @@ DepositPreauth::preflight(PreflightContext const& ctx) { bool const authArrPresent = ctx.tx.isFieldPresent(sfAuthorizeCredentials); bool const unauthArrPresent = ctx.tx.isFieldPresent(sfUnauthorizeCredentials); - int const authCredPresent = static_cast(authArrPresent) + static_cast(unauthArrPresent); + int const authCredPresent = + static_cast(authArrPresent) + static_cast(unauthArrPresent); auto const optAuth = ctx.tx[~sfAuthorize]; auto const optUnauth = ctx.tx[~sfUnauthorize]; - int const authPresent = static_cast(optAuth.has_value()) + static_cast(optUnauth.has_value()); + int const authPresent = + static_cast(optAuth.has_value()) + static_cast(optUnauth.has_value()); if (authPresent + authCredPresent != 1) { @@ -64,7 +67,8 @@ DepositPreauth::preflight(PreflightContext const& ctx) else { if (auto err = credentials::checkArray( - ctx.tx.getFieldArray(authArrPresent ? sfAuthorizeCredentials : sfUnauthorizeCredentials), + ctx.tx.getFieldArray( + authArrPresent ? sfAuthorizeCredentials : sfUnauthorizeCredentials), maxCredentialsArraySize, ctx.j); !isTesSuccess(err)) @@ -120,8 +124,10 @@ DepositPreauth::preclaim(PreclaimContext const& ctx) else if (ctx.tx.isFieldPresent(sfUnauthorizeCredentials)) { // Verify that the Preauth entry is in the ledger. - if (!ctx.view.exists(keylet::depositPreauth( - account, credentials::makeSorted(ctx.tx.getFieldArray(sfUnauthorizeCredentials))))) + if (!ctx.view.exists( + keylet::depositPreauth( + account, + credentials::makeSorted(ctx.tx.getFieldArray(sfUnauthorizeCredentials))))) return tecNO_ENTRY; } return tesSUCCESS; @@ -140,9 +146,10 @@ DepositPreauth::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. { - STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; + STAmount const reserve{ + view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -156,10 +163,11 @@ DepositPreauth::doApply() slePreauth->setAccountID(sfAuthorize, auth); view().insert(slePreauth); - auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_)); + auto const page = + view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_)); - JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKeylet.key) << ": " - << (page ? "success" : "failure"); + JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " + << to_string(preauthKeylet.key) << ": " << (page ? "success" : "failure"); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE @@ -185,16 +193,18 @@ DepositPreauth::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. { - STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; + STAmount const reserve{ + view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } // Preclaim already verified that the Preauth entry does not yet exist. // Create and populate the Preauth entry. - auto const sortedTX = credentials::makeSorted(ctx_.tx.getFieldArray(sfAuthorizeCredentials)); + auto const sortedTX = + credentials::makeSorted(ctx_.tx.getFieldArray(sfAuthorizeCredentials)); STArray sortedLE(sfAuthorizeCredentials, sortedTX.size()); for (auto const& p : sortedTX) { @@ -214,10 +224,11 @@ DepositPreauth::doApply() view().insert(slePreauth); - auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_)); + auto const page = + view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_)); - JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key) << ": " - << (page ? "success" : "failure"); + JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key) + << ": " << (page ? "success" : "failure"); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE @@ -229,8 +240,8 @@ DepositPreauth::doApply() } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) { - auto const preauthKey = - keylet::depositPreauth(account_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials))); + auto const preauthKey = keylet::depositPreauth( + account_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials))); return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_); } @@ -240,7 +251,7 @@ DepositPreauth::doApply() TER DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, beast::Journal j) { - // Existence already checked in preclaim and DeleteAccount + // Existence already checked in preclaim and AccountDelete auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))}; if (!slePreauth) { diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp similarity index 86% rename from src/xrpld/app/tx/detail/Payment.cpp rename to src/libxrpl/tx/transactors/payment/Payment.cpp index 390548b7ef..38765016b5 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -1,15 +1,18 @@ -#include -#include -#include -#include - #include -#include #include +#include +#include +#include +#include #include #include +#include #include #include +#include +#include +#include +#include namespace xrpl { @@ -28,18 +31,25 @@ Payment::makeTxConsequences(PreflightContext const& ctx) } STAmount -getMaxSourceAmount(AccountID const& account, STAmount const& dstAmount, std::optional const& sendMax) +getMaxSourceAmount( + AccountID const& account, + STAmount const& dstAmount, + std::optional const& sendMax) { if (sendMax) + { return *sendMax; - else if (dstAmount.native() || dstAmount.holds()) + } + if (dstAmount.native() || dstAmount.holds()) + { return dstAmount; - else - return STAmount( - Issue{dstAmount.get().currency, account}, - dstAmount.mantissa(), - dstAmount.exponent(), - dstAmount < beast::zero); + } + + return STAmount( + Issue{dstAmount.get().currency, account}, + dstAmount.mantissa(), + dstAmount.exponent(), + dstAmount < beast::zero); } bool @@ -95,8 +105,9 @@ Payment::preflight(PreflightContext const& ctx) if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) || (!mptDirect && maxSourceAmount.holds())) { - JLOG(j.trace()) << "Malformed transaction: inconsistent issues: " << dstAmount.getFullText() << " " - << maxSourceAmount.getFullText() << " " << deliverMin.value_or(STAmount{}).getFullText(); + JLOG(j.trace()) << "Malformed transaction: inconsistent issues: " << dstAmount.getFullText() + << " " << maxSourceAmount.getFullText() << " " + << deliverMin.value_or(STAmount{}).getFullText(); return temMALFORMED; } @@ -118,7 +129,8 @@ Payment::preflight(PreflightContext const& ctx) } if (hasMax && maxSourceAmount <= beast::zero) { - JLOG(j.trace()) << "Malformed transaction: bad max amount: " << maxSourceAmount.getFullText(); + JLOG(j.trace()) << "Malformed transaction: bad max amount: " + << maxSourceAmount.getFullText(); return temBAD_AMOUNT; } if (dstAmount <= beast::zero) @@ -136,8 +148,8 @@ Payment::preflight(PreflightContext const& ctx) // You're signing yourself a payment. // If hasPaths is true, you might be trying some arbitrage. JLOG(j.trace()) << "Malformed transaction: " - << "Redundant payment from " << to_string(account) << " to self without path for " - << to_string(dstAsset); + << "Redundant payment from " << to_string(account) + << " to self without path for " << to_string(dstAsset); return temREDUNDANT; } if (xrpDirect && hasMax) @@ -189,8 +201,8 @@ Payment::preflight(PreflightContext const& ctx) auto const dMin = *deliverMin; if (!isLegalNet(dMin) || dMin <= beast::zero) { - JLOG(j.trace()) << "Malformed transaction: Invalid " << jss::DeliverMin.c_str() << " amount. " - << dMin.getFullText(); + JLOG(j.trace()) << "Malformed transaction: Invalid " << jss::DeliverMin.c_str() + << " amount. " << dMin.getFullText(); return temBAD_AMOUNT; } if (dMin.asset() != dstAmount.asset()) @@ -202,8 +214,8 @@ Payment::preflight(PreflightContext const& ctx) } if (dMin > dstAmount) { - JLOG(j.trace()) << "Malformed transaction: Dst amount less than " << jss::DeliverMin.c_str() << ". " - << dMin.getFullText(); + JLOG(j.trace()) << "Malformed transaction: Dst amount less than " + << jss::DeliverMin.c_str() << ". " << dMin.getFullText(); return temBAD_AMOUNT; } } @@ -227,7 +239,7 @@ Payment::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; std::unordered_set granularPermissions; @@ -237,10 +249,12 @@ Payment::checkPermission(ReadView const& view, STTx const& tx) auto const& amountAsset = dstAmount.asset(); // Granular permissions are only valid for direct payments. - if ((tx.isFieldPresent(sfSendMax) && tx[sfSendMax].asset() != amountAsset) || tx.isFieldPresent(sfPaths)) + if ((tx.isFieldPresent(sfSendMax) && tx[sfSendMax].asset() != amountAsset) || + tx.isFieldPresent(sfPaths)) return terNO_DELEGATE_PERMISSION; - if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) && amountAsset.getIssuer() == tx[sfAccount]) + if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) && + amountAsset.getIssuer() == tx[sfAccount]) return tesSUCCESS; if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) && @@ -276,7 +290,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return tecNO_DST; } - else if (ctx.view.open() && partialPaymentAllowed) + if (ctx.view.open() && partialPaymentAllowed) { // You cannot fund an account with a partial payment. // Make retry work smaller, by rejecting this. @@ -287,7 +301,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return telNO_DST_PARTIAL; } - else if (dstAmount < STAmount(ctx.view.fees().reserve)) + if (dstAmount < STAmount(ctx.view.fees().reserve)) { // accountReserve is the minimum amount that an account can have. // Reserve is not scaled by load. @@ -318,13 +332,16 @@ Payment::preclaim(PreclaimContext const& ctx) STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths); if (paths.size() > MaxPathSize || - std::any_of(paths.begin(), paths.end(), [](STPath const& path) { return path.size() > MaxPathLength; })) + std::any_of(paths.begin(), paths.end(), [](STPath const& path) { + return path.size() > MaxPathLength; + })) { return telBAD_PATH_COUNT; } } - if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); !isTesSuccess(err)) + if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); + !isTesSuccess(err)) return err; if (ctx.tx.isFieldPresent(sfDomainID)) @@ -357,7 +374,8 @@ Payment::doApply() bool const mptDirect = dstAmount.holds(); STAmount const maxSourceAmount = getMaxSourceAmount(account_, dstAmount, sendMax); - JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText() << " dstAmount=" << dstAmount.getFullText(); + JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText() + << " dstAmount=" << dstAmount.getFullText(); // Open a ledger for editing. auto const k = keylet::account(dstAccountID); @@ -392,7 +410,8 @@ Payment::doApply() // 1. If Account == Destination, or // 2. If Account is deposit preauthorized by destination. - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); + if (auto err = verifyDepositPreauth( + ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); !isTesSuccess(err)) return err; @@ -414,7 +433,7 @@ Payment::doApply() account_, ctx_.tx.getFieldPathSet(sfPaths), ctx_.tx[~sfDomainID], - ctx_.app.logs(), + ctx_.registry.logs(), &rcInput); // VFALCO NOTE We might not need to apply, depending // on the TER. But always applying *should* @@ -424,12 +443,16 @@ Payment::doApply() // TODO: is this right? If the amount is the correct amount, was // the delivered amount previously set? - if (rc.result() == tesSUCCESS && rc.actualAmountOut != dstAmount) + if (isTesSuccess(rc.result()) && rc.actualAmountOut != dstAmount) { if (deliverMin && rc.actualAmountOut < *deliverMin) + { rc.setResult(tecPATH_PARTIAL); + } else + { ctx_.deliver(rc.actualAmountOut); + } } auto terResult = rc.result(); @@ -442,21 +465,23 @@ Payment::doApply() terResult = tecPATH_DRY; return terResult; } - else if (mptDirect) + if (mptDirect) { JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText(); auto const& mptIssue = dstAmount.get(); - if (auto const ter = requireAuth(view(), mptIssue, account_); ter != tesSUCCESS) + if (auto const ter = requireAuth(view(), mptIssue, account_); !isTesSuccess(ter)) return ter; - if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); ter != tesSUCCESS) + if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); !isTesSuccess(ter)) return ter; - if (auto const ter = canTransfer(view(), mptIssue, account_, dstAccountID); ter != tesSUCCESS) + if (auto const ter = canTransfer(view(), mptIssue, account_, dstAccountID); + !isTesSuccess(ter)) return ter; - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); + if (auto err = verifyDepositPreauth( + ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); !isTesSuccess(err)) return err; @@ -494,12 +519,13 @@ Payment::doApply() amountDeliver = divide(maxSourceAmount, rate); } - if (requiredMaxSourceAmount > maxSourceAmount || (deliverMin && amountDeliver < *deliverMin)) + if (requiredMaxSourceAmount > maxSourceAmount || + (deliverMin && amountDeliver < *deliverMin)) return tecPATH_PARTIAL; PaymentSandbox pv(&view()); auto res = accountSend(pv, account_, dstAccountID, amountDeliver, ctx_.journal); - if (res == tesSUCCESS) + if (isTesSuccess(res)) { pv.apply(ctx_.rawView()); @@ -510,7 +536,9 @@ Payment::doApply() ctx_.deliver(amountDeliver); } else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY) + { res = tecPATH_PARTIAL; + } return res; } @@ -530,17 +558,18 @@ Payment::doApply() // This is the total reserve in drops. auto const reserve = view().fees().accountReserve(ownerCount); - // mPriorBalance is the balance on the sending account BEFORE the + // preFeeBalance_ is the balance on the sending account BEFORE the // fees were charged. We want to make sure we have enough reserve // to send. Allow final spend to use reserve for fee. auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp()); - if (mPriorBalance < dstAmount.xrp() + mmm) + if (preFeeBalance_ < dstAmount.xrp() + mmm) { // Vote no. However the transaction might succeed, if applied in // a different order. - JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " << to_string(mPriorBalance) << " / " - << to_string(dstAmount.xrp() + mmm) << " (" << to_string(reserve) << ")"; + JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " << to_string(preFeeBalance_) + << " / " << to_string(dstAmount.xrp() + mmm) << " (" << to_string(reserve) + << ")"; return tecUNFUNDED_PAYMENT; } @@ -579,13 +608,14 @@ Payment::doApply() if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve) { - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); + if (auto err = verifyDepositPreauth( + ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal); !isTesSuccess(err)) return err; } // Do the arithmetic for the transfer and make the ledger change. - sleSrc->setFieldAmount(sfBalance, mSourceBalance - dstAmount); + sleSrc->setFieldAmount(sfBalance, sleSrc->getFieldAmount(sfBalance) - dstAmount); sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount); // Re-arm the password change fee if we can and need to. diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp new file mode 100644 index 0000000000..6ba489bbc3 --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +bool +PaymentChannelClaim::checkExtraFeatures(PreflightContext const& ctx) +{ + return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); +} + +std::uint32_t +PaymentChannelClaim::getFlagsMask(PreflightContext const&) +{ + return tfPaymentChannelClaimMask; +} + +NotTEC +PaymentChannelClaim::preflight(PreflightContext const& ctx) +{ + auto const bal = ctx.tx[~sfBalance]; + if (bal && (!isXRP(*bal) || *bal <= beast::zero)) + return temBAD_AMOUNT; + + auto const amt = ctx.tx[~sfAmount]; + if (amt && (!isXRP(*amt) || *amt <= beast::zero)) + return temBAD_AMOUNT; + + if (bal && amt && *bal > *amt) + return temBAD_AMOUNT; + + { + auto const flags = ctx.tx.getFlags(); + + if ((flags & tfClose) && (flags & tfRenew)) + return temMALFORMED; + } + + if (auto const sig = ctx.tx[~sfSignature]) + { + if (!(ctx.tx[~sfPublicKey] && bal)) + return temMALFORMED; + + // Check the signature + // The signature isn't needed if txAccount == src, but if it's + // present, check it + + auto const reqBalance = bal->xrp(); + auto const authAmt = amt ? amt->xrp() : reqBalance; + + if (reqBalance > authAmt) + return temBAD_AMOUNT; + + Keylet const k(ltPAYCHAN, ctx.tx[sfChannel]); + if (!publicKeyType(ctx.tx[sfPublicKey])) + return temMALFORMED; + + PublicKey const pk(ctx.tx[sfPublicKey]); + Serializer msg; + serializePayChanAuthorization(msg, k.key, authAmt); + if (!verify(pk, msg.slice(), *sig)) + return temBAD_SIGNATURE; + } + + if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +TER +PaymentChannelClaim::preclaim(PreclaimContext const& ctx) +{ + if (!ctx.view.rules().enabled(featureCredentials)) + return Transactor::preclaim(ctx); + + if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j); + !isTesSuccess(err)) + return err; + + return tesSUCCESS; +} + +TER +PaymentChannelClaim::doApply() +{ + Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); + auto const slep = ctx_.view().peek(k); + if (!slep) + return tecNO_TARGET; + + AccountID const src = (*slep)[sfAccount]; + AccountID const dst = (*slep)[sfDestination]; + AccountID const txAccount = ctx_.tx[sfAccount]; + + auto const curExpiration = (*slep)[~sfExpiration]; + { + auto const cancelAfter = (*slep)[~sfCancelAfter]; + auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); + if ((cancelAfter && closeTime >= *cancelAfter) || + (curExpiration && closeTime >= *curExpiration)) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); + } + + if (txAccount != src && txAccount != dst) + return tecNO_PERMISSION; + + if (ctx_.tx[~sfBalance]) + { + auto const chanBalance = slep->getFieldAmount(sfBalance).xrp(); + auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); + auto const reqBalance = ctx_.tx[sfBalance].xrp(); + + if (txAccount == dst && !ctx_.tx[~sfSignature]) + return temBAD_SIGNATURE; + + if (ctx_.tx[~sfSignature]) + { + PublicKey const pk((*slep)[sfPublicKey]); + if (ctx_.tx[sfPublicKey] != pk) + return temBAD_SIGNER; + } + + if (reqBalance > chanFunds) + return tecUNFUNDED_PAYMENT; + + if (reqBalance <= chanBalance) + { + // nothing requested + return tecUNFUNDED_PAYMENT; + } + + auto const sled = ctx_.view().peek(keylet::account(dst)); + if (!sled) + return tecNO_DST; + + if (auto err = + verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + + (*slep)[sfBalance] = ctx_.tx[sfBalance]; + XRPAmount const reqDelta = reqBalance - chanBalance; + XRPL_ASSERT( + reqDelta >= beast::zero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta"); + (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; + ctx_.view().update(sled); + ctx_.view().update(slep); + } + + if (ctx_.tx.getFlags() & tfRenew) + { + if (src != txAccount) + return tecNO_PERMISSION; + (*slep)[~sfExpiration] = std::nullopt; + ctx_.view().update(slep); + } + + if (ctx_.tx.getFlags() & tfClose) + { + // Channel will close immediately if dry or the receiver closes + if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); + + auto const settleExpiration = + ctx_.view().header().parentCloseTime.time_since_epoch().count() + + (*slep)[sfSettleDelay]; + + if (!curExpiration || *curExpiration > settleExpiration) + { + (*slep)[~sfExpiration] = settleExpiration; + ctx_.view().update(slep); + } + } + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp new file mode 100644 index 0000000000..3f7e1e1814 --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +/* + PaymentChannel + + Payment channels permit off-ledger checkpoints of XRP payments flowing + in a single direction. A channel sequesters the owner's XRP in its own + ledger entry. The owner can authorize the recipient to claim up to a + given balance by giving the receiver a signed message (off-ledger). The + recipient can use this signed message to claim any unpaid balance while + the channel remains open. The owner can top off the line as needed. If + the channel has not paid out all its funds, the owner must wait out a + delay to close the channel to give the recipient a chance to supply any + claims. The recipient can close the channel at any time. Any transaction + that touches the channel after the expiration time will close the + channel. The total amount paid increases monotonically as newer claims + are issued. When the channel is closed any remaining balance is returned + to the owner. Channels are intended to permit intermittent off-ledger + settlement of ILP trust lines as balances get substantial. For + bidirectional channels, a payment channel can be used in each direction. +*/ + +//------------------------------------------------------------------------------ + +TxConsequences +PaymentChannelCreate::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; +} + +NotTEC +PaymentChannelCreate::preflight(PreflightContext const& ctx) +{ + if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + return temBAD_AMOUNT; + + if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + return temDST_IS_SRC; + + if (!publicKeyType(ctx.tx[sfPublicKey])) + return temMALFORMED; + + return tesSUCCESS; +} + +TER +PaymentChannelCreate::preclaim(PreclaimContext const& ctx) +{ + auto const account = ctx.tx[sfAccount]; + auto const sle = ctx.view.read(keylet::account(account)); + if (!sle) + return terNO_ACCOUNT; + + // Check reserve and funds availability + { + auto const balance = (*sle)[sfBalance]; + auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); + + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + if (balance < reserve + ctx.tx[sfAmount]) + return tecUNFUNDED; + } + + auto const dst = ctx.tx[sfDestination]; + + { + // Check destination account + auto const sled = ctx.view.read(keylet::account(dst)); + if (!sled) + return tecNO_DST; + + auto const flags = sled->getFlags(); + + // Check if they have disallowed incoming payment channels + if (flags & lsfDisallowIncomingPayChan) + return tecNO_PERMISSION; + + if ((flags & lsfRequireDestTag) && !ctx.tx[~sfDestinationTag]) + return tecDST_TAG_NEEDED; + + // Pseudo-accounts cannot receive payment channels, other than native + // to their underlying ledger object - implemented in their respective + // transaction types. Note, this is not amendment-gated because all + // writes to pseudo-account discriminator fields **are** amendment + // gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) + return tecNO_PERMISSION; + } + + return tesSUCCESS; +} + +TER +PaymentChannelCreate::doApply() +{ + auto const account = ctx_.tx[sfAccount]; + auto const sle = ctx_.view().peek(keylet::account(account)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (ctx_.view().rules().enabled(fixPayChanCancelAfter)) + { + auto const closeTime = ctx_.view().header().parentCloseTime; + if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter])) + return tecEXPIRED; + } + + auto const dst = ctx_.tx[sfDestination]; + + // Create PayChan in ledger. + // + // Note that we we use the value from the sequence or ticket as the + // payChan sequence. For more explanation see comments in SeqProxy.h. + Keylet const payChanKeylet = keylet::payChan(account, dst, ctx_.tx.getSeqValue()); + auto const slep = std::make_shared(payChanKeylet); + + // Funds held in this channel + (*slep)[sfAmount] = ctx_.tx[sfAmount]; + // Amount channel has already paid + (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); + (*slep)[sfAccount] = account; + (*slep)[sfDestination] = dst; + (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; + (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; + (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; + (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; + (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) + { + (*slep)[sfSequence] = ctx_.tx.getSeqValue(); + } + + ctx_.view().insert(slep); + + // Add PayChan to owner directory + { + auto const page = ctx_.view().dirInsert( + keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfOwnerNode] = *page; + } + + // Add PayChan to the recipient's owner directory + { + auto const page = + ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst)); + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*slep)[sfDestinationNode] = *page; + } + + // Deduct owner's balance, increment owner count + (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + ctx_.view().update(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp new file mode 100644 index 0000000000..6c08cc466c --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#include + +namespace xrpl { + +TxConsequences +PaymentChannelFund::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; +} + +NotTEC +PaymentChannelFund::preflight(PreflightContext const& ctx) +{ + if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) + return temBAD_AMOUNT; + + return tesSUCCESS; +} + +TER +PaymentChannelFund::doApply() +{ + Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]); + auto const slep = ctx_.view().peek(k); + if (!slep) + return tecNO_ENTRY; + + AccountID const src = (*slep)[sfAccount]; + auto const txAccount = ctx_.tx[sfAccount]; + auto const expiration = (*slep)[~sfExpiration]; + + { + auto const cancelAfter = (*slep)[~sfCancelAfter]; + auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count(); + if ((cancelAfter && closeTime >= *cancelAfter) || (expiration && closeTime >= *expiration)) + return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View")); + } + + if (src != txAccount) + { + // only the owner can add funds or extend + return tecNO_PERMISSION; + } + + if (auto extend = ctx_.tx[~sfExpiration]) + { + auto minExpiration = ctx_.view().header().parentCloseTime.time_since_epoch().count() + + (*slep)[sfSettleDelay]; + if (expiration && *expiration < minExpiration) + minExpiration = *expiration; + + if (*extend < minExpiration) + return temBAD_EXPIRATION; + (*slep)[~sfExpiration] = *extend; + ctx_.view().update(slep); + } + + auto const sle = ctx_.view().peek(keylet::account(txAccount)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + { + // Check reserve and funds availability + auto const balance = (*sle)[sfBalance]; + auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); + + if (balance < reserve) + return tecINSUFFICIENT_RESERVE; + + if (balance < reserve + ctx_.tx[sfAmount]) + return tecUNFUNDED; + } + + // do not allow adding funds if dst does not exist + if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst))) + { + return tecNO_DST; + } + + (*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount]; + ctx_.view().update(slep); + + (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; + ctx_.view().update(sle); + + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.cpp new file mode 100644 index 0000000000..176b920e6b --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include + +namespace xrpl { + +TER +closeChannel( + std::shared_ptr const& slep, + ApplyView& view, + uint256 const& key, + beast::Journal j) +{ + AccountID const src = (*slep)[sfAccount]; + // Remove PayChan from owner directory + { + auto const page = (*slep)[sfOwnerNode]; + if (!view.dirRemove(keylet::ownerDir(src), page, key, true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Could not remove paychan from src owner directory"; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Remove PayChan from recipient's owner directory, if present. + if (auto const page = (*slep)[~sfDestinationNode]) + { + auto const dst = (*slep)[sfDestination]; + if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Could not remove paychan from dst owner directory"; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + } + + // Transfer amount back to owner, decrement owner count + auto const sle = view.peek(keylet::account(src)); + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + + XRPL_ASSERT( + (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); + (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; + adjustOwnerCount(view, sle, -1, j); + view.update(sle); + + // Remove PayChan from ledger + view.erase(slep); + return tesSUCCESS; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.h b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.h new file mode 100644 index 0000000000..5d1a4c0aa1 --- /dev/null +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelHelpers.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace xrpl { + +TER +closeChannel( + std::shared_ptr const& slep, + ApplyView& view, + uint256 const& key, + beast::Journal j); + +} // namespace xrpl diff --git a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp similarity index 78% rename from src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp rename to src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index f979637e27..565631b3fd 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -1,7 +1,7 @@ -#include - #include +#include #include +#include namespace xrpl { @@ -19,7 +19,7 @@ TER PermissionedDomainDelete::preclaim(PreclaimContext const& ctx) { auto const domain = ctx.tx.getFieldH256(sfDomainID); - auto const sleDomain = ctx.view.read({ltPERMISSIONED_DOMAIN, domain}); + auto const sleDomain = ctx.view.read(keylet::permissionedDomain(domain)); if (!sleDomain) return tecNO_ENTRY; @@ -37,9 +37,11 @@ PermissionedDomainDelete::preclaim(PreclaimContext const& ctx) TER PermissionedDomainDelete::doApply() { - XRPL_ASSERT(ctx_.tx.isFieldPresent(sfDomainID), "xrpl::PermissionedDomainDelete::doApply : required field present"); + XRPL_ASSERT( + ctx_.tx.isFieldPresent(sfDomainID), + "xrpl::PermissionedDomainDelete::doApply : required field present"); - auto const slePd = view().peek({ltPERMISSIONED_DOMAIN, ctx_.tx.at(sfDomainID)}); + auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID))); auto const page = (*slePd)[sfOwnerNode]; if (!view().dirRemove(keylet::ownerDir(account_), page, slePd->key(), true)) diff --git a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp similarity index 79% rename from src/xrpld/app/tx/detail/PermissionedDomainSet.cpp rename to src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 19f68426d1..e8df51c9d9 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -1,12 +1,12 @@ -#include - -#include +#include +// #include +#include +#include +#include #include #include -#include - namespace xrpl { bool @@ -19,7 +19,9 @@ NotTEC PermissionedDomainSet::preflight(PreflightContext const& ctx) { if (auto err = credentials::checkArray( - ctx.tx.getFieldArray(sfAcceptedCredentials), maxPermissionedDomainCredentialsArraySize, ctx.j); + ctx.tx.getFieldArray(sfAcceptedCredentials), + maxPermissionedDomainCredentialsArraySize, + ctx.j); !isTesSuccess(err)) return err; @@ -47,7 +49,8 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx) if (ctx.tx.isFieldPresent(sfDomainID)) { - auto const sleDomain = ctx.view.read(keylet::permissionedDomain(ctx.tx.getFieldH256(sfDomainID))); + auto const sleDomain = + ctx.view.read(keylet::permissionedDomain(ctx.tx.getFieldH256(sfDomainID))); if (!sleDomain) return tecNO_ENTRY; if (sleDomain->getAccountID(sfOwner) != account) @@ -65,7 +68,8 @@ PermissionedDomainSet::doApply() if (!ownerSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const sortedTxCredentials = credentials::makeSorted(ctx_.tx.getFieldArray(sfAcceptedCredentials)); + auto const sortedTxCredentials = + credentials::makeSorted(ctx_.tx.getFieldArray(sfAcceptedCredentials)); STArray sortedLE(sfAcceptedCredentials, sortedTxCredentials.size()); for (auto const& p : sortedTxCredentials) { @@ -93,7 +97,8 @@ PermissionedDomainSet::doApply() if (balance < reserve) return tecINSUFFICIENT_RESERVE; - Keylet const pdKeylet = keylet::permissionedDomain(account_, ctx_.tx.getFieldU32(sfSequence)); + Keylet const pdKeylet = + keylet::permissionedDomain(account_, ctx_.tx.getFieldU32(sfSequence)); auto slePd = std::make_shared(pdKeylet); if (!slePd) return tefINTERNAL; // LCOV_EXCL_LINE @@ -101,7 +106,8 @@ PermissionedDomainSet::doApply() slePd->setAccountID(sfOwner, account_); slePd->setFieldU32(sfSequence, ctx_.tx.getFieldU32(sfSequence)); slePd->peekFieldArray(sfAcceptedCredentials) = std::move(sortedLE); - auto const page = view().dirInsert(keylet::ownerDir(account_), pdKeylet, describeOwnerDir(account_)); + auto const page = + view().dirInsert(keylet::ownerDir(account_), pdKeylet, describeOwnerDir(account_)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp similarity index 93% rename from src/xrpld/app/tx/detail/Batch.cpp rename to src/libxrpl/tx/transactors/system/Batch.cpp index 34b08beb2f..b2a111a182 100644 --- a/src/xrpld/app/tx/detail/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -1,13 +1,13 @@ -#include -#include - #include #include #include #include #include +#include #include #include +#include +#include namespace xrpl { @@ -79,7 +79,8 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx) // LCOV_EXCL_START if (txnFees > maxAmount - fee) { - JLOG(debugLog().error()) << "BatchTrace: XRPAmount overflow in txnFees calculation."; + JLOG(debugLog().error()) + << "BatchTrace: XRPAmount overflow in txnFees calculation."; return XRPAmount{INITIAL_XRP}; } // LCOV_EXCL_STOP @@ -104,9 +105,13 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx) for (STObject const& signer : signers) { if (signer.isFieldPresent(sfTxnSignature)) + { signerCount += 1; + } else if (signer.isFieldPresent(sfSigners)) + { signerCount += signer.getFieldArray(sfSigners).size(); + } } } @@ -207,8 +212,9 @@ Batch::preflight(PreflightContext const& ctx) // Validation Inner Batch Txns std::unordered_set uniqueHashes; std::unordered_map> accountSeqTicket; - auto checkSignatureFields = [&parentBatchId, &j = ctx.j]( - STObject const& sig, uint256 const& hash, char const* label = "") -> NotTEC { + auto checkSignatureFields = + [&parentBatchId, &j = ctx.j]( + STObject const& sig, uint256 const& hash, char const* label = "") -> NotTEC { if (sig.isFieldPresent(sfTxnSignature)) { JLOG(j.debug()) << "BatchTrace[" << parentBatchId << "]: " @@ -256,9 +262,10 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID; } - if (std::any_of(disabledTxTypes.begin(), disabledTxTypes.end(), [txType](auto const& disabled) { - return txType == disabled; - })) + if (std::any_of( + disabledTxTypes.begin(), disabledTxTypes.end(), [txType](auto const& disabled) { + return txType == disabled; + })) { return temINVALID_INNER_BATCH; } @@ -279,7 +286,8 @@ Batch::preflight(PreflightContext const& ctx) if (stx.isFieldPresent(sfCounterpartySignature)) { auto const counterpartySignature = stx.getFieldObject(sfCounterpartySignature); - if (auto const ret = checkSignatureFields(counterpartySignature, hash, "counterparty signature ")) + if (auto const ret = + checkSignatureFields(counterpartySignature, hash, "counterparty signature ")) { return ret; } @@ -295,11 +303,13 @@ Batch::preflight(PreflightContext const& ctx) } auto const innerAccount = stx.getAccountID(sfAccount); - if (auto const preflightResult = xrpl::preflight(ctx.app, ctx.rules, parentBatchId, stx, tapBATCH, ctx.j); - preflightResult.ter != tesSUCCESS) + if (auto const preflightResult = + xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, tapBATCH, ctx.j); + !isTesSuccess(preflightResult.ter)) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn preflight failed: " << transHuman(preflightResult.ter) << " " + << "inner txn preflight failed: " << transHuman(preflightResult.ter) + << " " << "txID: " << hash; return temINVALID_INNER_BATCH; } @@ -374,7 +384,8 @@ Batch::preflightSigValidated(PreflightContext const& ctx) requiredSigners.insert(innerAccount); // Some transactions have a Counterparty, who must also sign the // transaction if they are not the outer account - if (auto const counterparty = rb.at(~sfCounterparty); counterparty && counterparty != outerAccount) + if (auto const counterparty = rb.at(~sfCounterparty); + counterparty && counterparty != outerAccount) requiredSigners.insert(*counterparty); } diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/libxrpl/tx/transactors/system/Change.cpp similarity index 83% rename from src/xrpld/app/tx/detail/Change.cpp rename to src/libxrpl/tx/transactors/system/Change.cpp index a2eb587a93..c84d056c24 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/libxrpl/tx/transactors/system/Change.cpp @@ -1,14 +1,11 @@ -#include -#include -#include -#include -#include - #include +#include #include #include #include #include +#include +#include #include @@ -19,10 +16,11 @@ NotTEC Transactor::invokePreflight(PreflightContext const& ctx) { // 0 means "Allow any flags" - // The check for tfChangeMask is gated by LendingProtocol because that - // feature introduced this parameter, and it's not worth adding another + // The check for tfEnableAmendmentMask is gated by LendingProtocol because + // that feature introduced this parameter, and it's not worth adding another // amendment just for this. - if (auto const ret = preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfChangeMask : 0)) + if (auto const ret = + preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0)) return ret; auto account = ctx.tx.getAccountID(sfAccount); @@ -40,7 +38,8 @@ Transactor::invokePreflight(PreflightContext const& ctx) return temBAD_FEE; } - if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() || ctx.tx.isFieldPresent(sfSigners)) + if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() || + ctx.tx.isFieldPresent(sfSigners)) { JLOG(ctx.j.warn()) << "Change: Bad signature"; return temBAD_SIGNATURE; @@ -74,14 +73,17 @@ Change::preclaim(PreclaimContext const& ctx) // The ttFEE transaction format defines these fields as // optional, but once the XRPFees feature is enabled, they are // required. - if (!ctx.tx.isFieldPresent(sfBaseFeeDrops) || !ctx.tx.isFieldPresent(sfReserveBaseDrops) || + if (!ctx.tx.isFieldPresent(sfBaseFeeDrops) || + !ctx.tx.isFieldPresent(sfReserveBaseDrops) || !ctx.tx.isFieldPresent(sfReserveIncrementDrops)) return temMALFORMED; // The ttFEE transaction format defines these fields as // optional, but once the XRPFees feature is enabled, they are // forbidden. - if (ctx.tx.isFieldPresent(sfBaseFee) || ctx.tx.isFieldPresent(sfReferenceFeeUnits) || - ctx.tx.isFieldPresent(sfReserveBase) || ctx.tx.isFieldPresent(sfReserveIncrement)) + if (ctx.tx.isFieldPresent(sfBaseFee) || + ctx.tx.isFieldPresent(sfReferenceFeeUnits) || + ctx.tx.isFieldPresent(sfReserveBase) || + ctx.tx.isFieldPresent(sfReserveIncrement)) return temMALFORMED; } else @@ -90,25 +92,30 @@ Change::preclaim(PreclaimContext const& ctx) // as required. When the XRPFees feature was implemented, they // were changed to be optional. Until the feature has been // enabled, they are required. - if (!ctx.tx.isFieldPresent(sfBaseFee) || !ctx.tx.isFieldPresent(sfReferenceFeeUnits) || - !ctx.tx.isFieldPresent(sfReserveBase) || !ctx.tx.isFieldPresent(sfReserveIncrement)) + if (!ctx.tx.isFieldPresent(sfBaseFee) || + !ctx.tx.isFieldPresent(sfReferenceFeeUnits) || + !ctx.tx.isFieldPresent(sfReserveBase) || + !ctx.tx.isFieldPresent(sfReserveIncrement)) return temMALFORMED; // The ttFEE transaction format defines these fields as // optional, but without the XRPFees feature, they are // forbidden. - if (ctx.tx.isFieldPresent(sfBaseFeeDrops) || ctx.tx.isFieldPresent(sfReserveBaseDrops) || + if (ctx.tx.isFieldPresent(sfBaseFeeDrops) || + ctx.tx.isFieldPresent(sfReserveBaseDrops) || ctx.tx.isFieldPresent(sfReserveIncrementDrops)) return temDISABLED; } if (ctx.view.rules().enabled(featureSmartEscrow)) { - if (!ctx.tx.isFieldPresent(sfExtensionComputeLimit) || !ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + if (!ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + !ctx.tx.isFieldPresent(sfExtensionSizeLimit) || !ctx.tx.isFieldPresent(sfGasPrice)) return temMALFORMED; } else { - if (ctx.tx.isFieldPresent(sfExtensionComputeLimit) || ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + if (ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + ctx.tx.isFieldPresent(sfExtensionSizeLimit) || ctx.tx.isFieldPresent(sfGasPrice)) return temDISABLED; } @@ -207,7 +214,7 @@ Change::applyAmendment() entry[sfAmendment] = amendment; entry[sfCloseTime] = view().parentCloseTime().time_since_epoch().count(); - if (!ctx_.app.getAmendmentTable().isSupported(amendment)) + if (!ctx_.registry.getAmendmentTable().isSupported(amendment)) { JLOG(j_.warn()) << "Unsupported amendment " << amendment << " received a majority."; } @@ -218,19 +225,24 @@ Change::applyAmendment() amendments.push_back(amendment); amendmentObject->setFieldV256(sfAmendments, amendments); - ctx_.app.getAmendmentTable().enable(amendment); + ctx_.registry.getAmendmentTable().enable(amendment); - if (!ctx_.app.getAmendmentTable().isSupported(amendment)) + if (!ctx_.registry.getAmendmentTable().isSupported(amendment)) { - JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated: server blocked."; - ctx_.app.getOPs().setAmendmentBlocked(); + JLOG(j_.error()) << "Unsupported amendment " << amendment + << " activated: server blocked."; + ctx_.registry.getOPs().setAmendmentBlocked(); } } if (newMajorities.empty()) + { amendmentObject->makeFieldAbsent(sfMajorities); + } else + { amendmentObject->setFieldArray(sfMajorities, newMajorities); + } view().update(amendmentObject); @@ -249,7 +261,9 @@ Change::applyFee() feeObject = std::make_shared(k); view().insert(feeObject); } - auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) { feeObject->at(field) = tx[field]; }; + auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) { + feeObject->at(field) = tx[field]; + }; if (view().rules().enabled(featureXRPFees)) { set(feeObject, ctx_.tx, sfBaseFeeDrops); @@ -290,8 +304,9 @@ Change::applyUNLModify() return tefFAILURE; } - if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) || ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 || - !ctx_.tx.isFieldPresent(sfLedgerSequence) || !ctx_.tx.isFieldPresent(sfUNLModifyValidator)) + if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) || + ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 || !ctx_.tx.isFieldPresent(sfLedgerSequence) || + !ctx_.tx.isFieldPresent(sfUNLModifyValidator)) { JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong Tx format."; return tefFAILURE; @@ -312,8 +327,8 @@ Change::applyUNLModify() return tefFAILURE; } - JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable") << " seq=" << seq - << " validator data:" << strHex(validator); + JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable") + << " seq=" << seq << " validator data:" << strHex(validator); auto const k = keylet::negativeUNL(); SLE::pointer negUnlObject = view().peek(k); diff --git a/src/xrpld/app/tx/detail/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp similarity index 65% rename from src/xrpld/app/tx/detail/LedgerStateFix.cpp rename to src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index 43001e2fbf..0ce0720ba0 100644 --- a/src/xrpld/app/tx/detail/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -1,10 +1,9 @@ -#include -#include - #include #include #include #include +#include +#include namespace xrpl { @@ -36,15 +35,13 @@ LedgerStateFix::calculateBaseFee(ReadView const& view, STTx const& tx) TER LedgerStateFix::preclaim(PreclaimContext const& ctx) { - switch (ctx.tx[sfLedgerFixType]) + if (ctx.tx[sfLedgerFixType] == FixType::nfTokenPageLink) { - case FixType::nfTokenPageLink: { - AccountID const owner{ctx.tx[sfOwner]}; - if (!ctx.view.read(keylet::account(owner))) - return tecOBJECT_NOT_FOUND; + AccountID const owner{ctx.tx[sfOwner]}; + if (!ctx.view.read(keylet::account(owner))) + return tecOBJECT_NOT_FOUND; - return tesSUCCESS; - } + return tesSUCCESS; } // preflight is supposed to verify that only valid FixTypes get to preclaim. @@ -54,13 +51,12 @@ LedgerStateFix::preclaim(PreclaimContext const& ctx) TER LedgerStateFix::doApply() { - switch (ctx_.tx[sfLedgerFixType]) + if (ctx_.tx[sfLedgerFixType] == FixType::nfTokenPageLink) { - case FixType::nfTokenPageLink: - if (!nft::repairNFTokenDirectoryLinks(view(), ctx_.tx[sfOwner])) - return tecFAILED_PROCESSING; + if (!nft::repairNFTokenDirectoryLinks(view(), ctx_.tx[sfOwner])) + return tecFAILED_PROCESSING; - return tesSUCCESS; + return tesSUCCESS; } // preflight is supposed to verify that only valid FixTypes get to doApply. diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp similarity index 77% rename from src/xrpld/app/tx/detail/CreateTicket.cpp rename to src/libxrpl/tx/transactors/system/TicketCreate.cpp index e6965ca1cf..a064c158d1 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -1,30 +1,33 @@ -#include - #include +#include +#include +#include #include #include #include +#include namespace xrpl { TxConsequences -CreateTicket::makeTxConsequences(PreflightContext const& ctx) +TicketCreate::makeTxConsequences(PreflightContext const& ctx) { // Create TxConsequences identifying the number of sequences consumed. return TxConsequences{ctx.tx, ctx.tx[sfTicketCount]}; } NotTEC -CreateTicket::preflight(PreflightContext const& ctx) +TicketCreate::preflight(PreflightContext const& ctx) { - if (std::uint32_t const count = ctx.tx[sfTicketCount]; count < minValidCount || count > maxValidCount) + if (std::uint32_t const count = ctx.tx[sfTicketCount]; + count < minValidCount || count > maxValidCount) return temINVALID_COUNT; return tesSUCCESS; } TER -CreateTicket::preclaim(PreclaimContext const& ctx) +TicketCreate::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; auto const sleAccountRoot = ctx.view.read(keylet::account(id)); @@ -50,7 +53,7 @@ CreateTicket::preclaim(PreclaimContext const& ctx) } TER -CreateTicket::doApply() +TicketCreate::doApply() { SLE::pointer const sleAccountRoot = view().peek(keylet::account(account_)); if (!sleAccountRoot) @@ -61,13 +64,14 @@ CreateTicket::doApply() // reserve to pay fees. std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; { - XRPAmount const reserve = view().fees().accountReserve(sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); + XRPAmount const reserve = + view().fees().accountReserve(sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } - beast::Journal viewJ{ctx_.app.journal("View")}; + beast::Journal viewJ{ctx_.registry.journal("View")}; // The starting ticket sequence is the same as the current account // root sequence. Before we got here to doApply(), the transaction @@ -77,7 +81,8 @@ CreateTicket::doApply() // Sanity check that the transaction machinery really did already // increment the account root Sequence. - if (std::uint32_t const txSeq = ctx_.tx[sfSequence]; txSeq != 0 && txSeq != (firstTicketSeq - 1)) + if (std::uint32_t const txSeq = ctx_.tx[sfSequence]; + txSeq != 0 && txSeq != (firstTicketSeq - 1)) return tefINTERNAL; // LCOV_EXCL_LINE for (std::uint32_t i = 0; i < ticketCount; ++i) @@ -90,9 +95,11 @@ CreateTicket::doApply() sleTicket->setFieldU32(sfTicketSequence, curTicketSeq); view().insert(sleTicket); - auto const page = view().dirInsert(keylet::ownerDir(account_), ticketKeylet, describeOwnerDir(account_)); + auto const page = + view().dirInsert(keylet::ownerDir(account_), ticketKeylet, describeOwnerDir(account_)); - JLOG(j_.trace()) << "Creating ticket " << to_string(ticketKeylet.key) << ": " << (page ? "success" : "failure"); + JLOG(j_.trace()) << "Creating ticket " << to_string(ticketKeylet.key) << ": " + << (page ? "success" : "failure"); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/libxrpl/tx/transactors/token/Clawback.cpp similarity index 81% rename from src/xrpld/app/tx/detail/Clawback.cpp rename to src/libxrpl/tx/transactors/token/Clawback.cpp index ad4a2f24d0..e8ba3cc0c5 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/libxrpl/tx/transactors/token/Clawback.cpp @@ -1,11 +1,12 @@ -#include - #include +#include +#include #include #include #include #include #include +#include namespace xrpl { @@ -55,17 +56,12 @@ preflightHelper(PreflightContext const& ctx) return tesSUCCESS; } -std::uint32_t -Clawback::getFlagsMask(PreflightContext const& ctx) -{ - return tfClawbackMask; -} - NotTEC Clawback::preflight(PreflightContext const& ctx) { - if (auto const ret = - std::visit([&](T const&) { return preflightHelper(ctx); }, ctx.tx[sfAmount].asset().value()); + if (auto const ret = std::visit( + [&](T const&) { return preflightHelper(ctx); }, + ctx.tx[sfAmount].asset().value()); !isTesSuccess(ret)) return ret; @@ -97,7 +93,8 @@ preclaimHelper( if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || (issuerFlagsIn & lsfNoFreeze)) return tecNO_PERMISSION; - auto const sleRippleState = ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency())); + auto const sleRippleState = + ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency())); if (!sleRippleState) return tecNO_LINE; @@ -120,7 +117,8 @@ preclaimHelper( // We can't directly check the balance of trustline because // the available balance of a trustline is prone to new changes (eg. // XLS-34). So we must use `accountHolds`. - if (accountHolds(ctx.view, holder, clawAmount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j) <= beast::zero) + if (accountHolds(ctx.view, holder, clawAmount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j) <= + beast::zero) return tecINSUFFICIENT_FUNDS; return tesSUCCESS; @@ -149,7 +147,8 @@ preclaimHelper( if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder))) return tecOBJECT_NOT_FOUND; - if (accountHolds(ctx.view, holder, clawAmount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) <= + if (accountHolds( + ctx.view, holder, clawAmount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) <= beast::zero) return tecINSUFFICIENT_FUNDS; @@ -171,12 +170,18 @@ Clawback::preclaim(PreclaimContext const& ctx) // Note the order of checks - when SAV is active, this check here will make // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant. if (ctx.view.rules().enabled(featureSingleAssetVault) && isPseudoAccount(sleHolder)) + { return tecPSEUDO_ACCOUNT; - else if (sleHolder->isFieldPresent(sfAMMID)) + } + if (sleHolder->isFieldPresent(sfAMMID)) + { return tecAMM_ACCOUNT; + } return std::visit( - [&](T const&) { return preclaimHelper(ctx, *sleIssuer, issuer, holder, clawAmount); }, + [&](T const&) { + return preclaimHelper(ctx, *sleIssuer, issuer, holder, clawAmount); + }, ctx.tx[sfAmount].asset().value()); } @@ -199,9 +204,15 @@ applyHelper(ApplyContext& ctx) // Get the spendable balance. Must use `accountHolds`. STAmount const spendableAmount = accountHolds( - ctx.view(), holder, clawAmount.getCurrency(), clawAmount.getIssuer(), fhIGNORE_FREEZE, ctx.journal); + ctx.view(), + holder, + clawAmount.getCurrency(), + clawAmount.getIssuer(), + fhIGNORE_FREEZE, + ctx.journal); - return rippleCredit(ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal); + return rippleCredit( + ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal); } template <> @@ -213,8 +224,13 @@ applyHelper(ApplyContext& ctx) AccountID const holder = ctx.tx[sfHolder]; // Get the spendable balance. Must use `accountHolds`. - STAmount const spendableAmount = - accountHolds(ctx.view(), holder, clawAmount.get(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.journal); + STAmount const spendableAmount = accountHolds( + ctx.view(), + holder, + clawAmount.get(), + fhIGNORE_FREEZE, + ahIGNORE_AUTH, + ctx.journal); return rippleCredit( ctx.view(), @@ -228,7 +244,9 @@ applyHelper(ApplyContext& ctx) TER Clawback::doApply() { - return std::visit([&](T const&) { return applyHelper(ctx_); }, ctx_.tx[sfAmount].asset().value()); + return std::visit( + [&](T const&) { return applyHelper(ctx_); }, + ctx_.tx[sfAmount].asset().value()); } } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp similarity index 84% rename from src/xrpld/app/tx/detail/MPTokenAuthorize.cpp rename to src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 1eb9a0359a..77551b372b 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -1,9 +1,11 @@ -#include - #include +#include +#include +#include #include #include #include +#include namespace xrpl { @@ -36,7 +38,8 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // `holderID` is NOT used if (!holderID) { - std::shared_ptr sleMpt = ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID)); + std::shared_ptr sleMpt = + ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID)); // There is an edge case where all holders have zero balance, issuance // is legally destroyed, then outstanding MPT(s) are deleted afterwards. @@ -52,7 +55,8 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) if ((*sleMpt)[sfMPTAmount] != 0) { - auto const sleMptIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID])); + auto const sleMptIssuance = + ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID])); if (!sleMptIssuance) return tefINTERNAL; // LCOV_EXCL_LINE @@ -61,7 +65,8 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) if ((*sleMpt)[~sfLockedAmount].value_or(0) != 0) { - auto const sleMptIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID])); + auto const sleMptIssuance = + ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID])); if (!sleMptIssuance) return tefINTERNAL; // LCOV_EXCL_LINE @@ -136,7 +141,8 @@ MPTokenAuthorize::createMPToken( { auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto const ownerNode = view.dirInsert(keylet::ownerDir(account), mptokenKey, describeOwnerDir(account)); + auto const ownerNode = + view.dirInsert(keylet::ownerDir(account), mptokenKey, describeOwnerDir(account)); if (!ownerNode) return tecDIR_FULL; // LCOV_EXCL_LINE @@ -157,7 +163,13 @@ MPTokenAuthorize::doApply() { auto const& tx = ctx_.tx; return authorizeMPToken( - ctx_.view(), mPriorBalance, tx[sfMPTokenIssuanceID], account_, ctx_.journal, tx.getFlags(), tx[~sfHolder]); + ctx_.view(), + preFeeBalance_, + tx[sfMPTokenIssuanceID], + account_, + ctx_.journal, + tx.getFlags(), + tx[~sfHolder]); } } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp similarity index 86% rename from src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 9a9dc6a11c..58accf0271 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -1,8 +1,9 @@ -#include - #include +#include +#include #include #include +#include namespace xrpl { @@ -10,7 +11,8 @@ bool MPTokenIssuanceCreate::checkExtraFeatures(PreflightContext const& ctx) { if (ctx.tx.isFieldPresent(sfDomainID) && - !(ctx.rules.enabled(featurePermissionedDomains) && ctx.rules.enabled(featureSingleAssetVault))) + !(ctx.rules.enabled(featurePermissionedDomains) && + ctx.rules.enabled(featureSingleAssetVault))) return false; if (ctx.tx.isFieldPresent(sfMutableFlags) && !ctx.rules.enabled(featureDynamicMPT)) @@ -58,7 +60,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) if (auto const metadata = ctx.tx[~sfMPTokenMetadata]) { - if (metadata->length() == 0 || metadata->length() > maxMPTokenMetadataLength) + if (metadata->empty() || metadata->length() > maxMPTokenMetadataLength) return temMALFORMED; } @@ -81,7 +83,8 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate if (!acct) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - if (args.priorBalance && *(args.priorBalance) < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) + if (args.priorBalance && + *(args.priorBalance) < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) return Unexpected(tecINSUFFICIENT_RESERVE); auto const mptId = makeMptID(args.sequence, args.account); @@ -89,8 +92,8 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate // create the MPTokenIssuance { - auto const ownerNode = - view.dirInsert(keylet::ownerDir(args.account), mptIssuanceKeylet, describeOwnerDir(args.account)); + auto const ownerNode = view.dirInsert( + keylet::ownerDir(args.account), mptIssuanceKeylet, describeOwnerDir(args.account)); if (!ownerNode) return Unexpected(tecDIR_FULL); // LCOV_EXCL_LINE @@ -137,7 +140,7 @@ MPTokenIssuanceCreate::doApply() view(), j_, { - .priorBalance = mPriorBalance, + .priorBalance = preFeeBalance_, .account = account_, .sequence = tx.getSeqValue(), .flags = tx.getFlags(), diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp similarity index 88% rename from src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index cec06c5494..8ec1f37886 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -1,17 +1,11 @@ -#include - #include +#include #include #include +#include namespace xrpl { -std::uint32_t -MPTokenIssuanceDestroy::getFlagsMask(PreflightContext const& ctx) -{ - return tfMPTokenIssuanceDestroyMask; -} - NotTEC MPTokenIssuanceDestroy::preflight(PreflightContext const& ctx) { diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp similarity index 86% rename from src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index cf3b15ed78..dc5827419b 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -1,9 +1,8 @@ -#include -#include - #include #include #include +#include +#include namespace xrpl { @@ -11,7 +10,8 @@ bool MPTokenIssuanceSet::checkExtraFeatures(PreflightContext const& ctx) { return !ctx.tx.isFieldPresent(sfDomainID) || - (ctx.rules.enabled(featurePermissionedDomains) && ctx.rules.enabled(featureSingleAssetVault)); + (ctx.rules.enabled(featurePermissionedDomains) && + ctx.rules.enabled(featureSingleAssetVault)); } std::uint32_t @@ -92,9 +92,12 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) return temINVALID_FLAG; // Can not set and clear the same flag - if (std::any_of(mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags](auto const& f) { - return (*mutableFlags & f.setFlag) && (*mutableFlags & f.clearFlag); - })) + if (std::any_of( + mptMutabilityFlags.begin(), + mptMutabilityFlags.end(), + [mutableFlags](auto const& f) { + return (*mutableFlags & f.setFlag) && (*mutableFlags & f.clearFlag); + })) return temINVALID_FLAG; // Trying to set a non-zero TransferFee and clear MPTCanTransfer @@ -120,14 +123,14 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; auto const txFlags = tx.getFlags(); // this is added in case more flags will be added for MPTokenIssuanceSet // in the future. Currently unreachable. - if (txFlags & tfMPTokenIssuanceSetPermissionMask) + if (txFlags & tfMPTokenIssuanceSetMask) return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE std::unordered_set granularPermissions; @@ -153,10 +156,15 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) if (!sleMptIssuance->isFlag(lsfMPTCanLock)) { // For readability two separate `if` rather than `||` of two conditions - if (!ctx.view.rules().enabled(featureSingleAssetVault) && !ctx.view.rules().enabled(featureDynamicMPT)) + if (!ctx.view.rules().enabled(featureSingleAssetVault) && + !ctx.view.rules().enabled(featureDynamicMPT)) + { return tecNO_PERMISSION; - else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)) + } + if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)) + { return tecNO_PERMISSION; + } } // ensure it is issued by the tx submitter @@ -191,13 +199,18 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) // the ledger. auto const currentMutableFlags = sleMptIssuance->getFieldU32(sfMutableFlags); - auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { return currentMutableFlags & mutableFlag; }; + auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { + return currentMutableFlags & mutableFlag; + }; if (auto const mutableFlags = ctx.tx[~sfMutableFlags]) { if (std::any_of( - mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags, &isMutableFlag](auto const& f) { - return !isMutableFlag(f.canMutateFlag) && ((*mutableFlags & (f.setFlag | f.clearFlag))); + mptMutabilityFlags.begin(), + mptMutabilityFlags.end(), + [mutableFlags, &isMutableFlag](auto const& f) { + return !isMutableFlag(f.canMutateFlag) && + ((*mutableFlags & (f.setFlag | f.clearFlag))); })) return tecNO_PERMISSION; } @@ -231,9 +244,13 @@ MPTokenIssuanceSet::doApply() std::shared_ptr sle; if (holderID) + { sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID)); + } else + { sle = view().peek(keylet::mptIssuance(mptIssuanceID)); + } if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -242,18 +259,26 @@ MPTokenIssuanceSet::doApply() std::uint32_t flagsOut = flagsIn; if (txFlags & tfMPTLock) + { flagsOut |= lsfMPTLocked; + } else if (txFlags & tfMPTUnlock) + { flagsOut &= ~lsfMPTLocked; + } if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0)) { for (auto const& f : mptMutabilityFlags) { if (mutableFlags & f.setFlag) + { flagsOut |= f.canMutateFlag; + } else if (mutableFlags & f.clearFlag) + { flagsOut &= ~f.canMutateFlag; + } } if (mutableFlags & tmfMPTClearCanTransfer) @@ -274,23 +299,33 @@ MPTokenIssuanceSet::doApply() // - If the field is present, it must be non-zero. // Therefore, when TransferFee is 0, the field should be removed. if (transferFee == 0) + { sle->makeFieldAbsent(sfTransferFee); + } else + { sle->setFieldU16(sfTransferFee, *transferFee); + } } if (auto const metadata = ctx_.tx[~sfMPTokenMetadata]) { if (metadata->empty()) + { sle->makeFieldAbsent(sfMPTokenMetadata); + } else + { sle->setFieldVL(sfMPTokenMetadata, *metadata); + } } if (domainID) { // This is enforced in preflight. - XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance"); + XRPL_ASSERT( + sle->getType() == ltMPTOKEN_ISSUANCE, + "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance"); if (*domainID != beast::zero) { diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp similarity index 89% rename from src/xrpld/app/tx/detail/SetTrust.cpp rename to src/libxrpl/tx/transactors/token/TrustSet.cpp index ce8ca05a09..22588b977a 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -1,14 +1,15 @@ -#include -#include - #include #include +#include +#include #include #include #include #include #include #include +#include +#include namespace { @@ -47,13 +48,13 @@ computeFreezeFlags( namespace xrpl { std::uint32_t -SetTrust::getFlagsMask(PreflightContext const& ctx) +TrustSet::getFlagsMask(PreflightContext const& ctx) { return tfTrustSetMask; } NotTEC -SetTrust::preflight(PreflightContext const& ctx) +TrustSet::preflight(PreflightContext const& ctx) { auto& tx = ctx.tx; auto& j = ctx.j; @@ -77,7 +78,8 @@ SetTrust::preflight(PreflightContext const& ctx) if (saLimitAmount.native()) { - JLOG(j.trace()) << "Malformed transaction: specifies native limit " << saLimitAmount.getFullText(); + JLOG(j.trace()) << "Malformed transaction: specifies native limit " + << saLimitAmount.getFullText(); return temBAD_LIMIT; } @@ -106,7 +108,7 @@ SetTrust::preflight(PreflightContext const& ctx) } NotTEC -SetTrust::checkPermission(ReadView const& view, STTx const& tx) +TrustSet::checkPermission(ReadView const& view, STTx const& tx) { auto const delegate = tx[~sfDelegate]; if (!delegate) @@ -118,7 +120,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) if (!sle) return terNO_DELEGATE_PERMISSION; - if (checkTxPermission(sle, tx) == tesSUCCESS) + if (isTesSuccess(checkTxPermission(sle, tx))) return tesSUCCESS; std::uint32_t const txFlags = tx.getFlags(); @@ -133,8 +135,8 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) return terNO_DELEGATE_PERMISSION; auto const saLimitAmount = tx.getFieldAmount(sfLimitAmount); - auto const sleRippleState = - view.read(keylet::line(tx[sfAccount], saLimitAmount.getIssuer(), saLimitAmount.getCurrency())); + auto const sleRippleState = view.read( + keylet::line(tx[sfAccount], saLimitAmount.getIssuer(), saLimitAmount.getCurrency())); // if the trustline does not exist, granular permissions are // not allowed to create trustline @@ -153,8 +155,9 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) // updating LimitAmount is not allowed only with granular permissions, // unless there's a new granular permission for this in the future. - auto const curLimit = tx[sfAccount] > saLimitAmount.getIssuer() ? sleRippleState->getFieldAmount(sfHighLimit) - : sleRippleState->getFieldAmount(sfLowLimit); + auto const curLimit = tx[sfAccount] > saLimitAmount.getIssuer() + ? sleRippleState->getFieldAmount(sfHighLimit) + : sleRippleState->getFieldAmount(sfLowLimit); STAmount saLimitAllow = saLimitAmount; saLimitAllow.setIssuer(tx[sfAccount]); @@ -166,7 +169,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx) } TER -SetTrust::preclaim(PreclaimContext const& ctx) +TrustSet::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; @@ -194,7 +197,8 @@ SetTrust::preclaim(PreclaimContext const& ctx) // This might be nullptr auto const sleDst = ctx.view.read(keylet::account(uDstAccountID)); - if ((ammEnabled(ctx.view.rules()) || ctx.view.rules().enabled(featureSingleAssetVault)) && sleDst == nullptr) + if ((ammEnabled(ctx.view.rules()) || ctx.view.rules().enabled(featureSingleAssetVault)) && + sleDst == nullptr) return tecNO_DST; // If the destination has opted to disallow incoming trustlines @@ -212,7 +216,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) // pass } else + { return tecNO_PERMISSION; + } } // In general, trust lines to pseudo accounts are not permitted, unless @@ -222,7 +228,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) if (sleDst && isPseudoAccount(sleDst)) { // If destination is AMM and the trustline doesn't exist then only allow - // SetTrust if the asset is AMM LP token and AMM is not in empty state. + // TrustSet if the asset is AMM LP token and AMM is not in empty state. if (sleDst->isFieldPresent(sfAMMID)) { if (ctx.view.exists(keylet::line(id, uDstAccountID, currency))) @@ -231,13 +237,20 @@ SetTrust::preclaim(PreclaimContext const& ctx) } else if (auto const ammSle = ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)})) { - if (auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance); lpTokens == beast::zero) + auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance); + if (lpTokens == beast::zero) + { return tecAMM_EMPTY; - else if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) + } + if (lpTokens.getCurrency() != saLimitAmount.getCurrency()) + { return tecNO_PERMISSION; + } } else + { return tecINTERNAL; // LCOV_EXCL_LINE + } } else if (sleDst->isFieldPresent(sfVaultID) || sleDst->isFieldPresent(sfLoanBrokerID)) { @@ -246,7 +259,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) // else pass } else + { return tecPSEUDO_ACCOUNT; + } } // Checking all freeze/deep freeze flag invariants. @@ -276,8 +291,8 @@ SetTrust::preclaim(PreclaimContext const& ctx) auto const sleRippleState = ctx.view.read(keylet::line(id, uDstAccountID, currency)); std::uint32_t uFlags = sleRippleState ? sleRippleState->getFieldU32(sfFlags) : 0u; // Computing expected trust line state - uFlags = - computeFreezeFlags(uFlags, bHigh, bNoFreeze, bSetFreeze, bClearFreeze, bSetDeepFreeze, bClearDeepFreeze); + uFlags = computeFreezeFlags( + uFlags, bHigh, bNoFreeze, bSetFreeze, bClearFreeze, bSetDeepFreeze, bClearDeepFreeze); auto const frozen = uFlags & (bHigh ? lsfHighFreeze : lsfLowFreeze); auto const deepFrozen = uFlags & (bHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze); @@ -295,7 +310,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) } TER -SetTrust::doApply() +TrustSet::doApply() { TER terResult = tesSUCCESS; @@ -352,7 +367,7 @@ SetTrust::doApply() bool const bSetDeepFreeze = (uTxFlags & tfSetDeepFreeze); bool const bClearDeepFreeze = (uTxFlags & tfClearDeepFreeze); - auto viewJ = ctx_.app.journal("View"); + auto viewJ = ctx_.registry.journal("View"); SLE::pointer sleDst = view().peek(keylet::account(uDstAccountID)); @@ -373,10 +388,10 @@ SetTrust::doApply() STAmount saLowLimit; STAmount saHighBalance; STAmount saHighLimit; - std::uint32_t uLowQualityIn; - std::uint32_t uLowQualityOut; - std::uint32_t uHighQualityIn; - std::uint32_t uHighQualityOut; + std::uint32_t uLowQualityIn = 0; + std::uint32_t uLowQualityOut = 0; + std::uint32_t uHighQualityIn = 0; + std::uint32_t uHighQualityOut = 0; auto const& uLowAccountID = !bHigh ? account_ : uDstAccountID; auto const& uHighAccountID = bHigh ? account_ : uDstAccountID; SLE::ref sleLowAccount = !bHigh ? sle : sleDst; @@ -470,11 +485,14 @@ SetTrust::doApply() if (bSetNoRipple && !bClearNoRipple) { if ((bHigh ? saHighBalance : saLowBalance) >= beast::zero) + { uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple); - + } else + { // Cannot set noRipple on a negative balance. return tecNO_PERMISSION; + } } else if (bClearNoRipple && !bSetNoRipple) { @@ -483,8 +501,14 @@ SetTrust::doApply() // Have to use lsfNoFreeze to maintain pre-deep freeze behavior bool const bNoFreeze = sle->isFlag(lsfNoFreeze); - uFlagsOut = - computeFreezeFlags(uFlagsOut, bHigh, bNoFreeze, bSetFreeze, bClearFreeze, bSetDeepFreeze, bClearDeepFreeze); + uFlagsOut = computeFreezeFlags( + uFlagsOut, + bHigh, + bNoFreeze, + bSetFreeze, + bClearFreeze, + bSetDeepFreeze, + bClearDeepFreeze); if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0; @@ -496,13 +520,13 @@ SetTrust::doApply() bool const bHighDefRipple = sleHighAccount->getFlags() & lsfDefaultRipple; bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || - ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || (uFlagsOut & lsfLowFreeze) || saLowLimit || - saLowBalance > beast::zero; + ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || (uFlagsOut & lsfLowFreeze) || + saLowLimit || saLowBalance > beast::zero; bool const bLowReserveClear = !bLowReserveSet; bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || - ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || (uFlagsOut & lsfHighFreeze) || saHighLimit || - saHighBalance > beast::zero; + ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || (uFlagsOut & lsfHighFreeze) || + saHighLimit || saHighBalance > beast::zero; bool const bHighReserveClear = !bHighReserveSet; bool const bDefault = bLowReserveClear && bHighReserveClear; @@ -561,7 +585,7 @@ SetTrust::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (bReserveIncrease && mPriorBalance < reserveCreate) + else if (bReserveIncrease && preFeeBalance_ < reserveCreate) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -589,8 +613,8 @@ SetTrust::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (mPriorBalance < reserveCreate) // Reserve is not scaled by - // load. + else if (preFeeBalance_ < reserveCreate) // Reserve is not scaled by + // load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; diff --git a/src/xrpld/app/tx/detail/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp similarity index 88% rename from src/xrpld/app/tx/detail/VaultClawback.cpp rename to src/libxrpl/tx/transactors/vault/VaultClawback.cpp index 5aa63fac1a..a650aed310 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -1,7 +1,7 @@ -#include -// #include #include +#include +#include #include #include #include @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include @@ -27,8 +29,10 @@ VaultClawback::preflight(PreflightContext const& ctx) { // Note, zero amount is valid, it means "all". It is also the default. if (*amount < beast::zero) + { return temBAD_AMOUNT; - else if (isXRP(amount->asset())) + } + if (isXRP(amount->asset())) { JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP."; return temMALFORMED; @@ -115,7 +119,12 @@ VaultClawback::preclaim(PreclaimContext const& ctx) if (amount != beast::zero) { Number const& sharesHeld = accountHolds( - ctx.view, holder, share, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, ctx.j); + ctx.view, + holder, + share, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + ctx.j); // The VaultOwner must burn all shares if (amount != sharesHeld) @@ -219,8 +228,13 @@ VaultClawback::assetsToClawback( if (clawbackAmount == beast::zero) { - auto const sharesDestroyed = - accountHolds(view(), holder, share, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_); + auto const sharesDestroyed = accountHolds( + view(), + holder, + share, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE @@ -233,7 +247,8 @@ VaultClawback::assetsToClawback( try { { - auto const maybeShares = assetsToSharesWithdraw(vault, sleShareIssuance, assetsRecovered); + auto const maybeShares = + assetsToSharesWithdraw(vault, sleShareIssuance, assetsRecovered); if (!maybeShares) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE sharesDestroyed = *maybeShares; @@ -252,14 +267,15 @@ VaultClawback::assetsToClawback( // otherwise the corresponding assets might breach the // AssetsAvailable { - auto const maybeShares = - assetsToSharesWithdraw(vault, sleShareIssuance, assetsRecovered, TruncateShares::yes); + auto const maybeShares = assetsToSharesWithdraw( + vault, sleShareIssuance, assetsRecovered, TruncateShares::yes); if (!maybeShares) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE sharesDestroyed = *maybeShares; } - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE assetsRecovered = *maybeAssets; @@ -280,7 +296,8 @@ VaultClawback::assetsToClawback( << "VaultClawback: overflow error with" << " scale=" << (int)vault->at(sfScale).value() // << ", assetsTotal=" << vault->at(sfAssetsTotal).value() - << ", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount) << ", amount=" << clawbackAmount.value(); + << ", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount) + << ", amount=" << clawbackAmount.value(); return Unexpected(tecPATH_DRY); } @@ -314,7 +331,8 @@ VaultClawback::doApply() [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); XRPL_ASSERT( - lossUnrealized <= (assetsTotal - assetsAvailable), "xrpl::VaultClawback::doApply : loss and assets do balance"); + lossUnrealized <= (assetsTotal - assetsAvailable), + "xrpl::VaultClawback::doApply : loss and assets do balance"); AccountID holder = tx[sfHolder]; STAmount sharesDestroyed = {share}; @@ -323,8 +341,13 @@ VaultClawback::doApply() // The Owner is burning shares if (account_ == vault->at(sfOwner) && amount.asset() == share) { - sharesDestroyed = - accountHolds(view(), holder, share, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_); + sharesDestroyed = accountHolds( + view(), + holder, + share, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); } else // The Issuer is clawbacking vault assets { @@ -347,7 +370,8 @@ VaultClawback::doApply() auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from holder to vault. - if (auto const ter = accountSend(view(), holder, vaultAccount, sharesDestroyed, j_, WaiveTransferFee::Yes); + if (auto const ter = + accountSend(view(), holder, vaultAccount, sharesDestroyed, j_, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -356,7 +380,8 @@ VaultClawback::doApply() // Keep MPToken if holder is the vault owner. if (holder != vault->at(sfOwner)) { - if (auto const ter = removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); isTesSuccess(ter)) + if (auto const ter = removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); + isTesSuccess(ter)) { JLOG(j_.debug()) // << "VaultClawback: removed empty MPToken for vault shares" @@ -380,7 +405,8 @@ VaultClawback::doApply() if (assetsRecovered > beast::zero) { // Transfer assets from vault to issuer. - if (auto const ter = accountSend(view(), vaultAccount, account_, assetsRecovered, j_, WaiveTransferFee::Yes); + if (auto const ter = accountSend( + view(), vaultAccount, account_, assetsRecovered, j_, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp similarity index 85% rename from src/xrpld/app/tx/detail/VaultCreate.cpp rename to src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 6711a7e13f..0fc074bae2 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -1,8 +1,7 @@ -#include -#include -#include - #include +#include +#include +#include #include #include #include @@ -14,6 +13,9 @@ #include #include #include +#include +#include +#include namespace xrpl { @@ -51,9 +53,13 @@ VaultCreate::preflight(PreflightContext const& ctx) if (auto const domain = ctx.tx[~sfDomainID]) { if (*domain == beast::zero) + { return temMALFORMED; - else if ((ctx.tx.getFlags() & tfVaultPrivate) == 0) + } + if ((ctx.tx.getFlags() & tfVaultPrivate) == 0) + { return temMALFORMED; // DomainID only allowed on private vaults + } } if (auto const assetMax = ctx.tx[~sfAssetsMaximum]) @@ -64,7 +70,7 @@ VaultCreate::preflight(PreflightContext const& ctx) if (auto const metadata = ctx.tx[~sfMPTokenMetadata]) { - if (metadata->length() == 0 || metadata->length() > maxMPTokenMetadataLength) + if (metadata->empty() || metadata->length() > maxMPTokenMetadataLength) return temMALFORMED; } @@ -138,7 +144,7 @@ VaultCreate::doApply() // We will create Vault and PseudoAccount, hence increase OwnerCount by 2 adjustOwnerCount(view(), owner, 2, j_); auto const ownerCount = owner->at(sfOwnerCount); - if (mPriorBalance < view().fees().accountReserve(ownerCount)) + if (preFeeBalance_ < view().fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); @@ -148,11 +154,12 @@ VaultCreate::doApply() auto pseudoId = pseudo->at(sfAccount); auto asset = tx[sfAsset]; - if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_); !isTesSuccess(ter)) + if (auto ter = addEmptyHolding(view(), pseudoId, preFeeBalance_, asset, j_); !isTesSuccess(ter)) return ter; - std::uint8_t const scale = - (asset.holds() || asset.native()) ? 0 : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale); + std::uint8_t const scale = (asset.holds() || asset.native()) + ? 0 + : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale); auto txFlags = tx.getFlags(); std::uint32_t mptFlags = 0; @@ -197,23 +204,28 @@ VaultCreate::doApply() vault->at(sfData) = *value; // Required field, default to vaultStrategyFirstComeFirstServe if (auto value = tx[~sfWithdrawalPolicy]) + { vault->at(sfWithdrawalPolicy) = *value; + } else + { vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe; + } if (scale) vault->at(sfScale) = scale; view().insert(vault); // Explicitly create MPToken for the vault owner - if (auto const err = authorizeMPToken(view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal); + if (auto const err = + authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal); !isTesSuccess(err)) return err; // If the vault is private, set the authorized flag for the vault owner if (txFlags & tfVaultPrivate) { - if (auto const err = - authorizeMPToken(view(), mPriorBalance, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); + if (auto const err = authorizeMPToken( + view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); !isTesSuccess(err)) return err; } diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp similarity index 95% rename from src/xrpld/app/tx/detail/VaultDelete.cpp rename to src/libxrpl/tx/transactors/vault/VaultDelete.cpp index f5c388762f..1e2e46e165 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -1,12 +1,14 @@ -#include - #include +#include +#include +#include #include #include #include #include #include #include +#include namespace xrpl { @@ -113,7 +115,8 @@ VaultDelete::doApply() // Try to remove MPToken for vault shares for the vault owner if it exists. if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, account_))) { - if (auto const ter = removeEmptyHolding(view(), account_, MPTIssue(shareMPTID), j_); !isTesSuccess(ter)) + if (auto const ter = removeEmptyHolding(view(), account_, MPTIssue(shareMPTID), j_); + !isTesSuccess(ter)) { // LCOV_EXCL_START JLOG(j_.error()) // diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp similarity index 87% rename from src/xrpld/app/tx/detail/VaultDeposit.cpp rename to src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index d5fc0e4ad6..04b249d211 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -1,8 +1,8 @@ -#include -#include - -#include #include +#include +#include +#include +#include #include #include #include @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace xrpl { @@ -102,7 +104,9 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) return err; } else + { return tecNO_AUTH; + } } // Source MPToken must exist (if asset is an MPT) @@ -146,7 +150,8 @@ VaultDeposit::doApply() // Note, vault owner is always authorized if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner)) { - if (auto const err = enforceMPTokenAuthorization(ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_); + if (auto const err = enforceMPTokenAuthorization( + ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_); !isTesSuccess(err)) return err; } @@ -155,8 +160,8 @@ VaultDeposit::doApply() // No authorization needed, but must ensure there is MPToken if (!view().exists(keylet::mptoken(mptIssuanceID, account_))) { - if (auto const err = - authorizeMPToken(view(), mPriorBalance, mptIssuanceID->value(), account_, ctx_.journal); + if (auto const err = authorizeMPToken( + view(), preFeeBalance_, mptIssuanceID->value(), account_, ctx_.journal); !isTesSuccess(err)) return err; } @@ -165,10 +170,11 @@ VaultDeposit::doApply() if (vault->isFlag(lsfVaultPrivate)) { // This follows from the reverse of the outer enclosing if condition - XRPL_ASSERT(account_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner"); + XRPL_ASSERT( + account_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner"); if (auto const err = authorizeMPToken( view(), - mPriorBalance, // priorBalance + preFeeBalance_, // priorBalance mptIssuanceID->value(), // mptIssuanceID sleIssuance->at(sfIssuer), // account ctx_.journal, @@ -195,8 +201,10 @@ VaultDeposit::doApply() auto const maybeAssets = sharesToAssetsDeposit(vault, sleIssuance, sharesCreated); if (!maybeAssets) + { return tecINTERNAL; // LCOV_EXCL_LINE - else if (*maybeAssets > amount) + } + if (*maybeAssets > amount) { // LCOV_EXCL_START JLOG(j_.error()) << "VaultDeposit: would take more than offered."; @@ -218,7 +226,8 @@ VaultDeposit::doApply() } XRPL_ASSERT( - sharesCreated.asset() != assetsDeposited.asset(), "xrpl::VaultDeposit::doApply : assets are not shares"); + sharesCreated.asset() != assetsDeposited.asset(), + "xrpl::VaultDeposit::doApply : assets are not shares"); vault->at(sfAssetsTotal) += assetsDeposited; vault->at(sfAssetsAvailable) += assetsDeposited; @@ -230,7 +239,8 @@ VaultDeposit::doApply() return tecLIMIT_EXCEEDED; // Transfer assets from depositor to vault. - if (auto const ter = accountSend(view(), account_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes); + if (auto const ter = + accountSend(view(), account_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -250,7 +260,8 @@ VaultDeposit::doApply() } // Transfer shares from vault to depositor. - if (auto const ter = accountSend(view(), vaultAccount, account_, sharesCreated, j_, WaiveTransferFee::Yes); + if (auto const ter = + accountSend(view(), vaultAccount, account_, sharesCreated, j_, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultSet.cpp b/src/libxrpl/tx/transactors/vault/VaultSet.cpp similarity index 97% rename from src/xrpld/app/tx/detail/VaultSet.cpp rename to src/libxrpl/tx/transactors/vault/VaultSet.cpp index c0aaf5bac6..532edcdcef 100644 --- a/src/xrpld/app/tx/detail/VaultSet.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultSet.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -9,6 +7,7 @@ #include #include #include +#include namespace xrpl { @@ -48,7 +47,8 @@ VaultSet::preflight(PreflightContext const& ctx) } } - if (!ctx.tx.isFieldPresent(sfDomainID) && !ctx.tx.isFieldPresent(sfAssetsMaximum) && !ctx.tx.isFieldPresent(sfData)) + if (!ctx.tx.isFieldPresent(sfDomainID) && !ctx.tx.isFieldPresent(sfAssetsMaximum) && + !ctx.tx.isFieldPresent(sfData)) { JLOG(ctx.j.debug()) << "VaultSet: nothing is being updated."; return temMALFORMED; diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp similarity index 89% rename from src/xrpld/app/tx/detail/VaultWithdraw.cpp rename to src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 88616e34f8..e4c671b462 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -1,7 +1,7 @@ -#include - -#include #include +#include +#include +#include #include #include #include @@ -9,6 +9,7 @@ #include #include #include +#include namespace xrpl { @@ -145,7 +146,9 @@ VaultWithdraw::doApply() assetsWithdrawn = *maybeAssets; } else + { return tefINTERNAL; // LCOV_EXCL_LINE + } } catch (std::overflow_error const&) { @@ -155,12 +158,18 @@ VaultWithdraw::doApply() << "VaultWithdraw: overflow error with" << " scale=" << (int)vault->at(sfScale).value() // << ", assetsTotal=" << vault->at(sfAssetsTotal).value() - << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) << ", amount=" << amount.value(); + << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) + << ", amount=" << amount.value(); return tecPATH_DRY; } - if (accountHolds(view(), account_, share, FreezeHandling::fhZERO_IF_FROZEN, AuthHandling::ahIGNORE_AUTH, j_) < - sharesRedeemed) + if (accountHolds( + view(), + account_, + share, + FreezeHandling::fhZERO_IF_FROZEN, + AuthHandling::ahIGNORE_AUTH, + j_) < sharesRedeemed) { JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares"; return tecINSUFFICIENT_FUNDS; @@ -170,7 +179,8 @@ VaultWithdraw::doApply() auto assetsTotal = vault->at(sfAssetsTotal); [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); XRPL_ASSERT( - lossUnrealized <= (assetsTotal - assetsAvailable), "xrpl::VaultWithdraw::doApply : loss and assets do balance"); + lossUnrealized <= (assetsTotal - assetsAvailable), + "xrpl::VaultWithdraw::doApply : loss and assets do balance"); // The vault must have enough assets on hand. The vault may hold assets // that it has already pledged. That is why we look at AssetAvailable @@ -187,7 +197,8 @@ VaultWithdraw::doApply() auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from depositor to vault. - if (auto const ter = accountSend(view(), account_, vaultAccount, sharesRedeemed, j_, WaiveTransferFee::Yes); + if (auto const ter = + accountSend(view(), account_, vaultAccount, sharesRedeemed, j_, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -196,7 +207,8 @@ VaultWithdraw::doApply() // Keep MPToken if holder is the vault owner. if (account_ != vault->at(sfOwner)) { - if (auto const ter = removeEmptyHolding(view(), account_, sharesRedeemed.asset(), j_); isTesSuccess(ter)) + if (auto const ter = removeEmptyHolding(view(), account_, sharesRedeemed.asset(), j_); + isTesSuccess(ter)) { JLOG(j_.debug()) // << "VaultWithdraw: removed empty MPToken for vault shares" @@ -221,7 +233,8 @@ VaultWithdraw::doApply() associateAsset(*vault, vaultAsset); - return doWithdraw(view(), ctx_.tx, account_, dstAcct, vaultAccount, mPriorBalance, assetsWithdrawn, j_); + return doWithdraw( + view(), ctx_.tx, account_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_); } } // namespace xrpl diff --git a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp b/src/libxrpl/tx/wasm/HostFuncImpl.cpp similarity index 81% rename from src/xrpld/app/wasm/detail/HostFuncImpl.cpp rename to src/libxrpl/tx/wasm/HostFuncImpl.cpp index dfff35727f..7f6dd4255c 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImpl.cpp @@ -1,7 +1,6 @@ -#include - #include #include +#include namespace xrpl { @@ -25,7 +24,10 @@ WasmHostFunctionsImpl::updateData(Slice const& data) // ========================================================= Expected -WasmHostFunctionsImpl::checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) +WasmHostFunctionsImpl::checkSignature( + Slice const& message, + Slice const& signature, + Slice const& pubkey) const { if (!publicKeyType(pubkey)) return Unexpected(HostFunctionError::INVALID_PARAMS); @@ -35,7 +37,7 @@ WasmHostFunctionsImpl::checkSignature(Slice const& message, Slice const& signatu } Expected -WasmHostFunctionsImpl::computeSha512HalfHash(Slice const& data) +WasmHostFunctionsImpl::computeSha512HalfHash(Slice const& data) const { auto const hash = sha512Half(data); return hash; diff --git a/src/xrpld/app/wasm/detail/HostFuncImplFloat.cpp b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp similarity index 91% rename from src/xrpld/app/wasm/detail/HostFuncImplFloat.cpp rename to src/libxrpl/tx/wasm/HostFuncImplFloat.cpp index abff96a08d..030148da14 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplFloat.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp @@ -1,7 +1,6 @@ -#include - #include #include +#include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 @@ -143,7 +142,8 @@ public: auto const data = msg.getData(); #ifdef DEBUG_OUTPUT - std::cout << "m: " << std::setw(20) << mantissa() << ", e: " << std::setw(12) << exponent() << ", hex: "; + std::cout << "m: " << std::setw(20) << mantissa() << ", e: " << std::setw(12) << exponent() + << ", hex: "; std::cout << std::hex << std::uppercase << std::setfill('0'); for (auto const& c : data) std::cout << std::setw(2) << (unsigned)c << " "; @@ -162,7 +162,8 @@ struct FloatState MantissaRange::mantissa_scale oldScale_; bool good_; - FloatState(int32_t mode) : oldMode_(Number::getround()), oldScale_(Number::getMantissaScale()), good_(false) + FloatState(int32_t mode) + : oldMode_(Number::getround()), oldScale_(Number::getMantissaScale()), good_(false) { if (mode < Number::rounding_mode::to_nearest || mode > Number::rounding_mode::upward) return; @@ -219,6 +220,7 @@ floatFromIntImpl(int64_t x, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -240,6 +242,7 @@ floatFromUintImpl(uint64_t x, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -260,6 +263,7 @@ floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode) } catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } @@ -282,6 +286,7 @@ floatCompareImpl(Slice const& x, Slice const& y) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -309,6 +314,7 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -335,6 +341,7 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -361,6 +368,7 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -386,6 +394,7 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) } catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } @@ -413,6 +422,7 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -443,6 +453,7 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -468,6 +479,7 @@ floatLogImpl(Slice const& x, int32_t mode) // LCOV_EXCL_START catch (...) { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP @@ -480,67 +492,67 @@ floatLogImpl(Slice const& x, int32_t mode) // ========================================================= Expected -WasmHostFunctionsImpl::floatFromInt(int64_t x, int32_t mode) +WasmHostFunctionsImpl::floatFromInt(int64_t x, int32_t mode) const { return wasm_float::floatFromIntImpl(x, mode); } Expected -WasmHostFunctionsImpl::floatFromUint(uint64_t x, int32_t mode) +WasmHostFunctionsImpl::floatFromUint(uint64_t x, int32_t mode) const { return wasm_float::floatFromUintImpl(x, mode); } Expected -WasmHostFunctionsImpl::floatSet(int64_t mantissa, int32_t exponent, int32_t mode) +WasmHostFunctionsImpl::floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const { return wasm_float::floatSetImpl(mantissa, exponent, mode); } Expected -WasmHostFunctionsImpl::floatCompare(Slice const& x, Slice const& y) +WasmHostFunctionsImpl::floatCompare(Slice const& x, Slice const& y) const { return wasm_float::floatCompareImpl(x, y); } Expected -WasmHostFunctionsImpl::floatAdd(Slice const& x, Slice const& y, int32_t mode) +WasmHostFunctionsImpl::floatAdd(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatAddImpl(x, y, mode); } Expected -WasmHostFunctionsImpl::floatSubtract(Slice const& x, Slice const& y, int32_t mode) +WasmHostFunctionsImpl::floatSubtract(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatSubtractImpl(x, y, mode); } Expected -WasmHostFunctionsImpl::floatMultiply(Slice const& x, Slice const& y, int32_t mode) +WasmHostFunctionsImpl::floatMultiply(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatMultiplyImpl(x, y, mode); } Expected -WasmHostFunctionsImpl::floatDivide(Slice const& x, Slice const& y, int32_t mode) +WasmHostFunctionsImpl::floatDivide(Slice const& x, Slice const& y, int32_t mode) const { return wasm_float::floatDivideImpl(x, y, mode); } Expected -WasmHostFunctionsImpl::floatRoot(Slice const& x, int32_t n, int32_t mode) +WasmHostFunctionsImpl::floatRoot(Slice const& x, int32_t n, int32_t mode) const { return wasm_float::floatRootImpl(x, n, mode); } Expected -WasmHostFunctionsImpl::floatPower(Slice const& x, int32_t n, int32_t mode) +WasmHostFunctionsImpl::floatPower(Slice const& x, int32_t n, int32_t mode) const { return wasm_float::floatPowerImpl(x, n, mode); } Expected -WasmHostFunctionsImpl::floatLog(Slice const& x, int32_t mode) +WasmHostFunctionsImpl::floatLog(Slice const& x, int32_t mode) const { return wasm_float::floatLogImpl(x, mode); } diff --git a/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp b/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp similarity index 86% rename from src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp rename to src/libxrpl/tx/wasm/HostFuncImplGetter.cpp index 0a3247604a..1344d4f5bc 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp @@ -1,7 +1,6 @@ -#include - #include #include +#include namespace xrpl { @@ -35,77 +34,69 @@ getAnyFieldData(STBase const* obj) case STI_UNKNOWN: case STI_NOTPRESENT: return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - break; - // LCOV_EXCL_STOP + // LCOV_EXCL_STOP + case STI_OBJECT: case STI_ARRAY: case STI_VECTOR256: return Unexpected(HostFunctionError::NOT_LEAF_FIELD); - break; + case STI_ACCOUNT: { auto const* account(static_cast(obj)); auto const& data = account->value(); return Bytes{data.begin(), data.end()}; } - break; - case STI_AMOUNT: - // will be processed by serializer - break; + case STI_ISSUE: { auto const* issue(static_cast(obj)); Asset const& asset(issue->value()); // XRP and IOU will be processed by serializer if (asset.holds()) { - // MPT auto const& mptIssue = asset.get(); auto const& mptID = mptIssue.getMptID(); return Bytes{mptID.cbegin(), mptID.cend()}; } + break; // Use serializer } - break; + case STI_VL: { auto const* vl(static_cast(obj)); auto const& data = vl->value(); return Bytes{data.begin(), data.end()}; } - break; - case STI_UINT16: { + + case STI_UINT16: return getIntBytes(obj); - } - break; - case STI_UINT32: { + + case STI_UINT32: return getIntBytes(obj); - } + // LCOV_EXCL_START - case STI_UINT64: { + case STI_UINT64: return getIntBytes(obj); - } - break; - case STI_INT32: { + + case STI_INT32: return getIntBytes(obj); - } - break; - case STI_INT64: { + + case STI_INT64: return getIntBytes(obj); - } - // LCOV_EXCL_STOP - break; + // LCOV_EXCL_STOP + case STI_UINT256: { auto const* uint256Obj(static_cast(obj)); auto const& data = uint256Obj->value(); return Bytes{data.begin(), data.end()}; } - break; + + case STI_AMOUNT: default: - break; // default to serializer + break; // Use serializer } Serializer msg; obj->add(msg); - auto const data = msg.getData(); - - return data; + return msg.getData(); } static Expected @@ -170,7 +161,7 @@ locateField(STObject const& obj, Slice const& locator) if (STI_ARRAY == field->getSType()) { auto const* arr = static_cast(field); - if (sfieldCode >= arr->size()) + if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, arr->size())) return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS); field = &(arr->operator[](sfieldCode)); } @@ -188,7 +179,7 @@ locateField(STObject const& obj, Slice const& locator) else if (STI_VECTOR256 == field->getSType()) { auto const* v = static_cast(field); - if (sfieldCode >= v->size()) + if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, v->size())) return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS); return FieldValue(&(v->operator[](sfieldCode))); } @@ -231,7 +222,7 @@ WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) if (cacheIdx == 0) { for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx) - if (!cache[cacheIdx]) + if (!cache_[cacheIdx]) break; } else @@ -242,8 +233,8 @@ WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) if (cacheIdx >= MAX_CACHE) return Unexpected(HostFunctionError::SLOTS_FULL); - cache[cacheIdx] = ctx.view().read(keylet); - if (!cache[cacheIdx]) + cache_[cacheIdx] = ctx_.view().read(keylet); + if (!cache_[cacheIdx]) return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); return cacheIdx + 1; // return 1-based index } @@ -251,13 +242,13 @@ WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) // Subsection: top level getters Expected -WasmHostFunctionsImpl::getTxField(SField const& fname) +WasmHostFunctionsImpl::getTxField(SField const& fname) const { - return detail::getAnyFieldData(ctx.tx.peekAtPField(fname)); + return detail::getAnyFieldData(ctx_.tx.peekAtPField(fname)); } Expected -WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname) +WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname) const { auto const sle = getCurrentLedgerObj(); if (!sle.has_value()) @@ -266,20 +257,20 @@ WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname) } Expected -WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname) +WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname) const { auto const normalizedIdx = normalizeCacheIndex(cacheIdx); if (!normalizedIdx.has_value()) return Unexpected(normalizedIdx.error()); - return detail::getAnyFieldData(cache[normalizedIdx.value()]->peekAtPField(fname)); + return detail::getAnyFieldData(cache_[normalizedIdx.value()]->peekAtPField(fname)); } // Subsection: nested getters Expected -WasmHostFunctionsImpl::getTxNestedField(Slice const& locator) +WasmHostFunctionsImpl::getTxNestedField(Slice const& locator) const { - auto const r = detail::locateField(ctx.tx, locator); + auto const r = detail::locateField(ctx_.tx, locator); if (!r) return Unexpected(r.error()); @@ -287,7 +278,7 @@ WasmHostFunctionsImpl::getTxNestedField(Slice const& locator) } Expected -WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator) +WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator) const { auto const sle = getCurrentLedgerObj(); if (!sle.has_value()) @@ -301,13 +292,13 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator) } Expected -WasmHostFunctionsImpl::getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) +WasmHostFunctionsImpl::getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const { auto const normalizedIdx = normalizeCacheIndex(cacheIdx); if (!normalizedIdx.has_value()) return Unexpected(normalizedIdx.error()); - auto const r = detail::locateField(*cache[normalizedIdx.value()], locator); + auto const r = detail::locateField(*cache_[normalizedIdx.value()], locator); if (!r) return Unexpected(r.error()); @@ -317,12 +308,12 @@ WasmHostFunctionsImpl::getLedgerObjNestedField(int32_t cacheIdx, Slice const& lo // Subsection: array length getters Expected -WasmHostFunctionsImpl::getTxArrayLen(SField const& fname) +WasmHostFunctionsImpl::getTxArrayLen(SField const& fname) const { if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256) return Unexpected(HostFunctionError::NO_ARRAY); - auto const* field = ctx.tx.peekAtPField(fname); + auto const* field = ctx_.tx.peekAtPField(fname); if (detail::noField(field)) return Unexpected(HostFunctionError::FIELD_NOT_FOUND); @@ -330,7 +321,7 @@ WasmHostFunctionsImpl::getTxArrayLen(SField const& fname) } Expected -WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname) +WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname) const { if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256) return Unexpected(HostFunctionError::NO_ARRAY); @@ -347,7 +338,7 @@ WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname) } Expected -WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) +WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const { if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256) return Unexpected(HostFunctionError::NO_ARRAY); @@ -356,7 +347,7 @@ WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fnam if (!normalizedIdx.has_value()) return Unexpected(normalizedIdx.error()); - auto const* field = cache[normalizedIdx.value()]->peekAtPField(fname); + auto const* field = cache_[normalizedIdx.value()]->peekAtPField(fname); if (detail::noField(field)) return Unexpected(HostFunctionError::FIELD_NOT_FOUND); @@ -366,9 +357,9 @@ WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fnam // Subsection: nested array length getters Expected -WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator) +WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator) const { - auto const r = detail::locateField(ctx.tx, locator); + auto const r = detail::locateField(ctx_.tx, locator); if (!r) return Unexpected(r.error()); @@ -377,7 +368,7 @@ WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator) } Expected -WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator) +WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator) const { auto const sle = getCurrentLedgerObj(); if (!sle.has_value()) @@ -391,13 +382,13 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator) } Expected -WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) +WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const { auto const normalizedIdx = normalizeCacheIndex(cacheIdx); if (!normalizedIdx.has_value()) return Unexpected(normalizedIdx.error()); - auto const r = detail::locateField(*cache[normalizedIdx.value()], locator); + auto const r = detail::locateField(*cache_[normalizedIdx.value()], locator); if (!r) return Unexpected(r.error()); diff --git a/src/xrpld/app/wasm/detail/HostFuncImplKeylet.cpp b/src/libxrpl/tx/wasm/HostFuncImplKeylet.cpp similarity index 86% rename from src/xrpld/app/wasm/detail/HostFuncImplKeylet.cpp rename to src/libxrpl/tx/wasm/HostFuncImplKeylet.cpp index a006b584e4..8953790464 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplKeylet.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplKeylet.cpp @@ -1,12 +1,11 @@ -#include - #include #include +#include namespace xrpl { Expected -WasmHostFunctionsImpl::accountKeylet(AccountID const& account) +WasmHostFunctionsImpl::accountKeylet(AccountID const& account) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -15,7 +14,7 @@ WasmHostFunctionsImpl::accountKeylet(AccountID const& account) } Expected -WasmHostFunctionsImpl::ammKeylet(Asset const& issue1, Asset const& issue2) +WasmHostFunctionsImpl::ammKeylet(Asset const& issue1, Asset const& issue2) const { if (issue1 == issue2) return Unexpected(HostFunctionError::INVALID_PARAMS); @@ -29,7 +28,7 @@ WasmHostFunctionsImpl::ammKeylet(Asset const& issue1, Asset const& issue2) } Expected -WasmHostFunctionsImpl::checkKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::checkKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -38,7 +37,10 @@ WasmHostFunctionsImpl::checkKeylet(AccountID const& account, std::uint32_t seq) } Expected -WasmHostFunctionsImpl::credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) +WasmHostFunctionsImpl::credentialKeylet( + AccountID const& subject, + AccountID const& issuer, + Slice const& credentialType) const { if (!subject || !issuer) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -52,7 +54,7 @@ WasmHostFunctionsImpl::credentialKeylet(AccountID const& subject, AccountID cons } Expected -WasmHostFunctionsImpl::didKeylet(AccountID const& account) +WasmHostFunctionsImpl::didKeylet(AccountID const& account) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -61,7 +63,7 @@ WasmHostFunctionsImpl::didKeylet(AccountID const& account) } Expected -WasmHostFunctionsImpl::delegateKeylet(AccountID const& account, AccountID const& authorize) +WasmHostFunctionsImpl::delegateKeylet(AccountID const& account, AccountID const& authorize) const { if (!account || !authorize) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -73,6 +75,7 @@ WasmHostFunctionsImpl::delegateKeylet(AccountID const& account, AccountID const& Expected WasmHostFunctionsImpl::depositPreauthKeylet(AccountID const& account, AccountID const& authorize) + const { if (!account || !authorize) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -83,7 +86,7 @@ WasmHostFunctionsImpl::depositPreauthKeylet(AccountID const& account, AccountID } Expected -WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -92,7 +95,10 @@ WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq) } Expected -WasmHostFunctionsImpl::lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) +WasmHostFunctionsImpl::lineKeylet( + AccountID const& account1, + AccountID const& account2, + Currency const& currency) const { if (!account1 || !account2) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -106,7 +112,7 @@ WasmHostFunctionsImpl::lineKeylet(AccountID const& account1, AccountID const& ac } Expected -WasmHostFunctionsImpl::mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) +WasmHostFunctionsImpl::mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const { if (!issuer) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -116,7 +122,7 @@ WasmHostFunctionsImpl::mptIssuanceKeylet(AccountID const& issuer, std::uint32_t } Expected -WasmHostFunctionsImpl::mptokenKeylet(MPTID const& mptid, AccountID const& holder) +WasmHostFunctionsImpl::mptokenKeylet(MPTID const& mptid, AccountID const& holder) const { if (!mptid) return Unexpected(HostFunctionError::INVALID_PARAMS); @@ -128,7 +134,7 @@ WasmHostFunctionsImpl::mptokenKeylet(MPTID const& mptid, AccountID const& holder } Expected -WasmHostFunctionsImpl::nftOfferKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::nftOfferKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -137,7 +143,7 @@ WasmHostFunctionsImpl::nftOfferKeylet(AccountID const& account, std::uint32_t se } Expected -WasmHostFunctionsImpl::offerKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::offerKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -146,7 +152,7 @@ WasmHostFunctionsImpl::offerKeylet(AccountID const& account, std::uint32_t seq) } Expected -WasmHostFunctionsImpl::oracleKeylet(AccountID const& account, std::uint32_t documentId) +WasmHostFunctionsImpl::oracleKeylet(AccountID const& account, std::uint32_t documentId) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -155,7 +161,10 @@ WasmHostFunctionsImpl::oracleKeylet(AccountID const& account, std::uint32_t docu } Expected -WasmHostFunctionsImpl::paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) +WasmHostFunctionsImpl::paychanKeylet( + AccountID const& account, + AccountID const& destination, + std::uint32_t seq) const { if (!account || !destination) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -166,7 +175,7 @@ WasmHostFunctionsImpl::paychanKeylet(AccountID const& account, AccountID const& } Expected -WasmHostFunctionsImpl::permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -175,7 +184,7 @@ WasmHostFunctionsImpl::permissionedDomainKeylet(AccountID const& account, std::u } Expected -WasmHostFunctionsImpl::signersKeylet(AccountID const& account) +WasmHostFunctionsImpl::signersKeylet(AccountID const& account) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -184,7 +193,7 @@ WasmHostFunctionsImpl::signersKeylet(AccountID const& account) } Expected -WasmHostFunctionsImpl::ticketKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::ticketKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -193,7 +202,7 @@ WasmHostFunctionsImpl::ticketKeylet(AccountID const& account, std::uint32_t seq) } Expected -WasmHostFunctionsImpl::vaultKeylet(AccountID const& account, std::uint32_t seq) +WasmHostFunctionsImpl::vaultKeylet(AccountID const& account, std::uint32_t seq) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); diff --git a/src/xrpld/app/wasm/detail/HostFuncImplLedgerHeader.cpp b/src/libxrpl/tx/wasm/HostFuncImplLedgerHeader.cpp similarity index 51% rename from src/xrpld/app/wasm/detail/HostFuncImplLedgerHeader.cpp rename to src/libxrpl/tx/wasm/HostFuncImplLedgerHeader.cpp index 57ef79e11a..bb7e25614a 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplLedgerHeader.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplLedgerHeader.cpp @@ -1,7 +1,6 @@ -#include -#include - +#include #include +#include namespace xrpl { @@ -10,41 +9,41 @@ namespace xrpl { // ========================================================= Expected -WasmHostFunctionsImpl::getLedgerSqn() +WasmHostFunctionsImpl::getLedgerSqn() const { - return ctx.view().seq(); + return ctx_.view().seq(); } Expected -WasmHostFunctionsImpl::getParentLedgerTime() +WasmHostFunctionsImpl::getParentLedgerTime() const { - return ctx.view().parentCloseTime().time_since_epoch().count(); + return ctx_.view().parentCloseTime().time_since_epoch().count(); } Expected -WasmHostFunctionsImpl::getParentLedgerHash() +WasmHostFunctionsImpl::getParentLedgerHash() const { - return ctx.view().header().parentHash; + return ctx_.view().header().parentHash; } Expected -WasmHostFunctionsImpl::getBaseFee() +WasmHostFunctionsImpl::getBaseFee() const { - return ctx.view().fees().base.drops(); + return ctx_.view().fees().base.drops(); } Expected -WasmHostFunctionsImpl::isAmendmentEnabled(uint256 const& amendmentId) +WasmHostFunctionsImpl::isAmendmentEnabled(uint256 const& amendmentId) const { - return ctx.view().rules().enabled(amendmentId); + return ctx_.view().rules().enabled(amendmentId); } Expected -WasmHostFunctionsImpl::isAmendmentEnabled(std::string_view const& amendmentName) +WasmHostFunctionsImpl::isAmendmentEnabled(std::string_view const& amendmentName) const { - auto const& table = ctx.app.getAmendmentTable(); + auto const& table = ctx_.registry.getAmendmentTable(); auto const amendment = table.find(std::string(amendmentName)); - return ctx.view().rules().enabled(amendment); + return ctx_.view().rules().enabled(amendment); } } // namespace xrpl diff --git a/src/xrpld/app/wasm/detail/HostFuncImplNFT.cpp b/src/libxrpl/tx/wasm/HostFuncImplNFT.cpp similarity index 73% rename from src/xrpld/app/wasm/detail/HostFuncImplNFT.cpp rename to src/libxrpl/tx/wasm/HostFuncImplNFT.cpp index 7c4ee00406..fbf85d8a12 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplNFT.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplNFT.cpp @@ -1,8 +1,7 @@ -#include -#include - #include #include +#include +#include namespace xrpl { @@ -11,7 +10,7 @@ namespace xrpl { // ========================================================= Expected -WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId) +WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId) const { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -19,7 +18,7 @@ WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId) if (!nftId) return Unexpected(HostFunctionError::INVALID_PARAMS); - auto obj = nft::findToken(ctx.view(), account, nftId); + auto obj = nft::findToken(ctx_.view(), account, nftId); if (!obj) return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); @@ -32,7 +31,7 @@ WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId) } Expected -WasmHostFunctionsImpl::getNFTIssuer(uint256 const& nftId) +WasmHostFunctionsImpl::getNFTIssuer(uint256 const& nftId) const { auto const issuer = nft::getIssuer(nftId); if (!issuer) @@ -42,25 +41,25 @@ WasmHostFunctionsImpl::getNFTIssuer(uint256 const& nftId) } Expected -WasmHostFunctionsImpl::getNFTTaxon(uint256 const& nftId) +WasmHostFunctionsImpl::getNFTTaxon(uint256 const& nftId) const { return nft::toUInt32(nft::getTaxon(nftId)); } Expected -WasmHostFunctionsImpl::getNFTFlags(uint256 const& nftId) +WasmHostFunctionsImpl::getNFTFlags(uint256 const& nftId) const { return nft::getFlags(nftId); } Expected -WasmHostFunctionsImpl::getNFTTransferFee(uint256 const& nftId) +WasmHostFunctionsImpl::getNFTTransferFee(uint256 const& nftId) const { return nft::getTransferFee(nftId); } Expected -WasmHostFunctionsImpl::getNFTSerial(uint256 const& nftId) +WasmHostFunctionsImpl::getNFTSerial(uint256 const& nftId) const { return nft::getSerial(nftId); } diff --git a/src/xrpld/app/wasm/detail/HostFuncImplTrace.cpp b/src/libxrpl/tx/wasm/HostFuncImplTrace.cpp similarity index 80% rename from src/xrpld/app/wasm/detail/HostFuncImplTrace.cpp rename to src/libxrpl/tx/wasm/HostFuncImplTrace.cpp index c3b0b95529..33f9dc8e2b 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplTrace.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplTrace.cpp @@ -1,7 +1,6 @@ -#include - #include #include +#include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 @@ -10,11 +9,13 @@ namespace xrpl { Expected -WasmHostFunctionsImpl::trace(std::string_view const& msg, Slice const& data, bool asHex) +WasmHostFunctionsImpl::trace(std::string_view const& msg, Slice const& data, bool asHex) const { if (!asHex) { - log(msg, [&data] { return std::string_view(reinterpret_cast(data.data()), data.size()); }); + log(msg, [&data] { + return std::string_view(reinterpret_cast(data.data()), data.size()); + }); } else { @@ -30,28 +31,28 @@ WasmHostFunctionsImpl::trace(std::string_view const& msg, Slice const& data, boo } Expected -WasmHostFunctionsImpl::traceNum(std::string_view const& msg, int64_t data) +WasmHostFunctionsImpl::traceNum(std::string_view const& msg, int64_t data) const { log(msg, [data] { return data; }); return 0; } Expected -WasmHostFunctionsImpl::traceAccount(std::string_view const& msg, AccountID const& account) +WasmHostFunctionsImpl::traceAccount(std::string_view const& msg, AccountID const& account) const { log(msg, [&account] { return toBase58(account); }); return 0; } Expected -WasmHostFunctionsImpl::traceFloat(std::string_view const& msg, Slice const& data) +WasmHostFunctionsImpl::traceFloat(std::string_view const& msg, Slice const& data) const { log(msg, [&data] { return wasm_float::floatToString(data); }); return 0; } Expected -WasmHostFunctionsImpl::traceAmount(std::string_view const& msg, STAmount const& amount) +WasmHostFunctionsImpl::traceAmount(std::string_view const& msg, STAmount const& amount) const { log(msg, [&amount] { return amount.getFullText(); }); return 0; diff --git a/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp b/src/libxrpl/tx/wasm/HostFuncWrapper.cpp similarity index 93% rename from src/xrpld/app/wasm/detail/HostFuncWrapper.cpp rename to src/libxrpl/tx/wasm/HostFuncWrapper.cpp index 34cfac416e..656e51f75e 100644 --- a/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp +++ b/src/libxrpl/tx/wasm/HostFuncWrapper.cpp @@ -1,17 +1,25 @@ -#include -#include -#include - #include +#include +#include +#include #include +#include +#include #include +#include +#include namespace xrpl { using SFieldCRef = std::reference_wrapper; static int32_t -setData(InstanceWrapper const* runtime, int32_t dst, int32_t dstSize, uint8_t const* src, int32_t srcSize) +setData( + InstanceWrapper const* runtime, + int32_t dst, + int32_t dstSize, + uint8_t const* src, + int32_t srcSize) { if (!srcSize) return 0; // LCOV_EXCL_LINE @@ -215,8 +223,9 @@ getDataAsset(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) if (slice->size() == (AccountID::bytes + Currency::bytes)) { - auto const issue = - Issue(Currency::fromVoid(slice->data()), AccountID::fromVoid(slice->data() + Currency::bytes)); + auto const issue = Issue( + Currency::fromVoid(slice->data()), + AccountID::fromVoid(slice->data() + Currency::bytes)); if (issue.native()) return Unexpected(HostFunctionError::INVALID_PARAMS); @@ -271,13 +280,23 @@ returnResult( { return hfResult( results, - setData(runtime, params->data[index].of.i32, params->data[index + 1].of.i32, res->data(), res->size())); + setData( + runtime, + params->data[index].of.i32, + params->data[index + 1].of.i32, + res->data(), + res->size())); } else if constexpr (std::is_same_v) { return hfResult( results, - setData(runtime, params->data[index].of.i32, params->data[index + 1].of.i32, res->data(), res->size())); + setData( + runtime, + params->data[index].of.i32, + params->data[index + 1].of.i32, + res->data(), + res->size())); } else if constexpr (std::is_same_v) { @@ -318,9 +337,9 @@ checkGas(void* env) auto const* runtime = reinterpret_cast(hf->getRT()); if (!runtime) { - wasm_trap_t* trap = - reinterpret_cast(WasmEngine::instance().newTrap("hf no runtime")); // LCOV_EXCL_LINE - return Unexpected(trap); // LCOV_EXCL_LINE + wasm_trap_t* trap = reinterpret_cast( + WasmEngine::instance().newTrap("hf no runtime")); // LCOV_EXCL_LINE + return Unexpected(trap); // LCOV_EXCL_LINE } int64_t const gas = runtime->getGas(); @@ -329,14 +348,15 @@ checkGas(void* env) if (runtime->setGas(x) < 0) { - wasm_trap_t* trap = - reinterpret_cast(WasmEngine::instance().newTrap("can't set gas")); // LCOV_EXCL_LINE - return Unexpected(trap); // LCOV_EXCL_LINE + wasm_trap_t* trap = reinterpret_cast( + WasmEngine::instance().newTrap("can't set gas")); // LCOV_EXCL_LINE + return Unexpected(trap); // LCOV_EXCL_LINE } if (gas < impFunc.gas) { - wasm_trap_t* trap = reinterpret_cast(WasmEngine::instance().newTrap("hf out of gas")); + wasm_trap_t* trap = + reinterpret_cast(WasmEngine::instance().newTrap("hf out of gas")); return Unexpected(trap); } @@ -526,7 +546,10 @@ getTxNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* r } wasm_trap_t* -getCurrentLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +getCurrentLedgerObjNestedField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) { if (auto g = checkGas(env); !g) return g.error(); // LCOV_EXCL_LINE @@ -539,7 +562,8 @@ getCurrentLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, was { return hfResult(results, bytes.error()); } - return returnResult(runtime, params, results, hf->getCurrentLedgerObjNestedField(*bytes), index); + return returnResult( + runtime, params, results, hf->getCurrentLedgerObjNestedField(*bytes), index); } wasm_trap_t* @@ -563,7 +587,8 @@ getLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_v return hfResult(results, bytes.error()); } - return returnResult(runtime, params, results, hf->getLedgerObjNestedField(*cache, *bytes), index); + return returnResult( + runtime, params, results, hf->getLedgerObjNestedField(*cache, *bytes), index); } wasm_trap_t* @@ -645,7 +670,10 @@ getTxNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t } wasm_trap_t* -getCurrentLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +getCurrentLedgerObjNestedArrayLen_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) { if (auto g = checkGas(env); !g) return g.error(); // LCOV_EXCL_LINE @@ -659,7 +687,8 @@ getCurrentLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, return hfResult(results, bytes.error()); } - return returnResult(runtime, params, results, hf->getCurrentLedgerObjNestedArrayLen(*bytes), index); + return returnResult( + runtime, params, results, hf->getCurrentLedgerObjNestedArrayLen(*bytes), index); } wasm_trap_t* getLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) @@ -681,7 +710,8 @@ getLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_va { return hfResult(results, bytes.error()); } - return returnResult(runtime, params, results, hf->getLedgerObjNestedArrayLen(*cache, *bytes), index); + return returnResult( + runtime, params, results, hf->getLedgerObjNestedArrayLen(*cache, *bytes), index); } wasm_trap_t* @@ -729,7 +759,8 @@ checkSignature_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* res return hfResult(results, pubkey.error()); } - return returnResult(runtime, params, results, hf->checkSignature(*message, *signature, *pubkey), index); + return returnResult( + runtime, params, results, hf->checkSignature(*message, *signature, *pubkey), index); } wasm_trap_t* @@ -788,7 +819,8 @@ ammKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) return hfResult(results, issue2.error()); } - return returnResult(runtime, params, results, hf->ammKeylet(issue1.value(), issue2.value()), index); + return returnResult( + runtime, params, results, hf->ammKeylet(issue1.value(), issue2.value()), index); } wasm_trap_t* @@ -842,7 +874,8 @@ credentialKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* r return hfResult(results, credType.error()); } - return returnResult(runtime, params, results, hf->credentialKeylet(*subj, *iss, *credType), index); + return returnResult( + runtime, params, results, hf->credentialKeylet(*subj, *iss, *credType), index); } wasm_trap_t* @@ -866,7 +899,8 @@ delegateKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* res return hfResult(results, authorize.error()); } - return returnResult(runtime, params, results, hf->delegateKeylet(acc.value(), authorize.value()), index); + return returnResult( + runtime, params, results, hf->delegateKeylet(acc.value(), authorize.value()), index); } wasm_trap_t* @@ -890,7 +924,8 @@ depositPreauthKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_ return hfResult(results, authorize.error()); } - return returnResult(runtime, params, results, hf->depositPreauthKeylet(acc.value(), authorize.value()), index); + return returnResult( + runtime, params, results, hf->depositPreauthKeylet(acc.value(), authorize.value()), index); } wasm_trap_t* @@ -962,7 +997,12 @@ lineKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results return hfResult(results, currency.error()); } - return returnResult(runtime, params, results, hf->lineKeylet(acc1.value(), acc2.value(), currency.value()), index); + return returnResult( + runtime, + params, + results, + hf->lineKeylet(acc1.value(), acc2.value(), currency.value()), + index); } wasm_trap_t* @@ -986,7 +1026,8 @@ mptIssuanceKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* return hfResult(results, seq.error()); } - return returnResult(runtime, params, results, hf->mptIssuanceKeylet(acc.value(), seq.value()), index); + return returnResult( + runtime, params, results, hf->mptIssuanceKeylet(acc.value(), seq.value()), index); } wasm_trap_t* @@ -1040,7 +1081,8 @@ nftOfferKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* res return hfResult(results, seq.error()); } - return returnResult(runtime, params, results, hf->nftOfferKeylet(acc.value(), seq.value()), index); + return returnResult( + runtime, params, results, hf->nftOfferKeylet(acc.value(), seq.value()), index); } wasm_trap_t* @@ -1117,7 +1159,8 @@ paychanKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* resu return hfResult(results, seq.error()); } - return returnResult(runtime, params, results, hf->paychanKeylet(acc.value(), dest.value(), seq.value()), index); + return returnResult( + runtime, params, results, hf->paychanKeylet(acc.value(), dest.value(), seq.value()), index); } wasm_trap_t* @@ -1141,7 +1184,8 @@ permissionedDomainKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_ return hfResult(results, seq.error()); } - return returnResult(runtime, params, results, hf->permissionedDomainKeylet(acc.value(), seq.value()), index); + return returnResult( + runtime, params, results, hf->permissionedDomainKeylet(acc.value(), seq.value()), index); } wasm_trap_t* @@ -1183,7 +1227,8 @@ ticketKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* resul return hfResult(results, seq.error()); } - return returnResult(runtime, params, results, hf->ticketKeylet(acc.value(), seq.value()), index); + return returnResult( + runtime, params, results, hf->ticketKeylet(acc.value(), seq.value()), index); } wasm_trap_t* @@ -1355,6 +1400,10 @@ trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { return hfResult(results, asHex.error()); // LCOV_EXCL_LINE } + if (*asHex != 0 && *asHex != 1) + { + return hfResult(results, HostFunctionError::INVALID_PARAMS); + } return returnResult(runtime, params, results, hf->trace(*msg, *data, *asHex), index); } @@ -1825,14 +1874,16 @@ testGetDataIncrement() int index = 0; auto const result = getDataString(&runtime, ¶ms, index); - if (!result || result.value() != std::string_view(reinterpret_cast(buffer.data()), 5) || + if (!result || + result.value() != std::string_view(reinterpret_cast(buffer.data()), 5) || index != 2) return false; } { // test account - AccountID const id(calcAccountID(generateKeyPair(KeyType::secp256k1, generateSeed("alice")).first)); + AccountID const id( + calcAccountID(generateKeyPair(KeyType::secp256k1, generateSeed("alice")).first)); wasm_val_vec_t params = {2, &values[0]}; diff --git a/src/xrpld/app/wasm/detail/WasmVM.cpp b/src/libxrpl/tx/wasm/WasmVM.cpp similarity index 88% rename from src/xrpld/app/wasm/detail/WasmVM.cpp rename to src/libxrpl/tx/wasm/WasmVM.cpp index 1cdbc8a78f..c8a1e27048 100644 --- a/src/xrpld/app/wasm/detail/WasmVM.cpp +++ b/src/libxrpl/tx/wasm/WasmVM.cpp @@ -2,14 +2,13 @@ // #define DEBUG_OUTPUT 1 #endif -#include -#include -#include - #include #include #include #include +#include +#include +#include #include @@ -89,13 +88,13 @@ setCommonHostFunctions(HostFunctions* hfs, ImportVec& i) // clang-format on } -std::shared_ptr +ImportVec createWasmImport(HostFunctions& hfs) { - std::shared_ptr i(std::make_shared()); + ImportVec i; - setCommonHostFunctions(&hfs, *i); - WASM_IMPORT_FUNC2(*i, updateData, "update_data", &hfs, 1000); + setCommonHostFunctions(&hfs, i); + WASM_IMPORT_FUNC2(i, updateData, "update_data", &hfs, 1000); return i; } @@ -103,16 +102,17 @@ createWasmImport(HostFunctions& hfs) Expected runEscrowWasm( Bytes const& wasmCode, - std::shared_ptr const& hfs, + HostFunctions& hfs, + int64_t gasLimit, std::string_view funcName, - std::vector const& params, - int64_t gasLimit) + std::vector const& params) { // create VM and set cost limit auto& vm = WasmEngine::instance(); // vm.initMaxPages(MAX_PAGES); - auto const ret = vm.run(wasmCode, funcName, params, createWasmImport(*hfs), hfs, gasLimit, hfs->getJournal()); + auto const ret = + vm.run(wasmCode, hfs, gasLimit, funcName, params, createWasmImport(hfs), hfs.getJournal()); // std::cout << "runEscrowWasm, mod size: " << wasmCode.size() // << ", gasLimit: " << gasLimit << ", funcName: " << funcName; @@ -134,7 +134,7 @@ runEscrowWasm( NotTEC preflightEscrowWasm( Bytes const& wasmCode, - std::shared_ptr const& hfs, + HostFunctions& hfs, std::string_view funcName, std::vector const& params) { @@ -142,14 +142,15 @@ preflightEscrowWasm( auto& vm = WasmEngine::instance(); // vm.initMaxPages(MAX_PAGES); - auto const ret = vm.check(wasmCode, funcName, params, createWasmImport(*hfs), hfs, hfs->getJournal()); + auto const ret = + vm.check(wasmCode, hfs, funcName, params, createWasmImport(hfs), hfs.getJournal()); return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -WasmEngine::WasmEngine() : impl(std::make_unique()) +WasmEngine::WasmEngine() : impl_(std::make_unique()) { } @@ -163,39 +164,39 @@ WasmEngine::instance() Expected, TER> WasmEngine::run( Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gasLimit, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, - int64_t gasLimit, + ImportVec const& imports, beast::Journal j) { - return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j); + return impl_->run(wasmCode, hfs, gasLimit, funcName, params, imports, j); } NotTEC WasmEngine::check( Bytes const& wasmCode, + HostFunctions& hfs, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, + ImportVec const& imports, beast::Journal j) { - return impl->check(wasmCode, funcName, params, imports, hfs, j); + return impl_->check(wasmCode, hfs, funcName, params, imports, j); } void* WasmEngine::newTrap(std::string const& msg) { - return impl->newTrap(msg); + return impl_->newTrap(msg); } // LCOV_EXCL_START beast::Journal WasmEngine::getJournal() const { - return impl->getJournal(); + return impl_->getJournal(); } // LCOV_EXCL_STOP diff --git a/src/xrpld/app/wasm/detail/WasmiVM.cpp b/src/libxrpl/tx/wasm/WasmiVM.cpp similarity index 83% rename from src/xrpld/app/wasm/detail/WasmiVM.cpp rename to src/libxrpl/tx/wasm/WasmiVM.cpp index 6ef0315b97..6d264c5e50 100644 --- a/src/xrpld/app/wasm/detail/WasmiVM.cpp +++ b/src/libxrpl/tx/wasm/WasmiVM.cpp @@ -1,6 +1,5 @@ -#include - #include +#include #include @@ -30,7 +29,8 @@ print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog) if (error_message.size) { - j << "WASMI Error: " << msg << ", " << std::string_view(error_message.data, error_message.size - 1); + j << "WASMI Error: " << msg << ", " + << std::string_view(error_message.data, error_message.size - 1); } else j << "WASMI Error: " << msg; @@ -51,10 +51,16 @@ print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog) } // namespace InstancePtr -InstanceWrapper::init(StorePtr& s, ModulePtr& m, WasmExternVec& expt, WasmExternVec const& imports, beast::Journal j) +InstanceWrapper::init( + StorePtr& s, + ModulePtr& m, + WasmExternVec& expt, + WasmExternVec const& imports, + beast::Journal j) { wasm_trap_t* trap = nullptr; - InstancePtr mi = InstancePtr(wasm_instance_new(s.get(), m.get(), &imports.vec_, &trap), &wasm_instance_delete); + InstancePtr mi = InstancePtr( + wasm_instance_new(s.get(), m.get(), &imports.vec_, &trap), &wasm_instance_delete); if (!mi || trap) { @@ -76,7 +82,11 @@ InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) : instance_(nullptr, &wasm } // LCOV_EXCL_STOP -InstanceWrapper::InstanceWrapper(StorePtr& s, ModulePtr& m, WasmExternVec const& imports, beast::Journal j) +InstanceWrapper::InstanceWrapper( + StorePtr& s, + ModulePtr& m, + WasmExternVec const& imports, + beast::Journal j) : store_(s.get()), instance_(init(s, m, exports_, imports, j)), j_(j) { } @@ -99,7 +109,8 @@ InstanceWrapper::operator=(InstanceWrapper&& o) return *this; } -InstanceWrapper::operator bool() const +InstanceWrapper:: +operator bool() const { return static_cast(instance_); } @@ -232,7 +243,7 @@ ModuleWrapper::ModuleWrapper( StorePtr& s, Bytes const& wasmBin, bool instantiate, - std::shared_ptr const& imports, + ImportVec const& imports, beast::Journal j) : module_(init(s, wasmBin, j)), j_(j) { @@ -259,7 +270,8 @@ ModuleWrapper::operator=(ModuleWrapper&& o) return *this; } -ModuleWrapper::operator bool() const +ModuleWrapper:: +operator bool() const { return instanceWrap_; } @@ -319,14 +331,14 @@ makeImpReturn(WasmImportFunc const& imp) } WasmExternVec -ModuleWrapper::buildImports(StorePtr& s, std::shared_ptr const& imports) +ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) { WasmImporttypeVec importTypes; wasm_module_imports(module_.get(), &importTypes.vec_); if (!importTypes.vec_.size) return {}; - if (!imports) + if (imports.empty()) throw std::runtime_error("Missing imports"); WasmExternVec wimports(importTypes.vec_.size); @@ -343,14 +355,15 @@ ModuleWrapper::buildImports(StorePtr& s, std::shared_ptr const& impor wasm_externkind_t const itype = wasm_externtype_kind(wasm_importtype_type(importType)); if ((itype) != WASM_EXTERN_FUNC) - throw std::runtime_error("Invalid import type " + std::to_string(itype)); // LCOV_EXCL_LINE + throw std::runtime_error( + "Invalid import type " + std::to_string(itype)); // LCOV_EXCL_LINE // for multi-module support // if ((W_ENV != modName) && (W_HOST_LIB != modName)) // continue; bool impSet = false; - for (auto const& obj : *imports) + for (auto const& obj : imports) { auto const& imp = obj.second; if (imp.name != fieldName) @@ -366,7 +379,11 @@ ModuleWrapper::buildImports(StorePtr& s, std::shared_ptr const& impor results.release(); wasm_func_t* func = wasm_func_new_with_env( - s.get(), ftype.get(), reinterpret_cast(imp.wrap), (void*)&obj, nullptr); + s.get(), + ftype.get(), + reinterpret_cast(imp.wrap), + (void*)&obj, + nullptr); if (!func) { // LCOV_EXCL_START @@ -414,7 +431,8 @@ ModuleWrapper::getFuncType(std::string_view funcName) const auto const* exp_type(exportTypes_.vec_.data[i]); wasm_name_t const* name = wasm_exporttype_name(exp_type); wasm_externtype_t const* exn_type = wasm_exporttype_type(exp_type); - if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC && funcName == std::string_view(name->data, name->size)) + if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC && + funcName == std::string_view(name->data, name->size)) { return wasm_externtype_as_functype(const_cast(exn_type)); } @@ -500,7 +518,11 @@ WasmiEngine::WasmiEngine() : engine_(init()), store_(nullptr, &wasm_store_delete } int -WasmiEngine::addModule(Bytes const& wasmCode, bool instantiate, int64_t gas) +WasmiEngine::addModule( + Bytes const& wasmCode, + bool instantiate, + ImportVec const& imports, + int64_t gas) { moduleWrap_.reset(); store_.reset(); // to free the memory before creating new store @@ -518,7 +540,7 @@ WasmiEngine::addModule(Bytes const& wasmCode, bool instantiate, int64_t gas) // LCOV_EXCL_STOP } - moduleWrap_ = std::make_unique(store_, wasmCode, instantiate, imports_, j_); + moduleWrap_ = std::make_unique(store_, wasmCode, instantiate, imports, j_); if (!moduleWrap_) throw std::runtime_error("can't create module wrapper"); // LCOV_EXCL_LINE @@ -533,7 +555,7 @@ WasmiEngine::addModule(Bytes const& wasmCode, bool instantiate, int64_t gas) // } FuncInfo -WasmiEngine::getFunc(std::string_view funcName) +WasmiEngine::getFunc(std::string_view funcName) const { return moduleWrap_->getFunc(funcName); } @@ -554,19 +576,6 @@ WasmiEngine::convertParams(std::vector const& params) case WT_I64: v.push_back(WASM_I64_VAL(p.of.i64)); break; - // LCOV_EXCL_STOP - case WT_U8V: { - auto mem = getMem(); - if (!mem.s) - throw std::runtime_error("no memory exported"); // LCOV_EXCL_LINE - auto const sz = p.of.u8v.sz; - auto const ptr = allocate(sz); - memcpy(mem.p + ptr, p.of.u8v.d, sz); - v.push_back(WASM_I32_VAL(ptr)); - v.push_back(WASM_I32_VAL(sz)); - } - break; - // LCOV_EXCL_START default: throw std::runtime_error("unknown parameter type: " + std::to_string(p.type)); break; @@ -651,7 +660,8 @@ WasmiEngine::call(FuncInfo const& f, std::vector& in) // if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); // // wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); } - wasm_val_vec_t const inv = in.empty() ? wasm_val_vec_t WASM_EMPTY_VEC : wasm_val_vec_t{in.size(), in.data()}; + wasm_val_vec_t const inv = + in.empty() ? wasm_val_vec_t WASM_EMPTY_VEC : wasm_val_vec_t{in.size(), in.data()}; #ifdef SHOW_CALL_TIME auto const start = usecs(); @@ -693,22 +703,6 @@ WasmiEngine::call(FuncInfo const& f, std::vector& in, std::int64_t p return call(f, in, std::forward(args)...); } -template -WasmiResult -WasmiEngine::call(FuncInfo const& f, std::vector& in, uint8_t const* d, int32_t sz, Types&&... args) -{ - auto mem = getMem(); - if (!mem.s) - throw std::runtime_error("no memory exported"); // LCOV_EXCL_LINE - - auto const ptr = allocate(sz); - memcpy(mem.p + ptr, d, sz); - - add_param(in, ptr); - add_param(in, static_cast(sz)); - return call(f, in, std::forward(args)...); -} - template WasmiResult WasmiEngine::call(FuncInfo const& f, std::vector& in, Bytes const& p, Types&&... args) @@ -729,26 +723,22 @@ checkImports(ImportVec const& imports, HostFunctions* hfs) Expected, TER> WasmiEngine::run( Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gas, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, - int64_t gas, + ImportVec const& imports, beast::Journal j) { j_ = j; - if (!wasmCode.empty()) - { // save values for reuse - imports_ = imports; - hfs_ = hfs; - } + if (gas <= 0) + return Unexpected(temBAD_AMOUNT); try { - if (imports_) - checkImports(*imports_, hfs.get()); - return runHlp(wasmCode, funcName, params, gas); + checkImports(imports, &hfs); + return runHlp(wasmCode, hfs, gas, funcName, params, imports); } catch (std::exception const& e) { @@ -764,22 +754,29 @@ WasmiEngine::run( } Expected, TER> -WasmiEngine::runHlp(Bytes const& wasmCode, std::string_view funcName, std::vector const& params, int64_t gas) +WasmiEngine::runHlp( + Bytes const& wasmCode, + HostFunctions& hfs, + int64_t gas, + std::string_view funcName, + std::vector const& params, + ImportVec const& imports) { // currently only 1 module support, possible parallel UT run std::lock_guard lg(m_); + if (wasmCode.empty()) + throw std::runtime_error("empty module"); + if (!hfs.checkSelf()) + throw std::runtime_error("hfs isn't clean"); + // Create and instantiate the module. - if (!wasmCode.empty()) - { - [[maybe_unused]] int const m = addModule(wasmCode, true, gas); - } + [[maybe_unused]] int const m = addModule(wasmCode, true, imports, gas); if (!moduleWrap_ || !moduleWrap_->instanceWrap_) throw std::runtime_error("no instance"); // LCOV_EXCL_LINE - if (hfs_) - hfs_->setRT(&getRT()); + hfs.setRT(&getRT()); // Call main auto const f = getFunc(!funcName.empty() ? funcName : "_start"); @@ -797,11 +794,12 @@ WasmiEngine::runHlp(Bytes const& wasmCode, std::string_view funcName, std::vecto if (res.f) throw std::runtime_error("<" + std::string(funcName) + "> failure"); else if (!res.r.vec_.size) - throw std::runtime_error("<" + std::string(funcName) + "> return nothing"); // LCOV_EXCL_LINE + throw std::runtime_error( + "<" + std::string(funcName) + "> return nothing"); // LCOV_EXCL_LINE else if (res.r.vec_.data[0].kind != WASM_I32) throw std::runtime_error( - "<" + std::string(funcName) + - "> return type mismatch, ret: " + std::to_string(static_cast(res.r.vec_.data[0].kind))); + "<" + std::string(funcName) + "> return type mismatch, ret: " + + std::to_string(static_cast(res.r.vec_.data[0].kind))); if (gas == -1) gas = std::numeric_limits::max(); @@ -820,25 +818,18 @@ WasmiEngine::runHlp(Bytes const& wasmCode, std::string_view funcName, std::vecto NotTEC WasmiEngine::check( Bytes const& wasmCode, + HostFunctions& hfs, std::string_view funcName, std::vector const& params, - std::shared_ptr const& imports, - std::shared_ptr const& hfs, + ImportVec const& imports, beast::Journal j) { j_ = j; - if (!wasmCode.empty()) - { - imports_ = imports; - hfs_ = hfs; - } - try { - if (imports_) - checkImports(*imports_, hfs_.get()); - return checkHlp(wasmCode, funcName, params); + checkImports(imports, &hfs); + return checkHlp(wasmCode, hfs, funcName, params, imports); } catch (std::exception const& e) { @@ -855,7 +846,12 @@ WasmiEngine::check( } NotTEC -WasmiEngine::checkHlp(Bytes const& wasmCode, std::string_view funcName, std::vector const& params) +WasmiEngine::checkHlp( + Bytes const& wasmCode, + HostFunctions& hfs, + std::string_view funcName, + std::vector const& params, + ImportVec const& imports) { // currently only 1 module support, possible parallel UT run std::lock_guard lg(m_); @@ -864,7 +860,7 @@ WasmiEngine::checkHlp(Bytes const& wasmCode, std::string_view funcName, std::vec if (wasmCode.empty()) throw std::runtime_error("empty nodule"); - int const m = addModule(wasmCode, false, -1); + int const m = addModule(wasmCode, false, imports, -1); if ((m < 0) || !moduleWrap_) throw std::runtime_error("no module"); // LCOV_EXCL_LINE @@ -881,7 +877,7 @@ WasmiEngine::checkHlp(Bytes const& wasmCode, std::string_view funcName, std::vec // LCOV_EXCL_START std::int64_t -WasmiEngine::getGas() +WasmiEngine::getGas() const { return moduleWrap_ ? moduleWrap_->getGas() : -1; } @@ -894,32 +890,13 @@ WasmiEngine::getMem() const } InstanceWrapper const& -WasmiEngine::getRT(int m, int i) +WasmiEngine::getRT(int m, int i) const { if (!moduleWrap_) throw std::runtime_error("no module"); return moduleWrap_->getInstance(i); } -int32_t -WasmiEngine::allocate(int32_t sz) -{ - if (sz <= 0) - throw std::runtime_error("can't allocate memory, " + std::to_string(sz) + " bytes"); - - auto res = call<1>(W_ALLOC, sz); - - if (res.f || !res.r.vec_.size || (res.r.vec_.data[0].kind != WASM_I32)) - throw std::runtime_error("can't allocate memory, " + std::to_string(sz) + " bytes"); // LCOV_EXCL_LINE - - int32_t const p = res.r.vec_.data[0].of.i32; - auto const mem = getMem(); - if (p <= 0 || p + sz > mem.s) - throw std::runtime_error("invalid memory allocation, " + std::to_string(sz) + " bytes"); - - return p; -} - wasm_trap_t* WasmiEngine::newTrap(std::string const& txt) { diff --git a/src/test/app/AMMCalc_test.cpp b/src/test/app/AMMCalc_test.cpp index bc50f02d3d..c1068b08ba 100644 --- a/src/test/app/AMMCalc_test.cpp +++ b/src/test/app/AMMCalc_test.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include #include @@ -44,10 +43,14 @@ class AMMCalc_test : public beast::unit_test::suite if (delimited) *delimited = (match[3] != ""); if (match[1] == "XRP") + { return XRP(std::stoll(match[2])); - // drops - else if (match[1] == "XRPA") + // drops + } + if (match[1] == "XRPA") + { return XRPAmount{std::stoll(match[2])}; + } return amountFromString(gw[match[1]].asset(), match[2]); } return std::nullopt; @@ -121,7 +124,9 @@ class AMMCalc_test : public beast::unit_test::suite break; } else + { return std::nullopt; + } } return rates; } @@ -371,7 +376,8 @@ class AMMCalc_test : public beast::unit_test::suite { Account const amm("amm"); auto const LPT = amm["LPT"]; - std::cout << to_string(ammLPTokens(pool->first.in, pool->first.out, LPT).iou()) << std::endl; + std::cout << to_string(ammLPTokens(pool->first.in, pool->first.out, LPT).iou()) + << std::endl; return true; } } @@ -398,11 +404,17 @@ class AMMCalc_test : public beast::unit_test::suite env.current()->rules(), beast::Journal(beast::Journal::getNullSink())); ammOffer) - std::cout << "amm offer: " << toString(ammOffer->in) << " " << toString(ammOffer->out) - << "\nnew pool: " << toString(pool->first.in + ammOffer->in) << " " - << toString(pool->first.out - ammOffer->out) << std::endl; + { + std::cout << "amm offer: " << toString(ammOffer->in) << " " + << toString(ammOffer->out) + << "\nnew pool: " << toString(pool->first.in + ammOffer->in) + << " " << toString(pool->first.out - ammOffer->out) + << std::endl; + } else + { std::cout << "can't change the pool's SP quality" << std::endl; + } return true; } } diff --git a/src/test/app/AMMClawback_test.cpp b/src/test/app/AMMClawback_test.cpp index 65cf7f0a60..9033fe2bdd 100644 --- a/src/test/app/AMMClawback_test.cpp +++ b/src/test/app/AMMClawback_test.cpp @@ -2,9 +2,8 @@ #include #include -#include - #include +#include namespace xrpl { namespace test { @@ -36,7 +35,8 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, XRP(100), USD(100)); env.close(); - env(amm::ammClawback(gw, Account("unknown"), USD, XRP, std::nullopt), ter(terNO_ACCOUNT)); + env(amm::ammClawback(gw, Account("unknown"), USD, XRP, std::nullopt), + ter(terNO_ACCOUNT)); } // Test if asset pair provided does not exist. This should @@ -123,7 +123,8 @@ class AMMClawback_test : public beast::unit_test::suite // The Asset's issuer field is alice, while the Account field is gw. // This should return temMALFORMED because they do not match. - env(amm::ammClawback(gw, alice, Issue{gw["USD"].currency, alice.id()}, XRP, std::nullopt), + env(amm::ammClawback( + gw, alice, Issue{gw["USD"].currency, alice.id()}, XRP, std::nullopt), ter(temMALFORMED)); } @@ -151,7 +152,8 @@ class AMMClawback_test : public beast::unit_test::suite // The Asset's issuer subfield is gw account and Amount's issuer // subfield is alice account. Return temBAD_AMOUNT because // they do not match. - env(amm::ammClawback(gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, alice.id()}, 1}), + env(amm::ammClawback( + gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, alice.id()}, 1}), ter(temBAD_AMOUNT)); } @@ -177,11 +179,13 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Return temBAD_AMOUNT if the Amount value is less than 0. - env(amm::ammClawback(gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, gw.id()}, -1}), + env(amm::ammClawback( + gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, gw.id()}, -1}), ter(temBAD_AMOUNT)); // Return temBAD_AMOUNT if the Amount value is 0. - env(amm::ammClawback(gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, gw.id()}, 0}), + env(amm::ammClawback( + gw, alice, USD, XRP, STAmount{Issue{gw["USD"].currency, gw.id()}, 0}), ter(temBAD_AMOUNT)); } @@ -232,7 +236,9 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS)); // Return temINVALID_FLAG when providing invalid flag. - env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), txflags(tfTwoAssetIfEmpty), ter(temINVALID_FLAG)); + env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), + txflags(tfTwoAssetIfEmpty), + ter(temINVALID_FLAG)); } // Test if tfClawTwoAssets is set when the two assets in the AMM pool @@ -262,7 +268,9 @@ class AMMClawback_test : public beast::unit_test::suite // but the issuer only issues USD in the pool. The issuer is not // allowed to set tfClawTwoAssets flag if he did not issue both // assets in the pool. - env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), txflags(tfClawTwoAssets), ter(temINVALID_FLAG)); + env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), + txflags(tfClawTwoAssets), + ter(temINVALID_FLAG)); } // Test clawing back XRP is being prohibited. @@ -365,7 +373,8 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); - BEAST_EXPECT(amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); + BEAST_EXPECT( + amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); // gw clawback 1000 USD from the AMM pool. env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)), ter(tesSUCCESS)); @@ -522,9 +531,15 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + } // gw clawback 1000 USD from the AMM pool env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)), ter(tesSUCCESS)); @@ -543,15 +558,25 @@ class AMMClawback_test : public beast::unit_test::suite // 1000 USD and 1250 EUR was withdrawn from the AMM pool, so the // current balance is 3000 USD and 3750 EUR. if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249685, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249685, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249684, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(3000), EUR(3750), IOUAmount{3354101966249684, -12})); + } // Alice has 3/4 of its initial lptokens Left. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{3354101966249685, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{3354101966249684, -12})); + } // gw clawback another 500 USD from the AMM pool. env(amm::ammClawback(gw, alice, USD, EUR, USD(500)), ter(tesSUCCESS)); @@ -562,17 +587,27 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, USD(2000))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2500000000000001), -12}, STAmount{EUR, UINT64_C(3125000000000001), -12}, IOUAmount{2795084971874738, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(2500), EUR(3125), IOUAmount{2795084971874737, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2500), EUR(3125), IOUAmount{2795084971874737, -12})); + } if (!features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2874999999999999), -12)); + { + BEAST_EXPECT( + env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2874999999999999), -12)); + } else + { BEAST_EXPECT(env.balance(alice, EUR) == EUR(2875)); + } // gw clawback small amount, 1 USD. env(amm::ammClawback(gw, alice, USD, EUR, USD(1)), ter(tesSUCCESS)); @@ -582,24 +617,39 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(alice, USD(2000))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2499000000000002), -12}, STAmount{EUR, UINT64_C(3123750000000002), -12}, IOUAmount{2793966937885989, -12})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(2499), EUR(3123.75), IOUAmount{2793966937885987, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2499), EUR(3123.75), IOUAmount{2793966937885987, -12})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2499000000000001), -12}, STAmount{EUR, UINT64_C(3123750000000001), -12}, IOUAmount{2793966937885988, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) - BEAST_EXPECT(env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999998), -12)); + { + BEAST_EXPECT( + env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999998), -12)); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(env.balance(alice, EUR) == EUR(2876.25)); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999999), -12)); + { + BEAST_EXPECT( + env.balance(alice, EUR) == STAmount(EUR, UINT64_C(2876'249999999999), -12)); + } // gw clawback 4000 USD, exceeding the current balance. We // will clawback all. @@ -667,21 +717,39 @@ class AMMClawback_test : public beast::unit_test::suite // gw2 creates AMM pool of XRP/EUR, alice and bob deposit XRP/EUR. AMM amm2(env, gw2, XRP(3000), EUR(1000), ter(tesSUCCESS)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + } else - BEAST_EXPECT(amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + } amm2.deposit(alice, EUR(1000), XRP(3000)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + } else - BEAST_EXPECT(amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + } amm2.deposit(bob, EUR(1000), XRP(3000)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706634, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706634, -9})); + } else - BEAST_EXPECT(amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706631, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706631, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -701,24 +769,44 @@ class AMMClawback_test : public beast::unit_test::suite // Alice gets 1000 XRP back. if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) - BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount(1))); + { + BEAST_EXPECT( + expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount(1))); + } else + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } aliceXrpBalance = env.balance(alice, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932738, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932738, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932737, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2500), XRP(5000), IOUAmount{3535533905932737, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(2500), XRPAmount(5000000001), IOUAmount{3'535'533'905932738, -9})); + { + BEAST_EXPECT(amm.expectBalances( + USD(2500), XRPAmount(5000000001), IOUAmount{3'535'533'905932738, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865474, -10})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{707106781186548, -9})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1414213562373095, -9})); @@ -734,29 +822,50 @@ class AMMClawback_test : public beast::unit_test::suite bobXrpBalance = env.balance(bob, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - STAmount{USD, UINT64_C(2490000000000001), -12}, XRP(4980), IOUAmount{3521391770309008, -9})); + STAmount{USD, UINT64_C(2490000000000001), -12}, + XRP(4980), + IOUAmount{3521391770309008, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(2'490), XRP(4980), IOUAmount{3521391770309006, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2'490), XRP(4980), IOUAmount{3521391770309006, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(2490000000000001), -12}, XRPAmount(4980000001), IOUAmount{3521391'770309008, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{7071067811865474, -10})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{707106781186548, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749364, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } // gw2 clawback 200 EUR from amm2. env(amm::ammClawback(gw2, alice, EUR, XRP, EUR(200)), ter(tesSUCCESS)); @@ -766,28 +875,52 @@ class AMMClawback_test : public beast::unit_test::suite env.require(balance(bob, EUR(3000))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600))); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600))); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) - BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600) - XRPAmount{1})); + { + BEAST_EXPECT( + expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(600) - XRPAmount{1})); + } aliceXrpBalance = env.balance(alice, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192859, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192859, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192856, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2800), XRP(8400), IOUAmount{4849742261192856, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2800), XRPAmount(8400000001), IOUAmount{4849742261192856, -9})); + { + BEAST_EXPECT(amm2.expectBalances( + EUR(2800), XRPAmount(8400000001), IOUAmount{4849742261192856, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount{1385640646055103, -9})); + } else + { BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount{1385640646055102, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount{1732050807568878, -9})); + } else + { BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount{1732050807568877, -9})); + } // gw claw back 1000 USD from alice in amm, which exceeds alice's // balance. This will clawback all the remaining LP tokens of alice @@ -800,31 +933,53 @@ class AMMClawback_test : public beast::unit_test::suite // Alice gets 1000 XRP back. if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount{1})); + { + BEAST_EXPECT( + expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000) - XRPAmount{1})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000))); + } aliceXrpBalance = env.balance(alice, XRP); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749364, -9})); + } else if (features[fixAMMClawbackRounding] && features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - STAmount{USD, UINT64_C(1990000000000001), -12}, XRP(3980), IOUAmount{2814284989122460, -9})); + STAmount{USD, UINT64_C(1990000000000001), -12}, + XRP(3980), + IOUAmount{2814284989122460, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(1'990), XRPAmount{3'980'000'001}, IOUAmount{2814284989122459, -9})); + { + BEAST_EXPECT(amm.expectBalances( + USD(1'990), XRPAmount{3'980'000'001}, IOUAmount{2814284989122459, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(1990000000000001), -12}, XRPAmount{3'980'000'001}, IOUAmount{2814284989122460, -9})); + } // gw clawback 1000 USD from bob in amm, which also exceeds bob's // balance in amm. All bob's lptoken in amm will be consumed, which @@ -864,11 +1019,20 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(2000), XRP(6000), IOUAmount{3464101615137754, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(2000), XRPAmount(6000000001), IOUAmount{3464101615137754, -9})); + { + BEAST_EXPECT(amm2.expectBalances( + EUR(2000), XRPAmount(6000000001), IOUAmount{3464101615137754, -9})); + } // gw2 claw back 2000 EUR from bob in amm2, which exceeds bob's // balance. All bob's lptokens will be consumed, which corresponds @@ -891,11 +1055,20 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount(0))); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + { + BEAST_EXPECT( + amm2.expectBalances(EUR(1000), XRP(3000), IOUAmount{1732050807568877, -9})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm2.expectBalances(EUR(1000), XRPAmount(3000000001), IOUAmount{1732050807568877, -9})); + { + BEAST_EXPECT(amm2.expectBalances( + EUR(1000), XRPAmount(3000000001), IOUAmount{1732050807568877, -9})); + } } } @@ -954,32 +1127,62 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999580, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(4000), EUR(5000), IOUAmount{4472135954999579, -12})); + } amm.deposit(bob, USD(2000), EUR(2500)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499370, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499370, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499368, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(6000), EUR(7500), IOUAmount{6708203932499368, -12})); + } amm.deposit(carol, USD(1000), EUR(1250)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249265, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249265, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249262, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(7000), EUR(8750), IOUAmount{7826237921249262, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2236067977499790, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2236067977499789, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749894, -12})); + } env.require(balance(alice, USD(2000))); env.require(balance(alice, EUR(1000))); @@ -993,25 +1196,37 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(4999999999999999), -12}, STAmount{EUR, UINT64_C(6249999999999999), -12}, IOUAmount{5590169943749475, -12})); + } else + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(5000000000000001), -12}, STAmount{EUR, UINT64_C(6250000000000001), -12}, IOUAmount{5590169943749473, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{1118033988749894, -12})); + } // Bob will get 2500 EUR back. env.require(balance(alice, USD(2000))); @@ -1019,9 +1234,15 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(3000000000000000), -12)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(bob, EUR) == STAmount(EUR, UINT64_C(5000000000000001), -12)); + { + BEAST_EXPECT( + env.balance(bob, EUR) == STAmount(EUR, UINT64_C(5000000000000001), -12)); + } else - BEAST_EXPECT(env.balance(bob, EUR) == STAmount(EUR, UINT64_C(4999999999999999), -12)); + { + BEAST_EXPECT( + env.balance(bob, EUR) == STAmount(EUR, UINT64_C(4999999999999999), -12)); + } env.require(balance(carol, USD(3000))); env.require(balance(carol, EUR(2750))); @@ -1029,20 +1250,28 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, carol, EUR, USD, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(3999999999999999), -12}, STAmount{EUR, UINT64_C(4999999999999999), -12}, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectBalances( STAmount{USD, UINT64_C(4000000000000001), -12}, STAmount{EUR, UINT64_C(5000000000000002), -12}, IOUAmount{4472135954999579, -12})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4472135954999579, -12})); + } BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(0))); @@ -1081,19 +1310,37 @@ class AMMClawback_test : public beast::unit_test::suite // gw creates AMM pool of XRP/USD, alice and bob deposit XRP/USD. AMM amm(env, gw, XRP(2000), USD(10000), ter(tesSUCCESS)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + } else - BEAST_EXPECT(amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999579, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999579, -9})); + } amm.deposit(alice, USD(1000), XRP(200)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499538, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499538, -9})); + } else - BEAST_EXPECT(amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499536, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(11000), XRP(2200), IOUAmount{4919349550499536, -9})); + } amm.deposit(bob, USD(2000), XRP(400)); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499453, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499453, -9})); + } else - BEAST_EXPECT(amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499451, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499451, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -1103,22 +1350,39 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(12000), XRP(2400), IOUAmount{5366563145999495, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(12000), XRP(2400), IOUAmount{5366563145999495, -9})); + } else - BEAST_EXPECT(amm.expectBalances(USD(12000), XRPAmount(2400000001), IOUAmount{5366563145999494, -9})); + { + BEAST_EXPECT(amm.expectBalances( + USD(12000), XRPAmount(2400000001), IOUAmount{5366563145999494, -9})); + } if (!features[fixAMMv1_3]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200))); + } else - BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200) - XRPAmount{1})); + { + BEAST_EXPECT( + expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200) - XRPAmount{1})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); // gw clawback all bob's USD in amm. (2000 USD / 400 XRP) env(amm::ammClawback(gw, bob, USD, XRP, std::nullopt), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3]) - BEAST_EXPECT(amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + { + BEAST_EXPECT( + amm.expectBalances(USD(10000), XRP(2000), IOUAmount{4472135954999580, -9})); + } else - BEAST_EXPECT(amm.expectBalances(USD(10000), XRPAmount(2000000001), IOUAmount{4472135954999579, -9})); + { + BEAST_EXPECT(amm.expectBalances( + USD(10000), XRPAmount(2000000001), IOUAmount{4472135954999579, -9})); + } BEAST_EXPECT(expectLedgerEntryRoot(env, bob, bobXrpBalance + XRP(400))); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); @@ -1173,9 +1437,13 @@ class AMMClawback_test : public beast::unit_test::suite amm.deposit(bob, USD(4000), EUR(1000)); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); if (!features[fixAMMv1_3]) + { amm.deposit(carol, USD(2000), EUR(500)); + } else + { amm.deposit(carol, USD(2000.25), EUR(500)); + } BEAST_EXPECT(amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000))); // gw clawback 1000 USD from carol. env(amm::ammClawback(gw, carol, USD, EUR, USD(1000)), ter(tesSUCCESS)); @@ -1190,16 +1458,23 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback 1000 USD from bob with tfClawTwoAssets flag. // then the corresponding EUR will also be clawed back // by gw. - env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)), txflags(tfClawTwoAssets), ter(tesSUCCESS)); + env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)), + txflags(tfClawTwoAssets), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); @@ -1212,13 +1487,20 @@ class AMMClawback_test : public beast::unit_test::suite // 250 EUR did not go back to bob because tfClawTwoAssets is set. BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. - env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), txflags(tfClawTwoAssets), ter(tesSUCCESS)); + env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), + txflags(tfClawTwoAssets), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(1000), IOUAmount(2000))); @@ -1230,9 +1512,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); } @@ -1278,24 +1565,39 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, gw["USD"](1000), gw2["USD"](1500), ter(tesSUCCESS)); env.close(); - BEAST_EXPECT(amm.expectBalances(gw["USD"](1000), gw2["USD"](1500), IOUAmount{1224744871391589, -12})); + BEAST_EXPECT(amm.expectBalances( + gw["USD"](1000), gw2["USD"](1500), IOUAmount{1224744871391589, -12})); amm.deposit(bob, gw["USD"](2000), gw2["USD"](3000)); - BEAST_EXPECT(amm.expectBalances(gw["USD"](3000), gw2["USD"](4500), IOUAmount{3674234614174767, -12})); + BEAST_EXPECT(amm.expectBalances( + gw["USD"](3000), gw2["USD"](4500), IOUAmount{3674234614174767, -12})); // Issuer does not match with asset. - env(amm::ammClawback(gw, alice, gw2["USD"], gw["USD"], STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}), + env(amm::ammClawback( + gw, + alice, + gw2["USD"], + gw["USD"], + STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}), ter(temMALFORMED)); // gw2 clawback 500 gw2[USD] from alice. - env(amm::ammClawback(gw2, alice, gw2["USD"], gw["USD"], STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}), + env(amm::ammClawback( + gw2, + alice, + gw2["USD"], + gw["USD"], + STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}), ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances( - STAmount{gw["USD"], UINT64_C(2666666666666667), -12}, gw2["USD"](4000), IOUAmount{3265986323710904, -12})); + STAmount{gw["USD"], UINT64_C(2666666666666667), -12}, + gw2["USD"](4000), + IOUAmount{3265986323710904, -12})); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{8164965809277260, -13})); BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2449489742783178, -12})); - BEAST_EXPECT(env.balance(alice, gw["USD"]) == STAmount(gw["USD"], UINT64_C(7333333333333333), -12)); + BEAST_EXPECT( + env.balance(alice, gw["USD"]) == STAmount(gw["USD"], UINT64_C(7333333333333333), -12)); BEAST_EXPECT(env.balance(alice, gw2["USD"]) == gw2["USD"](4500)); BEAST_EXPECT(env.balance(bob, gw["USD"]) == gw["USD"](5000)); BEAST_EXPECT(env.balance(bob, gw2["USD"]) == gw2["USD"](2000)); @@ -1304,11 +1606,14 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw, bob, gw["USD"], gw2["USD"], std::nullopt), ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances( - STAmount{gw["USD"], UINT64_C(6666666666666670), -13}, gw2["USD"](1000), IOUAmount{8164965809277260, -13})); + STAmount{gw["USD"], UINT64_C(6666666666666670), -13}, + gw2["USD"](1000), + IOUAmount{8164965809277260, -13})); BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{8164965809277260, -13})); BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0))); - BEAST_EXPECT(env.balance(alice, gw["USD"]) == STAmount(gw["USD"], UINT64_C(7333333333333333), -12)); + BEAST_EXPECT( + env.balance(alice, gw["USD"]) == STAmount(gw["USD"], UINT64_C(7333333333333333), -12)); BEAST_EXPECT(env.balance(alice, gw2["USD"]) == gw2["USD"](4500)); BEAST_EXPECT(env.balance(bob, gw["USD"]) == gw["USD"](5000)); // Bob gets 3000 gw2["USD"] back and now his balance is 5000. @@ -1371,15 +1676,25 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw, gw2, USD, EUR, USD(1000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865475, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865475, -12})); + } else - BEAST_EXPECT(amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865474, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(5000), EUR(10000), IOUAmount{7071067811865474, -12})); + } BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{1414213562373095, -12})); if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12})); BEAST_EXPECT(env.balance(alice, USD) == USD(2000)); @@ -1391,25 +1706,46 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, gw, EUR, USD, EUR(1000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - USD(4500), STAmount(EUR, UINT64_C(9000000000000001), -12), IOUAmount{6363961030678928, -12})); + USD(4500), + STAmount(EUR, UINT64_C(9000000000000001), -12), + IOUAmount{6363961030678928, -12})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(4500), EUR(9000), IOUAmount{6363961030678928, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(4500), EUR(9000), IOUAmount{6363961030678928, -12})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - USD(4500), STAmount(EUR, UINT64_C(9000000000000001), -12), IOUAmount{6363961030678927, -12})); + USD(4500), + STAmount(EUR, UINT64_C(9000000000000001), -12), + IOUAmount{6363961030678927, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865475, -13})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12})); @@ -1422,25 +1758,46 @@ class AMMClawback_test : public beast::unit_test::suite env(amm::ammClawback(gw2, alice, EUR, USD, EUR(4000)), ter(tesSUCCESS)); env.close(); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - USD(2500), STAmount(EUR, UINT64_C(5000000000000001), -12), IOUAmount{3535533905932738, -12})); + USD(2500), + STAmount(EUR, UINT64_C(5000000000000001), -12), + IOUAmount{3535533905932738, -12})); + } else if (!features[fixAMMClawbackRounding]) - BEAST_EXPECT(amm.expectBalances(USD(2500), EUR(5000), IOUAmount{3535533905932738, -12})); + { + BEAST_EXPECT( + amm.expectBalances(USD(2500), EUR(5000), IOUAmount{3535533905932738, -12})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectBalances( - USD(2500), STAmount(EUR, UINT64_C(5000000000000001), -12), IOUAmount{3535533905932737, -12})); + USD(2500), + STAmount(EUR, UINT64_C(5000000000000001), -12), + IOUAmount{3535533905932737, -12})); + } if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } else if (!features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865475, -13})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13})); + } if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding]) + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12})); + } else + { BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373094, -12})); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{1414213562373095, -12})); BEAST_EXPECT(env.balance(alice, USD) == USD(4000)); @@ -1518,7 +1875,8 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); - BEAST_EXPECT(amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); + BEAST_EXPECT( + amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); // freeze trustline env(trust(gw, alice["USD"](0), tfSetFreeze)); @@ -1581,7 +1939,8 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); - BEAST_EXPECT(amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); + BEAST_EXPECT( + amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); // freeze trustlines env(trust(gw, alice["USD"](0), tfSetFreeze)); @@ -1630,7 +1989,8 @@ class AMMClawback_test : public beast::unit_test::suite AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS)); env.close(); - BEAST_EXPECT(amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); + BEAST_EXPECT( + amm.expectBalances(USD(2000), EUR(1000), IOUAmount{1414213562373095, -12})); // global freeze env(fset(gw, asfGlobalFreeze)); @@ -1687,9 +2047,13 @@ class AMMClawback_test : public beast::unit_test::suite amm.deposit(bob, USD(4000), EUR(1000)); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); if (!features[fixAMMv1_3]) + { amm.deposit(carol, USD(2000), EUR(500)); + } else + { amm.deposit(carol, USD(2000.25), EUR(500)); + } BEAST_EXPECT(amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000))); // global freeze @@ -1709,16 +2073,23 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback 1000 USD from bob with tfClawTwoAssets flag. // then the corresponding EUR will also be clawed back // by gw. - env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)), txflags(tfClawTwoAssets), ter(tesSUCCESS)); + env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)), + txflags(tfClawTwoAssets), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000))); @@ -1731,13 +2102,20 @@ class AMMClawback_test : public beast::unit_test::suite // 250 EUR did not go back to bob because tfClawTwoAssets is set. BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. - env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), txflags(tfClawTwoAssets), ter(tesSUCCESS)); + env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), + txflags(tfClawTwoAssets), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(1000), IOUAmount(2000))); @@ -1749,9 +2127,14 @@ class AMMClawback_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob, USD) == USD(5000)); BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(env.balance(carol, USD) == USD(6000)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + { + BEAST_EXPECT( + env.balance(carol, USD) == STAmount(USD, UINT64_C(5999'999999999999), -12)); + } BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); } } @@ -1800,16 +2183,24 @@ class AMMClawback_test : public beast::unit_test::suite env.close(); if (!features[fixAMMv1_3]) - BEAST_EXPECT( - amm.expectBalances(STAmount(USD, UINT64_C(5656854249492380), -13), XRP(70.710678), IOUAmount(200000))); + { + BEAST_EXPECT(amm.expectBalances( + STAmount(USD, UINT64_C(5656854249492380), -13), XRP(70.710678), IOUAmount(200000))); + } else - BEAST_EXPECT( - amm.expectBalances(STAmount(USD, UINT64_C(565'685424949238), -12), XRP(70.710679), IOUAmount(200000))); + { + BEAST_EXPECT(amm.expectBalances( + STAmount(USD, UINT64_C(565'685424949238), -12), XRP(70.710679), IOUAmount(200000))); + } BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0))); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(29.289322))); + } else + { BEAST_EXPECT(expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(29.289321))); + } } void @@ -1837,10 +2228,14 @@ class AMMClawback_test : public beast::unit_test::suite return USD; }; - auto getLPTokenBalances = - [&](auto& env, auto const& amm, auto const& account) -> std::pair { - auto const lpToken = getAccountLines(env, account, amm.lptIssue())[jss::lines][0u][jss::balance].asString(); - auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value].asString(); + auto getLPTokenBalances = [&](auto& env, + auto const& amm, + auto const& account) -> std::pair { + auto const lpToken = + getAccountLines(env, account, amm.lptIssue())[jss::lines][0u][jss::balance] + .asString(); + auto const lpTokenBalance = + amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value].asString(); return {lpToken, lpTokenBalance}; }; @@ -1871,8 +2266,8 @@ class AMMClawback_test : public beast::unit_test::suite { auto const lpBalance = IOUAmount{989, -12}; env(amm::ammClawback(gw, alice, USD, XRP, USD(1))); - BEAST_EXPECT( - amm.expectBalances(STAmount(USD, UINT64_C(7000000000000000), -28), XRPAmount(1), lpBalance)); + BEAST_EXPECT(amm.expectBalances( + STAmount(USD, UINT64_C(7000000000000000), -28), XRPAmount(1), lpBalance)); BEAST_EXPECT(amm.expectLPTokens(alice, lpBalance)); } } @@ -1913,8 +2308,8 @@ class AMMClawback_test : public beast::unit_test::suite else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) { auto const lpBalance = IOUAmount{708'9829046743238, -13}; - BEAST_EXPECT( - amm.expectBalances(STAmount(USD, UINT64_C(5013266196406999), -16), XRPAmount(1002655), lpBalance)); + BEAST_EXPECT(amm.expectBalances( + STAmount(USD, UINT64_C(5013266196406999), -16), XRPAmount(1002655), lpBalance)); BEAST_EXPECT(amm.expectLPTokens(alice, lpBalance)); } } @@ -1945,7 +2340,9 @@ class AMMClawback_test : public beast::unit_test::suite { env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt)); BEAST_EXPECT(amm.expectBalances( - STAmount(USD, UINT64_C(2410000000000000), -28), XRPAmount(1), IOUAmount{34, -11})); + STAmount(USD, UINT64_C(2410000000000000), -28), + XRPAmount(1), + IOUAmount{34, -11})); } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) { @@ -2026,7 +2423,9 @@ class AMMClawback_test : public beast::unit_test::suite } else { - env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), txflags(tfClawTwoAssets), ter(tecINTERNAL)); + env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt), + txflags(tfClawTwoAssets), + ter(tecINTERNAL)); BEAST_EXPECT(amm.ammExists()); } } @@ -2061,7 +2460,9 @@ class AMMClawback_test : public beast::unit_test::suite { env(amm::ammClawback(gw, alice, USD, EUR, USD(1))); BEAST_EXPECT(amm.expectBalances( - STAmount(USD, UINT64_C(4), -15), STAmount(EUR, UINT64_C(8), -9), IOUAmount{6, -12})); + STAmount(USD, UINT64_C(4), -15), + STAmount(EUR, UINT64_C(8), -9), + IOUAmount{6, -12})); } else if (!features[fixAMMClawbackRounding]) { @@ -2074,8 +2475,8 @@ class AMMClawback_test : public beast::unit_test::suite { env(amm::ammClawback(gw, alice, USD, EUR, USD(1)), txflags(tfClawTwoAssets)); auto const lpBalance = IOUAmount{5, -12}; - BEAST_EXPECT( - amm.expectBalances(STAmount(USD, UINT64_C(4), -15), STAmount(EUR, UINT64_C(8), -9), lpBalance)); + BEAST_EXPECT(amm.expectBalances( + STAmount(USD, UINT64_C(4), -15), STAmount(EUR, UINT64_C(8), -9), lpBalance)); BEAST_EXPECT(amm.expectLPTokens(alice, lpBalance)); } } @@ -2086,11 +2487,13 @@ class AMMClawback_test : public beast::unit_test::suite { // For now, just disable SAV entirely, which locks in the small Number // mantissas - FeatureBitset const all = jtx::testable_amendments() - featureSingleAssetVault - featureLendingProtocol; + FeatureBitset const all = + jtx::testable_amendments() - featureSingleAssetVault - featureLendingProtocol; testInvalidRequest(); testFeatureDisabled(all - featureAMMClawback); - for (auto const& features : {all - fixAMMv1_3 - fixAMMClawbackRounding, all - fixAMMClawbackRounding, all}) + for (auto const& features : + {all - fixAMMv1_3 - fixAMMClawbackRounding, all - fixAMMClawbackRounding, all}) { testAMMClawbackSpecificAmount(features); testAMMClawbackExceedBalance(features); diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 4eecb05905..93ad92af3d 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -5,15 +5,15 @@ #include #include -#include -#include #include -#include -#include #include #include #include +#include +#include +#include +#include #include #include @@ -66,7 +66,10 @@ private: PathSet paths(Path(XRP, USD), Path(USD)); - env(pay(alice, bob, USD(100)), json(paths.json()), sendmax(BTC(1'000)), txflags(tfPartialPayment)); + env(pay(alice, bob, USD(100)), + json(paths.json()), + sendmax(BTC(1'000)), + txflags(tfPartialPayment)); if (!features[fixAMMv1_1]) { @@ -145,7 +148,10 @@ private: AMM ammDan(env, dan, XRP(10'000), USD1(10'050)); - env(pay(alice, carol, USD2(50)), path(~USD1, bob), sendmax(XRP(50)), txflags(tfNoRippleDirect)); + env(pay(alice, carol, USD2(50)), + path(~USD1, bob), + sendmax(XRP(50)), + txflags(tfNoRippleDirect)); BEAST_EXPECT(ammDan.expectBalances(XRP(10'050), USD1(10'000), ammDan.tokens())); BEAST_EXPECT(expectLedgerEntryRoot(env, alice, XRP(20'000) - XRP(50) - txfee(env, 1))); @@ -181,7 +187,8 @@ private: // Order that can be filled env(offer(carol, XRP(100), USD(100)), txflags(tfFillOrKill), ter(tesSUCCESS)); BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'100), ammAlice.tokens())); - BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) + XRP(100) - txfee(env, 2))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, carol, XRP(30'000) + XRP(100) - txfee(env, 2))); BEAST_EXPECT(expectHolding(env, carol, USD(29'900))); BEAST_EXPECT(expectOffers(env, carol, 0)); }, @@ -194,13 +201,16 @@ private: // and add nothing on the books. testAMM( [&](AMM& ammAlice, Env& env) { - env(offer(carol, XRP(200), USD(200)), txflags(tfImmediateOrCancel), ter(tesSUCCESS)); + env(offer(carol, XRP(200), USD(200)), + txflags(tfImmediateOrCancel), + ter(tesSUCCESS)); // AMM generates a synthetic offer of 100USD/100XRP // to match the CLOB offer quality. BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'100), ammAlice.tokens())); // +AMM - offer * fee - BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) + XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, carol, XRP(30'000) + XRP(100) - txfee(env, 1))); // AMM BEAST_EXPECT(expectHolding(env, carol, USD(29'900))); BEAST_EXPECT(expectOffers(env, carol, 0)); @@ -217,7 +227,8 @@ private: // Carol's offer should stay in the ledger. env(offer(carol, XRP(100), USD(100), tfPassive)); env.close(); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), STAmount{USD, 10'000}, ammAlice.tokens())); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), STAmount{USD, 10'000}, ammAlice.tokens())); BEAST_EXPECT(expectOffers(env, carol, 1, {{{XRP(100), STAmount{USD, 100}}}})); }, {{XRP(10'100), USD(10'000)}}, @@ -236,7 +247,9 @@ private: env(offer(carol, XRP(100), USD(100), tfPassive)); env.close(); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'900), STAmount{USD, UINT64_C(9'082'56880733945), -11}, ammAlice.tokens())); + XRP(10'900), + STAmount{USD, UINT64_C(9'082'56880733945), -11}, + ammAlice.tokens())); BEAST_EXPECT(expectOffers(env, carol, 0)); BEAST_EXPECT(expectOffers(env, alice, 1)); }, @@ -265,10 +278,12 @@ private: auto const xrpTransferred = XRPAmount{3'061'224'490}; env(offer(bob, USD(1), XRP(4'000))); - BEAST_EXPECT(ammAlice.expectBalances(XRP(150'000) + xrpTransferred, USD(49), IOUAmount{273'861'278752583, -8})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(150'000) + xrpTransferred, USD(49), IOUAmount{273'861'278752583, -8})); BEAST_EXPECT(expectHolding(env, bob, STAmount{USD, 101})); - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(300'000) - xrpTransferred - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(300'000) - xrpTransferred - txfee(env, 1))); BEAST_EXPECT(expectOffers(env, bob, 0)); } @@ -350,12 +365,16 @@ private: // we permit partial payment env(pay(alice, alice, XRP(100)), sendmax(USD(100)), txflags(tfPartialPayment)); env.close(); - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{9'900'990'100}, USD(10'100), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{9'900'990'100}, USD(10'100), ammAlice.tokens())); // initial 30,000 - 10,000AMM - 100pay BEAST_EXPECT(expectHolding(env, alice, USD(19'900))); // initial 30,000 - 10,0000AMM + 99.009900pay - fee*3 BEAST_EXPECT(expectLedgerEntryRoot( - env, alice, XRP(30'000) - XRP(10'000) + XRPAmount{99'009'900} - ammCrtFee(env) - txfee(env, 2))); + env, + alice, + XRP(30'000) - XRP(10'000) + XRPAmount{99'009'900} - ammCrtFee(env) - + txfee(env, 2))); }, {{XRP(10'000), USD(10'000)}}, 0, @@ -401,7 +420,8 @@ private: env.close(); env(pay(alice, bob, XRP(100)), sendmax(USD(100))); BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'100), ammAlice.tokens())); - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(1'000) + XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(1'000) + XRP(100) - txfee(env, 1))); }, {{XRP(10'100), USD(10'000)}}, 0, @@ -449,8 +469,8 @@ private: jtp[0u][0u][jss::currency] = "XRP"; env(pay(alice, bob, EUR1(30)), json(jss::Paths, jtp), sendmax(USD1(333))); env.close(); - BEAST_EXPECT( - ammCarol.expectBalances(XRP(49'700), STAmount{USD1, UINT64_C(5'030'181086519115), -12}, ammCarol.tokens())); + BEAST_EXPECT(ammCarol.expectBalances( + XRP(49'700), STAmount{USD1, UINT64_C(5'030'181086519115), -12}, ammCarol.tokens())); BEAST_EXPECT(expectOffers(env, dan, 1, {{Amounts{XRP(200), EUR(20)}}})); BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR1, 30})); } @@ -479,7 +499,8 @@ private: // fees: // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 - auto const starting_xrp = XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + auto const starting_xrp = + XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; env.fund(starting_xrp, gw1, gw2, gw3, alice); env.fund(XRP(2'000), bob); @@ -500,8 +521,8 @@ private: // The pool gets only 100XRP for ~109.09USD, even though // it can exchange more. - BEAST_EXPECT( - ammBob.expectBalances(XRP(1'100), STAmount{USD1, UINT64_C(1'090'909090909091), -12}, ammBob.tokens())); + BEAST_EXPECT(ammBob.expectBalances( + XRP(1'100), STAmount{USD1, UINT64_C(1'090'909090909091), -12}, ammBob.tokens())); auto jrr = ledgerEntryState(env, alice, gw1, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "109.090909090909"); @@ -552,7 +573,8 @@ private: BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(9'999), ammAlice.tokens())); BEAST_EXPECT(expectOffers(env, carol, 0)); BEAST_EXPECT(expectHolding(env, carol, USD(30'101))); - BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) - XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, carol, XRP(30'000) - XRP(100) - txfee(env, 1))); }, {{XRP(9'900), USD(10'100)}}, 0, @@ -615,7 +637,8 @@ private: payment[jss::id] = env.seq(bob); payment[jss::build_path] = true; payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1)); - payment[jss::tx_json][jss::Sequence] = env.current()->read(keylet::account(bob.id()))->getFieldU32(sfSequence); + payment[jss::tx_json][jss::Sequence] = + env.current()->read(keylet::account(bob.id()))->getFieldU32(sfSequence); payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base); payment[jss::tx_json][jss::SendMax] = bob["XTS"](1.5).value().getJson(JsonOptions::none); payment[jss::tx_json][jss::Flags] = tfPartialPayment; @@ -624,14 +647,14 @@ private: BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS"); if (!features[fixAMMv1_1]) { - BEAST_EXPECT( - ammAlice.expectBalances(STAmount(XTS, UINT64_C(101'010101010101), -12), XXX(99), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectBalances( + STAmount(XTS, UINT64_C(101'010101010101), -12), XXX(99), ammAlice.tokens())); BEAST_EXPECT(expectHolding(env, bob, STAmount{XTS, UINT64_C(98'989898989899), -12})); } else { - BEAST_EXPECT( - ammAlice.expectBalances(STAmount(XTS, UINT64_C(101'0101010101011), -13), XXX(99), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectBalances( + STAmount(XTS, UINT64_C(101'0101010101011), -13), XXX(99), ammAlice.tokens())); BEAST_EXPECT(expectHolding(env, bob, STAmount{XTS, UINT64_C(98'9898989898989), -13})); } BEAST_EXPECT(expectHolding(env, bob, XXX(101))); @@ -757,9 +780,10 @@ private: // Even though tfSell is present it doesn't matter this time. env(offer(alice, USD(2), XRP(220), tfSell | tfFillOrKill)); env.close(); + BEAST_EXPECT(ammBob.expectBalances( + XRP(20'220), STAmount{USD, UINT64_C(197'8239366963403), -13}, ammBob.tokens())); BEAST_EXPECT( - ammBob.expectBalances(XRP(20'220), STAmount{USD, UINT64_C(197'8239366963403), -13}, ammBob.tokens())); - BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(1'002'17606330366), -11})); + expectHolding(env, alice, STAmount{USD, UINT64_C(1'002'17606330366), -11})); BEAST_EXPECT(expectOffers(env, alice, 0)); } { @@ -772,9 +796,10 @@ private: env(offer(alice, USD(10), XRP(1'500), tfSell | tfFillOrKill)); env.close(); + BEAST_EXPECT(ammBob.expectBalances( + XRP(21'500), STAmount{USD, UINT64_C(186'046511627907), -12}, ammBob.tokens())); BEAST_EXPECT( - ammBob.expectBalances(XRP(21'500), STAmount{USD, UINT64_C(186'046511627907), -12}, ammBob.tokens())); - BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(1'013'953488372093), -12})); + expectHolding(env, alice, STAmount{USD, UINT64_C(1'013'953488372093), -12})); BEAST_EXPECT(expectOffers(env, alice, 0)); } { @@ -1194,7 +1219,8 @@ private: AMM ammAlice(env, alice, USD(1'000), XRP(1'050)); // Set up authorized trust line for AMM. - env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), txflags(tfSetfAuth)); + env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), + txflags(tfSetfAuth)); env.close(); env(pay(gw, bob, USD(50))); @@ -1264,7 +1290,8 @@ private: AMM ammAlice(env, alice, USD(1'000), XRP(1'050)); // Set up authorized trust line for AMM. - env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), txflags(tfSetfAuth)); + env(trust(gw, STAmount{Issue{USD.currency, ammAlice.ammAccount()}, 10}), + txflags(tfSetfAuth)); env.close(); // Now bob creates his offer again, which crosses with alice's AMM. @@ -1282,7 +1309,8 @@ private: using namespace jtx; // For now, just disable SAV entirely, which locks in the small Number // mantissas - FeatureBitset const all{testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const all{ + testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; testRmFundedOffer(all); testRmFundedOffer(all - fixAMMv1_1 - fixAMMv1_3); @@ -1327,9 +1355,11 @@ private: STPathSet st; STAmount sa; STAmount da; - std::tie(st, sa, da) = find_paths(env, alice, bob, bob["AUD"](-1), std::optional(XRP(100'000'000))); + std::tie(st, sa, da) = + find_paths(env, alice, bob, bob["AUD"](-1), std::optional(XRP(100'000'000))); BEAST_EXPECT(st.empty()); - std::tie(st, sa, da) = find_paths(env, alice, bob, bob["USD"](-1), std::optional(XRP(100'000'000))); + std::tie(st, sa, da) = + find_paths(env, alice, bob, bob["USD"](-1), std::optional(XRP(100'000'000))); // Alice sends all requested 100,000,000XRP BEAST_EXPECT(sa == XRP(100'000'000)); // Bob gets ~99.99USD. This is the amount Bob @@ -1383,7 +1413,8 @@ private: { auto const& pathElem = st[0][0]; BEAST_EXPECT( - pathElem.isOffer() && pathElem.getIssuerID() == gw.id() && pathElem.getCurrency() == USD.currency); + pathElem.isOffer() && pathElem.getIssuerID() == gw.id() && + pathElem.getCurrency() == USD.currency); } } { @@ -1459,7 +1490,8 @@ private: // no path should exist for this since dest account // does not exist. auto const& send_amt = XRP(200); - std::tie(st, sa, da) = find_paths(env, A1, Account{"A0"}, send_amt, std::nullopt, xrpCurrency()); + std::tie(st, sa, da) = + find_paths(env, A1, Account{"A0"}, send_amt, std::nullopt, xrpCurrency()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(st.empty()); } @@ -1576,7 +1608,8 @@ private: // A) Borrow or repay -- // Source -> Destination (repay source issuer) auto const& send_amt = G1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1586,7 +1619,8 @@ private: // A2) Borrow or repay -- // Source -> Destination (repay destination issuer) auto const& send_amt = A1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1596,7 +1630,8 @@ private: // B) Common gateway -- // Source -> AC -> Destination auto const& send_amt = A3["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same(st, stpath(G1))); @@ -1606,18 +1641,24 @@ private: // C) Gateway to gateway -- // Source -> OB -> Destination auto const& send_amt = G2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G1["HKD"](10))); - BEAST_EXPECT( - same(st, stpath(IPE(G2["HKD"])), stpath(M1), stpath(M2), stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); + BEAST_EXPECT(same( + st, + stpath(IPE(G2["HKD"])), + stpath(M1), + stpath(M2), + stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); } { // D) User to unlinked gateway via order book -- // Source -> AC -> OB -> Destination auto const& send_amt = G2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1633,7 +1674,8 @@ private: // Source -> AC -> OB to XRP -> OB from XRP -> AC -> // Destination auto const& send_amt = A2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1929,8 +1971,10 @@ private: return false; Sandbox sb(&view, tapNONE); for (auto const& o : flowResult.removableOffers) + { if (auto ok = sb.peek(keylet::offer(o))) offerDelete(sb, ok, flowJournal); + } sb.apply(view); return true; }); @@ -2005,17 +2049,18 @@ private: if (!features[fixAMMv1_1]) { // 120GBP is swapped in for 107.1428USD - BEAST_EXPECT( - amm.expectBalances(GBP(1'120), STAmount{USD, UINT64_C(892'8571428571428), -13}, amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + GBP(1'120), STAmount{USD, UINT64_C(892'8571428571428), -13}, amm.tokens())); } else { - BEAST_EXPECT( - amm.expectBalances(GBP(1'120), STAmount{USD, UINT64_C(892'8571428571429), -13}, amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + GBP(1'120), STAmount{USD, UINT64_C(892'8571428571429), -13}, amm.tokens())); } // 25% of 85.7142USD is paid in tr fee // 85.7142*1.25 = 107.1428USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'085'714285714286), -12))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'085'714285714286), -12))); } { @@ -2023,7 +2068,8 @@ private: Env env(*this, features); Account const ed("ed"); - fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'000), EUR(1'000), GBP(1'000)}); + fund( + env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'000), EUR(1'000), GBP(1'000)}); env(rate(gw, 1.25)); env.close(); @@ -2048,16 +2094,19 @@ private: BEAST_EXPECT(expectOffers(env, ed, 1, {Amounts{GBP(880), EUR(880)}})); // 25% on 96EUR is paid in tr fee 96*1.25 = 120EUR // 96EUR is swapped in for 87.5912USD - BEAST_EXPECT(amm.expectBalances(EUR(1'096), STAmount{USD, UINT64_C(912'4087591240876), -13}, amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + EUR(1'096), STAmount{USD, UINT64_C(912'4087591240876), -13}, amm.tokens())); // 25% on 70.0729USD is paid in tr fee 70.0729*1.25 = 87.5912USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'070'07299270073), -11))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'070'07299270073), -11))); } { // Payment via AMM, AMM Env env(*this, features); Account const ed("ed"); - fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'000), EUR(1'000), GBP(1'000)}); + fund( + env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'000), EUR(1'000), GBP(1'000)}); env(rate(gw, 1.25)); env.close(); @@ -2076,8 +2125,8 @@ private: // alice buys 107.1428EUR with 120GBP and pays 25% tr fee on // 120GBP 1,000 - 120*1.25 = 850GBP 120GBP is swapped in for // 107.1428EUR - BEAST_EXPECT( - amm1.expectBalances(GBP(1'120), STAmount{EUR, UINT64_C(892'8571428571428), -13}, amm1.tokens())); + BEAST_EXPECT(amm1.expectBalances( + GBP(1'120), STAmount{EUR, UINT64_C(892'8571428571428), -13}, amm1.tokens())); // 25% on 85.7142EUR is paid in tr fee 85.7142*1.25 = // 107.1428EUR 85.7142EUR is swapped in for 78.9473USD BEAST_EXPECT(amm2.expectBalances( @@ -2090,8 +2139,8 @@ private: // alice buys 107.1428EUR with 120GBP and pays 25% tr fee on // 120GBP 1,000 - 120*1.25 = 850GBP 120GBP is swapped in for // 107.1428EUR - BEAST_EXPECT( - amm1.expectBalances(GBP(1'120), STAmount{EUR, UINT64_C(892'8571428571429), -13}, amm1.tokens())); + BEAST_EXPECT(amm1.expectBalances( + GBP(1'120), STAmount{EUR, UINT64_C(892'8571428571429), -13}, amm1.tokens())); // 25% on 85.7142EUR is paid in tr fee 85.7142*1.25 = // 107.1428EUR 85.7142EUR is swapped in for 78.9473USD BEAST_EXPECT(amm2.expectBalances( @@ -2100,7 +2149,8 @@ private: amm2.tokens())); } // 25% on 63.1578USD is paid in tr fee 63.1578*1.25 = 78.9473USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12))); } { // AMM offer crossing @@ -2142,9 +2192,11 @@ private: // alice buys 125USD with 142.8571GBP and pays 25% tr fee // on 142.8571GBP // 1,000 - 142.8571*1.25 = 821.4285GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount(GBP, UINT64_C(821'4285714285712), -13))); + BEAST_EXPECT( + expectHolding(env, alice, STAmount(GBP, UINT64_C(821'4285714285712), -13))); // 142.8571GBP is swapped in for 125USD - BEAST_EXPECT(amm.expectBalances(STAmount{GBP, UINT64_C(1'142'857142857143), -12}, USD(875), amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + STAmount{GBP, UINT64_C(1'142'857142857143), -12}, USD(875), amm.tokens())); // 25% on 100USD is paid in tr fee // 100*1.25 = 125USD BEAST_EXPECT(expectHolding(env, carol, USD(1'100))); @@ -2182,10 +2234,13 @@ private: // alice buys 28.125USD with 24GBP and pays 25% tr fee // on 24GBP // 1,200 - 24*1.25 =~ 1,170GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'169'999999999999), -12})); - // 24GBP is swapped in for 28.125USD BEAST_EXPECT( - amm.expectBalances(STAmount{GBP, UINT64_C(1'024'000000000001), -12}, USD(1'171.875), amm.tokens())); + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'169'999999999999), -12})); + // 24GBP is swapped in for 28.125USD + BEAST_EXPECT(amm.expectBalances( + STAmount{GBP, UINT64_C(1'024'000000000001), -12}, + USD(1'171.875), + amm.tokens())); } // 25% on 22.5USD is paid in tr fee // 22.5*1.25 = 28.125USD @@ -2197,7 +2252,8 @@ private: Env env(*this, features); Account const ed("ed"); - fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); + fund( + env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); env(rate(gw, 1.25)); env.close(); @@ -2219,7 +2275,8 @@ private: // alice buys 70.4210EUR with 70.4210GBP via the offer // and pays 25% tr fee on 70.4210GBP // 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'311'973684210527), -12})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'311'973684210527), -12})); // ed doesn't pay tr fee, the balances reflect consumed offer // 70.4210GBP/70.4210EUR BEAST_EXPECT(expectHolding( @@ -2246,7 +2303,8 @@ private: // alice buys 70.4210EUR with 70.4210GBP via the offer // and pays 25% tr fee on 70.4210GBP // 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'311'973684210525), -12})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'311'973684210525), -12})); // ed doesn't pay tr fee, the balances reflect consumed offer // 70.4210GBP/70.4210EUR BEAST_EXPECT(expectHolding( @@ -2269,7 +2327,8 @@ private: amm.tokens())); } // 25% on 59.7321USD is paid in tr fee 59.7321*1.25 = 74.6651USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12))); } { // Payment via AMM and offer with limit quality, deliver less @@ -2277,7 +2336,8 @@ private: Env env(*this, features); Account const ed("ed"); - fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); + fund( + env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); env(rate(gw, 1.25)); env.close(); @@ -2299,7 +2359,8 @@ private: // alice buys 53.3322EUR with 56.3368GBP via the amm // and pays 25% tr fee on 56.3368GBP // 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'329'578947368421), -12})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'329'578947368421), -12})); //// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 ///= 70.4210EUR // 56.3368GBP is swapped in for 53.3322EUR @@ -2313,7 +2374,8 @@ private: // alice buys 53.3322EUR with 56.3368GBP via the amm // and pays 25% tr fee on 56.3368GBP // 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'329'57894736842), -11})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'329'57894736842), -11})); //// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 ///= 70.4210EUR // 56.3368GBP is swapped in for 53.3322EUR @@ -2337,7 +2399,8 @@ private: STAmount{EUR, UINT64_C(957'3341836734693), -13}, STAmount{USD, UINT64_C(1'340'267857142857), -12}}})); // 25% on 47.7857USD is paid in tr fee 47.7857*1.25 = 59.7321USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'447'785714285714), -12))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'447'785714285714), -12))); } { // Payment via AMM, AMM with limit quality, deliver less @@ -2345,7 +2408,8 @@ private: Env env(*this, features); Account const ed("ed"); - fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); + fund( + env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(1'400), EUR(1'400), GBP(1'400)}); env(rate(gw, 1.25)); env.close(); @@ -2365,7 +2429,8 @@ private: // alice buys 53.3322EUR with 107.5308GBP // 25% on 86.0246GBP is paid in tr fee // 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'292'469135802469), -12})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'292'469135802469), -12})); // 86.0246GBP is swapped in for 79.2106EUR BEAST_EXPECT(amm1.expectBalances( STAmount{GBP, UINT64_C(1'086'024691358025), -12}, @@ -2383,7 +2448,8 @@ private: // alice buys 53.3322EUR with 107.5308GBP // 25% on 86.0246GBP is paid in tr fee // 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP - BEAST_EXPECT(expectHolding(env, alice, STAmount{GBP, UINT64_C(1'292'469135802466), -12})); + BEAST_EXPECT( + expectHolding(env, alice, STAmount{GBP, UINT64_C(1'292'469135802466), -12})); // 86.0246GBP is swapped in for 79.2106EUR BEAST_EXPECT(amm1.expectBalances( STAmount{GBP, UINT64_C(1'086'024691358027), -12}, @@ -2397,7 +2463,8 @@ private: amm2.tokens())); } // 25% on 66.7432USD is paid in tr fee 66.7432*1.25 = 83.4291USD - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12))); } { // Payment by the issuer via AMM, AMM with limit quality, @@ -2448,7 +2515,8 @@ private: amm2.tokens())); } // 25% on 81.1111USD is paid in tr fee 81.1111*1.25 = 101.3888USD - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12})); } } @@ -2567,9 +2635,13 @@ private: // offer, removes 999 more as unfunded, then hits the step limit. env(offer(alice, USD(1'000), XRP(1'000))); if (!features[fixAMMv1_1]) + { env.require(balance(alice, STAmount{USD, UINT64_C(2'050126257867561), -15})); + } else + { env.require(balance(alice, STAmount{USD, UINT64_C(2'050125257867587), -15})); + } env.require(owners(alice, 2)); env.require(balance(bob, USD(0))); env.require(owners(bob, 1'001)); @@ -2599,13 +2671,22 @@ private: fund(env, gw, {alice, bob, carol}, XRP(10'000)); env.trust(USD(100), alice, bob, carol); env(pay(alice, bob, USD(10)), deliver_min(USD(10)), ter(temBAD_AMOUNT)); - env(pay(alice, bob, USD(10)), deliver_min(USD(-5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); - env(pay(alice, bob, USD(10)), deliver_min(XRP(5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); + env(pay(alice, bob, USD(10)), + deliver_min(USD(-5)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); + env(pay(alice, bob, USD(10)), + deliver_min(XRP(5)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); env(pay(alice, bob, USD(10)), deliver_min(Account(carol)["USD"](5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); - env(pay(alice, bob, USD(10)), deliver_min(USD(15)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); + env(pay(alice, bob, USD(10)), + deliver_min(USD(15)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); env(pay(gw, carol, USD(50))); AMM ammCarol(env, carol, XRP(10), USD(15)); env(pay(alice, bob, USD(10)), @@ -2685,7 +2766,9 @@ private: env.require(balance(bob, USD(0))); env.require(balance(carol, STAmount{USD, UINT64_C(200'00000090909), -11})); BEAST_EXPECT(ammDan.expectBalances( - XRPAmount{1'100'000'001}, STAmount{USD, UINT64_C(999'99999909091), -11}, ammDan.tokens())); + XRPAmount{1'100'000'001}, + STAmount{USD, UINT64_C(999'99999909091), -11}, + ammDan.tokens())); } } } @@ -2942,7 +3025,8 @@ private: if (!BEAST_EXPECT(checkArraySize(affected, 2u))) return; auto ff = affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName]; - BEAST_EXPECT(ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfLowFreeze)); BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze)); env.close(); @@ -2989,7 +3073,8 @@ private: // Account without GlobalFreeze (proving operations normally // work) // test: visible offers where taker_pays is unfrozen issuer - auto offers = env.rpc("book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; + auto offers = env.rpc( + "book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 1u))) return; std::set accounts; @@ -3000,7 +3085,8 @@ private: BEAST_EXPECT(accounts.find(A2.human()) != std::end(accounts)); // test: visible offers where taker_gets is unfrozen issuer - offers = env.rpc("book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; + offers = env.rpc( + "book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 1u))) return; accounts.clear(); @@ -3053,11 +3139,13 @@ private: { // test: book_offers shows offers // (should these actually be filtered?) - auto offers = env.rpc("book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; + auto offers = env.rpc( + "book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 1u))) return; - offers = env.rpc("book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; + offers = env.rpc( + "book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 1u))) return; } @@ -3270,7 +3358,10 @@ private: AMM ammBob(env, bob, XRP(100), USD(100)); // payment path: XRP -> XRP/USD -> USD/XRP - env(pay(alice, carol, XRP(100)), path(~USD, ~XRP), txflags(tfNoRippleDirect), ter(temBAD_SEND_XRP_PATHS)); + env(pay(alice, carol, XRP(100)), + path(~USD, ~XRP), + txflags(tfNoRippleDirect), + ter(temBAD_SEND_XRP_PATHS)); } { @@ -3362,7 +3453,8 @@ private: using namespace jtx; // For now, just disable SAV entirely, which locks in the small Number // mantissas in the transaction engine - FeatureBitset const all{testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const all{ + testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; testFalseDry(all); testBookStep(all); @@ -3378,7 +3470,8 @@ private: using namespace jtx; // For now, just disable SAV entirely, which locks in the small Number // mantissas in the transaction engine - FeatureBitset const all{testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const all{ + testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; testStepLimit(all); testStepLimit(all - fixAMMv1_1 - fixAMMv1_3); } @@ -3389,7 +3482,8 @@ private: using namespace jtx; // For now, just disable SAV entirely, which locks in the small Number // mantissas in the transaction engine - FeatureBitset const all{testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const all{ + testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; test_convert_all_of_an_asset(all); test_convert_all_of_an_asset(all - fixAMMv1_1 - fixAMMv1_3); } @@ -3399,7 +3493,8 @@ private: { // For now, just disable SAV entirely, which locks in the small Number // mantissas in the transaction engine - FeatureBitset const all{jtx::testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const all{ + jtx::testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; testPayment(all); testPayIOU(); } @@ -3410,7 +3505,8 @@ private: using namespace test::jtx; // For now, just disable SAV entirely, which locks in the small Number // mantissas in the transaction engine - FeatureBitset const sa{testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; + FeatureBitset const sa{ + testable_amendments() - featureSingleAssetVault - featureLendingProtocol}; testRippleState(sa); testGlobalFreeze(sa); testOffersWhenFrozen(sa); diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 346c05fd7d..e36ba6514e 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -6,15 +6,15 @@ #include #include -#include -#include -#include -#include - #include +#include #include #include #include +#include +#include +#include +#include #include @@ -53,7 +53,8 @@ private: // XRP to IOU, with featureSingleAssetVault testAMM( [&](AMM& ammAlice, Env&) { - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); }, {}, 0, @@ -64,7 +65,8 @@ private: // XRP to IOU, without featureSingleAssetVault testAMM( [&](AMM& ammAlice, Env&) { - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); }, {}, 0, @@ -132,8 +134,12 @@ private: 1'000); // Make sure asset comparison works. - BEAST_EXPECT(STIssue(sfAsset, STAmount(XRP(2'000)).issue()) == STIssue(sfAsset, STAmount(XRP(2'000)).issue())); - BEAST_EXPECT(STIssue(sfAsset, STAmount(XRP(2'000)).issue()) != STIssue(sfAsset, STAmount(USD(2'000)).issue())); + BEAST_EXPECT( + STIssue(sfAsset, STAmount(XRP(2'000)).issue()) == + STIssue(sfAsset, STAmount(XRP(2'000)).issue())); + BEAST_EXPECT( + STIssue(sfAsset, STAmount(XRP(2'000)).issue()) != + STIssue(sfAsset, STAmount(USD(2'000)).issue())); } void @@ -217,8 +223,9 @@ private: } // AMM already exists - testAMM( - [&](AMM& ammAlice, Env& env) { AMM ammCarol(env, carol, XRP(10'000), USD(10'000), ter(tecDUPLICATE)); }); + testAMM([&](AMM& ammAlice, Env& env) { + AMM ammCarol(env, carol, XRP(10'000), USD(10'000), ter(tecDUPLICATE)); + }); // Invalid flags { @@ -354,9 +361,17 @@ private: testAMM([&](AMM& ammAlice, Env& env) { fund(env, gw, {alice}, {EUR(10'000)}, Fund::IOUOnly); AMM ammAMMToken( - env, alice, EUR(10'000), STAmount{ammAlice.lptIssue(), 1'000'000}, ter(tecAMM_INVALID_TOKENS)); + env, + alice, + EUR(10'000), + STAmount{ammAlice.lptIssue(), 1'000'000}, + ter(tecAMM_INVALID_TOKENS)); AMM ammAMMToken1( - env, alice, STAmount{ammAlice.lptIssue(), 1'000'000}, EUR(10'000), ter(tecAMM_INVALID_TOKENS)); + env, + alice, + STAmount{ammAlice.lptIssue(), 1'000'000}, + EUR(10'000), + ter(tecAMM_INVALID_TOKENS)); }); // AMM with two LPTokens from other AMMs. @@ -366,7 +381,11 @@ private: auto const token1 = ammAlice.lptIssue(); auto const token2 = ammAlice1.lptIssue(); AMM ammAMMTokens( - env, alice, STAmount{token1, 1'000'000}, STAmount{token2, 1'000'000}, ter(tecAMM_INVALID_TOKENS)); + env, + alice, + STAmount{token1, 1'000'000}, + STAmount{token2, 1'000'000}, + ter(tecAMM_INVALID_TOKENS)); }); // Issuer has DefaultRipple disabled @@ -415,30 +434,95 @@ private: // flags, tokens, asset1In, asset2in, EPrice, tfee {tfLPToken, 1'000, std::nullopt, USD(100), std::nullopt, std::nullopt}, {tfLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, std::nullopt}, - {tfLPToken, 1'000, std::nullopt, std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, - {tfLPToken, std::nullopt, USD(100), std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, + {tfLPToken, + 1'000, + std::nullopt, + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, + {tfLPToken, + std::nullopt, + USD(100), + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, {tfLPToken, 1'000, XRP(100), std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, {tfLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, 1'000}, {tfSingleAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfSingleAsset, std::nullopt, std::nullopt, USD(100), std::nullopt, std::nullopt}, - {tfSingleAsset, std::nullopt, std::nullopt, std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, + {tfSingleAsset, + std::nullopt, + std::nullopt, + USD(100), + std::nullopt, + std::nullopt}, + {tfSingleAsset, + std::nullopt, + std::nullopt, + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, {tfSingleAsset, std::nullopt, USD(100), std::nullopt, std::nullopt, 1'000}, {tfTwoAsset, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfTwoAsset, std::nullopt, XRP(100), USD(100), STAmount{USD, 1, -1}, std::nullopt}, + {tfTwoAsset, + std::nullopt, + XRP(100), + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}, {tfTwoAsset, std::nullopt, XRP(100), std::nullopt, std::nullopt, std::nullopt}, {tfTwoAsset, std::nullopt, XRP(100), USD(100), std::nullopt, 1'000}, - {tfTwoAsset, std::nullopt, std::nullopt, USD(100), STAmount{USD, 1, -1}, std::nullopt}, - {tfOneAssetLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfOneAssetLPToken, std::nullopt, XRP(100), USD(100), std::nullopt, std::nullopt}, - {tfOneAssetLPToken, std::nullopt, XRP(100), std::nullopt, STAmount{USD, 1, -1}, std::nullopt}, + {tfTwoAsset, + std::nullopt, + std::nullopt, + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}, + {tfOneAssetLPToken, + 1'000, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt}, + {tfOneAssetLPToken, + std::nullopt, + XRP(100), + USD(100), + std::nullopt, + std::nullopt}, + {tfOneAssetLPToken, + std::nullopt, + XRP(100), + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt}, {tfOneAssetLPToken, 1'000, XRP(100), std::nullopt, std::nullopt, 1'000}, {tfLimitLPToken, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, {tfLimitLPToken, 1'000, USD(100), std::nullopt, std::nullopt, std::nullopt}, {tfLimitLPToken, std::nullopt, USD(100), XRP(100), std::nullopt, std::nullopt}, - {tfLimitLPToken, std::nullopt, XRP(100), std::nullopt, STAmount{USD, 1, -1}, 1'000}, - {tfTwoAssetIfEmpty, std::nullopt, std::nullopt, std::nullopt, std::nullopt, 1'000}, - {tfTwoAssetIfEmpty, 1'000, std::nullopt, std::nullopt, std::nullopt, std::nullopt}, - {tfTwoAssetIfEmpty, std::nullopt, XRP(100), USD(100), STAmount{USD, 1, -1}, std::nullopt}, + {tfLimitLPToken, + std::nullopt, + XRP(100), + std::nullopt, + STAmount{USD, 1, -1}, + 1'000}, + {tfTwoAssetIfEmpty, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + 1'000}, + {tfTwoAssetIfEmpty, + 1'000, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt}, + {tfTwoAssetIfEmpty, + std::nullopt, + XRP(100), + USD(100), + STAmount{USD, 1, -1}, + std::nullopt}, {tfTwoAssetIfEmpty | tfLPToken, std::nullopt, XRP(100), @@ -473,7 +557,8 @@ private: // Invalid tokens ammAlice.deposit(alice, 0, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); - ammAlice.deposit(alice, IOUAmount{-1}, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); + ammAlice.deposit( + alice, IOUAmount{-1}, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); { Json::Value jv = Json::objectValue; @@ -517,21 +602,28 @@ private: } // Depositing mismatched token, invalid Asset1In.issue - ammAlice.deposit(alice, GBP(100), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); + ammAlice.deposit( + alice, GBP(100), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); // Depositing mismatched token, invalid Asset2In.issue - ammAlice.deposit(alice, USD(100), GBP(100), std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); + ammAlice.deposit( + alice, USD(100), GBP(100), std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); // Depositing mismatched token, Asset1In.issue == Asset2In.issue - ammAlice.deposit(alice, USD(100), USD(100), std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); + ammAlice.deposit( + alice, USD(100), USD(100), std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); // Invalid amount value - ammAlice.deposit(alice, USD(0), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMOUNT)); - ammAlice.deposit(alice, USD(-1'000), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMOUNT)); - ammAlice.deposit(alice, USD(10), std::nullopt, USD(-1), std::nullopt, ter(temBAD_AMOUNT)); + ammAlice.deposit( + alice, USD(0), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMOUNT)); + ammAlice.deposit( + alice, USD(-1'000), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_AMOUNT)); + ammAlice.deposit( + alice, USD(10), std::nullopt, USD(-1), std::nullopt, ter(temBAD_AMOUNT)); // Bad currency - ammAlice.deposit(alice, BAD(100), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_CURRENCY)); + ammAlice.deposit( + alice, BAD(100), std::nullopt, std::nullopt, std::nullopt, ter(temBAD_CURRENCY)); // Invalid Account Account bad("bad"); @@ -591,12 +683,25 @@ private: // Deposit amount is invalid // Calculated amount to deposit is 98,000,000 - ammAlice.deposit(alice, USD(0), std::nullopt, STAmount{USD, 1, -1}, std::nullopt, ter(tecUNFUNDED_AMM)); + ammAlice.deposit( + alice, + USD(0), + std::nullopt, + STAmount{USD, 1, -1}, + std::nullopt, + ter(tecUNFUNDED_AMM)); // Calculated amount is 0 - ammAlice.deposit(alice, USD(0), std::nullopt, STAmount{USD, 2'000, -6}, std::nullopt, ter(tecAMM_FAILED)); + ammAlice.deposit( + alice, + USD(0), + std::nullopt, + STAmount{USD, 2'000, -6}, + std::nullopt, + ter(tecAMM_FAILED)); // Deposit non-empty AMM - ammAlice.deposit(carol, XRP(100), USD(100), std::nullopt, tfTwoAssetIfEmpty, ter(tecAMM_NOT_EMPTY)); + ammAlice.deposit( + carol, XRP(100), USD(100), std::nullopt, tfTwoAssetIfEmpty, ter(tecAMM_NOT_EMPTY)); }); // Tiny deposit @@ -612,7 +717,12 @@ private: // Pre/post-amendment LPTokens is rounded to 0 and deposit // fails with tecAMM_INVALID_TOKENS. ammAlice.deposit( - carol, STAmount{USD, 1, -12}, std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + carol, + STAmount{USD, 1, -12}, + std::nullopt, + std::nullopt, + std::nullopt, + ter(tecAMM_INVALID_TOKENS)); }, std::nullopt, 0, @@ -630,18 +740,25 @@ private: [&](AMM& ammAlice, Env& env) { env(fset(gw, asfGlobalFreeze)); if (!features[featureAMMClawback]) + { // If the issuer set global freeze, the holder still can // deposit the other non-frozen token when AMMClawback is // not enabled. ammAlice.deposit(carol, XRP(100)); + } else + { // If the issuer set global freeze, the holder cannot // deposit the other non-frozen token when AMMClawback is // enabled. - ammAlice.deposit(carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + } + ammAlice.deposit( + carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, XRP(100), USD(100), std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, XRP(100), USD(100), std::nullopt, std::nullopt, ter(tecFROZEN)); }, std::nullopt, 0, @@ -654,24 +771,34 @@ private: env(trust(gw, carol["USD"](0), tfSetFreeze)); env.close(); if (!features[featureAMMClawback]) + { // Can deposit non-frozen token if AMMClawback is not // enabled ammAlice.deposit(carol, XRP(100)); + } else + { // Cannot deposit non-frozen token if the other token is // frozen when AMMClawback is enabled - ammAlice.deposit(carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + } ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); env(trust(gw, carol["USD"](0), tfClearFreeze)); // Individually frozen AMM - env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); + env(trust( + gw, + STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, + tfSetFreeze)); env.close(); // Can deposit non-frozen token ammAlice.deposit(carol, XRP(100)); ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); }, std::nullopt, 0, @@ -685,14 +812,19 @@ private: env(trust(gw, carol["BTC"](0), tfSetFreeze)); env.close(); ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecFROZEN)); env(trust(gw, carol["USD"](0), tfClearFreeze)); // Individually frozen AMM - env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); + env(trust( + gw, + STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, + tfSetFreeze)); env.close(); // Cannot deposit non-frozen token ammAlice.deposit(carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); - ammAlice.deposit(carol, USD(100), BTC(0.01), std::nullopt, std::nullopt, ter(tecFROZEN)); + ammAlice.deposit( + carol, USD(100), BTC(0.01), std::nullopt, std::nullopt, ter(tecFROZEN)); }, {{USD(20'000), BTC(0.5)}}); @@ -714,12 +846,18 @@ private: env.close(); if (features[featureAMMClawback]) + { // if featureAMMClawback is enabled, bob can not deposit XRP // because he's not authorized to hold the paired token // gw["USD"]. - amm.deposit(bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tecNO_AUTH)); + amm.deposit( + bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tecNO_AUTH)); + } else - amm.deposit(bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tesSUCCESS)); + { + amm.deposit( + bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tesSUCCESS)); + } } // Insufficient XRP balance @@ -728,14 +866,16 @@ private: env.close(); // Adds LPT trustline ammAlice.deposit(bob, XRP(10)); - ammAlice.deposit(bob, XRP(1'000), std::nullopt, std::nullopt, std::nullopt, ter(tecUNFUNDED_AMM)); + ammAlice.deposit( + bob, XRP(1'000), std::nullopt, std::nullopt, std::nullopt, ter(tecUNFUNDED_AMM)); }); // Insufficient USD balance testAMM([&](AMM& ammAlice, Env& env) { fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct); env.close(); - ammAlice.deposit(bob, USD(1'001), std::nullopt, std::nullopt, std::nullopt, ter(tecUNFUNDED_AMM)); + ammAlice.deposit( + bob, USD(1'001), std::nullopt, std::nullopt, std::nullopt, ter(tecUNFUNDED_AMM)); }); // Insufficient USD balance by tokens @@ -791,10 +931,22 @@ private: env(offer(carol, XRP(100), USD(101))); env(offer(carol, XRP(100), USD(102))); AMM ammAlice(env, alice, XRP(1'000), USD(1'000)); - ammAlice.deposit(carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecINSUF_RESERVE_LINE)); + ammAlice.deposit( + carol, + XRP(100), + std::nullopt, + std::nullopt, + std::nullopt, + ter(tecINSUF_RESERVE_LINE)); env(offer(carol, XRP(100), USD(103))); - ammAlice.deposit(carol, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecINSUF_RESERVE_LINE)); + ammAlice.deposit( + carol, + USD(100), + std::nullopt, + std::nullopt, + std::nullopt, + ter(tecINSUF_RESERVE_LINE)); } // Insufficient reserve, IOU/IOU @@ -817,7 +969,13 @@ private: env(offer(carol, XRP(100), USD(101))); env(offer(carol, XRP(100), USD(102))); AMM ammAlice(env, alice, XRP(1'000), USD(1'000)); - ammAlice.deposit(carol, XRP(100), std::nullopt, std::nullopt, std::nullopt, ter(tecINSUF_RESERVE_LINE)); + ammAlice.deposit( + carol, + XRP(100), + std::nullopt, + std::nullopt, + std::nullopt, + ter(tecINSUF_RESERVE_LINE)); } // Invalid min @@ -955,30 +1113,43 @@ private: // Equal deposit limit, tokens rounded to 0 testAMM( [&](AMM& amm, Env& env) { - amm.deposit(DepositArg{ - .asset1In = STAmount{USD, 1, -15}, .asset2In = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)}); + amm.deposit( + DepositArg{ + .asset1In = STAmount{USD, 1, -15}, + .asset2In = XRPAmount{1}, + .err = ter(tecAMM_INVALID_TOKENS)}); }, {.pool = {{USD(1'000'000), XRP(1'000'000)}}, .features = {features - fixAMMv1_3}}); testAMM([&](AMM& amm, Env& env) { - amm.deposit(DepositArg{ - .asset1In = STAmount{USD, 1, -15}, .asset2In = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)}); + amm.deposit( + DepositArg{ + .asset1In = STAmount{USD, 1, -15}, + .asset2In = XRPAmount{1}, + .err = ter(tecAMM_INVALID_TOKENS)}); }); // Single deposit by asset, tokens rounded to 0 testAMM([&](AMM& amm, Env& env) { - amm.deposit(DepositArg{.asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)}); + amm.deposit( + DepositArg{.asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)}); }); // Single deposit by tokens, tokens rounded to 0 testAMM([&](AMM& amm, Env& env) { - amm.deposit(DepositArg{ - .tokens = IOUAmount{1, -10}, .asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)}); + amm.deposit( + DepositArg{ + .tokens = IOUAmount{1, -10}, + .asset1In = STAmount{USD, 1, -15}, + .err = ter(tecAMM_INVALID_TOKENS)}); }); // Single deposit with EPrice, tokens rounded to 0 testAMM([&](AMM& amm, Env& env) { - amm.deposit(DepositArg{ - .asset1In = STAmount{USD, 1, -15}, .maxEP = STAmount{USD, 1, -1}, .err = ter(tecAMM_INVALID_TOKENS)}); + amm.deposit( + DepositArg{ + .asset1In = STAmount{USD, 1, -15}, + .maxEP = STAmount{USD, 1, -1}, + .err = ter(tecAMM_INVALID_TOKENS)}); }); } @@ -994,7 +1165,8 @@ private: testAMM([&](AMM& ammAlice, Env& env) { auto const baseFee = env.current()->fees().base; ammAlice.deposit(carol, 1'000'000); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); // 30,000 less deposited 1,000 BEAST_EXPECT(expectHolding(env, carol, USD(29'000))); // 30,000 less deposited 1,000 and 10 drops tx fee @@ -1040,12 +1212,15 @@ private: // initial LPTokens (1e7) + newLPTokens BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000) + depositXRP, USD(10'000) + depositUSD, IOUAmount{1, 7} + newLPTokens)); + XRP(10'000) + depositXRP, + USD(10'000) + depositUSD, + IOUAmount{1, 7} + newLPTokens)); // 30,000 less deposited depositUSD BEAST_EXPECT(expectHolding(env, carol, USD(30'000) - depositUSD)); // 30,000 less deposited depositXRP and 10 drops tx fee - BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) - depositXRP - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, carol, XRP(30'000) - depositXRP - txfee(env, 1))); }); } @@ -1056,44 +1231,52 @@ private: // Deposit 100USD/100XRP testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(100), XRP(100)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); }); // Equal limit deposit. // Try to deposit 200USD/100XRP. Is truncated to 100USD/100XRP. testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(200), XRP(100)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); }); // Try to deposit 100USD/200XRP. Is truncated to 100USD/100XRP. testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(100), XRP(200)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0})); }); // Single deposit: 1000 USD testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(1'000)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8})); + XRP(10'000), + STAmount{USD, UINT64_C(10'999'99999999999), -11}, + IOUAmount{10'488'088'48170151, -8})); }); // Single deposit: 1000 XRP testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, XRP(1'000)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8})); }); // Single deposit: 100000 tokens worth of USD testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, 100000, USD(205)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'201), IOUAmount{10'100'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'201), IOUAmount{10'100'000, 0})); }); // Single deposit: 100000 tokens worth of XRP testAMM([&](AMM& ammAlice, Env& env) { ammAlice.deposit(carol, 100'000, XRP(205)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'201), USD(10'000), IOUAmount{10'100'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'201), USD(10'000), IOUAmount{10'100'000, 0})); }); // Single deposit with EP not exceeding specified: @@ -1101,21 +1284,25 @@ private: testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1}); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8})); + XRP(10'000), + STAmount{USD, UINT64_C(10'999'99999999999), -11}, + IOUAmount{10'488'088'48170151, -8})); }); // Single deposit with EP not exceeding specified: // 100USD with EP not to exceed 0.002004 (AssetIn/TokensOut) testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(100), std::nullopt, STAmount{USD, 2004, -6}); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0})); }); // Single deposit with EP not exceeding specified: // 0USD with EP not to exceed 0.002004 (AssetIn/TokensOut) testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(0), std::nullopt, STAmount{USD, 2004, -6}); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0})); }); // IOU to IOU + transfer fee @@ -1140,19 +1327,23 @@ private: testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, IOUAmount{1, -3}); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{10'000'000'001}, STAmount{USD, UINT64_C(10'000'000001), -6}, IOUAmount{10'000'000'001, -3})); + XRPAmount{10'000'000'001}, + STAmount{USD, UINT64_C(10'000'000001), -6}, + IOUAmount{10'000'000'001, -3})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1, -3})); }); testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, XRPAmount{1}); - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount{10'000'000'001}, USD(10'000), IOUAmount{1'000'000'000049999, -8})); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{10'000'000'001}, USD(10'000), IOUAmount{1'000'000'000049999, -8})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{49999, -8})); }); testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, STAmount{USD, 1, -10}); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'000'00000000008), -11}, IOUAmount{10'000'000'00000004, -8})); + XRP(10'000), + STAmount{USD, UINT64_C(10'000'00000000008), -11}, + IOUAmount{10'000'000'00000004, -8})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4, -8})); }); @@ -1167,7 +1358,9 @@ private: BEAST_EXPECT(ammGw.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000})); ammGw.deposit(gw, USD(1'000)); BEAST_EXPECT(ammGw.expectBalances( - XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8})); + XRP(11'000), + STAmount{USD, UINT64_C(11'999'99999999998), -11}, + IOUAmount{11'489'125'29307605, -8})); } // Issuer deposit @@ -1176,34 +1369,69 @@ private: BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000})); ammAlice.deposit(gw, USD(1'000)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8})); + XRP(11'000), + STAmount{USD, UINT64_C(11'999'99999999998), -11}, + IOUAmount{11'489'125'29307605, -8})); }); // Min deposit testAMM([&](AMM& ammAlice, Env& env) { // Equal deposit by tokens ammAlice.deposit( - carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfLPToken, std::nullopt, std::nullopt); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + carol, + 1'000'000, + XRP(1'000), + USD(1'000), + std::nullopt, + tfLPToken, + std::nullopt, + std::nullopt); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); }); testAMM([&](AMM& ammAlice, Env& env) { // Equal deposit by asset ammAlice.deposit( - carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfTwoAsset, std::nullopt, std::nullopt); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + carol, + 1'000'000, + XRP(1'000), + USD(1'000), + std::nullopt, + tfTwoAsset, + std::nullopt, + std::nullopt); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); }); testAMM([&](AMM& ammAlice, Env& env) { // Single deposit by asset ammAlice.deposit( - carol, 488'088, XRP(1'000), std::nullopt, std::nullopt, tfSingleAsset, std::nullopt, std::nullopt); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8})); - }); - testAMM([&](AMM& ammAlice, Env& env) { - // Single deposit by asset - ammAlice.deposit( - carol, 488'088, USD(1'000), std::nullopt, std::nullopt, tfSingleAsset, std::nullopt, std::nullopt); + carol, + 488'088, + XRP(1'000), + std::nullopt, + std::nullopt, + tfSingleAsset, + std::nullopt, + std::nullopt); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8})); + XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8})); + }); + testAMM([&](AMM& ammAlice, Env& env) { + // Single deposit by asset + ammAlice.deposit( + carol, + 488'088, + USD(1'000), + std::nullopt, + std::nullopt, + tfSingleAsset, + std::nullopt, + std::nullopt); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), + STAmount{USD, UINT64_C(10'999'99999999999), -11}, + IOUAmount{10'488'088'48170151, -8})); }); } @@ -1291,24 +1519,74 @@ private: NotTEC>> invalidOptions = { // tokens, asset1Out, asset2Out, EPrice, flags, ter - {std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, temMALFORMED}, - {std::nullopt, std::nullopt, std::nullopt, std::nullopt, tfSingleAsset | tfTwoAsset, temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + tfSingleAsset | tfTwoAsset, + temMALFORMED}, {1'000, std::nullopt, std::nullopt, std::nullopt, tfWithdrawAll, temMALFORMED}, - {std::nullopt, USD(0), XRP(100), std::nullopt, tfWithdrawAll | tfLPToken, temMALFORMED}, - {std::nullopt, std::nullopt, USD(100), std::nullopt, tfWithdrawAll, temMALFORMED}, + {std::nullopt, + USD(0), + XRP(100), + std::nullopt, + tfWithdrawAll | tfLPToken, + temMALFORMED}, + {std::nullopt, + std::nullopt, + USD(100), + std::nullopt, + tfWithdrawAll, + temMALFORMED}, {std::nullopt, std::nullopt, std::nullopt, std::nullopt, tfWithdrawAll | tfOneAssetWithdrawAll, temMALFORMED}, - {std::nullopt, USD(100), std::nullopt, std::nullopt, tfWithdrawAll, temMALFORMED}, - {std::nullopt, std::nullopt, std::nullopt, std::nullopt, tfOneAssetWithdrawAll, temMALFORMED}, + {std::nullopt, + USD(100), + std::nullopt, + std::nullopt, + tfWithdrawAll, + temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + tfOneAssetWithdrawAll, + temMALFORMED}, {1'000, std::nullopt, USD(100), std::nullopt, std::nullopt, temMALFORMED}, - {std::nullopt, std::nullopt, std::nullopt, IOUAmount{250, 0}, tfWithdrawAll, temMALFORMED}, - {1'000, std::nullopt, std::nullopt, IOUAmount{250, 0}, std::nullopt, temMALFORMED}, - {std::nullopt, std::nullopt, USD(100), IOUAmount{250, 0}, std::nullopt, temMALFORMED}, - {std::nullopt, XRP(100), USD(100), IOUAmount{250, 0}, std::nullopt, temMALFORMED}, + {std::nullopt, + std::nullopt, + std::nullopt, + IOUAmount{250, 0}, + tfWithdrawAll, + temMALFORMED}, + {1'000, + std::nullopt, + std::nullopt, + IOUAmount{250, 0}, + std::nullopt, + temMALFORMED}, + {std::nullopt, + std::nullopt, + USD(100), + IOUAmount{250, 0}, + std::nullopt, + temMALFORMED}, + {std::nullopt, + XRP(100), + USD(100), + IOUAmount{250, 0}, + std::nullopt, + temMALFORMED}, {1'000, XRP(100), USD(100), std::nullopt, std::nullopt, temMALFORMED}, {std::nullopt, XRP(100), USD(100), std::nullopt, tfWithdrawAll, temMALFORMED}}; for (auto const& it : invalidOptions) @@ -1327,7 +1605,8 @@ private: // Invalid tokens ammAlice.withdraw(alice, 0, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); - ammAlice.withdraw(alice, IOUAmount{-1}, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); + ammAlice.withdraw( + alice, IOUAmount{-1}, std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); // Mismatched token, invalid Asset1Out issue ammAlice.withdraw(alice, GBP(100), std::nullopt, std::nullopt, ter(temBAD_AMM_TOKENS)); @@ -1392,9 +1671,11 @@ private: // Withdrawing from one side. // XRP by tokens - ammAlice.withdraw(alice, IOUAmount(9'999'999'9999, -4), XRP(0), std::nullopt, ter(tecAMM_BALANCE)); + ammAlice.withdraw( + alice, IOUAmount(9'999'999'9999, -4), XRP(0), std::nullopt, ter(tecAMM_BALANCE)); // USD by tokens - ammAlice.withdraw(alice, IOUAmount(9'999'999'9, -1), USD(0), std::nullopt, ter(tecAMM_BALANCE)); + ammAlice.withdraw( + alice, IOUAmount(9'999'999'9, -1), USD(0), std::nullopt, ter(tecAMM_BALANCE)); // XRP ammAlice.withdraw(alice, XRP(10'000), std::nullopt, std::nullopt, ter(tecAMM_BALANCE)); // USD @@ -1416,9 +1697,13 @@ private: // Post-amendment: // Most of the pool is withdrawn with remaining tiny amounts auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE); - ammAlice.withdraw(alice, IOUAmount{9'999'999'9999, -4}, std::nullopt, std::nullopt, err); + ammAlice.withdraw( + alice, IOUAmount{9'999'999'9999, -4}, std::nullopt, std::nullopt, err); if (env.enabled(fixAMMv1_3)) - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -7}, IOUAmount{1, -4})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount(1), STAmount{USD, 1, -7}, IOUAmount{1, -4})); + } }, std::nullopt, 0, @@ -1434,9 +1719,13 @@ private: // this results in full withdraw of XRP pool only, // while leaving a tiny amount in USD pool. auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE); - ammAlice.withdraw(alice, IOUAmount{9'999'999'999999999, -9}, std::nullopt, std::nullopt, err); + ammAlice.withdraw( + alice, IOUAmount{9'999'999'999999999, -9}, std::nullopt, std::nullopt, err); if (env.enabled(fixAMMv1_3)) - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -11}, IOUAmount{1, -8})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount(1), STAmount{USD, 1, -11}, IOUAmount{1, -8})); + } }, std::nullopt, 0, @@ -1469,7 +1758,8 @@ private: ammAlice.withdraw(alice, USD(100), std::nullopt, std::nullopt, ter(tecFROZEN)); env(trust(gw, alice["USD"](0), tfClearFreeze)); // Individually frozen AMM - env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); + env(trust( + gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); // Can withdraw non-frozen token ammAlice.withdraw(alice, XRP(100)); ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN)); @@ -1481,10 +1771,13 @@ private: // Single deposit of 100000 worth of tokens, // which is 10% of the pool. Carol is LP now. ammAlice.deposit(carol, 1'000'000); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); - ammAlice.withdraw(carol, 2'000'000, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + ammAlice.withdraw( + carol, 2'000'000, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); }); // Withdraw with EPrice limit. Fails to withdraw, calculated tokens @@ -1492,7 +1785,8 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { ammAlice.deposit(carol, 1'000'000); - auto const err = env.enabled(fixAMMv1_3) ? ter(tecAMM_INVALID_TOKENS) : ter(tecAMM_FAILED); + auto const err = + env.enabled(fixAMMv1_3) ? ter(tecAMM_INVALID_TOKENS) : ter(tecAMM_FAILED); ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{500, 0}, err); }, std::nullopt, @@ -1504,28 +1798,32 @@ private: // to withdraw are greater than the LP shares. testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, 1'000'000); - ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{600, 0}, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + carol, USD(100), std::nullopt, IOUAmount{600, 0}, ter(tecAMM_INVALID_TOKENS)); }); // Withdraw with EPrice limit. Fails to withdraw, amount1 // to withdraw is less than 1700USD. testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, 1'000'000); - ammAlice.withdraw(carol, USD(1'700), std::nullopt, IOUAmount{520, 0}, ter(tecAMM_FAILED)); + ammAlice.withdraw( + carol, USD(1'700), std::nullopt, IOUAmount{520, 0}, ter(tecAMM_FAILED)); }); // Deposit/Withdraw the same amount with the trading fee testAMM( [&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, USD(1'000)); - ammAlice.withdraw(carol, USD(1'000), std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + carol, USD(1'000), std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); }, std::nullopt, 1'000); testAMM( [&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, XRP(1'000)); - ammAlice.withdraw(carol, XRP(1'000), std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + carol, XRP(1'000), std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); }, std::nullopt, 1'000); @@ -1533,33 +1831,62 @@ private: // Deposit/Withdraw the same amount fails due to the tokens adjustment testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, STAmount{USD, 1, -6}); - ammAlice.withdraw(carol, STAmount{USD, 1, -6}, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + carol, + STAmount{USD, 1, -6}, + std::nullopt, + std::nullopt, + ter(tecAMM_INVALID_TOKENS)); }); // Withdraw close to one side of the pool. Account's LP tokens // are rounded to all LP tokens. testAMM( [&](AMM& ammAlice, Env& env) { - auto const err = env.enabled(fixAMMv1_3) ? ter(tecINVARIANT_FAILED) : ter(tecAMM_BALANCE); + auto const err = + env.enabled(fixAMMv1_3) ? ter(tecINVARIANT_FAILED) : ter(tecAMM_BALANCE); ammAlice.withdraw( - alice, STAmount{USD, UINT64_C(9'999'999999999999), -12}, std::nullopt, std::nullopt, err); + alice, + STAmount{USD, UINT64_C(9'999'999999999999), -12}, + std::nullopt, + std::nullopt, + err); }, {.features = {all, all - fixAMMv1_3}, .noLog = true}); // Tiny withdraw testAMM([&](AMM& ammAlice, Env&) { // XRP amount to withdraw is 0 - ammAlice.withdraw(alice, IOUAmount{1, -5}, std::nullopt, std::nullopt, ter(tecAMM_FAILED)); + ammAlice.withdraw( + alice, IOUAmount{1, -5}, std::nullopt, std::nullopt, ter(tecAMM_FAILED)); // Calculated tokens to withdraw are 0 - ammAlice.withdraw(alice, std::nullopt, STAmount{USD, 1, -11}, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + alice, + std::nullopt, + STAmount{USD, 1, -11}, + std::nullopt, + ter(tecAMM_INVALID_TOKENS)); ammAlice.deposit(carol, STAmount{USD, 1, -10}); - ammAlice.withdraw(carol, std::nullopt, STAmount{USD, 1, -9}, std::nullopt, ter(tecAMM_INVALID_TOKENS)); - ammAlice.withdraw(carol, std::nullopt, XRPAmount{1}, std::nullopt, ter(tecAMM_INVALID_TOKENS)); - ammAlice.withdraw(WithdrawArg{.tokens = IOUAmount{1, -10}, .err = ter(tecAMM_INVALID_TOKENS)}); - ammAlice.withdraw(WithdrawArg{ - .asset1Out = STAmount{USD, 1, -15}, .asset2Out = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)}); - ammAlice.withdraw(WithdrawArg{ - .tokens = IOUAmount{1, -10}, .asset1Out = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)}); + ammAlice.withdraw( + carol, + std::nullopt, + STAmount{USD, 1, -9}, + std::nullopt, + ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + carol, std::nullopt, XRPAmount{1}, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.withdraw( + WithdrawArg{.tokens = IOUAmount{1, -10}, .err = ter(tecAMM_INVALID_TOKENS)}); + ammAlice.withdraw( + WithdrawArg{ + .asset1Out = STAmount{USD, 1, -15}, + .asset2Out = XRPAmount{1}, + .err = ter(tecAMM_INVALID_TOKENS)}); + ammAlice.withdraw( + WithdrawArg{ + .tokens = IOUAmount{1, -10}, + .asset1Out = STAmount{USD, 1, -15}, + .err = ter(tecAMM_INVALID_TOKENS)}); }); } @@ -1578,7 +1905,8 @@ private: // Single deposit of 100000 worth of tokens, // which is 10% of the pool. Carol is LP now. ammAlice.deposit(carol, 1'000'000); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0})); // 30,000 less deposited 1,000 BEAST_EXPECT(expectHolding(env, carol, USD(29'000))); @@ -1589,7 +1917,8 @@ private: ammAlice.withdraw(carol, 1'000'000); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero()))); BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); - BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - 2 * baseFee})); + BEAST_EXPECT( + expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - 2 * baseFee})); }); // Equal withdrawal by tokens 1000000, 10% @@ -1620,10 +1949,15 @@ private: [&](AMM& ammAlice, Env& env) { ammAlice.withdraw(alice, XRP(1'000)); if (!env.enabled(fixAMMv1_3)) - BEAST_EXPECT(ammAlice.expectBalances(XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{9'000'000'001}, USD(10'000), IOUAmount{9'486'832'98050514, -8})); + } }, std::nullopt, 0, @@ -1633,14 +1967,16 @@ private: // Single withdrawal by tokens 10000. testAMM([&](AMM& ammAlice, Env&) { ammAlice.withdraw(alice, 10'000, USD(0)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(9980.01), IOUAmount{9'990'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(9980.01), IOUAmount{9'990'000, 0})); }); // Withdraw all tokens. testAMM([&](AMM& ammAlice, Env& env) { env(trust(carol, STAmount{ammAlice.lptIssue(), 10'000})); - // Can SetTrust only for AMM LP tokens - env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), ter(tecNO_PERMISSION)); + // Can TrustSet only for AMM LP tokens + env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), + ter(tecNO_PERMISSION)); env.close(); ammAlice.withdrawAll(alice); BEAST_EXPECT(!ammAlice.ammExists()); @@ -1649,14 +1985,16 @@ private: // Can create AMM for the XRP/USD pair AMM ammCarol(env, carol, XRP(10'000), USD(10'000)); - BEAST_EXPECT(ammCarol.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT( + ammCarol.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); }); // Single deposit 1000USD, withdraw all tokens in USD testAMM([&](AMM& ammAlice, Env& env) { ammAlice.deposit(carol, USD(1'000)); ammAlice.withdrawAll(carol, USD(0)); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero()))); }); @@ -1665,7 +2003,9 @@ private: ammAlice.deposit(carol, USD(1'000)); ammAlice.withdrawAll(carol, XRP(0)); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount(9'090'909'091), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'000'000, 0})); + XRPAmount(9'090'909'091), + STAmount{USD, UINT64_C(10'999'99999999999), -11}, + IOUAmount{10'000'000, 0})); }); // Single deposit/withdraw by the same account @@ -1681,9 +2021,15 @@ private: lpTokens = ammAlice.deposit(carol, XRPAmount(1)); ammAlice.withdraw(carol, lpTokens, XRPAmount(0)); if (!env.enabled(fixAMMv1_3)) - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens())); + { + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens())); + } else - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens())); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens())); + } BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }, std::nullopt, @@ -1707,7 +2053,8 @@ private: testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, 1'000'000); ammAlice.withdrawAll(carol); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0})); }); // Equal deposit 10%, withdraw all tokens in USD @@ -1715,14 +2062,17 @@ private: ammAlice.deposit(carol, 1'000'000); ammAlice.withdrawAll(carol, USD(0)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(11'000), STAmount{USD, UINT64_C(9'090'909090909092), -12}, IOUAmount{10'000'000, 0})); + XRP(11'000), + STAmount{USD, UINT64_C(9'090'909090909092), -12}, + IOUAmount{10'000'000, 0})); }); // Equal deposit 10%, withdraw all tokens in XRP testAMM([&](AMM& ammAlice, Env&) { ammAlice.deposit(carol, 1'000'000); ammAlice.withdrawAll(carol, XRP(0)); - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(9'090'909'091), USD(11'000), IOUAmount{10'000'000, 0})); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount(9'090'909'091), USD(11'000), IOUAmount{10'000'000, 0})); }); // Withdraw with EPrice limit. @@ -1732,20 +2082,26 @@ private: ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0}); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8})); if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'781065088757), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'781065088769), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(11'000'000'000), STAmount{USD, UINT64_C(9'372'78106508877), -11}, IOUAmount{10'153'846'15384616, -8})); + } ammAlice.withdrawAll(carol); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }, @@ -1758,20 +2114,26 @@ private: ammAlice.withdraw(carol, USD(0), std::nullopt, IOUAmount{520, 0}); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8})); if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'781065088757), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'781065088769), -12}, IOUAmount{10'153'846'15384616, -8})); + } else if (env.enabled(fixAMMv1_3)) + { BEAST_EXPECT(ammAlice.expectBalances( XRP(11'000), STAmount{USD, UINT64_C(9'372'78106508877), -11}, IOUAmount{10'153'846'15384616, -8})); + } }, std::nullopt, 0, @@ -1808,17 +2170,24 @@ private: // By tokens ammAlice.withdraw(alice, IOUAmount{1, -3}); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{9'999'999'999}, STAmount{USD, UINT64_C(9'999'999999), -6}, IOUAmount{9'999'999'999, -3})); + XRPAmount{9'999'999'999}, + STAmount{USD, UINT64_C(9'999'999999), -6}, + IOUAmount{9'999'999'999, -3})); }); testAMM( [&](AMM& ammAlice, Env& env) { // Single XRP pool ammAlice.withdraw(alice, std::nullopt, XRPAmount{1}); if (!env.enabled(fixAMMv1_3)) - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount{9'999'999'999}, USD(10'000), IOUAmount{9'999'999'9995, -4})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{9'999'999'999}, USD(10'000), IOUAmount{9'999'999'9995, -4})); + } else - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4})); + } }, std::nullopt, 0, @@ -1828,14 +2197,17 @@ private: // Single USD pool ammAlice.withdraw(alice, std::nullopt, STAmount{USD, 1, -10}); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(9'999'9999999999), -10}, IOUAmount{9'999'999'99999995, -8})); + XRP(10'000), + STAmount{USD, UINT64_C(9'999'9999999999), -10}, + IOUAmount{9'999'999'99999995, -8})); }); // Withdraw close to entire pool // Equal by tokens testAMM([&](AMM& ammAlice, Env&) { ammAlice.withdraw(alice, IOUAmount{9'999'999'999, -3}); - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, STAmount{USD, 1, -6}, IOUAmount{1, -3})); + BEAST_EXPECT( + ammAlice.expectBalances(XRPAmount{1}, STAmount{USD, 1, -6}, IOUAmount{1, -3})); }); // USD by tokens testAMM([&](AMM& ammAlice, Env&) { @@ -1850,7 +2222,8 @@ private: // USD testAMM([&](AMM& ammAlice, Env&) { ammAlice.withdraw(alice, STAmount{USD, UINT64_C(9'999'99999999999), -11}); - BEAST_EXPECT(ammAlice.expectBalances(XRP(10000), STAmount{USD, 1, -11}, IOUAmount{316227765, -9})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10000), STAmount{USD, 1, -11}, IOUAmount{316227765, -9})); }); // XRP testAMM([&](AMM& ammAlice, Env&) { @@ -1867,10 +2240,17 @@ private: testAMM([&](AMM& ammAlice, Env& env) { // Invalid flags - ammAlice.vote(std::nullopt, 1'000, tfWithdrawAll, std::nullopt, std::nullopt, ter(temINVALID_FLAG)); + ammAlice.vote( + std::nullopt, + 1'000, + tfWithdrawAll, + std::nullopt, + std::nullopt, + ter(temINVALID_FLAG)); // Invalid fee. - ammAlice.vote(std::nullopt, 1'001, std::nullopt, std::nullopt, std::nullopt, ter(temBAD_FEE)); + ammAlice.vote( + std::nullopt, 1'001, std::nullopt, std::nullopt, std::nullopt, ter(temBAD_FEE)); BEAST_EXPECT(ammAlice.expectTradingFee(0)); // Invalid Account @@ -1882,7 +2262,8 @@ private: ammAlice.vote(alice, 1'000, std::nullopt, std::nullopt, {{USD, GBP}}, ter(terNO_AMM)); // Account is not LP - ammAlice.vote(carol, 1'000, std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); + ammAlice.vote( + carol, 1'000, std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_INVALID_TOKENS)); }); // Invalid AMM @@ -2002,7 +2383,7 @@ private: // The vote is not added to the slots ammAlice.vote(carol, 1'000); auto const info = ammAlice.ammRpcInfo()[jss::amm][jss::vote_slots]; - for (std::uint16_t i = 0; i < info.size(); ++i) + for (std::uint32_t i = 0; i < info.size(); ++i) BEAST_EXPECT(info[i][jss::account] != carol.human()); // But the slots are refreshed and the fee is changed BEAST_EXPECT(ammAlice.expectTradingFee(82)); @@ -2253,7 +2634,8 @@ private: env(ammAlice.bid({.account = carol, .bidMin = 110})); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110})); // 110 tokens are burned. - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890, 0})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890, 0})); }, std::nullopt, 0, @@ -2267,11 +2649,13 @@ private: // Bid exactly 110. Pay 110 because the pay price is < 110. env(ammAlice.bid({.account = carol, .bidMin = 110, .bidMax = 110})); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110})); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890})); // Bid exactly 180-200. Pay 180 because the pay price is < 180. env(ammAlice.bid({.account = alice, .bidMin = 180, .bidMax = 200})); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{180})); - BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'814'5, -1})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'814'5, -1})); }, std::nullopt, 0, @@ -2327,10 +2711,15 @@ private: fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct); ammAlice.deposit(bob, 1'000'000); if (!features[fixAMMv1_3]) - BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{12'000'000, 0})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRP(12'000), USD(12'000), IOUAmount{12'000'000, 0})); + } else - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{12'000'000, 0})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{12'000'000, 0})); + } // Initial state. Pay bidMin. env(ammAlice.bid({.account = carol, .bidMin = 110})).close(); @@ -2348,7 +2737,8 @@ private: // 20th Interval (expired) after close, price for 10th interval. env(ammAlice.bid({.account = bob})); - env.close(seconds(AUCTION_SLOT_TIME_INTERVALS * AUCTION_SLOT_INTERVAL_DURATION + 1)); + env.close( + seconds(AUCTION_SLOT_TIME_INTERVALS * AUCTION_SLOT_INTERVAL_DURATION + 1)); BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{127'33875, -5})); // 0 Interval. @@ -2356,10 +2746,15 @@ private: BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{110})); // ~321.09 tokens burnt on bidding fees. if (!features[fixAMMv1_3]) - BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{11'999'678'91, -2})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRP(12'000), USD(12'000), IOUAmount{11'999'678'91, -2})); + } else - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2})); + { + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2})); + } }, std::nullopt, 0, @@ -2389,9 +2784,14 @@ private: ammTokens -= slotPrice; BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, slotPrice)); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances(XRP(13'000), USD(13'000), ammTokens)); + } else - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{13'000'000'003}, USD(13'000), ammTokens)); + { + BEAST_EXPECT( + ammAlice.expectBalances(XRPAmount{13'000'000'003}, USD(13'000), ammTokens)); + } // Discounted trade for (int i = 0; i < 10; ++i) { @@ -2405,25 +2805,41 @@ private: // carol, bob, and ed pay ~0.99USD in fees. if (!features[fixAMMv1_1]) { - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620545), -11)); - BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616195), -11)); - BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'00572611841), -11)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'499'00572620545), -11)); + BEAST_EXPECT( + env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616195), -11)); + BEAST_EXPECT( + env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'00572611841), -11)); // USD pool is slightly higher because of the fees. BEAST_EXPECT(ammAlice.expectBalances( XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151419), -11), ammTokens)); } else { - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620544), -11)); - BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616194), -11)); - BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'0057261184), -10)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'499'00572620544), -11)); + BEAST_EXPECT( + env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616194), -11)); + BEAST_EXPECT( + env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'0057261184), -10)); // USD pool is slightly higher because of the fees. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens)); + XRP(13'000), + STAmount(USD, UINT64_C(13'002'98282151422), -11), + ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'003}, STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens)); + XRPAmount{13'000'000'003}, + STAmount(USD, UINT64_C(13'002'98282151422), -11), + ammTokens)); + } } ammTokens = ammAlice.getLPTokensBalance(); // Trade with the fee @@ -2437,7 +2853,8 @@ private: // than the trading fee. if (!features[fixAMMv1_1]) { - BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'056722744), -9)); + BEAST_EXPECT( + env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'056722744), -9)); // USD pool gains more in dan's fees. BEAST_EXPECT(ammAlice.expectBalances( XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens)); @@ -2451,40 +2868,74 @@ private: // carol pays 100000 drops in fees // 99900668XRP swapped in for 100USD BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens)); + XRPAmount{13'100'000'668}, + STAmount{USD, UINT64_C(13'012'92609877019), -11}, + ammTokens)); } else { if (!features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274399), -11)); + { + BEAST_EXPECT( + env.balance(dan, USD) == + STAmount(USD, UINT64_C(19'490'05672274399), -11)); + } else - BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274398), -11)); + { + BEAST_EXPECT( + env.balance(dan, USD) == + STAmount(USD, UINT64_C(19'490'05672274398), -11)); + } // USD pool gains more in dan's fees. if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens)); + XRP(13'000), + STAmount{USD, UINT64_C(13'012'92609877023), -11}, + ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens)); + XRPAmount{13'000'000'003}, + STAmount{USD, UINT64_C(13'012'92609877024), -11}, + ammTokens)); + } // Discounted fee payment ammAlice.deposit(carol, USD(100)); ammTokens = ammAlice.getLPTokensBalance(); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877023), -11}, ammTokens)); + XRP(13'000), + STAmount{USD, UINT64_C(13'112'92609877023), -11}, + ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'112'92609877024), -11}, ammTokens)); + XRPAmount{13'000'000'003}, + STAmount{USD, UINT64_C(13'112'92609877024), -11}, + ammTokens)); + } env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110))); env.close(); // carol pays 100000 drops in fees // 99900668XRP swapped in for 100USD if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens)); + XRPAmount{13'100'000'668}, + STAmount{USD, UINT64_C(13'012'92609877023), -11}, + ammTokens)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'100'000'671}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens)); + XRPAmount{13'100'000'671}, + STAmount{USD, UINT64_C(13'012'92609877024), -11}, + ammTokens)); + } } // Payment with the trading fee env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(110))); @@ -2495,26 +2946,40 @@ private: if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047264), -11}, ammTokens)); + XRPAmount{13'000'000'668}, + STAmount{USD, UINT64_C(13'114'03663047264), -11}, + ammTokens)); } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047269), -11}, ammTokens)); + XRPAmount{13'000'000'668}, + STAmount{USD, UINT64_C(13'114'03663047269), -11}, + ammTokens)); } else { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'114'03663044937), -11}, ammTokens)); + XRPAmount{13'000'000'671}, + STAmount{USD, UINT64_C(13'114'03663044937), -11}, + ammTokens)); } // Auction slot expired, no discounted fee env.close(seconds(TOTAL_TIME_SLOT_SECS + 1)); // clock is parent's based env.close(); if (!features[fixAMMv1_1]) - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620545), -11)); + { + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'399'00572620545), -11)); + } else if (!features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620544), -11)); + { + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'399'00572620544), -11)); + } ammTokens = ammAlice.getLPTokensBalance(); for (int i = 0; i < 10; ++i) { @@ -2525,21 +2990,33 @@ private: // trading fees vs discounted fee. if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) { - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177128), -11)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'389'06197177128), -11)); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490681), -11}, ammTokens)); + XRPAmount{13'000'000'668}, + STAmount{USD, UINT64_C(13'123'98038490681), -11}, + ammTokens)); } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) { - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177124), -11)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'389'06197177124), -11)); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490689), -11}, ammTokens)); + XRPAmount{13'000'000'668}, + STAmount{USD, UINT64_C(13'123'98038490689), -11}, + ammTokens)); } else { - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177129), -11)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'389'06197177129), -11)); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'123'98038488352), -11}, ammTokens)); + XRPAmount{13'000'000'671}, + STAmount{USD, UINT64_C(13'123'98038488352), -11}, + ammTokens)); } env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110))); env.close(); @@ -2549,17 +3026,23 @@ private: if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490681), -11}, ammTokens)); + XRPAmount(13'100'824'790), + STAmount{USD, UINT64_C(13'023'98038490681), -11}, + ammTokens)); } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490689), -11}, ammTokens)); + XRPAmount(13'100'824'790), + STAmount{USD, UINT64_C(13'023'98038490689), -11}, + ammTokens)); } else { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount(13'100'824'793), STAmount{USD, UINT64_C(13'023'98038488352), -11}, ammTokens)); + XRPAmount(13'100'824'793), + STAmount{USD, UINT64_C(13'023'98038488352), -11}, + ammTokens)); } }, std::nullopt, @@ -2637,7 +3120,8 @@ private: // But trades with the discounted fee since she still owns the slot. // Alice pays 10011 drops in fees env(pay(alice, bob, USD(10)), path(~USD), sendmax(XRP(11))); - BEAST_EXPECT(amm.expectBalances(XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9})); + BEAST_EXPECT(amm.expectBalances( + XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9})); // Bob pays the full fee ~0.1USD env(pay(bob, alice, XRP(10)), path(~XRP), sendmax(USD(11))); if (!features[fixAMMv1_1]) @@ -2668,7 +3152,8 @@ private: { auto jtx = env.jt(tx, seq(1), fee(baseFee)); env.app().config().features.erase(featureAMM); - PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); + PreflightContext pfCtx( + env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); BEAST_EXPECT(pf == temDISABLED); env.app().config().features.insert(featureAMM); @@ -2678,9 +3163,10 @@ private: auto jtx = env.jt(tx, seq(1), fee(baseFee)); jtx.jv["TxnSignature"] = "deadbeef"; jtx.stx = env.ust(jtx); - PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); + PreflightContext pfCtx( + env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); - BEAST_EXPECT(pf != tesSUCCESS); + BEAST_EXPECT(!isTesSuccess(pf)); } { @@ -2688,7 +3174,8 @@ private: jtx.jv["Asset2"]["currency"] = "XRP"; jtx.jv["Asset2"].removeMember("issuer"); jtx.stx = env.ust(jtx); - PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); + PreflightContext pfCtx( + env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal); auto pf = Transactor::invokePreflight(pfCtx); BEAST_EXPECT(pf == temBAD_AMM_TOKENS); } @@ -2747,21 +3234,29 @@ private: auto const pk = carol.pk(); auto const settleDelay = 100s; NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime + 200s; - env(paychan::create(carol, ammAlice.ammAccount(), XRP(1'000), settleDelay, pk, cancelAfter), + env(paychan::create( + carol, ammAlice.ammAccount(), XRP(1'000), settleDelay, pk, cancelAfter), ter(tecNO_PERMISSION)); }); // Can't pay into AMM with checks. testAMM([&](AMM& ammAlice, Env& env) { - env(check::create(env.master.id(), ammAlice.ammAccount(), XRP(100)), ter(tecNO_PERMISSION)); + env(check::create(env.master.id(), ammAlice.ammAccount(), XRP(100)), + ter(tecNO_PERMISSION)); }); // Pay amounts close to one side of the pool testAMM( [&](AMM& ammAlice, Env& env) { // Can't consume whole pool - env(pay(alice, carol, USD(100)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL)); - env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(1'000'000'000)), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(100)), + path(~USD), + sendmax(XRP(1'000'000'000)), + ter(tecPATH_PARTIAL)); + env(pay(alice, carol, XRP(100)), + path(~XRP), + sendmax(USD(1'000'000'000)), + ter(tecPATH_PARTIAL)); // Overflow env(pay(alice, carol, STAmount{USD, UINT64_C(99'999999999), -9}), path(~USD), @@ -2776,7 +3271,10 @@ private: sendmax(USD(1'000'000'000)), ter(tecPATH_PARTIAL)); // Sender doesn't have enough funds - env(pay(alice, carol, USD(99.99)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(99.99)), + path(~USD), + sendmax(XRP(1'000'000'000)), + ter(tecPATH_PARTIAL)); env(pay(alice, carol, STAmount{xrpIssue(), 99'990'000}), path(~XRP), sendmax(USD(1'000'000'000)), @@ -2802,7 +3300,8 @@ private: // Individually frozen AMM testAMM([&](AMM& ammAlice, Env& env) { - env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); + env(trust( + gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze)); env.close(); env(pay(alice, carol, USD(1)), path(~USD), @@ -2837,7 +3336,8 @@ private: // For now, just disable SAV entirely, which locks in the small Number // mantissas - features = features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol; + features = + features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol; // Payment 100USD for 100XRP. // Force one path with tfNoRippleDirect. @@ -2845,13 +3345,17 @@ private: [&](AMM& ammAlice, Env& env) { env.fund(jtx::XRP(30'000), bob); env.close(); - env(pay(bob, carol, USD(100)), path(~USD), sendmax(XRP(100)), txflags(tfNoRippleDirect)); + env(pay(bob, carol, USD(100)), + path(~USD), + sendmax(XRP(100)), + txflags(tfNoRippleDirect)); env.close(); BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens())); // Initial balance 30,000 + 100 BEAST_EXPECT(expectHolding(env, carol, USD(30'100))); // Initial balance 30,000 - 100(sendmax) - 10(tx fee) - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); }, {{XRP(10'000), USD(10'100)}}, 0, @@ -2869,7 +3373,8 @@ private: // Initial balance 30,000 + 100 BEAST_EXPECT(expectHolding(env, carol, USD(30'100))); // Initial balance 30,000 - 100(sendmax) - 10(tx fee) - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); }, {{XRP(10'000), USD(10'100)}}, 0, @@ -2888,7 +3393,8 @@ private: // Initial balance 30,000 + 100 BEAST_EXPECT(expectHolding(env, carol, USD(30'100))); // Initial balance 30,000 - 100(sendmax) - 10(tx fee) - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); }, {{XRP(10'000), USD(10'100)}}, 0, @@ -2912,7 +3418,8 @@ private: BEAST_EXPECT(expectHolding(env, carol, USD(30'010))); // Initial balance 30,000 - 10(limited by limitQuality) - 10(tx // fee) - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1))); // Fails because of limitQuality. Would have sent // ~98.91USD/110XRP has it not been for limitQuality. @@ -2945,8 +3452,10 @@ private: env.close(); BEAST_EXPECT(ammAlice.expectBalances(XRP(10'010), USD(10'000), ammAlice.tokens())); // 10USD - 10% transfer fee - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'009'09090909091), -11})); - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1))); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(30'009'09090909091), -11})); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1))); }, {{XRP(10'000), USD(10'010)}}, 0, @@ -2983,10 +3492,15 @@ private: auto ammUSD_EUR = AMM(env, alice, EUR(10'000), USD(10'000)); env(offer(alice, XRP(101), USD(100)), txflags(tfPassive)); env.close(); - env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment)); + env(pay(bob, carol, USD(100)), + path(~EUR, ~USD), + sendmax(XRP(102)), + txflags(tfPartialPayment)); env.close(); BEAST_EXPECT(ammEUR_XRP.expectBalances( - XRPAmount(10'030'082'730), STAmount(EUR, UINT64_C(9'970'007498125468), -12), ammEUR_XRP.tokens())); + XRPAmount(10'030'082'730), + STAmount(EUR, UINT64_C(9'970'007498125468), -12), + ammEUR_XRP.tokens())); if (!features[fixAMMv1_1]) { BEAST_EXPECT(ammUSD_EUR.expectBalances( @@ -2997,7 +3511,8 @@ private: // fixReducedOffersV2 changes the expected results slightly. Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2) ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787816), -14)} - : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787818), -14)}; + : Amounts{ + XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787818), -14)}; BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}})); } @@ -3011,7 +3526,8 @@ private: // fixReducedOffersV2 changes the expected results slightly. Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2) ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782839), -14)} - : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782840), -14)}; + : Amounts{ + XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782840), -14)}; BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}})); } @@ -3019,7 +3535,9 @@ private: BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, 30'100})); // Initial 1,000 - 30082730(AMM pool) - 70798251(offer) - 10(tx fee) BEAST_EXPECT(expectLedgerEntryRoot( - env, bob, XRP(1'000) - XRPAmount{30'082'730} - XRPAmount{70'798'251} - txfee(env, 1))); + env, + bob, + XRP(1'000) - XRPAmount{30'082'730} - XRPAmount{70'798'251} - txfee(env, 1))); } // Default path (with AMM) has a better quality than a non-default path. @@ -3036,10 +3554,15 @@ private: env.close(); env(offer(alice, EUR(100), USD(100)), txflags(tfPassive)); env.close(); - env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment)); + env(pay(bob, carol, USD(100)), + path(~EUR, ~USD), + sendmax(XRP(102)), + txflags(tfPartialPayment)); env.close(); BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount(10'050'238'637), STAmount(USD, UINT64_C(9'950'01249687578), -11), ammAlice.tokens())); + XRPAmount(10'050'238'637), + STAmount(USD, UINT64_C(9'950'01249687578), -11), + ammAlice.tokens())); BEAST_EXPECT(expectOffers( env, alice, @@ -3049,11 +3572,14 @@ private: STAmount(EUR, UINT64_C(49'98750312422), -11), STAmount(USD, UINT64_C(49'98750312422), -11)}}})); // Initial 30,000 + 99.99999999999 - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11})); // Initial 1,000 - 50238637(AMM pool) - 50512622(offer) - 10(tx // fee) BEAST_EXPECT(expectLedgerEntryRoot( - env, bob, XRP(1'000) - XRPAmount{50'238'637} - XRPAmount{50'512'622} - txfee(env, 1))); + env, + bob, + XRP(1'000) - XRPAmount{50'238'637} - XRPAmount{50'512'622} - txfee(env, 1))); }, std::nullopt, 0, @@ -3072,20 +3598,27 @@ private: env.close(); if (!features[fixAMMv1_1]) { - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens())); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens())); // Initial 30,000 + 200 BEAST_EXPECT(expectHolding(env, carol, USD(30'200))); } else { BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'100), STAmount(USD, UINT64_C(10'000'00000000001), -11), ammAlice.tokens())); - BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(30'199'99999999999), -11))); + XRP(10'100), + STAmount(USD, UINT64_C(10'000'00000000001), -11), + ammAlice.tokens())); + BEAST_EXPECT(expectHolding( + env, carol, STAmount(USD, UINT64_C(30'199'99999999999), -11))); } // Initial 30,000 - 10000(AMM pool LP) - 100(AMM offer) - // - 100(offer) - 10(tx fee) - one reserve BEAST_EXPECT(expectLedgerEntryRoot( - env, alice, XRP(30'000) - XRP(10'000) - XRP(100) - XRP(100) - ammCrtFee(env) - txfee(env, 1))); + env, + alice, + XRP(30'000) - XRP(10'000) - XRP(100) - XRP(100) - ammCrtFee(env) - + txfee(env, 1))); BEAST_EXPECT(expectOffers(env, bob, 0)); }, {{XRP(10'000), USD(10'100)}}, @@ -3121,7 +3654,8 @@ private: // Initial 1,000 + 100 BEAST_EXPECT(expectHolding(env, bob, USD(1'100))); // Initial 30,000 - 100(offer) - 10(tx fee) - BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); + BEAST_EXPECT( + expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1))); BEAST_EXPECT(expectOffers(env, bob, 0)); }, {{XRP(10'000), USD(10'100)}}, @@ -3182,12 +3716,19 @@ private: // quality. // AMM offer ~50USD/91XRP BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens())); + XRPAmount(909'090'909), + STAmount{USD, UINT64_C(550'000000055), -9}, + amm.tokens())); // Offer ~91XRP/49.99USD - BEAST_EXPECT( - expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); + BEAST_EXPECT(expectOffers( + env, + carol, + 1, + {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); // Carol pays 0.1% fee on ~50USD =~ 0.05USD - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'949'94999999494), -11)); + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(29'949'94999999494), -11)); } }, {{XRP(1'000), USD(500)}}, @@ -3202,14 +3743,14 @@ private: env.close(); if (!features[fixAMMv1_1]) { - BEAST_EXPECT( - amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + XRP(990), STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens())); BEAST_EXPECT(expectOffers(env, carol, 0)); } else { - BEAST_EXPECT( - amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens())); + BEAST_EXPECT(amm.expectBalances( + XRP(990), STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens())); BEAST_EXPECT(expectOffers(env, carol, 0)); } }, @@ -3263,9 +3804,11 @@ private: // = 58.825 = ~29941.17 // carol bought ~72.93EUR at the cost of ~70.68GBP // the offer is partially consumed - BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'941'16770347333), -11})); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{GBP, UINT64_C(29'941'16770347333), -11})); // Initial 30,000 + ~49.3(offers = 39.3(AMM) + 10(LOB)) - BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'049'31517120716), -11})); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{EUR, UINT64_C(30'049'31517120716), -11})); } else { @@ -3296,9 +3839,11 @@ private: // = 88.35 = ~29911.64 // carol bought ~72.93EUR at the cost of ~70.68GBP // the offer is partially consumed - BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'911'64396400896), -11})); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{GBP, UINT64_C(29'911'64396400896), -11})); // Initial 30,000 + ~72.93(offers = 62.93(AMM) + 10(LOB)) - BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'072'93416277865), -11})); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{EUR, UINT64_C(30'072'93416277865), -11})); } // Initial 2000 + 10 = 2010 BEAST_EXPECT(expectHolding(env, bob, GBP(2'010))); @@ -3322,7 +3867,10 @@ private: fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct); env(rate(gw, 1.25)); env.close(); - env(pay(bob, carol, EUR(100)), path(~EUR), sendmax(GBP(125)), txflags(tfPartialPayment)); + env(pay(bob, carol, EUR(100)), + path(~EUR), + sendmax(GBP(125)), + txflags(tfPartialPayment)); env.close(); BEAST_EXPECT(ammAlice.expectBalances(GBP(1'100), EUR(1'000), ammAlice.tokens())); BEAST_EXPECT(expectHolding(env, bob, GBP(75))); @@ -3407,7 +3955,12 @@ private: { Env env(*this, features); auto const ETH = gw["ETH"]; - fund(env, gw, {alice}, XRP(100'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); + fund( + env, + gw, + {alice}, + XRP(100'000), + {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); fund(env, gw, {carol, bob}, XRP(1'000), {USD(200)}, Fund::Acct); AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000)); AMM eur_btc(env, alice, EUR(10'000), BTC(10'200)); @@ -3426,7 +3979,9 @@ private: // XRP-ETH-EUR-USD // This path provides ~26.06USD/26.2XRP BEAST_EXPECT(xrp_eth.expectBalances( - XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244494), -11}, xrp_eth.tokens())); + XRPAmount(10'026'208'900), + STAmount{ETH, UINT64_C(10'073'65779244494), -11}, + xrp_eth.tokens())); BEAST_EXPECT(eth_eur.expectBalances( STAmount{ETH, UINT64_C(10'926'34220755506), -11}, STAmount{EUR, UINT64_C(10'973'54232078752), -11}, @@ -3438,12 +3993,16 @@ private: // XRP-USD path // This path provides ~73.9USD/74.1XRP BEAST_EXPECT(xrp_usd.expectBalances( - XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287914), -11}, xrp_usd.tokens())); + XRPAmount(10'224'106'246), + STAmount{USD, UINT64_C(10'126'06848287914), -11}, + xrp_usd.tokens())); } else { BEAST_EXPECT(xrp_eth.expectBalances( - XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244461), -11}, xrp_eth.tokens())); + XRPAmount(10'026'208'900), + STAmount{ETH, UINT64_C(10'073'65779244461), -11}, + xrp_eth.tokens())); BEAST_EXPECT(eth_eur.expectBalances( STAmount{ETH, UINT64_C(10'926'34220755539), -11}, STAmount{EUR, UINT64_C(10'973'5423207872), -10}, @@ -3455,7 +4014,9 @@ private: // XRP-USD path // This path provides ~73.9USD/74.1XRP BEAST_EXPECT(xrp_usd.expectBalances( - XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287943), -11}, xrp_usd.tokens())); + XRPAmount(10'224'106'246), + STAmount{USD, UINT64_C(10'126'06848287943), -11}, + xrp_usd.tokens())); } // XRP-EUR-BTC-USD @@ -3475,28 +4036,42 @@ private: { Env env(*this, features); auto const ETH = gw["ETH"]; - fund(env, gw, {alice}, XRP(40'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); + fund( + env, + gw, + {alice}, + XRP(40'000), + {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)}); fund(env, gw, {carol, bob}, XRP(1000), {USD(200)}, Fund::Acct); AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000)); AMM eur_btc(env, alice, EUR(10'000), BTC(10'200)); AMM btc_usd(env, alice, BTC(10'100), USD(10'000)); AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100)); AMM eth_eur(env, alice, ETH(10'900), EUR(11'000)); - env(pay(bob, carol, USD(100)), path(~EUR, ~BTC, ~USD), path(~ETH, ~EUR, ~BTC, ~USD), sendmax(XRP(200))); + env(pay(bob, carol, USD(100)), + path(~EUR, ~BTC, ~USD), + path(~ETH, ~EUR, ~BTC, ~USD), + sendmax(XRP(200))); if (!features[fixAMMv1_1]) { // XRP-EUR-BTC-USD path provides ~17.8USD/~18.7XRP // XRP-ETH-EUR-BTC-USD path provides ~82.2USD/82.4XRP BEAST_EXPECT(xrp_eur.expectBalances( - XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337968), -12}, xrp_eur.tokens())); + XRPAmount(10'118'738'472), + STAmount{EUR, UINT64_C(9'981'544436337968), -12}, + xrp_eur.tokens())); BEAST_EXPECT(eur_btc.expectBalances( STAmount{EUR, UINT64_C(10'101'16096785173), -11}, STAmount{BTC, UINT64_C(10'097'91426968066), -11}, eur_btc.tokens())); BEAST_EXPECT(btc_usd.expectBalances( - STAmount{BTC, UINT64_C(10'202'08573031934), -11}, USD(9'900), btc_usd.tokens())); + STAmount{BTC, UINT64_C(10'202'08573031934), -11}, + USD(9'900), + btc_usd.tokens())); BEAST_EXPECT(xrp_eth.expectBalances( - XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072778012), -11}, xrp_eth.tokens())); + XRPAmount(10'082'446'397), + STAmount{ETH, UINT64_C(10'017'41072778012), -11}, + xrp_eth.tokens())); BEAST_EXPECT(eth_eur.expectBalances( STAmount{ETH, UINT64_C(10'982'58927221988), -11}, STAmount{EUR, UINT64_C(10'917'2945958103), -10}, @@ -3505,15 +4080,21 @@ private: else { BEAST_EXPECT(xrp_eur.expectBalances( - XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337923), -12}, xrp_eur.tokens())); + XRPAmount(10'118'738'472), + STAmount{EUR, UINT64_C(9'981'544436337923), -12}, + xrp_eur.tokens())); BEAST_EXPECT(eur_btc.expectBalances( STAmount{EUR, UINT64_C(10'101'16096785188), -11}, STAmount{BTC, UINT64_C(10'097'91426968059), -11}, eur_btc.tokens())); BEAST_EXPECT(btc_usd.expectBalances( - STAmount{BTC, UINT64_C(10'202'08573031941), -11}, USD(9'900), btc_usd.tokens())); + STAmount{BTC, UINT64_C(10'202'08573031941), -11}, + USD(9'900), + btc_usd.tokens())); BEAST_EXPECT(xrp_eth.expectBalances( - XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072777996), -11}, xrp_eth.tokens())); + XRPAmount(10'082'446'397), + STAmount{ETH, UINT64_C(10'017'41072777996), -11}, + xrp_eth.tokens())); BEAST_EXPECT(eth_eur.expectBalances( STAmount{ETH, UINT64_C(10'982'58927222004), -11}, STAmount{EUR, UINT64_C(10'917'2945958102), -10}, @@ -3542,14 +4123,20 @@ private: { // Carol gets ~29.91USD because of the AMM offers limit BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807577), -12}, ammAlice.tokens())); - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919241), -11})); + XRP(10'030), + STAmount{USD, UINT64_C(9'970'089730807577), -12}, + ammAlice.tokens())); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{USD, UINT64_C(30'029'91026919241), -11})); } else { BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807827), -12}, ammAlice.tokens())); - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919217), -11})); + XRP(10'030), + STAmount{USD, UINT64_C(9'970'089730807827), -12}, + ammAlice.tokens())); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{USD, UINT64_C(30'029'91026919217), -11})); } BEAST_EXPECT(expectOffers(env, alice, 1, {{{EUR(140), XRP(100)}}})); }, @@ -3572,18 +4159,23 @@ private: path(~XRP, ~USD), sendmax(EUR(400)), txflags(tfPartialPayment | tfNoRippleDirect)); - BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{10'101'010'102}, USD(9'900), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{10'101'010'102}, USD(9'900), ammAlice.tokens())); if (!features[fixAMMv1_1]) { // Carol gets ~100USD - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11})); + BEAST_EXPECT(expectHolding( + env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11})); } else { BEAST_EXPECT(expectHolding(env, carol, USD(30'100))); } - BEAST_EXPECT( - expectOffers(env, alice, 1, {{{STAmount{EUR, UINT64_C(39'1858572), -7}, XRPAmount{27'989'898}}}})); + BEAST_EXPECT(expectOffers( + env, + alice, + 1, + {{{STAmount{EUR, UINT64_C(39'1858572), -7}, XRPAmount{27'989'898}}}})); }, std::nullopt, 0, @@ -3601,16 +4193,26 @@ private: if (!features[fixAMMv1_1]) { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{10'049'825'373}, STAmount{USD, UINT64_C(10'049'92586949302), -11}, ammAlice.tokens())); + XRPAmount{10'049'825'373}, + STAmount{USD, UINT64_C(10'049'92586949302), -11}, + ammAlice.tokens())); BEAST_EXPECT(expectOffers( - env, bob, 1, {{{XRPAmount{50'074'629}, STAmount{USD, UINT64_C(50'07513050698), -11}}}})); + env, + bob, + 1, + {{{XRPAmount{50'074'629}, STAmount{USD, UINT64_C(50'07513050698), -11}}}})); } else { BEAST_EXPECT(ammAlice.expectBalances( - XRPAmount{10'049'825'372}, STAmount{USD, UINT64_C(10'049'92587049303), -11}, ammAlice.tokens())); + XRPAmount{10'049'825'372}, + STAmount{USD, UINT64_C(10'049'92587049303), -11}, + ammAlice.tokens())); BEAST_EXPECT(expectOffers( - env, bob, 1, {{{XRPAmount{50'074'628}, STAmount{USD, UINT64_C(50'07512950697), -11}}}})); + env, + bob, + 1, + {{{XRPAmount{50'074'628}, STAmount{USD, UINT64_C(50'07512950697), -11}}}})); BEAST_EXPECT(expectHolding(env, carol, USD(30'100))); } } @@ -3644,7 +4246,10 @@ private: auto const baseFee = env.current()->fees().base.drops(); auto const token1 = ammAlice.lptIssue(); auto priceXRP = ammAssetOut( - STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 10'000'000}, STAmount{token1, 5'000'000}, 0); + STAmount{XRPAmount{10'000'000'000}}, + STAmount{token1, 10'000'000}, + STAmount{token1, 5'000'000}, + 0); // Carol places an order to buy LPTokens env(offer(carol, STAmount{token1, 5'000'000}, priceXRP)); // Alice places an order to sell LPTokens @@ -3665,12 +4270,15 @@ private: BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{100})); BEAST_EXPECT(accountBalance(env, carol) == std::to_string(22500000000 - 4 * baseFee)); priceXRP = ammAssetOut( - STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 9'999'900}, STAmount{token1, 4'999'900}, 0); + STAmount{XRPAmount{10'000'000'000}}, + STAmount{token1, 9'999'900}, + STAmount{token1, 4'999'900}, + 0); // Carol withdraws ammAlice.withdrawAll(carol, XRP(0)); BEAST_EXPECT(accountBalance(env, carol) == std::to_string(29999949999 - 5 * baseFee)); - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount{10'000'000'000} - priceXRP, USD(10'000), IOUAmount{5'000'000})); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount{10'000'000'000} - priceXRP, USD(10'000), IOUAmount{5'000'000})); BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000})); BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }); @@ -3763,7 +4371,9 @@ private: testAMM([&](AMM& ammAlice, Env& env) { auto const info = env.rpc( - "json", "account_info", std::string("{\"account\": \"" + to_string(ammAlice.ammAccount()) + "\"}")); + "json", + "account_info", + std::string("{\"account\": \"" + to_string(ammAlice.ammAccount()) + "\"}")); auto const flags = info[jss::result][jss::account_data][jss::Flags].asUInt(); BEAST_EXPECT(flags == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth)); }); @@ -3806,7 +4416,7 @@ private: AMM amm(env, C, TSTA(5'000), TSTB(5'000)); auto const ammIss = Issue(TSTA.currency, amm.ammAccount()); - // Can SetTrust only for AMM LP tokens + // Can TrustSet only for AMM LP tokens env(trust(D, STAmount{ammIss, 10'000}), ter(tecNO_PERMISSION)); env.close(); @@ -3864,14 +4474,26 @@ private: prep( [&](Env& env) { if (!features[fixAMMv1_1]) - env(offer(LP1, XRPAmount{18'095'133}, STAmount{TST, UINT64_C(1'68737984885388), -14}), + { + env(offer( + LP1, + XRPAmount{18'095'133}, + STAmount{TST, UINT64_C(1'68737984885388), -14}), txflags(tfPassive)); + } else - env(offer(LP1, XRPAmount{18'095'132}, STAmount{TST, UINT64_C(1'68737976189735), -14}), + { + env(offer( + LP1, + XRPAmount{18'095'132}, + STAmount{TST, UINT64_C(1'68737976189735), -14}), txflags(tfPassive)); + } }, [&](Env& env) { - BEAST_EXPECT(lp2TSTBalance == getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString()); + BEAST_EXPECT( + lp2TSTBalance == + getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString()); auto const offer = getAccountOffers(env, LP2)["offers"][0u]; BEAST_EXPECT(lp2TakerGets == offer["taker_gets"].asString()); BEAST_EXPECT(lp2TakerPays == offer["taker_pays"]["value"].asString()); @@ -3905,7 +4527,8 @@ private: ammAlice.vote(alice, 0); ammAlice.withdrawAll(carol, USD(0)); // Carol gets back less than the original deposit - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'96220068281), -11})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'96220068281), -11})); }, {{USD(1'000), EUR(1'000)}}, 0, @@ -3917,7 +4540,8 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { auto const balance = env.balance(carol, USD); - auto tokensFee = ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1}); + auto tokensFee = + ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1}); auto const deposit = balance - env.balance(carol, USD); ammAlice.withdrawAll(carol, USD(0)); ammAlice.vote(alice, 0); @@ -3938,7 +4562,8 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { auto const balance = env.balance(carol, USD); - auto const tokensFee = ammAlice.deposit(carol, USD(200), std::nullopt, STAmount{USD, 2020, -6}); + auto const tokensFee = + ammAlice.deposit(carol, USD(200), std::nullopt, STAmount{USD, 2020, -6}); auto const deposit = balance - env.balance(carol, USD); ammAlice.withdrawAll(carol, USD(0)); ammAlice.vote(alice, 0); @@ -3967,7 +4592,8 @@ private: BEAST_EXPECT(ammAlice.expectTradingFee(1'000)); // Single withdrawal. Carol gets ~5USD less than deposited. ammAlice.withdrawAll(carol, USD(0)); - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'97487437186), -11})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'97487437186), -11})); }, {{USD(1'000), EUR(1'000)}}, 0, @@ -3978,15 +4604,20 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { ammAlice.deposit(carol, 1'000'000); - auto const tokensFee = ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0}); + auto const tokensFee = + ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0}); // carol withdraws ~1,443.44USD auto const balanceAfterWithdraw = [&]() { if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { return STAmount(USD, UINT64_C(30'443'43891402715), -11); - else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + } + if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { return STAmount(USD, UINT64_C(30'443'43891402714), -11); - else - return STAmount(USD, UINT64_C(30'443'43891402713), -11); + } + + return STAmount(USD, UINT64_C(30'443'43891402713), -11); }(); BEAST_EXPECT(env.balance(carol, USD) == balanceAfterWithdraw); // Set to original pool size @@ -3997,19 +4628,37 @@ private: BEAST_EXPECT(ammAlice.expectTradingFee(0)); auto const tokensNoFee = ammAlice.withdraw(carol, deposit); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402717), -11)); + { + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(30'443'43891402717), -11)); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402716), -11)); + { + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(30'443'43891402716), -11)); + } else - BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402713), -11)); + { + BEAST_EXPECT( + env.balance(carol, USD) == + STAmount(USD, UINT64_C(30'443'43891402713), -11)); + } // carol pays ~4008 LPTokens in fees or ~0.5% of the no-fee // LPTokens if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779913, -8)); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779912, -8)); + } else + { BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779911, -8)); + } BEAST_EXPECT(tokensFee == IOUAmount(750'588'23529411, -8)); }, std::nullopt, @@ -4026,7 +4675,10 @@ private: BEAST_EXPECT(expectHolding(env, alice, USD(29'000))); BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); // Carol pays to Alice with no fee - env(pay(carol, alice, EUR(10)), path(~EUR), sendmax(USD(10)), txflags(tfNoRippleDirect)); + env(pay(carol, alice, EUR(10)), + path(~EUR), + sendmax(USD(10)), + txflags(tfNoRippleDirect)); env.close(); // Alice has 10EUR more and Carol has 10USD less BEAST_EXPECT(expectHolding(env, alice, EUR(29'000))); @@ -4037,14 +4689,20 @@ private: ammAlice.vote(alice, 1'000); BEAST_EXPECT(ammAlice.expectTradingFee(1'000)); // Bob pays to Carol with 1% fee - env(pay(bob, carol, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect)); + env(pay(bob, carol, USD(10)), + path(~USD), + sendmax(EUR(15)), + txflags(tfNoRippleDirect)); env.close(); // Bob sends 10.1~EUR to pay 10USD - BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13})); + BEAST_EXPECT( + expectHolding(env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13})); // Carol got 10USD BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); BEAST_EXPECT(ammAlice.expectBalances( - USD(1'000), STAmount{EUR, UINT64_C(1'010'10101010101), -11}, ammAlice.tokens())); + USD(1'000), + STAmount{EUR, UINT64_C(1'010'10101010101), -11}, + ammAlice.tokens())); }, {{USD(1'000), EUR(1'010)}}, 0, @@ -4069,8 +4727,10 @@ private: env.close(); // Alice gets fewer ~4.97EUR for ~5.02USD, the difference goes // to the pool - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'995'02512562814), -11})); - BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'004'97487437186), -11})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{USD, UINT64_C(29'995'02512562814), -11})); + BEAST_EXPECT( + expectHolding(env, carol, STAmount{EUR, UINT64_C(30'004'97487437186), -11})); BEAST_EXPECT(expectOffers( env, carol, @@ -4117,9 +4777,12 @@ private: } else { - BEAST_EXPECT(expectHolding(env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12))); + BEAST_EXPECT( + expectHolding(env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12))); BEAST_EXPECT(ammAlice.expectBalances( - USD(1'000), STAmount(EUR, UINT64_C(1005'000000000001), -12), ammAlice.tokens())); + USD(1'000), + STAmount(EUR, UINT64_C(1005'000000000001), -12), + ammAlice.tokens())); } BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -4137,15 +4800,21 @@ private: BEAST_EXPECT(expectHolding(env, ed, USD(2'010))); if (!features[fixAMMv1_1]) { - BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007618), -12})); + BEAST_EXPECT( + expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007618), -12})); BEAST_EXPECT(ammAlice.expectBalances( - USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992382), -12}, ammAlice.tokens())); + USD(1'000), + STAmount{EUR, UINT64_C(1'005'012546992382), -12}, + ammAlice.tokens())); } else { - BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007628), -12})); + BEAST_EXPECT( + expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007628), -12})); BEAST_EXPECT(ammAlice.expectBalances( - USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992372), -12}, ammAlice.tokens())); + USD(1'000), + STAmount{EUR, UINT64_C(1'005'012546992372), -12}, + ammAlice.tokens())); } BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -4205,7 +4874,12 @@ private: Account const simon("simon"); Account const ben("ben"); Account const natalie("natalie"); - fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, {USD(1'500'000)}, Fund::Acct); + fund( + env, + gw, + {bob, ed, paul, dan, chris, simon, ben, natalie}, + {USD(1'500'000)}, + Fund::Acct); for (int i = 0; i < 10; ++i) { ammAlice.deposit(ben, STAmount{USD, 1, -10}); @@ -4231,43 +4905,78 @@ private: // other have a tiny loss. The last account to withdraw // gets everything in the pool. if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000013), -10}, IOUAmount{10'000'000})); + XRP(10'000), + STAmount{USD, UINT64_C(10'000'0000000013), -10}, + IOUAmount{10'000'000})); + } else if (features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000003), -10}, IOUAmount{10'000'000})); + XRP(10'000), + STAmount{USD, UINT64_C(10'000'0000000003), -10}, + IOUAmount{10'000'000})); + } else - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000})); + { + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000})); + } BEAST_EXPECT(expectHolding(env, ben, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, simon, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, chris, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, dan, USD(1'500'000))); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) - BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'000'00000000001), -11})); + { + BEAST_EXPECT(expectHolding( + env, carol, STAmount{USD, UINT64_C(30'000'00000000001), -11})); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); + } else + { BEAST_EXPECT(expectHolding(env, carol, USD(30'000))); + } BEAST_EXPECT(expectHolding(env, ed, USD(1'500'000))); BEAST_EXPECT(expectHolding(env, paul, USD(1'500'000))); if (!features[fixAMMv1_1] && !features[fixAMMv1_3]) - BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000002), -9})); + { + BEAST_EXPECT(expectHolding( + env, natalie, STAmount{USD, UINT64_C(1'500'000'000000002), -9})); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) - BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000005), -9})); + { + BEAST_EXPECT(expectHolding( + env, natalie, STAmount{USD, UINT64_C(1'500'000'000000005), -9})); + } else + { BEAST_EXPECT(expectHolding(env, natalie, USD(1'500'000))); + } ammAlice.withdrawAll(alice); BEAST_EXPECT(!ammAlice.ammExists()); if (!features[fixAMMv1_1]) - BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000013), -10})); + { + BEAST_EXPECT( + expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000013), -10})); + } else if (features[fixAMMv1_3]) - BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000003), -10})); + { + BEAST_EXPECT( + expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000003), -10})); + } else + { BEAST_EXPECT(expectHolding(env, alice, USD(30'000))); + } // alice XRP balance is 30,000 initial - 50 AMMCreate fee - // 10drops fee BEAST_EXPECT( - accountBalance(env, alice) == std::to_string(29950000000 - env.current()->fees().base.drops())); + accountBalance(env, alice) == + std::to_string(29950000000 - env.current()->fees().base.drops())); }, std::nullopt, 0, @@ -4285,7 +4994,13 @@ private: Account const simon("simon"); Account const ben("ben"); Account const natalie("natalie"); - fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, XRP(2'000'000), {}, Fund::Acct); + fund( + env, + gw, + {bob, ed, paul, dan, chris, simon, ben, natalie}, + XRP(2'000'000), + {}, + Fund::Acct); for (int i = 0; i < 10; ++i) { ammAlice.deposit(ben, XRPAmount{1}); @@ -4311,7 +5026,8 @@ private: if (!features[fixAMMv1_3]) { // No round off with XRP in this test - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000})); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000})); ammAlice.withdrawAll(alice); BEAST_EXPECT(!ammAlice.ammExists()); // 20,000 initial - (deposit+withdraw) * 10 @@ -4322,19 +5038,22 @@ private: BEAST_EXPECT(accountBalance(env, dan) == xrpBalance); // 30,000 initial - (deposit+withdraw) * 10 - BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee)); + BEAST_EXPECT( + accountBalance(env, carol) == + std::to_string(30'000'000'000 - 20 * baseFee)); BEAST_EXPECT(accountBalance(env, ed) == xrpBalance); BEAST_EXPECT(accountBalance(env, paul) == xrpBalance); BEAST_EXPECT(accountBalance(env, natalie) == xrpBalance); // 30,000 initial - 50 AMMCreate fee - 10drops withdraw fee - BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee)); + BEAST_EXPECT( + accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee)); } else { // post-amendment the rounding takes place to ensure // AMM invariant - BEAST_EXPECT( - ammAlice.expectBalances(XRPAmount(10'000'000'080), USD(10'000), IOUAmount{10'000'000})); + BEAST_EXPECT(ammAlice.expectBalances( + XRPAmount(10'000'000'080), USD(10'000), IOUAmount{10'000'000})); ammAlice.withdrawAll(alice); BEAST_EXPECT(!ammAlice.ammExists()); auto const xrpBalance = XRP(2'000'000) - txfee(env, 20) - drops(10); @@ -4343,11 +5062,15 @@ private: BEAST_EXPECT(accountBalance(env, simon) == xrpBalanceText); BEAST_EXPECT(accountBalance(env, chris) == xrpBalanceText); BEAST_EXPECT(accountBalance(env, dan) == xrpBalanceText); - BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee - 10)); + BEAST_EXPECT( + accountBalance(env, carol) == + std::to_string(30'000'000'000 - 20 * baseFee - 10)); BEAST_EXPECT(accountBalance(env, ed) == (xrpBalance + drops(2)).getText()); BEAST_EXPECT(accountBalance(env, paul) == (xrpBalance + drops(3)).getText()); BEAST_EXPECT(accountBalance(env, natalie) == (xrpBalance + drops(5)).getText()); - BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee + 80)); + BEAST_EXPECT( + accountBalance(env, alice) == + std::to_string(29'950'000'000 - baseFee + 80)); } }, std::nullopt, @@ -4386,16 +5109,18 @@ private: amm.withdrawAll(gw); BEAST_EXPECT(amm.ammExists()); - // Bid,Vote,Deposit,Withdraw,SetTrust failing with + // Bid,Vote,Deposit,Withdraw,TrustSet failing with // tecAMM_EMPTY. Deposit succeeds with tfTwoAssetIfEmpty option. env(amm.bid({ .account = alice, .bidMin = 1000, }), ter(tecAMM_EMPTY)); - amm.vote(std::nullopt, 100, std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY)); + amm.vote( + std::nullopt, 100, std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY)); amm.withdraw(alice, 100, std::nullopt, std::nullopt, ter(tecAMM_EMPTY)); - amm.deposit(alice, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY)); + amm.deposit( + alice, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY)); env(trust(alice, STAmount{amm.lptIssue(), 10'000}), ter(tecAMM_EMPTY)); // Can deposit with tfTwoAssetIfEmpty option @@ -4473,11 +5198,15 @@ private: using namespace jtx; testAMM([&](AMM& amm, Env& env) { amm.setClose(false); - auto const info = - env.rpc("json", "account_info", std::string("{\"account\": \"" + to_string(amm.ammAccount()) + "\"}")); + auto const info = env.rpc( + "json", + "account_info", + std::string("{\"account\": \"" + to_string(amm.ammAccount()) + "\"}")); try { - BEAST_EXPECT(info[jss::result][jss::account_data][jss::AMMID].asString() == to_string(amm.ammID())); + BEAST_EXPECT( + info[jss::result][jss::account_data][jss::AMMID].asString() == + to_string(amm.ammID())); } catch (...) { @@ -4491,12 +5220,13 @@ private: for (auto const& node : affected) { if (node.isMember(sfModifiedNode.fieldName) && - node[sfModifiedNode.fieldName][sfLedgerEntryType.fieldName].asString() == "AccountRoot" && - node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::Account].asString() == - to_string(amm.ammAccount())) + node[sfModifiedNode.fieldName][sfLedgerEntryType.fieldName].asString() == + "AccountRoot" && + node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::Account] + .asString() == to_string(amm.ammAccount())) { - found = node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::AMMID].asString() == - to_string(amm.ammID()); + found = node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::AMMID] + .asString() == to_string(amm.ammID()); break; } } @@ -4545,7 +5275,7 @@ private: // verify that the quality is better in the first case, and CLOB // is selected in the second case. { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -4566,7 +5296,10 @@ private: BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens())); } BEAST_EXPECT(expectHolding(env, bob, USD(2'100))); - q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)}); + q[i] = Quality( + Amounts{ + ETH(2'000) - env.balance(carol, ETH), + env.balance(bob, USD) - USD(2'000)}); } // CLOB is better quality than AMM BEAST_EXPECT(q[0] > q[1]); @@ -4615,7 +5348,7 @@ private: // Same as the payment but reduced offer quality { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -4640,6 +5373,7 @@ private: if (rates.first == 1.5) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(expectOffers( env, ed, @@ -4647,7 +5381,9 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(378'6327949540823), -13}, STAmount{USD, UINT64_C(283'9745962155617), -13}}}})); + } else + { BEAST_EXPECT(expectOffers( env, ed, @@ -4655,10 +5391,12 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(378'6327949540813), -13}, STAmount{USD, UINT64_C(283'974596215561), -12}}}})); + } } else { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(expectOffers( env, ed, @@ -4666,7 +5404,9 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(325'299461620749), -12}, STAmount{USD, UINT64_C(243'9745962155617), -13}}}})); + } else + { BEAST_EXPECT(expectOffers( env, ed, @@ -4674,6 +5414,7 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(325'299461620748), -12}, STAmount{USD, UINT64_C(243'974596215561), -12}}}})); + } } } else if (i == 2) @@ -4700,7 +5441,10 @@ private: } } BEAST_EXPECT(expectHolding(env, bob, USD(2'100))); - q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)}); + q[i] = Quality( + Amounts{ + ETH(2'000) - env.balance(carol, ETH), + env.balance(bob, USD) - USD(2'000)}); } // AMM is better quality BEAST_EXPECT(q[1] > q[0]); @@ -4811,7 +5555,7 @@ private: // multiple AMM offers are generated, which results in slightly // worse overall quality. { - std::array q; + std::array q{}; for (auto i = 0; i < 3; ++i) { Env env(*this, features); @@ -4839,7 +5583,9 @@ private: { // Liquidity is consumed from AMM strand only BEAST_EXPECT(amm->expectBalances( - STAmount{ETH, UINT64_C(1'176'66038955758), -11}, USD(850), amm->tokens())); + STAmount{ETH, UINT64_C(1'176'66038955758), -11}, + USD(850), + amm->tokens())); } else { @@ -4867,7 +5613,9 @@ private: { // Liquidity is consumed from AMM strand only BEAST_EXPECT(amm->expectBalances( - STAmount{ETH, UINT64_C(1'176'660389557593), -12}, USD(850), amm->tokens())); + STAmount{ETH, UINT64_C(1'176'660389557593), -12}, + USD(850), + amm->tokens())); } else { @@ -4889,7 +5637,10 @@ private: }}})); } } - q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)}); + q[i] = Quality( + Amounts{ + ETH(2'000) - env.balance(carol, ETH), + env.balance(bob, USD) - USD(2'000)}); } BEAST_EXPECT(q[1] > q[0]); BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]); @@ -4938,7 +5689,14 @@ private: // second vote/withdraw still fail: second vote fails because // the initial trading fee is 0, consequently second withdraw fails // because the second vote fails - test(all - fixInnerObjTemplate, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, 0, false); + test( + all - fixInnerObjTemplate, + tefEXCEPTION, + tefEXCEPTION, + tefEXCEPTION, + tefEXCEPTION, + 0, + false); // if non-zero trading/discounted fee then vote/withdraw // don't fail whether the ledger is closed or not and // the amendment is enabled or not @@ -5072,18 +5830,26 @@ private: if (status == SucceedShouldSucceedResize) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(Quality{*amounts} < quality); + } else + { BEAST_EXPECT(Quality{*amounts} >= quality); + } } else if (status == Succeed) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT( Quality{*amounts} >= quality || withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7})); + } else + { BEAST_EXPECT(Quality{*amounts} >= quality); + } } else if (status == FailShouldSucceed) { @@ -5108,15 +5874,21 @@ private: if (isXRP(poolIn)) { auto const takerPays = STAmount{xrpIssue(), 1}; - return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; + return Amounts{ + takerPays, + swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; } - else if (isXRP(poolOut)) + if (isXRP(poolOut)) { auto const takerGets = STAmount{xrpIssue(), 1}; - return Amounts{swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee), takerGets}; + return Amounts{ + swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee), + takerGets}; } - auto const takerPays = toAmount(getIssue(poolIn), Number{1, -10} * poolIn); - return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; + auto const takerPays = + toAmount(getIssue(poolIn), Number{1, -10} * poolIn); + return Amounts{ + takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; }(); BEAST_EXPECT(Quality(tinyOffer) < quality); } @@ -5236,7 +6008,7 @@ private: STAmount const goodUsdBIT; STAmount const goodUsdBITr; IOUAmount const lpTokenBalance; - std::optional const lpTokenBalanceAlt = {}; + std::optional const lpTokenBalanceAlt = std::nullopt; double const offer1BtcGH = 0.1; double const offer2BtcGH = 0.1; double const offer2UsdGH = 1; @@ -5482,9 +6254,13 @@ private: auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT; auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH; auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT; - auto const lpTokenBalance = env.enabled(fixAMMv1_3) && input.lpTokenBalanceAlt - ? *input.lpTokenBalanceAlt - : input.lpTokenBalance; + auto const lpTokenBalance = [&] { + if (not env.enabled(fixAMMv1_3)) + return input.lpTokenBalance; + + return input.lpTokenBalanceAlt.value_or(input.lpTokenBalance); + }(); + if (!features[fixAMMOverflowOffer]) { BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance)); @@ -5583,7 +6359,8 @@ private: } else { - BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens())); + BEAST_EXPECT( + amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens())); BEAST_EXPECT(expectOffers(env, alice, 1, {{Amounts{XRP(1), USD(0.01)}}})); // Carol's offer crosses AMM BEAST_EXPECT(expectOffers(env, carol, 0)); @@ -5606,7 +6383,8 @@ private: env.close(); // The same result as with the blocking offer - BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens())); + BEAST_EXPECT( + amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens())); // Carol's offer crosses AMM BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -5632,9 +6410,14 @@ private: else { BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens())); - BEAST_EXPECT( - expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); + XRPAmount(909'090'909), + STAmount{USD, UINT64_C(550'000000055), -9}, + amm.tokens())); + BEAST_EXPECT(expectOffers( + env, + carol, + 1, + {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); BEAST_EXPECT(expectOffers(env, bob, 1, {{Amounts{USD(1), XRPAmount(500)}}})); } } @@ -5648,9 +6431,10 @@ private: AMM amm(env, alice, XRP(1'000), USD(500)); env(offer(carol, XRP(100), USD(55))); env.close(); - BEAST_EXPECT( - amm.expectBalances(XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens())); - BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); + BEAST_EXPECT(amm.expectBalances( + XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens())); + BEAST_EXPECT(expectOffers( + env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); } } @@ -5672,7 +6456,8 @@ private: BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{0})); amm.withdrawAll(carol); BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{0})); - auto const lpToken = getAccountLines(env, gw, amm.lptIssue())[jss::lines][0u][jss::balance]; + auto const lpToken = + getAccountLines(env, gw, amm.lptIssue())[jss::lines][0u][jss::balance]; auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value]; BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373"); if (!features[fixAMMv1_1]) @@ -5693,13 +6478,19 @@ private: { Env env(*this, features); auto const ABC = gw["ABC"]; - fund(env, gw, {alice, carol, bob}, XRP(1'000), {USD(1'000'000'000), ABC(1'000'000'000'000)}); + fund( + env, + gw, + {alice, carol, bob}, + XRP(1'000), + {USD(1'000'000'000), ABC(1'000'000'000'000)}); AMM amm(env, lp, ABC(2'000'000), USD(1)); amm.deposit(alice, IOUAmount{1'876123487565916, -15}); amm.deposit(carol, IOUAmount{1'000'000}); amm.withdrawAll(alice); amm.withdrawAll(carol); - auto const lpToken = getAccountLines(env, lp, amm.lptIssue())[jss::lines][0u][jss::balance]; + auto const lpToken = + getAccountLines(env, lp, amm.lptIssue())[jss::lines][0u][jss::balance]; auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value]; BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373"); if (!features[fixAMMv1_1]) @@ -5795,7 +6586,8 @@ private: // allowed for clawing back from an AMM account. Please notice the // `issuer` subfield represents the account being clawed back, which // is confusing. - auto const error = features[featureSingleAssetVault] ? ter{tecPSEUDO_ACCOUNT} : ter{tecAMM_ACCOUNT}; + auto const error = + features[featureSingleAssetVault] ? ter{tecPSEUDO_ACCOUNT} : ter{tecAMM_ACCOUNT}; Issue usd(USD.issue().currency, amm.ammAccount()); auto amount = amountFromString(usd, "10"); env(claw(gw, amount), error); @@ -5834,7 +6626,8 @@ private: { Env env(*this, features); testAMMDeposit(env, [&](AMM& amm) { - amm.deposit(alice, USD(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN)); + amm.deposit( + alice, USD(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN)); }); } @@ -5845,7 +6638,8 @@ private: // when feature AMMClawback is enabled. Env env(*this, features); testAMMDeposit(env, [&](AMM& amm) { - amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN)); + amm.deposit( + alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN)); }); } else @@ -5855,7 +6649,8 @@ private: // when feature AMMClawback is not enabled. Env env(*this, features); testAMMDeposit(env, [&](AMM& amm) { - amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tesSUCCESS)); + amm.deposit( + alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tesSUCCESS)); }); } } @@ -5887,8 +6682,12 @@ private: // Equal withdraw with a limit test([&](AMM& amm) { - amm.withdraw(WithdrawArg{.account = alice, .asset1Out = EUR(0.1), .asset2Out = USD(0.1), .err = err}); - amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1), .asset2Out = EUR(0.1), .err = err}); + amm.withdraw( + WithdrawArg{ + .account = alice, .asset1Out = EUR(0.1), .asset2Out = USD(0.1), .err = err}); + amm.withdraw( + WithdrawArg{ + .account = alice, .asset1Out = USD(0.1), .asset2Out = EUR(0.1), .err = err}); }); // Single withdraw @@ -5920,7 +6719,10 @@ private: { AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key); - env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill)); + env(pay(env.master.id(), accountId, XRP(1000)), + seq(autofill), + fee(autofill), + sig(autofill)); } AMM ammAlice( @@ -5975,9 +6777,13 @@ private: amm.withdraw(WithdrawArg{.asset1Out = STAmount{XPM, 1, -5}}); auto const [amount_, amount2_, lptAMM_] = amm.balances(XRP, XPM); if (!env.enabled(fixAMMv1_3)) + { BEAST_EXPECT((amount2 - amount2_) > withdraw); + } else + { BEAST_EXPECT((amount2 - amount2_) <= withdraw); + } }, 0); } @@ -5992,9 +6798,13 @@ private: auto const res = root2(amount * amount2); if (shouldFail) + { BEAST_EXPECT(res < lptBalance); + } else + { BEAST_EXPECT(res >= lptBalance); + } } void @@ -6018,7 +6828,11 @@ private: env.close(); ammAlice.deposit(DepositArg{.account = bob, .asset1In = deposit}); - invariant(ammAlice, env, "dep1", deposit == STAmount{EUR, 1, -3} && !env.enabled(fixAMMv1_3)); + invariant( + ammAlice, + env, + "dep1", + deposit == STAmount{EUR, 1, -3} && !env.enabled(fixAMMv1_3)); }, {{GBP(30'000), EUR(30'000)}}, 0, @@ -6035,7 +6849,8 @@ private: STAmount const depositEuro{EUR, UINT64_C(10'1234567890123456), -16}; STAmount const depositGBP{GBP, UINT64_C(10'1234567890123456), -16}; - ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP}); + ammAlice.deposit( + DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP}); invariant(ammAlice, env, "dep2", false); }, {{GBP(30'000), EUR(30'000)}}, @@ -6054,7 +6869,9 @@ private: STAmount const depositEuro{EUR, 1, exponent}; STAmount const depositGBP{GBP, 1, exponent}; - ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP}); + ammAlice.deposit( + DepositArg{ + .account = bob, .asset1In = depositEuro, .asset2In = depositGBP}); invariant(ammAlice, env, "dep3", exponent != -3 && !env.enabled(fixAMMv1_3)); }, {{GBP(10'000), EUR(30'000)}}, @@ -6069,7 +6886,8 @@ private: fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct); env.close(); - ammAlice.deposit(DepositArg{.account = bob, .tokens = IOUAmount{10'1234567890123456, -16}}); + ammAlice.deposit( + DepositArg{.account = bob, .tokens = IOUAmount{10'1234567890123456, -16}}); invariant(ammAlice, env, "dep4", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6090,10 +6908,18 @@ private: { testAMM( [&](AMM& ammAlice, Env& env) { - fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(1'000'000)}, Fund::Acct); + fund( + env, + gw, + {bob}, + XRP(10'000'000), + {GBP(100'000), EUR(1'000'000)}, + Fund::Acct); env.close(); - ammAlice.deposit(DepositArg{.account = bob, .tokens = tokens, .asset1In = STAmount{EUR, 1, 6}}); + ammAlice.deposit( + DepositArg{ + .account = bob, .tokens = tokens, .asset1In = STAmount{EUR, 1, 6}}); invariant(ammAlice, env, "dep5", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6150,11 +6976,12 @@ private: // tfTwoAsset withdraw mode testAMM( [&](AMM& ammAlice, Env& env) { - ammAlice.withdraw(WithdrawArg{ - .account = alice, - .asset1Out = STAmount{GBP, 3'500}, - .asset2Out = STAmount{EUR, 15'000}, - .flags = tfTwoAsset}); + ammAlice.withdraw( + WithdrawArg{ + .account = alice, + .asset1Out = STAmount{GBP, 3'500}, + .asset2Out = STAmount{EUR, 15'000}, + .flags = tfTwoAsset}); invariant(ammAlice, env, "with3", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6170,7 +6997,10 @@ private: testAMM( [&](AMM& ammAlice, Env& env) { ammAlice.withdraw( - WithdrawArg{.account = alice, .asset1Out = STAmount{GBP, 1'234}, .flags = tfSingleAsset}); + WithdrawArg{ + .account = alice, + .asset1Out = STAmount{GBP, 1'234}, + .flags = tfSingleAsset}); invariant(ammAlice, env, "with4", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6187,7 +7017,10 @@ private: ammAlice.deposit(DepositArg{.account = bob, .asset1In = STAmount{GBP, 3'456}}); ammAlice.withdraw( - WithdrawArg{.account = bob, .asset1Out = STAmount{GBP, 1'000}, .flags = tfOneAssetWithdrawAll}); + WithdrawArg{ + .account = bob, + .asset1Out = STAmount{GBP, 1'000}, + .flags = tfOneAssetWithdrawAll}); invariant(ammAlice, env, "with5", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6198,8 +7031,12 @@ private: // tfOneAssetLPToken mode testAMM( [&](AMM& ammAlice, Env& env) { - ammAlice.withdraw(WithdrawArg{ - .account = alice, .tokens = 1'000, .asset1Out = STAmount{GBP, 100}, .flags = tfOneAssetLPToken}); + ammAlice.withdraw( + WithdrawArg{ + .account = alice, + .tokens = 1'000, + .asset1Out = STAmount{GBP, 100}, + .flags = tfOneAssetLPToken}); invariant(ammAlice, env, "with6", false); }, {{GBP(7'000), EUR(30'000)}}, @@ -6210,8 +7047,12 @@ private: // tfLimitLPToken mode testAMM( [&](AMM& ammAlice, Env& env) { - ammAlice.withdraw(WithdrawArg{ - .account = alice, .asset1Out = STAmount{GBP, 100}, .maxEP = IOUAmount{2}, .flags = tfLimitLPToken}); + ammAlice.withdraw( + WithdrawArg{ + .account = alice, + .asset1Out = STAmount{GBP, 100}, + .maxEP = IOUAmount{2}, + .flags = tfLimitLPToken}); invariant(ammAlice, env, "with7", true); }, {{GBP(7'000), EUR(30'000)}}, diff --git a/src/test/app/AccountDelete_test.cpp b/src/test/app/AccountDelete_test.cpp index 7c9a593cae..91780800e9 100644 --- a/src/test/app/AccountDelete_test.cpp +++ b/src/test/app/AccountDelete_test.cpp @@ -361,15 +361,16 @@ public: // in her directory. // Lambda to close a PayChannel. - auto payChanClose = [](jtx::Account const& account, Keylet const& payChanKeylet, PublicKey const& pk) { - Json::Value jv; - jv[jss::TransactionType] = jss::PaymentChannelClaim; - jv[jss::Flags] = tfClose; - jv[jss::Account] = account.human(); - jv[sfChannel.jsonName] = to_string(payChanKeylet.key); - jv[sfPublicKey.jsonName] = strHex(pk.slice()); - return jv; - }; + auto payChanClose = + [](jtx::Account const& account, Keylet const& payChanKeylet, PublicKey const& pk) { + Json::Value jv; + jv[jss::TransactionType] = jss::PaymentChannelClaim; + jv[jss::Flags] = tfClose; + jv[jss::Account] = account.human(); + jv[sfChannel.jsonName] = to_string(payChanKeylet.key); + jv[sfPublicKey.jsonName] = strHex(pk.slice()); + return jv; + }; env(payChanClose(alice, alicePayChanKey, alice.pk())); env.close(); @@ -732,7 +733,10 @@ public: auto const acctDelFee{drops(env.current()->fees().increment)}; // becky use credentials but they aren't accepted - env(acctdelete(becky, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(tecBAD_CREDENTIALS)); + env(acctdelete(becky, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(tecBAD_CREDENTIALS)); env.close(); { @@ -744,7 +748,10 @@ public: } // Fail, credentials still not accepted - env(acctdelete(becky, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(tecBAD_CREDENTIALS)); + env(acctdelete(becky, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(tecBAD_CREDENTIALS)); env.close(); // becky accept the credentials @@ -752,10 +759,16 @@ public: env.close(); // Fail, credentials doesn’t belong to carol - env(acctdelete(carol, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(tecBAD_CREDENTIALS)); + env(acctdelete(carol, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(tecBAD_CREDENTIALS)); // Fail, no depositPreauth for provided credentials - env(acctdelete(becky, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(tecNO_PERMISSION)); + env(acctdelete(becky, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(tecNO_PERMISSION)); env.close(); // alice create DepositPreauth Object @@ -785,7 +798,8 @@ public: // check that credential object deleted too auto const jNoCred = credentials::ledgerEntry(env, becky, carol, credType); BEAST_EXPECT( - jNoCred.isObject() && jNoCred.isMember(jss::result) && jNoCred[jss::result].isMember(jss::error) && + jNoCred.isObject() && jNoCred.isMember(jss::result) && + jNoCred[jss::result].isMember(jss::error) && jNoCred[jss::result][jss::error] == "entryNotFound"); } @@ -796,7 +810,8 @@ public: env(credentials::accept(daria, carol, credType)); env.close(); std::string const credDaria = - credentials::ledgerEntry(env, daria, carol, credType)[jss::result][jss::index].asString(); + credentials::ledgerEntry(env, daria, carol, credType)[jss::result][jss::index] + .asString(); // daria use valid credentials, which aren't required and can // delete her account @@ -807,7 +822,8 @@ public: auto const jNoCred = credentials::ledgerEntry(env, daria, carol, credType); BEAST_EXPECT( - jNoCred.isObject() && jNoCred.isMember(jss::result) && jNoCred[jss::result].isMember(jss::error) && + jNoCred.isObject() && jNoCred.isMember(jss::result) && + jNoCred[jss::result].isMember(jss::error) && jNoCred[jss::result][jss::error] == "entryNotFound"); } @@ -823,7 +839,8 @@ public: env(credentials::accept(eaton, carol, credType)); env.close(); std::string const credEaton = - credentials::ledgerEntry(env, eaton, carol, credType)[jss::result][jss::index].asString(); + credentials::ledgerEntry(env, eaton, carol, credType)[jss::result][jss::index] + .asString(); // fred make pre-authorization through authorized account env(fset(fred, asfDepositAuth)); @@ -844,7 +861,8 @@ public: auto const jNoCred = credentials::ledgerEntry(env, eaton, carol, credType); BEAST_EXPECT( - jNoCred.isObject() && jNoCred.isMember(jss::result) && jNoCred[jss::result].isMember(jss::error) && + jNoCred.isObject() && jNoCred.isMember(jss::result) && + jNoCred[jss::result].isMember(jss::error) && jNoCred[jss::result][jss::error] == "entryNotFound"); } @@ -856,7 +874,8 @@ public: env.close(); auto jv = credentials::create(john, carol, credType); - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 20; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 20; jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -869,14 +888,18 @@ public: // credentials are expired // john use credentials but can't delete account - env(acctdelete(john, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(tecEXPIRED)); + env(acctdelete(john, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(tecEXPIRED)); env.close(); { // check that expired credential object deleted auto jv = credentials::ledgerEntry(env, john, carol, credType); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && jv[jss::result][jss::error] == "entryNotFound"); } } @@ -913,7 +936,10 @@ public: env(deposit::auth(alice, becky)); env.close(); - env(acctdelete(becky, alice), credentials::ids({credIdx}), fee(acctDelFee), ter(temDISABLED)); + env(acctdelete(becky, alice), + credentials::ids({credIdx}), + fee(acctDelFee), + ter(temDISABLED)); env.close(); } } @@ -957,7 +983,8 @@ public: BEAST_EXPECT(!env.le(credIdx)); auto const jv = credentials::ledgerEntry(env, becky, carol, credType); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && jv[jss::result][jss::error] == "entryNotFound"); } } @@ -998,7 +1025,8 @@ public: BEAST_EXPECT(!env.le(credIdx)); auto const jv = credentials::ledgerEntry(env, becky, carol, credType); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && jv[jss::result][jss::error] == "entryNotFound"); } } diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp similarity index 92% rename from src/test/rpc/AccountSet_test.cpp rename to src/test/app/AccountSet_test.cpp index 2e238773f9..7af917a0b9 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -1,12 +1,12 @@ #include -#include - +#include #include #include #include #include #include +#include namespace xrpl { @@ -44,7 +44,8 @@ public: env(regkey(alice, alie)); env.close(); - auto testFlags = [this, &alice, &alie, &env](std::initializer_list goodFlags) { + auto testFlags = [this, &alice, &alie, &env]( + std::initializer_list goodFlags) { std::uint32_t const orig_flags = (*env.le(alice))[sfFlags]; for (std::uint32_t flag{1u}; flag < std::numeric_limits::digits; ++flag) { @@ -189,7 +190,7 @@ public: BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain)); // The upper limit on the length is 256 bytes - // (defined as DOMAIN_BYTES_MAX in SetAccount) + // (defined as DOMAIN_BYTES_MAX in AccountSet) // test the edge cases: 255, 256, 257. std::size_t const maxLength = 256; for (std::size_t len = maxLength - 1; len <= maxLength + 1; ++len) @@ -248,7 +249,8 @@ public: env.fund(XRP(10000), alice); auto jt = noop(alice); - std::string const locator = "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF05"; + std::string const locator = + "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF05"; jt[sfWalletLocator.fieldName] = locator; env(jt); BEAST_EXPECT(to_string((*env.le(alice))[sfWalletLocator]) == locator); @@ -292,24 +294,29 @@ public: testcase("TransferRate"); using namespace test::jtx; - auto doTests = [this](FeatureBitset const& features, std::initializer_list testData) { - Env env(*this, features); + auto doTests = + [this](FeatureBitset const& features, std::initializer_list testData) { + Env env(*this, features); - Account const alice("alice"); - env.fund(XRP(10000), alice); + Account const alice("alice"); + env.fund(XRP(10000), alice); - for (auto const& r : testData) - { - env(rate(alice, r.set), ter(r.code)); - env.close(); + for (auto const& r : testData) + { + env(rate(alice, r.set), ter(r.code)); + env.close(); - // If the field is not present expect the default value - if (!(*env.le(alice))[~sfTransferRate]) - BEAST_EXPECT(r.get == 1.0); - else - BEAST_EXPECT(*(*env.le(alice))[~sfTransferRate] == r.get * QUALITY_ONE); - } - }; + // If the field is not present expect the default value + if (!(*env.le(alice))[~sfTransferRate]) + { + BEAST_EXPECT(r.get == 1.0); + } + else + { + BEAST_EXPECT(*(*env.le(alice))[~sfTransferRate] == r.get * QUALITY_ONE); + } + } + }; doTests( testable_amendments(), @@ -405,13 +412,15 @@ public: // We'll insert a replacement for the account root // with the higher (currently invalid) transfer rate. auto replacement = std::make_shared(*sle, sle->key()); - (*replacement)[sfTransferRate] = static_cast(transferRate * QUALITY_ONE); + (*replacement)[sfTransferRate] = + static_cast(transferRate * QUALITY_ONE); view.rawReplace(replacement); return true; }); auto const amount = USD(1); - auto const amountWithRate = toAmount(multiply(amount.value(), Rate(transferRate * QUALITY_ONE))); + auto const amountWithRate = + toAmount(multiply(amount.value(), Rate(transferRate * QUALITY_ONE))); env(pay(gw, alice, USD(10))); env(pay(alice, bob, amount), sendmax(USD(10))); @@ -571,6 +580,6 @@ public: } }; -BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, xrpl, 1); +BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1); } // namespace xrpl diff --git a/src/test/app/AccountTxPaging_test.cpp b/src/test/app/AccountTxPaging_test.cpp index 27078f0578..5efe307812 100644 --- a/src/test/app/AccountTxPaging_test.cpp +++ b/src/test/app/AccountTxPaging_test.cpp @@ -13,7 +13,9 @@ class AccountTxPaging_test : public beast::unit_test::suite bool checkTransaction(Json::Value const& tx, int sequence, int ledger) { - return (tx[jss::tx][jss::Sequence].asInt() == sequence && tx[jss::tx][jss::ledger_index].asInt() == ledger); + return ( + tx[jss::tx][jss::Sequence].asInt() == sequence && + tx[jss::tx][jss::ledger_index].asInt() == ledger); } auto diff --git a/src/test/app/AmendmentTable_test.cpp b/src/test/app/AmendmentTable_test.cpp index 1e4b096927..d8431e5696 100644 --- a/src/test/app/AmendmentTable_test.cpp +++ b/src/test/app/AmendmentTable_test.cpp @@ -1,13 +1,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -86,7 +86,8 @@ private: static std::vector makeDefaultYes(uint256 const amendment) { - std::vector result{{to_string(amendment), amendment, VoteBehavior::DefaultYes}}; + std::vector result{ + {to_string(amendment), amendment, VoteBehavior::DefaultYes}}; return result; } @@ -193,7 +194,8 @@ public: makeDefaultNo(enabled_), makeDefaultYes(vetoed_), makeObsolete(obsolete_)); - return makeTable(env.app(), majorityTime, supported, makeSection(enabled_), makeSection(vetoed_)); + return makeTable( + env.app(), majorityTime, supported, makeSection(enabled_), makeSection(vetoed_)); } void @@ -250,14 +252,16 @@ public: // Verify that unsupportedID is not in table. uint256 const unsupportedID = amendmentId(unsupported_[0]); { - Json::Value const unsupp = table->getJson(unsupportedID, true)[to_string(unsupportedID)]; + Json::Value const unsupp = + table->getJson(unsupportedID, true)[to_string(unsupportedID)]; BEAST_EXPECT(unsupp.size() == 0); } // After vetoing unsupportedID verify that it is in table. table->veto(unsupportedID); { - Json::Value const unsupp = table->getJson(unsupportedID, true)[to_string(unsupportedID)]; + Json::Value const unsupp = + table->getJson(unsupportedID, true)[to_string(unsupportedID)]; BEAST_EXPECT(unsupp[jss::vetoed].asBool()); } } @@ -386,9 +390,10 @@ public: { uint256 const supportedID = amendmentId(a); bool const enabled = table->isEnabled(supportedID); - bool const found = allEnabled.find(supportedID) != allEnabled.end(); + bool const found = allEnabled.contains(supportedID); BEAST_EXPECTS( - enabled == found, a + (enabled ? " enabled " : " disabled ") + (found ? " found" : " not found")); + enabled == found, + a + (enabled ? " enabled " : " disabled ") + (found ? " found" : " not found")); } // All supported and unVetoed amendments should be returned as desired. @@ -399,7 +404,7 @@ public: std::vector const desired = table->getDesired(); for (uint256 const& a : desired) - BEAST_EXPECT(vetoed.count(a) == 0); + BEAST_EXPECT(not vetoed.contains(a)); // Unveto an amendment that is already not vetoed. Shouldn't // hurt anything, but the values returned by getDesired() @@ -521,22 +526,22 @@ public: { case 0: // amendment goes from majority to enabled - if (enabled.find(hash) != enabled.end()) + if (enabled.contains(hash)) Throw("enabling already enabled"); - if (majority.find(hash) == majority.end()) + if (!majority.contains(hash)) Throw("enabling without majority"); enabled.insert(hash); majority.erase(hash); break; case tfGotMajority: - if (majority.find(hash) != majority.end()) + if (majority.contains(hash)) Throw("got majority while having majority"); majority[hash] = roundTime; break; case tfLostMajority: - if (majority.find(hash) == majority.end()) + if (!majority.contains(hash)) Throw("lost majority without majority"); majority.erase(hash); break; @@ -565,20 +570,30 @@ public: std::set enabled; majorityAmendments_t majority; - doRound(env.current()->rules(), *table, weeks{1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{1}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(majority.empty()); uint256 const unsupportedID = amendmentId(unsupported_[0]); { - Json::Value const unsupp = table->getJson(unsupportedID, false)[to_string(unsupportedID)]; + Json::Value const unsupp = + table->getJson(unsupportedID, false)[to_string(unsupportedID)]; BEAST_EXPECT(unsupp.size() == 0); } table->veto(unsupportedID); { - Json::Value const unsupp = table->getJson(unsupportedID, false)[to_string(unsupportedID)]; + Json::Value const unsupp = + table->getJson(unsupportedID, false)[to_string(unsupportedID)]; BEAST_EXPECT(!unsupp[jss::vetoed].asBool()); } @@ -586,7 +601,15 @@ public: votes.emplace_back(testAmendment, validators.size()); - doRound(env.current()->rules(), *table, weeks{2}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{2}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); @@ -594,7 +617,15 @@ public: // Note that the simulation code assumes others behave as we do, // so the amendment won't get enabled - doRound(env.current()->rules(), *table, weeks{5}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{5}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); } @@ -617,20 +648,44 @@ public: std::set enabled; majorityAmendments_t majority; - doRound(env.current()->rules(), *table, weeks{1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{1}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(majority.empty()); votes.emplace_back(testAmendment, validators.size()); - doRound(env.current()->rules(), *table, weeks{2}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{2}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); majority[testAmendment] = hourTime(weeks{1}); - doRound(env.current()->rules(), *table, weeks{5}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{5}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.empty()); BEAST_EXPECT(enabled.empty()); } @@ -652,18 +707,34 @@ public: majorityAmendments_t majority; // Week 1: We should vote for all known amendments not enabled - doRound(env.current()->rules(), *table, weeks{1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{1}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.size() == yes_.size()); BEAST_EXPECT(enabled.empty()); for (auto const& i : yes_) - BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end()); + BEAST_EXPECT(not majority.contains(amendmentId(i))); // Now, everyone votes for this feature for (auto const& i : yes_) votes.emplace_back(amendmentId(i), validators.size()); // Week 2: We should recognize a majority - doRound(env.current()->rules(), *table, weeks{2}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{2}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(ourVotes.size() == yes_.size()); BEAST_EXPECT(enabled.empty()); @@ -671,15 +742,31 @@ public: BEAST_EXPECT(majority[amendmentId(i)] == hourTime(weeks{2})); // Week 5: We should enable the amendment - doRound(env.current()->rules(), *table, weeks{5}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{5}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.size() == yes_.size()); // Week 6: We should remove it from our votes and from having a majority - doRound(env.current()->rules(), *table, weeks{6}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{6}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.size() == yes_.size()); BEAST_EXPECT(ourVotes.empty()); for (auto const& i : yes_) - BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end()); + BEAST_EXPECT(not majority.contains(amendmentId(i))); } // Detect majority at 80%, enable later @@ -690,7 +777,8 @@ public: auto const testAmendment = amendmentId("detectMajority"); test::jtx::Env env{*this, feat}; - auto table = makeTable(env, weeks(2), makeDefaultYes(testAmendment), emptySection_, emptySection_); + auto table = + makeTable(env, weeks(2), makeDefaultYes(testAmendment), emptySection_, emptySection_); auto const validators = makeValidators(16, table); @@ -705,7 +793,15 @@ public: if ((i > 0) && (i < 17)) votes.emplace_back(testAmendment, i); - doRound(env.current()->rules(), *table, weeks{i}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{i}, + validators, + votes, + ourVotes, + enabled, + majority); if (i < 13) // 13 => 13/16 = 0.8125 => > 80% { @@ -747,7 +843,8 @@ public: auto const testAmendment = amendmentId("lostMajority"); test::jtx::Env env{*this, feat}; - auto table = makeTable(env, weeks(8), makeDefaultYes(testAmendment), emptySection_, emptySection_); + auto table = + makeTable(env, weeks(8), makeDefaultYes(testAmendment), emptySection_, emptySection_); auto const validators = makeValidators(16, table); @@ -761,7 +858,15 @@ public: votes.emplace_back(testAmendment, validators.size()); - doRound(env.current()->rules(), *table, weeks{1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{1}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(!majority.empty()); @@ -775,7 +880,15 @@ public: // Gradually reduce support votes.emplace_back(testAmendment, validators.size() - i); - doRound(env.current()->rules(), *table, weeks{i + 1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{i + 1}, + validators, + votes, + ourVotes, + enabled, + majority); if (i < 4) // 16 - 3 = 13 => 13/16 = 0.8125 => > 80% { // 16 - 4 = 12 => 12/16 = 0.75 => < 80% @@ -802,7 +915,8 @@ public: auto const testAmendment = amendmentId("changedUNL"); test::jtx::Env env{*this, feat}; - auto table = makeTable(env, weeks(8), makeDefaultYes(testAmendment), emptySection_, emptySection_); + auto table = + makeTable(env, weeks(8), makeDefaultYes(testAmendment), emptySection_, emptySection_); std::vector> validators = makeValidators(10, table); @@ -816,7 +930,15 @@ public: votes.emplace_back(testAmendment, validators.size() - 2); - doRound(env.current()->rules(), *table, weeks{1}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{1}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(majority.empty()); @@ -832,9 +954,10 @@ public: // We need a hash_set to pass to trustChanged. hash_set trustedValidators; trustedValidators.reserve(validators.size()); - std::for_each(validators.begin(), validators.end(), [&trustedValidators](auto const& val) { - trustedValidators.insert(val.first); - }); + std::for_each( + validators.begin(), validators.end(), [&trustedValidators](auto const& val) { + trustedValidators.insert(val.first); + }); // Tell the AmendmentTable that the UNL changed. table->trustChanged(trustedValidators); @@ -850,7 +973,15 @@ public: votes.emplace_back(testAmendment, validators.size() - 2); - doRound(env.current()->rules(), *table, weeks{2}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{2}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(!majority.empty()); @@ -866,7 +997,15 @@ public: votes.emplace_back(testAmendment, validators.size() - 2); - doRound(env.current()->rules(), *table, weeks{3}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{3}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(majority.empty()); @@ -877,7 +1016,15 @@ public: votes.front().second = validators.size() - 2; - doRound(env.current()->rules(), *table, weeks{4}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{4}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(!majority.empty()); @@ -891,7 +1038,15 @@ public: votes.front().second = validators.size() - 2; - doRound(env.current()->rules(), *table, weeks{5}, validators, votes, ourVotes, enabled, majority); + doRound( + env.current()->rules(), + *table, + weeks{5}, + validators, + votes, + ourVotes, + enabled, + majority); BEAST_EXPECT(enabled.empty()); BEAST_EXPECT(majority.empty()); @@ -916,12 +1071,14 @@ public: { test::jtx::Env env{*this, feat}; auto const testAmendment = amendmentId("validatorFlapping"); - auto table = makeTable(env, weeks(1), makeDefaultYes(testAmendment), emptySection_, emptySection_); + auto table = makeTable( + env, weeks(1), makeDefaultYes(testAmendment), emptySection_, emptySection_); // Make two lists of validators, one with a missing validator, to // make it easy to simulate validator flapping. auto const allValidators = makeValidators(11, table); - decltype(allValidators) const mostValidators(allValidators.begin() + 1, allValidators.end()); + decltype(allValidators) + const mostValidators(allValidators.begin() + 1, allValidators.end()); BEAST_EXPECT(allValidators.size() == mostValidators.size() + 1); std::set enabled; @@ -963,7 +1120,8 @@ public: // no flapping. Otherwise we should only have majority // if allValidators vote -- which means there are no // missing validators. - bool const expectMajority = (delay <= 24) ? true : &thisHoursValidators == &allValidators; + bool const expectMajority = + (delay <= 24) ? true : &thisHoursValidators == &allValidators; BEAST_EXPECT(majority.empty() != expectMajority); } else @@ -993,8 +1151,9 @@ public: BEAST_EXPECT(table->needValidatedLedger(1)); std::set enabled; - std::for_each( - unsupported_.begin(), unsupported_.end(), [&enabled](auto const& s) { enabled.insert(amendmentId(s)); }); + std::for_each(unsupported_.begin(), unsupported_.end(), [&enabled](auto const& s) { + enabled.insert(amendmentId(s)); + }); majorityAmendments_t majority; table->doValidatedLedger(1, enabled, majority); @@ -1002,14 +1161,18 @@ public: BEAST_EXPECT(!table->firstUnsupportedExpected()); NetClock::duration t{1000s}; - std::for_each(unsupportedMajority_.begin(), unsupportedMajority_.end(), [&majority, &t](auto const& s) { - majority[amendmentId(s)] = NetClock::time_point{--t}; - }); + std::for_each( + unsupportedMajority_.begin(), + unsupportedMajority_.end(), + [&majority, &t](auto const& s) { + majority[amendmentId(s)] = NetClock::time_point{--t}; + }); table->doValidatedLedger(1, enabled, majority); BEAST_EXPECT(table->hasUnsupportedEnabled()); BEAST_EXPECT( - table->firstUnsupportedExpected() && *table->firstUnsupportedExpected() == NetClock::time_point{t} + w); + table->firstUnsupportedExpected() && + *table->firstUnsupportedExpected() == NetClock::time_point{t} + w); // Make sure the table knows when it needs an update. BEAST_EXPECT(!table->needValidatedLedger(256)); diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 72f3677e3b..ec12fbb21f 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -2,18 +2,18 @@ #include #include -#include -#include #include -#include -#include +#include #include #include #include #include #include #include +#include +#include +#include namespace xrpl { namespace test { @@ -334,7 +334,8 @@ class Batch_test : public beast::unit_test::suite tx1[sfSigners.jsonName] = Json::arrayValue; tx1[sfSigners.jsonName][0U][sfSigner.jsonName] = Json::objectValue; tx1[sfSigners.jsonName][0U][sfSigner.jsonName][sfAccount.jsonName] = alice.human(); - tx1[sfSigners.jsonName][0U][sfSigner.jsonName][sfSigningPubKey.jsonName] = strHex(alice.pk()); + tx1[sfSigners.jsonName][0U][sfSigner.jsonName][sfSigningPubKey.jsonName] = + strHex(alice.pk()); tx1[sfSigners.jsonName][0U][sfSigner.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; env(batch::outer(alice, seq, batchFee, tfAllOrNothing), batch::inner(tx1, seq + 1), @@ -553,8 +554,10 @@ class Batch_test : public beast::unit_test::suite Serializer msg; serializeBatch(msg, tfAllOrNothing, jt.stx->getBatchTransactionIDs()); auto const sig = xrpl::sign(bob.pk(), bob.sk(), msg.slice()); - jt.jv[sfBatchSigners.jsonName][0u][sfBatchSigner.jsonName][sfAccount.jsonName] = bob.human(); - jt.jv[sfBatchSigners.jsonName][0u][sfBatchSigner.jsonName][sfSigningPubKey.jsonName] = strHex(alice.pk()); + jt.jv[sfBatchSigners.jsonName][0u][sfBatchSigner.jsonName][sfAccount.jsonName] = + bob.human(); + jt.jv[sfBatchSigners.jsonName][0u][sfBatchSigner.jsonName][sfSigningPubKey.jsonName] = + strHex(alice.pk()); jt.jv[sfBatchSigners.jsonName][0u][sfBatchSigner.jsonName][sfTxnSignature.jsonName] = strHex(Slice{sig.data(), sig.size()}); @@ -1754,9 +1757,12 @@ class Batch_test : public beast::unit_test::suite env, tesSUCCESS, batch::outer(alice, seq, batchFee, tfOnlyOne), - batch::inner(offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 1), - batch::inner(offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 2), - batch::inner(offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3), + batch::inner( + offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 1), + batch::inner( + offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 2), + batch::inner( + offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3), batch::inner(pay(alice, bob, XRP(100)), seq + 4), batch::inner(pay(alice, carol, XRP(100)), seq + 5), batch::inner(pay(alice, dave, XRP(100)), seq + 6)); @@ -1975,7 +1981,8 @@ class Batch_test : public beast::unit_test::suite batch::outer(alice, seq, batchFee, tfUntilFailure), batch::inner(pay(alice, bob, XRP(100)), seq + 1), batch::inner(pay(alice, carol, XRP(100)), seq + 2), - batch::inner(offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3), + batch::inner( + offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3), batch::inner(pay(alice, dave, XRP(100)), seq + 4)); env.close(); @@ -2163,7 +2170,8 @@ class Batch_test : public beast::unit_test::suite batch::outer(alice, seq, batchFee, tfIndependent), batch::inner(pay(alice, bob, XRP(100)), seq + 1), batch::inner(pay(alice, carol, XRP(100)), seq + 2), - batch::inner(offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3)); + batch::inner( + offer(alice, alice["USD"](100), XRP(100), tfImmediateOrCancel), seq + 3)); env.close(); std::vector testCases = { @@ -2213,12 +2221,14 @@ class Batch_test : public beast::unit_test::suite std::optional expectedEnabled = std::nullopt, std::optional expectedDisabled = std::nullopt, bool expectInvalidFlag = false) { - testcase << testName << caseName << (expectInvalidFlag ? " - Expected to reach tx engine!" : ""); + testcase << testName << caseName + << (expectInvalidFlag ? " - Expected to reach tx engine!" : ""); auto const jrr = env.rpc("submit", strHex(slice))[jss::result]; - auto const expected = withBatch ? expectedEnabled.value_or( - "fails local checks: Malformed: Invalid inner batch " - "transaction.") - : expectedDisabled.value_or("fails local checks: Empty SigningPubKey."); + auto const expected = withBatch + ? expectedEnabled.value_or( + "fails local checks: Malformed: Invalid inner batch " + "transaction.") + : expectedDisabled.value_or("fails local checks: Empty SigningPubKey."); if (expectInvalidFlag) { expect( @@ -2249,7 +2259,7 @@ class Batch_test : public beast::unit_test::suite txn[sfTxnSignature] = "DEADBEEF"; STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate("TxnSignature set", s.slice(), __LINE__); } @@ -2263,9 +2273,13 @@ class Batch_test : public beast::unit_test::suite txn[sfSigningPubKey] = strHex(alice.pk()); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( - "SigningPubKey set", s.slice(), __LINE__, std::nullopt, "fails local checks: Invalid signature."); + "SigningPubKey set", + s.slice(), + __LINE__, + std::nullopt, + "fails local checks: Invalid signature."); } // Invalid RPC Submission: Signers @@ -2278,9 +2292,13 @@ class Batch_test : public beast::unit_test::suite txn[sfSigners] = Json::arrayValue; STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( - "Signers set", s.slice(), __LINE__, std::nullopt, "fails local checks: Invalid Signers array size."); + "Signers set", + s.slice(), + __LINE__, + std::nullopt, + "fails local checks: Invalid Signers array size."); } { @@ -2290,8 +2308,9 @@ class Batch_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jt.jv); Serializer s; - parsed.object->add(s); - submitAndValidate("Fully signed", s.slice(), __LINE__, std::nullopt, std::nullopt, !withBatch); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) + submitAndValidate( + "Fully signed", s.slice(), __LINE__, std::nullopt, std::nullopt, !withBatch); } // Invalid RPC Submission: tfInnerBatchTxn @@ -2303,7 +2322,7 @@ class Batch_test : public beast::unit_test::suite auto txn = batch::inner(pay(alice, bob, XRP(1)), env.seq(alice)); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "No signing fields set", s.slice(), @@ -2328,7 +2347,7 @@ class Batch_test : public beast::unit_test::suite auto txn = batch::inner(amendTx.getJson(JsonOptions::none), env.seq(alice)); STParsedJSONObject parsed("test", txn.getTxn()); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "Pseudo-transaction", s.slice(), @@ -2581,14 +2600,15 @@ class Batch_test : public beast::unit_test::suite { testcase("loan"); - bool const lendingBatchEnabled = - !std::any_of(Batch::disabledTxTypes.begin(), Batch::disabledTxTypes.end(), [](auto const& disabled) { + bool const lendingBatchEnabled = !std::any_of( + Batch::disabledTxTypes.begin(), Batch::disabledTxTypes.end(), [](auto const& disabled) { return disabled == ttLOAN_BROKER_SET; }); using namespace test::jtx; - test::jtx::Env env{*this, features | featureSingleAssetVault | featureLendingProtocol | featureMPTokensV1}; + test::jtx::Env env{ + *this, features | featureSingleAssetVault | featureLendingProtocol | featureMPTokensV1}; Account const issuer{"issuer"}; // For simplicity, lender will be the sole actor for the vault & @@ -2656,7 +2676,9 @@ class Batch_test : public beast::unit_test::suite fee(none), seq(none)), lenderSeq + 1), - batch::inner(pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), lenderSeq + 2)); + batch::inner( + pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), + lenderSeq + 2)); } { auto const [txIDs, batchID] = submitBatch( @@ -2671,7 +2693,9 @@ class Batch_test : public beast::unit_test::suite fee(none), seq(none)), lenderSeq + 1), - batch::inner(pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), lenderSeq + 2)); + batch::inner( + pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), + lenderSeq + 2)); } { auto const [txIDs, batchID] = submitBatch( @@ -2687,7 +2711,9 @@ class Batch_test : public beast::unit_test::suite fee(none), seq(none)), lenderSeq + 1), - batch::inner(pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), lenderSeq + 2)); + batch::inner( + pay(lender, loanKeylet.key, STAmount{asset, asset(500).value()}), + lenderSeq + 2)); } { // LoanSet normally charges at least 2x base fee, but since the @@ -3254,7 +3280,6 @@ class Batch_test : public beast::unit_test::suite batch::inner(pay(alice, bob, XRP(2)), aliceSeq + 2)); auto const noopTxn = env.jt(noop(alice), seq(aliceSeq + 1)); - auto const noopTxnID = to_string(noopTxn.stx->getTransactionID()); env(noopTxn, ter(tesSUCCESS)); env.close(); @@ -3347,7 +3372,6 @@ class Batch_test : public beast::unit_test::suite // AccountSet Txn auto const noopTxn = env.jt(noop(alice), ticket::use(aliceTicketSeq + 1)); - auto const noopTxnID = to_string(noopTxn.stx->getTransactionID()); env(noopTxn, ter(tesSUCCESS)); // Batch Txn @@ -3827,7 +3851,8 @@ class Batch_test : public beast::unit_test::suite jt.stx->add(s); std::string reason; auto transaction = std::make_shared(jt.stx, reason, env.app()); - env.app().getOPs().processTransaction(transaction, false, true, NetworkOPs::FailHard::yes); + env.app().getOPs().processTransaction( + transaction, false, true, NetworkOPs::FailHard::yes); return transaction->getID(); }; @@ -4328,6 +4353,7 @@ public: run() override { using namespace test::jtx; + auto const sa = testable_amendments(); testWithFeats(sa - fixBatchInnerSigs); testWithFeats(sa); diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 42c600c22b..671f8aab1a 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -14,7 +15,8 @@ private: std::uint32_t const expiry_; public: - explicit expiration(NetClock::time_point const& expiry) : expiry_{expiry.time_since_epoch().count()} + explicit expiration(NetClock::time_point const& expiry) + : expiry_{expiry.time_since_epoch().count()} { } @@ -257,7 +259,8 @@ class Check_test : public beast::unit_test::suite * Attempt to create two checks from `from` to `to` and * require they both result in error/success code `expected` */ - auto writeTwoChecksDI = [&env, &USD, this](Account const& from, Account const& to, TER expected) { + auto writeTwoChecksDI = [&env, &USD, this]( + Account const& from, Account const& to, TER expected) { std::uint32_t const fromOwnerCount{ownerCount(env, from)}; std::uint32_t const toOwnerCount{ownerCount(env, to)}; @@ -270,7 +273,7 @@ class Check_test : public beast::unit_test::suite env(check::create(from, to, USD(50)), ter(expected)); env.close(); - if (expected == tesSUCCESS) + if (isTesSuccess(expected)) { BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount + 2); BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount + 2); @@ -370,7 +373,9 @@ class Check_test : public beast::unit_test::suite } // Bad expiration. - env(check::create(alice, bob, USD(50)), expiration(NetClock::time_point{}), ter(temBAD_EXPIRATION)); + env(check::create(alice, bob, USD(50)), + expiration(NetClock::time_point{}), + ter(temBAD_EXPIRATION)); env.close(); // Destination does not exist. @@ -479,7 +484,9 @@ class Check_test : public beast::unit_test::suite env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri); env.close(); - env(check::create(cheri, bob, USD(50)), fee(drops(env.current()->fees().base)), ter(tecINSUFFICIENT_RESERVE)); + env(check::create(cheri, bob, USD(50)), + fee(drops(env.current()->fees().base)), + ter(tecINSUFFICIENT_RESERVE)); env.close(); env(pay(bob, cheri, drops(env.current()->fees().base + 1))); @@ -522,8 +529,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, startBalance - XRP(10) - drops(baseFeeDrops))); env.require(balance(bob, startBalance + XRP(10) - drops(baseFeeDrops))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -545,7 +552,8 @@ class Check_test : public beast::unit_test::suite // bob tries to cash for more than the check amount. env(check::cash(bob, chkId, checkAmount + drops(1)), ter(tecPATH_PARTIAL)); env.close(); - env(check::cash(bob, chkId, check::DeliverMin(checkAmount + drops(1))), ter(tecPATH_PARTIAL)); + env(check::cash(bob, chkId, check::DeliverMin(checkAmount + drops(1))), + ter(tecPATH_PARTIAL)); env.close(); // bob cashes exactly the check amount. This is successful @@ -555,8 +563,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, drops(checkAmount.mantissa())); env.require(balance(alice, reserve)); env.require(balance(bob, startBalance + checkAmount - drops(baseFeeDrops * 3))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -585,8 +593,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, drops(checkAmount.mantissa() - 1)); env.require(balance(alice, reserve)); env.require(balance(bob, startBalance + checkAmount - drops(baseFeeDrops * 2 + 1))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -656,8 +664,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(10))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -681,8 +689,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(2))); env.require(balance(bob, USD(8))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -748,8 +756,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(10))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -831,8 +839,8 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount(env, USD(2)); env.require(balance(alice, USD(0))); env.require(balance(bob, USD(8))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -883,8 +891,8 @@ class Check_test : public beast::unit_test::suite env.require(balance(alice, USD(8) - bobGot)); env.require(balance(bob, bobGot)); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); } @@ -938,8 +946,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(5))); env.require(balance(bob, USD(3))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 2); } @@ -1011,8 +1019,8 @@ class Check_test : public beast::unit_test::suite env.close(); env.require(balance(alice, USD(1000 - 125 - 60))); env.require(balance(bob, USD(0 + 100 + 50))); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); - BEAST_EXPECT(checksOnAccount(env, bob).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); + BEAST_EXPECT(checksOnAccount(env, bob).empty()); } void @@ -1049,53 +1057,59 @@ class Check_test : public beast::unit_test::suite // There are two test lambdas: one for a Payment and one for a Check. // This shows whether a Payment and a Check behave the same. - auto testNonIssuerQPay = - [&env, &alice, &bob, &USD]( - Account const& truster, IOU const& iou, auto const& inOrOut, double pct, double amount) { - // Capture bob's and alice's balances so we can test at the end. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; - STAmount const bobStart{env.balance(bob, USD.issue()).value()}; + auto testNonIssuerQPay = [&env, &alice, &bob, &USD]( + Account const& truster, + IOU const& iou, + auto const& inOrOut, + double pct, + double amount) { + // Capture bob's and alice's balances so we can test at the end. + STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; + STAmount const bobStart{env.balance(bob, USD.issue()).value()}; - // Set the modified quality. - env(trust(truster, iou(1000)), inOrOut(pct)); - env.close(); + // Set the modified quality. + env(trust(truster, iou(1000)), inOrOut(pct)); + env.close(); - env(pay(alice, bob, USD(amount)), sendmax(USD(10))); - env.close(); - env.require(balance(alice, aliceStart - USD(10))); - env.require(balance(bob, bobStart + USD(10))); + env(pay(alice, bob, USD(amount)), sendmax(USD(10))); + env.close(); + env.require(balance(alice, aliceStart - USD(10))); + env.require(balance(bob, bobStart + USD(10))); - // Return the quality to the unmodified state so it doesn't - // interfere with upcoming tests. - env(trust(truster, iou(1000)), inOrOut(0)); - env.close(); - }; + // Return the quality to the unmodified state so it doesn't + // interfere with upcoming tests. + env(trust(truster, iou(1000)), inOrOut(0)); + env.close(); + }; - auto testNonIssuerQCheck = - [&env, &alice, &bob, &USD]( - Account const& truster, IOU const& iou, auto const& inOrOut, double pct, double amount) { - // Capture bob's and alice's balances so we can test at the end. - STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; - STAmount const bobStart{env.balance(bob, USD.issue()).value()}; + auto testNonIssuerQCheck = [&env, &alice, &bob, &USD]( + Account const& truster, + IOU const& iou, + auto const& inOrOut, + double pct, + double amount) { + // Capture bob's and alice's balances so we can test at the end. + STAmount const aliceStart{env.balance(alice, USD.issue()).value()}; + STAmount const bobStart{env.balance(bob, USD.issue()).value()}; - // Set the modified quality. - env(trust(truster, iou(1000)), inOrOut(pct)); - env.close(); + // Set the modified quality. + env(trust(truster, iou(1000)), inOrOut(pct)); + env.close(); - uint256 const chkId = getCheckIndex(alice, env.seq(alice)); - env(check::create(alice, bob, USD(10))); - env.close(); + uint256 const chkId = getCheckIndex(alice, env.seq(alice)); + env(check::create(alice, bob, USD(10))); + env.close(); - env(check::cash(bob, chkId, USD(amount))); - env.close(); - env.require(balance(alice, aliceStart - USD(10))); - env.require(balance(bob, bobStart + USD(10))); + env(check::cash(bob, chkId, USD(amount))); + env.close(); + env.require(balance(alice, aliceStart - USD(10))); + env.require(balance(bob, bobStart + USD(10))); - // Return the quality to the unmodified state so it doesn't - // interfere with upcoming tests. - env(trust(truster, iou(1000)), inOrOut(0)); - env.close(); - }; + // Return the quality to the unmodified state so it doesn't + // interfere with upcoming tests. + env(trust(truster, iou(1000)), inOrOut(0)); + env.close(); + }; // pct amount testNonIssuerQPay(alice, gw["USD"], qIn, 50, 10); @@ -1292,13 +1306,16 @@ class Check_test : public beast::unit_test::suite env.close(); // Same set of failing cases for both IOU and XRP check cashing. - auto failingCases = [&env, &gw, &alice, &bob](uint256 const& chkId, STAmount const& amount) { + auto failingCases = [&env, &gw, &alice, &bob]( + uint256 const& chkId, STAmount const& amount) { // Bad fee. env(check::cash(bob, chkId, amount), fee(drops(-10)), ter(temBAD_FEE)); env.close(); // Bad flags. - env(check::cash(bob, chkId, amount), txflags(tfImmediateOrCancel), ter(temINVALID_FLAG)); + env(check::cash(bob, chkId, amount), + txflags(tfImmediateOrCancel), + ter(temINVALID_FLAG)); env.close(); // Missing both Amount and DeliverMin. @@ -1467,7 +1484,8 @@ class Check_test : public beast::unit_test::suite env.close(); env(check::cash(bob, chkIdNoDest1, USD(1)), ter(tecDST_TAG_NEEDED)); env.close(); - env(check::cash(bob, chkIdNoDest1, check::DeliverMin(USD(0.5))), ter(tecDST_TAG_NEEDED)); + env(check::cash(bob, chkIdNoDest1, check::DeliverMin(USD(0.5))), + ter(tecDST_TAG_NEEDED)); env.close(); // bob can cash a check with a destination tag. @@ -1638,7 +1656,7 @@ class Check_test : public beast::unit_test::suite env(check::cancel(bob, chkIdNotExp3)); env.close(); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); BEAST_EXPECT(ownerCount(env, alice) == 1); } } @@ -1660,7 +1678,9 @@ class Check_test : public beast::unit_test::suite env.close(); // Bad fee. - env(check::cancel(bob, getCheckIndex(alice, env.seq(alice))), fee(drops(-10)), ter(temBAD_FEE)); + env(check::cancel(bob, getCheckIndex(alice, env.seq(alice))), + fee(drops(-10)), + ter(temBAD_FEE)); env.close(); // Bad flags. @@ -1796,7 +1816,7 @@ class Check_test : public beast::unit_test::suite env.require(owners(alice, 6)); env.require(tickets(alice, env.seq(alice) - aliceTicketSeq)); - BEAST_EXPECT(checksOnAccount(env, alice).size() == 0); + BEAST_EXPECT(checksOnAccount(env, alice).empty()); BEAST_EXPECT(env.seq(alice) == aliceSeq); env.require(balance(alice, USD(700))); @@ -1828,11 +1848,12 @@ class Check_test : public beast::unit_test::suite void verifyOwners(std::uint32_t line) const { - suite.expect(ownerCount(env, acct) == owners, "Owner count mismatch", __FILE__, line); + suite.expect( + ownerCount(env, acct) == owners, "Owner count mismatch", __FILE__, line); } // Operators to make using the class more convenient. - operator Account const() const + operator Account() const { return acct; } @@ -1913,7 +1934,10 @@ class Check_test : public beast::unit_test::suite // between the same two accounts but with two different currencies. // The lambda expects the two trust lines to be largely similar. auto cmpTrustLines = [this, &env]( - Account const& acct1, Account const& acct2, IOU const& offerIou, IOU const& checkIou) { + Account const& acct1, + Account const& acct2, + IOU const& offerIou, + IOU const& checkIou) { auto const offerLine = env.le(keylet::line(acct1, acct2, offerIou.currency)); auto const checkLine = env.le(keylet::line(acct1, acct2, checkIou.currency)); if (offerLine == nullptr || checkLine == nullptr) @@ -1949,7 +1973,8 @@ class Check_test : public beast::unit_test::suite // Lambda that compares the contents of optional fields. auto cmpOptField = [this, offerLine, checkLine](auto const& sfield) { // Expect both fields to either be present or absent. - if (!BEAST_EXPECT(offerLine->isFieldPresent(sfield) == checkLine->isFieldPresent(sfield))) + if (!BEAST_EXPECT( + offerLine->isFieldPresent(sfield) == checkLine->isFieldPresent(sfield))) return; // If both fields are absent then there's nothing diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 31c867c24d..d23431f68e 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -56,12 +56,15 @@ struct Credentials_test : public beast::unit_test::suite BEAST_EXPECT(checkVL(sleCred, sfURI, uri)); auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } env(credentials::accept(subject, issuer, credType)); @@ -93,7 +96,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } } @@ -114,18 +118,22 @@ struct Credentials_test : public beast::unit_test::suite BEAST_EXPECT(sleCred->getAccountID(sfSubject) == issuer.id()); BEAST_EXPECT(sleCred->getAccountID(sfIssuer) == issuer.id()); BEAST_EXPECT((sleCred->getFieldU32(sfFlags) & lsfAccepted)); - BEAST_EXPECT(sleCred->getFieldU64(sfIssuerNode) == sleCred->getFieldU64(sfSubjectNode)); + BEAST_EXPECT( + sleCred->getFieldU64(sfIssuerNode) == sleCred->getFieldU64(sfSubjectNode)); BEAST_EXPECT(ownerCount(env, issuer) == 1); BEAST_EXPECT(checkVL(sleCred, sfCredentialType, credType)); BEAST_EXPECT(checkVL(sleCred, sfURI, uri)); auto const jle = credentials::ledgerEntry(env, issuer, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == issuer.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } env(credentials::deleteCred(issuer, issuer, issuer, credType)); @@ -137,7 +145,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, issuer, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } } @@ -185,7 +194,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } @@ -221,7 +231,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } @@ -255,7 +266,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } @@ -291,7 +303,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } @@ -327,7 +340,8 @@ struct Credentials_test : public beast::unit_test::suite // check no credential exists anymore auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } } @@ -348,7 +362,8 @@ struct Credentials_test : public beast::unit_test::suite BEAST_EXPECT(!ownerCount(env, issuer)); auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } } @@ -367,7 +382,8 @@ struct Credentials_test : public beast::unit_test::suite BEAST_EXPECT(!ownerCount(env, issuer)); auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + jle[jss::result].isMember(jss::error) && jle[jss::result][jss::error] == "entryNotFound"); } } @@ -440,19 +456,24 @@ struct Credentials_test : public beast::unit_test::suite "WULE" "fv28o37gfwEFB3872TFO8GSDSDVD"; static_assert(longURI.size() > maxCredentialURILength); - env(credentials::create(subject, issuer, credType), credentials::uri(longURI), ter(temMALFORMED)); + env(credentials::create(subject, issuer, credType), + credentials::uri(longURI), + ter(temMALFORMED)); } { testcase("Credentials fail, URI empty."); - env(credentials::create(subject, issuer, credType), credentials::uri(""), ter(temMALFORMED)); + env(credentials::create(subject, issuer, credType), + credentials::uri(""), + ter(temMALFORMED)); } { testcase("Credentials fail, expiration in the past."); auto jv = credentials::create(subject, issuer, credType); // current time in ripple epoch - 1s - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() - 1; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() - 1; jv[sfExpiration.jsonName] = t; env(jv, ter(tecEXPIRED)); } @@ -476,12 +497,15 @@ struct Credentials_test : public beast::unit_test::suite // check credential still present auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } { @@ -492,7 +516,10 @@ struct Credentials_test : public beast::unit_test::suite // Everything below can only be tested on open ledger. auto const res1 = directory::bumpLastPage( - env, directory::maximumPageIndex(env), keylet::ownerDir(issuer.id()), directory::adjustOwnerNode); + env, + directory::maximumPageIndex(env), + keylet::ownerDir(issuer.id()), + directory::adjustOwnerNode); BEAST_EXPECT(res1); auto const jv = credentials::create(issuer, subject, credType); @@ -503,7 +530,10 @@ struct Credentials_test : public beast::unit_test::suite // Fill subject directory env(ticket::create(subject, 63)); auto const res2 = directory::bumpLastPage( - env, directory::maximumPageIndex(env), keylet::ownerDir(subject.id()), directory::adjustOwnerNode); + env, + directory::maximumPageIndex(env), + keylet::ownerDir(subject.id()), + directory::adjustOwnerNode); BEAST_EXPECT(res2); env(jv, ter(tecDIR_FULL)); @@ -597,12 +627,15 @@ struct Credentials_test : public beast::unit_test::suite // check credential still present auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } } @@ -631,12 +664,15 @@ struct Credentials_test : public beast::unit_test::suite // check credential still present auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } { @@ -644,7 +680,8 @@ struct Credentials_test : public beast::unit_test::suite testcase("CredentialsAccept fail, expired credentials."); auto jv = credentials::create(subject, issuer, credType2); - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count(); + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count(); jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -657,7 +694,8 @@ struct Credentials_test : public beast::unit_test::suite auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType2); BEAST_EXPECT( jDelCred.isObject() && jDelCred.isMember(jss::result) && - jDelCred[jss::result].isMember(jss::error) && jDelCred[jss::result][jss::error] == "entryNotFound"); + jDelCred[jss::result].isMember(jss::error) && + jDelCred[jss::result][jss::error] == "entryNotFound"); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, subject) == 1); @@ -693,7 +731,8 @@ struct Credentials_test : public beast::unit_test::suite auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( jDelCred.isObject() && jDelCred.isMember(jss::result) && - jDelCred[jss::result].isMember(jss::error) && jDelCred[jss::result][jss::error] == "entryNotFound"); + jDelCred[jss::result].isMember(jss::error) && + jDelCred[jss::result][jss::error] == "entryNotFound"); } } } @@ -750,18 +789,22 @@ struct Credentials_test : public beast::unit_test::suite env.close(); // Other account can't delete credentials without expiration - env(credentials::deleteCred(other, subject, issuer, credType2), ter(tecNO_PERMISSION)); + env(credentials::deleteCred(other, subject, issuer, credType2), + ter(tecNO_PERMISSION)); env.close(); // check credential still present auto const jle = credentials::ledgerEntry(env, subject, issuer, credType2); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType2))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType2))); } { @@ -769,24 +812,29 @@ struct Credentials_test : public beast::unit_test::suite auto jv = credentials::create(subject, issuer, credType); // current time in ripple epoch + 1000s - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 1000; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 1000; jv[sfExpiration.jsonName] = t; env(jv); env.close(); // Other account can't delete credentials that not expired - env(credentials::deleteCred(other, subject, issuer, credType), ter(tecNO_PERMISSION)); + env(credentials::deleteCred(other, subject, issuer, credType), + ter(tecNO_PERMISSION)); env.close(); // check credential still present auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType))); } { @@ -810,7 +858,8 @@ struct Credentials_test : public beast::unit_test::suite { testcase("deleteSLE fail, bad SLE."); - auto view = std::make_shared(env.current().get(), ApplyFlags::tapNONE); + auto view = + std::make_shared(env.current().get(), ApplyFlags::tapNONE); auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal); BEAST_EXPECT(ter == tecNO_ENTRY); } @@ -945,9 +994,15 @@ struct Credentials_test : public beast::unit_test::suite { ter const expected(enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS)); - env(credentials::create(subject, issuer, credType), txflags(tfTransferable), expected); - env(credentials::accept(subject, issuer, credType), txflags(tfSellNFToken), expected); - env(credentials::deleteCred(subject, subject, issuer, credType), txflags(tfPassive), expected); + env(credentials::create(subject, issuer, credType), + txflags(tfTransferable), + expected); + env(credentials::accept(subject, issuer, credType), + txflags(tfSellNFToken), + expected); + env(credentials::deleteCred(subject, subject, issuer, credType), + txflags(tfPassive), + expected); } } } diff --git a/src/test/app/DID_test.cpp b/src/test/app/DID_test.cpp index f83571deab..20c367d64f 100644 --- a/src/test/app/DID_test.cpp +++ b/src/test/app/DID_test.cpp @@ -189,7 +189,7 @@ struct DID_test : public beast::unit_test::suite Account const edna{"edna"}; Account const francis{"francis"}; Account const george{"george"}; - env.fund(XRP(5000), alice, bob, charlie, dave, edna, francis); + env.fund(XRP(5000), alice, bob, charlie, dave, edna, francis, george); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -355,12 +355,14 @@ struct DID_test : public beast::unit_test::suite testAccountReserve(all); testSetInvalid(all); testDeleteInvalid(all); + testSetValidInitial(all); testSetModify(all); testEnabled(all - emptyDID); testAccountReserve(all - emptyDID); testSetInvalid(all - emptyDID); testDeleteInvalid(all - emptyDID); + testSetValidInitial(all - emptyDID); testSetModify(all - emptyDID); } }; diff --git a/src/test/app/DNS_test.cpp b/src/test/app/DNS_test.cpp index 0d41c0f419..9b75e66b85 100644 --- a/src/test/app/DNS_test.cpp +++ b/src/test/app/DNS_test.cpp @@ -15,7 +15,7 @@ class DNS_test : public beast::unit_test::suite using endpoint_type = boost::asio::ip::tcp::endpoint; using error_code = boost::system::error_code; std::weak_ptr work_; - endpoint_type lastEndpoint_{}; + endpoint_type lastEndpoint_; parsedURL pUrl_; std::string port_; jtx::Env env_; @@ -31,13 +31,14 @@ public: void makeRequest(endpoint_type const& lastEndpoint, bool lastStatus) { - auto onFetch = - [&](error_code const& errorCode, endpoint_type const& endpoint, xrpl::detail::response_type&& resp) { - BEAST_EXPECT(!errorCode); - lastEndpoint_ = endpoint; - resolved_[endpoint.address().to_string()]++; - cv_.notify_all(); - }; + auto onFetch = [&](error_code const& errorCode, + endpoint_type const& endpoint, + xrpl::detail::response_type const& resp) { + BEAST_EXPECT(!errorCode); + lastEndpoint_ = endpoint; + resolved_[endpoint.address().to_string()]++; + cv_.notify_all(); + }; auto sp = std::make_shared( pUrl_.domain, @@ -75,7 +76,7 @@ public: parse() { std::string url = arg(); - if (url == "") + if (url.empty()) url = "https://vl.ripple.com"; BEAST_EXPECT(parseUrl(pUrl_, url)); port_ = pUrl_.port ? std::to_string(*pUrl_.port) : "443"; diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 2207c83148..2618a95d0d 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -45,14 +45,12 @@ class Delegate_test : public beast::unit_test::suite env.close(); // delegating an empty permission list when the delegate ledger object - // does not exist will not create the ledger object - env(delegate::set(gw, alice, std::vector{})); + // does not exist is not allowed + env(delegate::set(gw, alice, {}), ter(tecNO_ENTRY)); env.close(); - auto const entry = delegate::entry(env, gw, alice); - BEAST_EXPECT(entry[jss::result][jss::error] == "entryNotFound"); - auto const permissions = - std::vector{"Payment", "EscrowCreate", "EscrowFinish", "TrustlineAuthorize", "CheckCreate"}; + auto const permissions = std::vector{ + "Payment", "EscrowCreate", "EscrowFinish", "TrustlineAuthorize", "CheckCreate"}; env(delegate::set(gw, alice, permissions)); env.close(); @@ -62,7 +60,8 @@ class Delegate_test : public beast::unit_test::suite std::vector const& permissions, Account const& account, Account const& authorize) { - BEAST_EXPECT(!jle[jss::result].isMember(jss::error) && jle[jss::result].isMember(jss::node)); + BEAST_EXPECT( + !jle[jss::result].isMember(jss::error) && jle[jss::result].isMember(jss::node)); BEAST_EXPECT(jle[jss::result][jss::node]["LedgerEntryType"] == jss::Delegate); BEAST_EXPECT(jle[jss::result][jss::node][jss::Account] == account.human()); BEAST_EXPECT(jle[jss::result][jss::node][sfAuthorize.jsonName] == authorize.human()); @@ -71,7 +70,9 @@ class Delegate_test : public beast::unit_test::suite unsigned i = 0; for (auto const& permission : permissions) { - BEAST_EXPECT(jPermissions[i][sfPermission.jsonName][sfPermissionValue.jsonName] == permission); + BEAST_EXPECT( + jPermissions[i][sfPermission.jsonName][sfPermissionValue.jsonName] == + permission); i++; } }; @@ -80,7 +81,8 @@ class Delegate_test : public beast::unit_test::suite comparePermissions(delegate::entry(env, gw, alice), permissions, gw, alice); // gw updates permission - auto const newPermissions = std::vector{"Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"}; + auto const newPermissions = + std::vector{"Payment", "AMMCreate", "AMMDeposit", "AMMWithdraw"}; env(delegate::set(gw, alice, newPermissions)); env.close(); @@ -88,9 +90,7 @@ class Delegate_test : public beast::unit_test::suite // newPermissions comparePermissions(delegate::entry(env, gw, alice), newPermissions, gw, alice); - // gw deletes all permissions delegated to alice, this will delete - // the - // ledger entry + // gw deletes all permissions delegated to alice, this will delete the ledger entry env(delegate::set(gw, alice, {})); env.close(); auto const jle = delegate::entry(env, gw, alice); @@ -200,27 +200,48 @@ class Delegate_test : public beast::unit_test::suite testcase("test reserve"); using namespace jtx; - // test reserve for DelegateSet + // reserve requirement not met { Env env(*this); Account alice{"alice"}; Account bob{"bob"}; - Account carol{"carol"}; - env.fund(drops(env.current()->fees().accountReserve(0)), alice); - env.fund(drops(env.current()->fees().accountReserve(1)), bob, carol); + auto const txFee = env.current()->fees().base; + env.fund(env.current()->fees().accountReserve(0) + txFee, alice); + env.fund(XRP(100000), bob); env.close(); // alice does not have enough reserve to create Delegate env(delegate::set(alice, bob, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + } - // bob has enough reserve - env(delegate::set(bob, alice, {"Payment"})); + // reserve recovered after deleting delegation object + { + Env env(*this); + Account bob{"bob"}; + Account alice{"alice"}; + Account carol{"carol"}; + + auto const txFee = env.current()->fees().base; + + env.fund(env.current()->fees().accountReserve(1) + (txFee * 4), alice); + env.fund(XRP(100000), bob, carol); env.close(); - // now bob create another Delegate, he does not have - // enough reserve - env(delegate::set(bob, carol, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + // alice consumes 1 txFee and requires 1 object reserve + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + // alice does not have enough reserve to create another delegation object + env(delegate::set(alice, carol, {"Payment"}), ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + // deleting delegation object recovers 1 reserve + env(delegate::set(alice, bob, {})); + env.close(); + + // now alice can delegate again + env(delegate::set(alice, carol, {"Payment"})); } // test reserve when sending transaction on behalf of other account @@ -266,7 +287,10 @@ class Delegate_test : public beast::unit_test::suite auto bobBalance = env.balance(bob); auto carolBalance = env.balance(carol); - env(pay(alice, carol, XRP(100)), fee(XRP(2000)), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(pay(alice, carol, XRP(100)), + fee(XRP(2000)), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -297,7 +321,10 @@ class Delegate_test : public beast::unit_test::suite auto bobBalance = env.balance(bob); auto carolBalance = env.balance(carol); - env(pay(alice, carol, XRP(100)), fee(XRP(2000)), delegate::as(bob), ter(terINSUF_FEE_B)); + env(pay(alice, carol, XRP(100)), + fee(XRP(2000)), + delegate::as(bob), + ter(terINSUF_FEE_B)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -312,7 +339,10 @@ class Delegate_test : public beast::unit_test::suite auto carolBalance = env.balance(carol); auto const feeAmt = XRP(10); - env(pay(alice, carol, XRP(20000)), fee(feeAmt), delegate::as(bob), ter(tecUNFUNDED_PAYMENT)); + env(pay(alice, carol, XRP(20000)), + fee(feeAmt), + delegate::as(bob), + ter(tecUNFUNDED_PAYMENT)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance - feeAmt); @@ -466,7 +496,9 @@ class Delegate_test : public beast::unit_test::suite env(check::create(alice, bob, XRP(10)), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); // carol does not have permission to create check - env(check::create(alice, bob, XRP(10)), delegate::as(carol), ter(terNO_DELEGATE_PERMISSION)); + env(check::create(alice, bob, XRP(10)), + delegate::as(carol), + ter(terNO_DELEGATE_PERMISSION)); } void @@ -796,24 +828,40 @@ class Delegate_test : public beast::unit_test::suite env.close(); // unsupported flags - env(trust(alice, gw["USD"](50), tfSetNoRipple), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); - env(trust(alice, gw["USD"](50), tfClearNoRipple), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); - env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); - env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(trust(alice, gw["USD"](50), tfSetNoRipple), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); + env(trust(alice, gw["USD"](50), tfClearNoRipple), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); env.close(); // supported flags with wrong permission - env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); - env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfSetfAuth), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfSetFreeze), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); env.close(); env(delegate::set(gw, bob, {"TrustlineAuthorize"})); env.close(); - env(trust(gw, gw["USD"](0), alice, tfClearFreeze), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfClearFreeze), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); env.close(); // although trustline authorize is granted, bob can not change the // limit number - env(trust(gw, gw["USD"](50), alice, tfSetfAuth), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](50), alice, tfSetfAuth), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); env.close(); // supported flags with correct permission @@ -829,7 +877,9 @@ class Delegate_test : public beast::unit_test::suite env.close(); // but bob can not freeze trustline because he no longer has freeze // permission - env(trust(gw, gw["USD"](0), alice, tfSetFreeze), delegate::as(bob), ter(terNO_DELEGATE_PERMISSION)); + env(trust(gw, gw["USD"](0), alice, tfSetFreeze), + delegate::as(bob), + ter(terNO_DELEGATE_PERMISSION)); // cannot update LimitAmount with granular permission, both high and // low account @@ -883,13 +933,17 @@ class Delegate_test : public beast::unit_test::suite // add TrustSet permission and some unrelated permission env(delegate::set( - alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"})); + alice, + bob, + {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"})); env.close(); env(trust(alice, gw["USD"](50)), delegate::as(bob)); env.close(); env(delegate::set( - gw, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"})); + gw, + bob, + {"TrustlineUnfreeze", "NFTokenCreateOffer", "TrustSet", "AccountTransferRateSet"})); env.close(); // since bob has TrustSet permission, he does not need @@ -919,7 +973,8 @@ class Delegate_test : public beast::unit_test::suite env(delegate::set(gw, bob, {"TrustlineAuthorize"})); env.close(); - env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig), delegate::as(bob)); + env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig), + delegate::as(bob)); } } @@ -998,7 +1053,8 @@ class Delegate_test : public beast::unit_test::suite env(jt, ter(terNO_DELEGATE_PERMISSION)); // alice give granular permission of AccountMessageKeySet to bob - env(delegate::set(alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); + env(delegate::set( + alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); env.close(); // bob can set message key for alice @@ -1015,7 +1071,10 @@ class Delegate_test : public beast::unit_test::suite env(delegate::set( alice, bob, - {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet", "AccountTransferRateSet"})); + {"AccountDomainSet", + "AccountEmailHashSet", + "AccountMessageKeySet", + "AccountTransferRateSet"})); env.close(); auto jtRate = rate(alice, 2.0); jtRate[sfDelegate] = bob.human(); @@ -1057,7 +1116,8 @@ class Delegate_test : public beast::unit_test::suite jt[sfTickSize] = 7; env(jt, ter(terNO_DELEGATE_PERMISSION)); - env(delegate::set(alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); + env(delegate::set( + alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); env.close(); // bob does not have permission to set wallet locater for alice @@ -1095,7 +1155,8 @@ class Delegate_test : public beast::unit_test::suite testSetClearFlag(asfAllowTrustLineClawback); // alice gives some granular permissions to bob - env(delegate::set(alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); + env(delegate::set( + alice, bob, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); env.close(); testSetClearFlag(asfDefaultRipple); @@ -1122,7 +1183,8 @@ class Delegate_test : public beast::unit_test::suite env(jt, ter(terNO_DELEGATE_PERMISSION)); // bob gives alice some permissions - env(delegate::set(bob, alice, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); + env(delegate::set( + bob, alice, {"AccountDomainSet", "AccountEmailHashSet", "AccountMessageKeySet"})); env.close(); // since we can not set asfNoFreeze if asfAllowTrustLineClawback is @@ -1139,7 +1201,10 @@ class Delegate_test : public beast::unit_test::suite Account const bobKey{"bobKey", KeyType::secp256k1}; env(regkey(bob, bobKey)); env.close(); - env(fset(alice, asfDisableMaster), delegate::as(bob), sig(bob), ter(terNO_DELEGATE_PERMISSION)); + env(fset(alice, asfDisableMaster), + delegate::as(bob), + sig(bob), + ter(terNO_DELEGATE_PERMISSION)); } // tfFullyCanonicalSig won't block delegated transaction @@ -1184,18 +1249,30 @@ class Delegate_test : public beast::unit_test::suite env.close(); // delegate ledger object is not created yet - mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); + mpt.set( + {.account = alice, + .flags = tfMPTLock, + .delegate = bob, + .err = terNO_DELEGATE_PERMISSION}); // alice gives granular permission to bob of MPTokenIssuanceUnlock env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"})); env.close(); // bob does not have lock permission - mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); + mpt.set( + {.account = alice, + .flags = tfMPTLock, + .delegate = bob, + .err = terNO_DELEGATE_PERMISSION}); // bob now has lock permission, but does not have unlock permission env(delegate::set(alice, bob, {"MPTokenIssuanceLock"})); env.close(); mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob}); - mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); + mpt.set( + {.account = alice, + .flags = tfMPTUnlock, + .delegate = bob, + .err = terNO_DELEGATE_PERMISSION}); // now bob can lock and unlock env(delegate::set(alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"})); @@ -1223,17 +1300,28 @@ class Delegate_test : public beast::unit_test::suite env.close(); mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob}); // bob does not have unlock permission - mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); + mpt.set( + {.account = alice, + .flags = tfMPTUnlock, + .delegate = bob, + .err = terNO_DELEGATE_PERMISSION}); // alice gives bob some unrelated permission with // MPTokenIssuanceLock env(delegate::set(alice, bob, {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn"})); env.close(); // bob can not unlock - mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); + mpt.set( + {.account = alice, + .flags = tfMPTUnlock, + .delegate = bob, + .err = terNO_DELEGATE_PERMISSION}); // alice add MPTokenIssuanceSet to permissions - env(delegate::set(alice, bob, {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn", "MPTokenIssuanceSet"})); + env(delegate::set( + alice, + bob, + {"NFTokenMint", "MPTokenIssuanceLock", "NFTokenBurn", "MPTokenIssuanceSet"})); mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob}); // alice can lock by herself mpt.set({.account = alice, .flags = tfMPTLock}); @@ -1309,7 +1397,11 @@ class Delegate_test : public beast::unit_test::suite auto bobBalance = env.balance(bob); auto carolBalance = env.balance(carol); - env(pay(alice, carol, XRP(100)), fee(XRP(10)), delegate::as(bob), sig(alice), ter(tefBAD_AUTH)); + env(pay(alice, carol, XRP(100)), + fee(XRP(10)), + delegate::as(bob), + sig(alice), + ter(tefBAD_AUTH)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -1446,7 +1538,11 @@ class Delegate_test : public beast::unit_test::suite auto dariaBalance = env.balance(daria); auto edwardBalance = env.balance(edward); - env(pay(alice, carol, XRP(100)), fee(XRP(10)), delegate::as(bob), msig(daria, edward), ter(tefBAD_QUORUM)); + env(pay(alice, carol, XRP(100)), + fee(XRP(10)), + delegate::as(bob), + msig(daria, edward), + ter(tefBAD_QUORUM)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -1535,13 +1631,7 @@ class Delegate_test : public beast::unit_test::suite {"CredentialDelete", featureCredentials}, {"NFTokenModify", featureDynamicNFT}, {"PermissionedDomainSet", featurePermissionedDomains}, - {"PermissionedDomainDelete", featurePermissionedDomains}, - {"VaultCreate", featureSingleAssetVault}, - {"VaultSet", featureSingleAssetVault}, - {"VaultDelete", featureSingleAssetVault}, - {"VaultDeposit", featureSingleAssetVault}, - {"VaultWithdraw", featureSingleAssetVault}, - {"VaultClawback", featureSingleAssetVault}}; + {"PermissionedDomainDelete", featurePermissionedDomains}}; // Can not delegate tx if any required feature disabled. { @@ -1581,6 +1671,56 @@ class Delegate_test : public beast::unit_test::suite } } + void + testTxDelegableCount() + { + testcase("Delegable Transactions Completeness"); + + std::size_t delegableCount = 0; + +#pragma push_macro("TRANSACTION") +#undef TRANSACTION + +#define TRANSACTION(tag, value, name, delegable, ...) \ + if (delegable == xrpl::delegable) \ + { \ + delegableCount++; \ + } + +#include + +#undef TRANSACTION +#pragma pop_macro("TRANSACTION") + + // ==================================================================== + // IMPORTANT NOTICE: + // + // If this test fails, it indicates that the 'Delegation::delegable' status + // in transactions.macro has been changed. Delegation allows accounts to act + // on behalf of others, significantly increasing the security surface. + // + // + // To ENSURE any added transaction is safe and compatible with delegation: + // + // 1. Verify that the transaction is intended to be delegable. + // 2. Every standard test case for that transaction MUST be + // duplicated and verified for a Delegated context. + // 3. Ensure that Fee, Reserve, and Signing are correctly handled. + // + // DO NOT modify expectedDelegableCount unless all scenarios, including + // edge cases, have been fully tested and verified. + // ==================================================================== + std::size_t const expectedDelegableCount = 75; + + BEAST_EXPECTS( + delegableCount == expectedDelegableCount, + "\n[SECURITY] New delegable transaction detected!" + "\n Expected: " + + std::to_string(expectedDelegableCount) + + "\n Actual: " + std::to_string(delegableCount) + + "\n Action: Verify security requirements to interact with Delegation feature"); + } + void run() override { @@ -1605,6 +1745,7 @@ class Delegate_test : public beast::unit_test::suite testMultiSignQuorumNotMet(); testPermissionValue(all); testTxRequireFeatures(all); + testTxDelegableCount(); } }; BEAST_DEFINE_TESTSUITE(Delegate, app, xrpl); diff --git a/src/test/app/DeliverMin_test.cpp b/src/test/app/DeliverMin_test.cpp index 711b29cfa8..fe044353b7 100644 --- a/src/test/app/DeliverMin_test.cpp +++ b/src/test/app/DeliverMin_test.cpp @@ -25,13 +25,22 @@ public: env.trust(USD(100), "alice", "bob", "carol"); env.close(); env(pay("alice", "bob", USD(10)), deliver_min(USD(10)), ter(temBAD_AMOUNT)); - env(pay("alice", "bob", USD(10)), deliver_min(USD(-5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); - env(pay("alice", "bob", USD(10)), deliver_min(XRP(5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); + env(pay("alice", "bob", USD(10)), + deliver_min(USD(-5)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); + env(pay("alice", "bob", USD(10)), + deliver_min(XRP(5)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); env(pay("alice", "bob", USD(10)), deliver_min(Account("carol")["USD"](5)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); - env(pay("alice", "bob", USD(10)), deliver_min(USD(15)), txflags(tfPartialPayment), ter(temBAD_AMOUNT)); + env(pay("alice", "bob", USD(10)), + deliver_min(USD(15)), + txflags(tfPartialPayment), + ter(temBAD_AMOUNT)); env(pay(gw, "carol", USD(50))); env(offer("carol", XRP(5), USD(5))); env(pay("alice", "bob", USD(10)), diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 0cd7e7449c..ad401499bc 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -258,41 +258,45 @@ struct DepositAuth_test : public beast::unit_test::suite IOU const USD1(gw1["USD"]); IOU const USD2(gw2["USD"]); - auto testIssuer = - [&](FeatureBitset const& features, bool noRipplePrev, bool noRippleNext, bool withDepositAuth) { - Env env(*this, features); + auto testIssuer = [&](FeatureBitset const& features, + bool noRipplePrev, + bool noRippleNext, + bool withDepositAuth) { + Env env(*this, features); - env.fund(XRP(10000), gw1, alice, bob); - env.close(); - env(trust(gw1, alice["USD"](10), noRipplePrev ? tfSetNoRipple : 0)); - env(trust(gw1, bob["USD"](10), noRippleNext ? tfSetNoRipple : 0)); - env.trust(USD1(10), alice, bob); + env.fund(XRP(10000), gw1, alice, bob); + env.close(); + env(trust(gw1, alice["USD"](10), noRipplePrev ? tfSetNoRipple : 0)); + env(trust(gw1, bob["USD"](10), noRippleNext ? tfSetNoRipple : 0)); + env.trust(USD1(10), alice, bob); - env(pay(gw1, alice, USD1(10))); + env(pay(gw1, alice, USD1(10))); - if (withDepositAuth) - env(fset(gw1, asfDepositAuth)); + if (withDepositAuth) + env(fset(gw1, asfDepositAuth)); - TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS}; - env(pay(alice, bob, USD1(10)), path(gw1), ter(result)); - }; + TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS}; + env(pay(alice, bob, USD1(10)), path(gw1), ter(result)); + }; - auto testNonIssuer = - [&](FeatureBitset const& features, bool noRipplePrev, bool noRippleNext, bool withDepositAuth) { - Env env(*this, features); + auto testNonIssuer = [&](FeatureBitset const& features, + bool noRipplePrev, + bool noRippleNext, + bool withDepositAuth) { + Env env(*this, features); - env.fund(XRP(10000), gw1, gw2, alice); - env.close(); - env(trust(alice, USD1(10), noRipplePrev ? tfSetNoRipple : 0)); - env(trust(alice, USD2(10), noRippleNext ? tfSetNoRipple : 0)); - env(pay(gw2, alice, USD2(10))); + env.fund(XRP(10000), gw1, gw2, alice); + env.close(); + env(trust(alice, USD1(10), noRipplePrev ? tfSetNoRipple : 0)); + env(trust(alice, USD2(10), noRippleNext ? tfSetNoRipple : 0)); + env(pay(gw2, alice, USD2(10))); - if (withDepositAuth) - env(fset(alice, asfDepositAuth)); + if (withDepositAuth) + env(fset(alice, asfDepositAuth)); - TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS}; - env(pay(gw1, gw2, USD2(10)), path(alice), sendmax(USD1(10)), ter(result)); - }; + TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS}; + env(pay(gw1, gw2, USD2(10)), path(alice), sendmax(USD1(10)), ter(result)); + }; // Test every combo of noRipplePrev, noRippleNext, and withDepositAuth for (int i = 0; i < 8; ++i) @@ -742,8 +746,9 @@ struct DepositPreauth_test : public beast::unit_test::suite auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); BEAST_EXPECT( - jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && - jDP[jss::result].isMember(jss::node) && jDP[jss::result][jss::node].isMember("LedgerEntryType") && + jDP.isObject() && jDP.isMember(jss::result) && + !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) && + jDP[jss::result][jss::node].isMember("LedgerEntryType") && jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth); // Alice can't pay - empty credentials array @@ -812,7 +817,8 @@ struct DepositPreauth_test : public beast::unit_test::suite } // Bob setup DepositPreauth object, duplicates is not allowed - env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}), ter(temMALFORMED)); + env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}), + ter(temMALFORMED)); // Bob setup DepositPreauth object env(deposit::authCredentials(bob, {{issuer, credType}})); @@ -823,12 +829,16 @@ struct DepositPreauth_test : public beast::unit_test::suite "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E" "01E034"; // Alice can't pay with non-existing credentials - env(pay(alice, bob, XRP(100)), credentials::ids({invalidIdx}), ter(tecBAD_CREDENTIALS)); + env(pay(alice, bob, XRP(100)), + credentials::ids({invalidIdx}), + ter(tecBAD_CREDENTIALS)); } { // maria can't pay using valid credentials but issued for // different account - env(pay(maria, bob, XRP(100)), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS)); + env(pay(maria, bob, XRP(100)), + credentials::ids({credIdx}), + ter(tecBAD_CREDENTIALS)); } { @@ -842,7 +852,9 @@ struct DepositPreauth_test : public beast::unit_test::suite std::string const credIdx2 = jv[jss::result][jss::index].asString(); // Alice can't pay with invalid set of valid credentials - env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}), ter(tecNO_PERMISSION)); + env(pay(alice, bob, XRP(100)), + credentials::ids({credIdx, credIdx2}), + ter(tecNO_PERMISSION)); } // Error, duplicate credentials @@ -936,7 +948,8 @@ struct DepositPreauth_test : public beast::unit_test::suite { // AuthorizeCredentials is larger than 8 elements - Account const a("a"), b("b"), c("c"), d("d"), e("e"), f("f"), g("g"), h("h"), i("i"); + Account const a("a"), b("b"), c("c"), d("d"), e("e"), f("f"), g("g"), h("h"), + i("i"); auto const& z = credType; auto jv = deposit::authCredentials( bob, {{a, z}, {b, z}, {c, z}, {d, z}, {e, z}, {f, z}, {g, z}, {h, z}, {i, z}}); @@ -972,8 +985,10 @@ struct DepositPreauth_test : public beast::unit_test::suite auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); BEAST_EXPECT( - jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && - jDP[jss::result].isMember(jss::node) && jDP[jss::result][jss::node].isMember("LedgerEntryType") && + jDP.isObject() && jDP.isMember(jss::result) && + !jDP[jss::result].isMember(jss::error) && + jDP[jss::result].isMember(jss::node) && + jDP[jss::result][jss::node].isMember("LedgerEntryType") && jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth); // Check object fields @@ -984,7 +999,8 @@ struct DepositPreauth_test : public beast::unit_test::suite { auto const& c(o[jss::Credential]); BEAST_EXPECT(c[jss::Issuer].asString() == issuer.human()); - BEAST_EXPECT(c["CredentialType"].asString() == strHex(std::string_view(credType))); + BEAST_EXPECT( + c["CredentialType"].asString() == strHex(std::string_view(credType))); } // can't create duplicate @@ -997,7 +1013,8 @@ struct DepositPreauth_test : public beast::unit_test::suite env.close(); auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); BEAST_EXPECT( - jDP.isObject() && jDP.isMember(jss::result) && jDP[jss::result].isMember(jss::error) && + jDP.isObject() && jDP.isMember(jss::result) && + jDP[jss::result].isMember(jss::error) && jDP[jss::result][jss::error] == "entryNotFound"); } } @@ -1028,7 +1045,8 @@ struct DepositPreauth_test : public beast::unit_test::suite auto jv = credentials::create(alice, issuer, credType); // Current time in ripple epoch. // Every time ledger close, unittest timer increase by 10s - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 60; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 60; jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -1039,7 +1057,8 @@ struct DepositPreauth_test : public beast::unit_test::suite // Create credential which not expired jv = credentials::create(alice, issuer, credType2); - uint32_t const t2 = env.current()->header().parentCloseTime.time_since_epoch().count() + 1000; + uint32_t const t2 = + env.current()->header().parentCloseTime.time_since_epoch().count() + 1000; jv[sfExpiration.jsonName] = t2; env(jv); env.close(); @@ -1069,7 +1088,9 @@ struct DepositPreauth_test : public beast::unit_test::suite env.close(); // Ledger closed, time increased, alice can't pay anymore - env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}), ter(tecEXPIRED)); + env(pay(alice, bob, XRP(100)), + credentials::ids({credIdx, credIdx2}), + ter(tecEXPIRED)); env.close(); { @@ -1085,13 +1106,15 @@ struct DepositPreauth_test : public beast::unit_test::suite // check that non-expired credential still present auto const jle = credentials::ledgerEntry(env, alice, issuer, credType2); BEAST_EXPECT( - jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && + jle.isObject() && jle.isMember(jss::result) && + !jle[jss::result].isMember(jss::error) && jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == alice.human() && - jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType2))); + jle[jss::result][jss::node]["CredentialType"] == + strHex(std::string_view(credType2))); } BEAST_EXPECT(ownerCount(env, issuer) == 0); @@ -1100,7 +1123,8 @@ struct DepositPreauth_test : public beast::unit_test::suite { auto jv = credentials::create(gw, issuer, credType); - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 40; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 40; jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -1125,7 +1149,8 @@ struct DepositPreauth_test : public beast::unit_test::suite auto const jDelCred = credentials::ledgerEntry(env, gw, issuer, credType); BEAST_EXPECT( jDelCred.isObject() && jDelCred.isMember(jss::result) && - jDelCred[jss::result].isMember(jss::error) && jDelCred[jss::result][jss::error] == "entryNotFound"); + jDelCred[jss::result].isMember(jss::error) && + jDelCred[jss::result][jss::error] == "entryNotFound"); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, gw) == 0); @@ -1144,7 +1169,8 @@ struct DepositPreauth_test : public beast::unit_test::suite // Create credentials auto jv = credentials::create(zelda, issuer, credType); - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 50; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 50; jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -1180,19 +1206,25 @@ struct DepositPreauth_test : public beast::unit_test::suite "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E" "01E034"; - env(escrow::finish(zelda, alice, seq), credentials::ids({invalidIdx}), ter(tecBAD_CREDENTIALS)); + env(escrow::finish(zelda, alice, seq), + credentials::ids({invalidIdx}), + ter(tecBAD_CREDENTIALS)); env.close(); } { // Ledger closed, time increased, zelda can't finish escrow - env(escrow::finish(zelda, alice, seq), credentials::ids({credIdx}), fee(1500), ter(tecEXPIRED)); + env(escrow::finish(zelda, alice, seq), + credentials::ids({credIdx}), + fee(1500), + ter(tecEXPIRED)); env.close(); } // check that expired credentials were deleted auto const jDelCred = credentials::ledgerEntry(env, zelda, issuer, credType); BEAST_EXPECT( - jDelCred.isObject() && jDelCred.isMember(jss::result) && jDelCred[jss::result].isMember(jss::error) && + jDelCred.isObject() && jDelCred.isMember(jss::result) && + jDelCred[jss::result].isMember(jss::error) && jDelCred[jss::result][jss::error] == "entryNotFound"); } } @@ -1213,7 +1245,14 @@ struct DepositPreauth_test : public beast::unit_test::suite env.fund(XRP(5000), stock, alice, bob); std::vector credentials = { - {"a", "a"}, {"b", "b"}, {"c", "c"}, {"d", "d"}, {"e", "e"}, {"f", "f"}, {"g", "g"}, {"h", "h"}}; + {"a", "a"}, + {"b", "b"}, + {"c", "c"}, + {"d", "d"}, + {"e", "e"}, + {"f", "f"}, + {"g", "g"}, + {"h", "h"}}; for (auto const& c : credentials) env.fund(XRP(5000), c.issuer); @@ -1244,7 +1283,10 @@ struct DepositPreauth_test : public beast::unit_test::suite auto issuer = c[jss::Issuer].asString(); if (BEAST_EXPECT(pubKey2Acc.contains(issuer))) - readCreds.emplace_back(pubKey2Acc.at(issuer), c["CredentialType"].asString()); + { + readCreds.emplace_back( + pubKey2Acc.at(issuer), c["CredentialType"].asString()); + } } BEAST_EXPECT(std::ranges::is_sorted(readCreds)); @@ -1270,7 +1312,8 @@ struct DepositPreauth_test : public beast::unit_test::suite testcase("Check duplicate credentials."); { // check duplicates in depositPreauth params - std::vector copyCredentials(credentials.begin(), credentials.end() - 1); + std::vector copyCredentials( + credentials.begin(), credentials.end() - 1); std::ranges::shuffle(copyCredentials, gen); for (auto const& c : copyCredentials) @@ -1290,7 +1333,9 @@ struct DepositPreauth_test : public beast::unit_test::suite env.close(); credentialIDs.push_back( - credentials::ledgerEntry(env, alice, c.issuer, c.credType)[jss::result][jss::index].asString()); + credentials::ledgerEntry( + env, alice, c.issuer, c.credType)[jss::result][jss::index] + .asString()); } // check duplicates in payment params diff --git a/src/test/app/Discrepancy_test.cpp b/src/test/app/Discrepancy_test.cpp index b56cb5b7c6..3f808b37d8 100644 --- a/src/test/app/Discrepancy_test.cpp +++ b/src/test/app/Discrepancy_test.cpp @@ -69,9 +69,14 @@ class Discrepancy_test : public beast::unit_test::suite env.close(); test::PathSet payPaths{ - test::Path{A2["JPY"], A2}, test::Path{XRP, A2["JPY"], A2}, test::Path{A6, XRP, A2["JPY"], A2}}; + test::Path{A2["JPY"], A2}, + test::Path{XRP, A2["JPY"], A2}, + test::Path{A6, XRP, A2["JPY"], A2}}; - env(pay(A1, A1, A2["JPY"](1000)), json(payPaths.json()), txflags(tfPartialPayment), sendmax(A3["CNY"](56))); + env(pay(A1, A1, A2["JPY"](1000)), + json(payPaths.json()), + txflags(tfPartialPayment), + sendmax(A3["CNY"](56))); env.close(); Json::Value jrq2; @@ -88,22 +93,36 @@ class Discrepancy_test : public beast::unit_test::suite { Json::Value node; if (an.isMember(sfCreatedNode.fieldName)) + { node = an[sfCreatedNode.fieldName]; + } else if (an.isMember(sfModifiedNode.fieldName)) + { node = an[sfModifiedNode.fieldName]; + } else if (an.isMember(sfDeletedNode.fieldName)) + { node = an[sfDeletedNode.fieldName]; + } if (node && node[sfLedgerEntryType.fieldName] == jss::AccountRoot) { - Json::Value prevFields = node.isMember(sfPreviousFields.fieldName) ? node[sfPreviousFields.fieldName] - : node[sfNewFields.fieldName]; - Json::Value finalFields = node.isMember(sfFinalFields.fieldName) ? node[sfFinalFields.fieldName] - : node[sfNewFields.fieldName]; + Json::Value prevFields = node.isMember(sfPreviousFields.fieldName) + ? node[sfPreviousFields.fieldName] + : node[sfNewFields.fieldName]; + Json::Value finalFields = node.isMember(sfFinalFields.fieldName) + ? node[sfFinalFields.fieldName] + : node[sfNewFields.fieldName]; if (prevFields) - sumPrev += beast::lexicalCastThrow(prevFields[sfBalance.fieldName].asString()); + { + sumPrev += beast::lexicalCastThrow( + prevFields[sfBalance.fieldName].asString()); + } if (finalFields) - sumFinal += beast::lexicalCastThrow(finalFields[sfBalance.fieldName].asString()); + { + sumFinal += beast::lexicalCastThrow( + finalFields[sfBalance.fieldName].asString()); + } } } // the difference in balances (final and prev) should be the diff --git a/src/test/app/EscrowSmart_test.cpp b/src/test/app/EscrowSmart_test.cpp index 465d934f9b..ee8e30f2f3 100644 --- a/src/test/app/EscrowSmart_test.cpp +++ b/src/test/app/EscrowSmart_test.cpp @@ -1,14 +1,13 @@ #include #include -#include -#include - #include #include #include #include #include +#include +#include #include #include @@ -177,7 +176,7 @@ struct EscrowSmart_test : public beast::unit_test::suite Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->START_UP = Config::FRESH; + cfg->START_UP = StartUpType::FRESH; return cfg; }), features); @@ -228,7 +227,10 @@ struct EscrowSmart_test : public beast::unit_test::suite // Failure situations (i.e. all other combinations) { // only FinishFunction - env(escrowCreate, escrow::finish_function(ledgerSqnWasmHex), fee(txnFees), ter(temBAD_EXPIRATION)); + env(escrowCreate, + escrow::finish_function(ledgerSqnWasmHex), + fee(txnFees), + ter(temBAD_EXPIRATION)); env.close(); } { @@ -320,8 +322,12 @@ struct EscrowSmart_test : public beast::unit_test::suite // featureSmartEscrow disabled Env env(*this, features - featureSmartEscrow); env.fund(XRP(5000), alice, carol); - XRPAmount const txnFees = env.current()->fees().base * 10 + ledgerSqnWasmHex.size() / 2 * 5; - env(escrow::finish(carol, alice, 1), fee(txnFees), escrow::comp_allowance(4), ter(temDISABLED)); + XRPAmount const txnFees = + env.current()->fees().base * 10 + ledgerSqnWasmHex.size() / 2 * 5; + env(escrow::finish(carol, alice, 1), + fee(txnFees), + escrow::comp_allowance(4), + ter(temDISABLED)); env.close(); } @@ -380,9 +386,9 @@ struct EscrowSmart_test : public beast::unit_test::suite sle->setFieldU32(sfFlags, 0); sle->setFieldU64(sfOwnerNode, 0); uint256 tmp; - BEAST_EXPECT( - tmp.parseHex("F63D1A452A96C19EFD77901FB37D236C59EAA746771A6" - "85D1BBA57A2238B9401")); + BEAST_EXPECT(tmp.parseHex( + "F63D1A452A96C19EFD77901FB37D236C59EAA746771A6" + "85D1BBA57A2238B9401")); sle->setFieldH256(sfPreviousTxnID, tmp); sle->setFieldU32(sfPreviousTxnLgrSeq, 4); sle->setFieldU32(sfSequence, seq); @@ -432,7 +438,10 @@ struct EscrowSmart_test : public beast::unit_test::suite // This function takes 4 gas // In testing, 1 gas costs 1 drop auto const finishFee = env.current()->fees().base + 3; - env(escrow::finish(carol, alice, seq), fee(finishFee), escrow::comp_allowance(4), ter(telINSUF_FEE_P)); + env(escrow::finish(carol, alice, seq), + fee(finishFee), + escrow::comp_allowance(4), + ter(telINSUF_FEE_P)); } { @@ -457,8 +466,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const allowance = 100; env(escrow::finish(carol, alice, seq2), - fee(env.current()->fees().base + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + - 1), + fee(env.current()->fees().base + + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1), escrow::comp_allowance(allowance), ter(tefNO_WASM)); } @@ -482,8 +491,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto [createFee, finishFee] = [&]() { Env env(*this, features); auto createFee = env.current()->fees().base * 10 + ledgerSqnWasmHex.size() / 2 * 5; - auto finishFee = - env.current()->fees().base + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1; + auto finishFee = env.current()->fees().base + + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1; return std::make_pair(createFee, finishFee); }(); @@ -543,7 +552,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const txMeta = env.meta(); if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) BEAST_EXPECTS( - txMeta->getFieldU32(sfGasUsed) == allowance, std::to_string(txMeta->getFieldU32(sfGasUsed))); + txMeta->getFieldU32(sfGasUsed) == allowance, + std::to_string(txMeta->getFieldU32(sfGasUsed))); if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode))) BEAST_EXPECTS( txMeta->getFieldI32(sfWasmReturnCode) == 5, @@ -566,7 +576,8 @@ struct EscrowSmart_test : public beast::unit_test::suite escrow::cancel_time(env.now() + 100s), fee(createFee)); env.close(); - auto const conditionFinishFee = finishFee + env.current()->fees().base * (32 + (escrow::fb1.size() / 16)); + auto const conditionFinishFee = + finishFee + env.current()->fees().base * (32 + (escrow::fb1.size() / 16)); if (BEAST_EXPECT(env.ownerCount(alice) == 2)) { @@ -613,7 +624,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const txMeta = env.meta(); if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) BEAST_EXPECTS( - txMeta->getFieldU32(sfGasUsed) == allowance, std::to_string(txMeta->getFieldU32(sfGasUsed))); + txMeta->getFieldU32(sfGasUsed) == allowance, + std::to_string(txMeta->getFieldU32(sfGasUsed))); if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode))) BEAST_EXPECTS( txMeta->getFieldI32(sfWasmReturnCode) == 5, @@ -763,8 +775,8 @@ struct EscrowSmart_test : public beast::unit_test::suite env.require(balance(alice, XRP(4000) - txnFees)); auto const allowance = 1420; - XRPAmount const finishFee = - env.current()->fees().base + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1; + XRPAmount const finishFee = env.current()->fees().base + + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1; // FinishAfter time hasn't passed env(escrow::finish(alice, alice, seq), @@ -775,7 +787,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const txMeta = env.meta(); if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed))) BEAST_EXPECTS( - txMeta->getFieldU32(sfGasUsed) == allowance, std::to_string(txMeta->getFieldU32(sfGasUsed))); + txMeta->getFieldU32(sfGasUsed) == allowance, + std::to_string(txMeta->getFieldU32(sfGasUsed))); if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode))) BEAST_EXPECTS( txMeta->getFieldI32(sfWasmReturnCode) == -256, @@ -840,7 +853,8 @@ struct EscrowSmart_test : public beast::unit_test::suite env.close(); auto const bigAllowance = 996'433; - uint64_t partialFeeCalc = (static_cast(bigAllowance) * 1'000'000) / MICRO_DROPS_PER_DROP + + uint64_t partialFeeCalc = + (static_cast(bigAllowance) * 1'000'000) / MICRO_DROPS_PER_DROP + 1; // to avoid an overflow auto finishFee = env.current()->fees().base + partialFeeCalc; BEAST_EXPECT(finishFee.drops() > bigAllowance); @@ -866,7 +880,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const txMeta = env.meta(); if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) BEAST_EXPECTS( - txMeta->getFieldU32(sfGasUsed) == allowance, std::to_string(txMeta->getFieldU32(sfGasUsed))); + txMeta->getFieldU32(sfGasUsed) == allowance, + std::to_string(txMeta->getFieldU32(sfGasUsed))); if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode))) BEAST_EXPECTS( txMeta->getFieldI32(sfWasmReturnCode) == 5, @@ -895,7 +910,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const seq = env.seq(alice); BEAST_EXPECT(env.ownerCount(alice) == 0); auto escrowCreate = escrow::create(alice, carol, XRP(1000)); - XRPAmount txnFees = env.current()->fees().base * 10 + allHostFunctionsWasmHex.size() / 2 * 5; + XRPAmount txnFees = + env.current()->fees().base * 10 + allHostFunctionsWasmHex.size() / 2 * 5; env(escrowCreate, escrow::finish_function(allHostFunctionsWasmHex), escrow::finish_time(env.now() + 11s), @@ -904,7 +920,8 @@ struct EscrowSmart_test : public beast::unit_test::suite fee(txnFees)); env.close(); - if (BEAST_EXPECT(env.ownerCount(alice) == (1 + allHostFunctionsWasmHex.size() / 2 / 500))) + if (BEAST_EXPECT( + env.ownerCount(alice) == (1 + allHostFunctionsWasmHex.size() / 2 / 500))) { env.require(balance(alice, XRP(4000) - txnFees)); env.require(balance(carol, XRP(5000))); @@ -935,7 +952,8 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const txMeta = env.meta(); if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed))) BEAST_EXPECTS( - txMeta->getFieldU32(sfGasUsed) == 64'292, std::to_string(txMeta->getFieldU32(sfGasUsed))); + txMeta->getFieldU32(sfGasUsed) == 64'292, + std::to_string(txMeta->getFieldU32(sfGasUsed))); if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode))) BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1); @@ -999,7 +1017,8 @@ struct EscrowSmart_test : public beast::unit_test::suite if (BEAST_EXPECTS(env.seq(alice) == 20, std::to_string(env.seq(alice)))) { auto const seq = env.seq(alice); - XRPAmount txnFees = env.current()->fees().base * 10 + allKeyletsWasmHex.size() / 2 * 5; + XRPAmount txnFees = + env.current()->fees().base * 10 + allKeyletsWasmHex.size() / 2 * 5; env(escrow::create(alice, carol, XRP(1000)), escrow::finish_function(allKeyletsWasmHex), escrow::finish_time(env.now() + 2s), @@ -1012,7 +1031,9 @@ struct EscrowSmart_test : public beast::unit_test::suite auto const allowance = 184'444; auto const finishFee = env.current()->fees().base + (allowance * env.current()->fees().gasPrice) / MICRO_DROPS_PER_DROP + 1; - env(escrow::finish(carol, alice, seq), escrow::comp_allowance(allowance), fee(finishFee)); + env(escrow::finish(carol, alice, seq), + escrow::comp_allowance(allowance), + fee(finishFee)); env.close(); auto const txMeta = env.meta(); @@ -1066,7 +1087,8 @@ struct EscrowSmart_test : public beast::unit_test::suite escrow::finish_function(wasmHex), escrow::cancel_time(env.now() + 100s), fee(env.current()->fees().base * 10 + wasmHex.size() / 2 * 5), - ter(expectedStatus == ExpectedStatus::Success ? TER{tesSUCCESS} : TER{temMALFORMED})); + ter(expectedStatus == ExpectedStatus::Success ? TER{tesSUCCESS} + : TER{temMALFORMED})); if (expectedStatus == ExpectedStatus::Crash) fail("Expected crash", loc.file_name(), loc.line()); else @@ -1093,23 +1115,47 @@ struct EscrowSmart_test : public beast::unit_test::suite std::vector const testCases = { // Code blob tests - {TestCase::BlobType::Code, 99'959, std::nullopt, ExpectedStatus::Success}, // just under 100kb - {TestCase::BlobType::Code, 99'961, std::nullopt, ExpectedStatus::Malformed}, // just over 100kb - {TestCase::BlobType::Code, 200'000, 10'000'000, ExpectedStatus::Success}, // ~200kb - {TestCase::BlobType::Code, 490'000, 10'000'000, ExpectedStatus::Success}, // just under 1MB JSON - {TestCase::BlobType::Code, 999'999, 10'000'000, ExpectedStatus::Crash}, // just over 1MB JSON + {TestCase::BlobType::Code, + 99'959, + std::nullopt, + ExpectedStatus::Success}, // just under 100kb + {TestCase::BlobType::Code, + 99'961, + std::nullopt, + ExpectedStatus::Malformed}, // just over 100kb + {TestCase::BlobType::Code, 200'000, 10'000'000, ExpectedStatus::Success}, // ~200kb + {TestCase::BlobType::Code, + 490'000, + 10'000'000, + ExpectedStatus::Success}, // just under 1MB JSON + {TestCase::BlobType::Code, + 999'999, + 10'000'000, + ExpectedStatus::Crash}, // just over 1MB JSON // Data blob tests - {TestCase::BlobType::Data, 99'946, std::nullopt, ExpectedStatus::Success}, // just under 100kb - {TestCase::BlobType::Data, 99'948, std::nullopt, ExpectedStatus::Malformed}, // just over 100kb - {TestCase::BlobType::Data, 200'000, 10'000'000, ExpectedStatus::Success}, // ~200kb - {TestCase::BlobType::Data, 490'000, 10'000'000, ExpectedStatus::Success}, // just under 1MB JSON - {TestCase::BlobType::Data, 999'950, 10'000'000, ExpectedStatus::Crash}, // just over 1MB JSON + {TestCase::BlobType::Data, + 99'946, + std::nullopt, + ExpectedStatus::Success}, // just under 100kb + {TestCase::BlobType::Data, + 99'948, + std::nullopt, + ExpectedStatus::Malformed}, // just over 100kb + {TestCase::BlobType::Data, 200'000, 10'000'000, ExpectedStatus::Success}, // ~200kb + {TestCase::BlobType::Data, + 490'000, + 10'000'000, + ExpectedStatus::Success}, // just under 1MB JSON + {TestCase::BlobType::Data, + 999'950, + 10'000'000, + ExpectedStatus::Crash}, // just over 1MB JSON }; for (auto const& tc : testCases) { - auto const wasm = - tc.type == TestCase::BlobType::Code ? generateCodeBlob(tc.size) : generateDataBlob(tc.size); + auto const wasm = tc.type == TestCase::BlobType::Code ? generateCodeBlob(tc.size) + : generateDataBlob(tc.size); runTest(wasm, tc.sizeLimit, tc.expected); } } diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 1f3b3bab70..280356c494 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -1,7 +1,5 @@ #include -#include - #include #include #include @@ -9,6 +7,7 @@ #include #include #include +#include #include #include @@ -232,7 +231,10 @@ struct EscrowToken_test : public beast::unit_test::suite auto const USD = gw["USD"]; env.fund(XRP(5000), alice, bob, gw); - env(escrow::create(alice, bob, USD(1)), escrow::finish_time(env.now() + 1s), fee(XRP(-1)), ter(temBAD_FEE)); + env(escrow::create(alice, bob, USD(1)), + escrow::finish_time(env.now() + 1s), + fee(XRP(-1)), + ter(temBAD_FEE)); env.close(); } @@ -542,7 +544,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(pay(gw, bob, USD(1))); env.close(); - bool const largeMantissa = features[featureSingleAssetVault] || features[featureLendingProtocol]; + bool const largeMantissa = + features[featureSingleAssetVault] || features[featureLendingProtocol]; // alice cannot create escrow for 1/10 iou - precision loss env(escrow::create(alice, bob, USD(1)), @@ -974,7 +977,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::create(alice, alice, USD(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 500s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const aa = env.le(keylet::escrow(alice.id(), aseq)); BEAST_EXPECT(aa); @@ -993,7 +997,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::create(bob, bob, USD(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const bb = env.le(keylet::escrow(bob.id(), bseq)); BEAST_EXPECT(bb); @@ -1014,7 +1019,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::finish(alice, alice, aseq)); { BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); @@ -1033,7 +1039,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::cancel(bob, bob, bseq)); { BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), bseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); @@ -1061,12 +1068,14 @@ struct EscrowToken_test : public beast::unit_test::suite auto const bseq = env.seq(bob); env(escrow::create(alice, bob, USD(1'000)), escrow::finish_time(env.now() + 1s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); env(escrow::create(bob, carol, USD(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const ab = env.le(keylet::escrow(alice.id(), aseq)); @@ -1160,7 +1169,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::create(alice, gw, USD(1'000)), escrow::finish_time(env.now() + 1s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); env(escrow::create(gw, carol, USD(1'000)), escrow::finish_time(env.now() + 1s), @@ -1248,9 +1258,13 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); if (t.hasTrustline) + { env.trust(USD(100'000), t.src, t.dst); + } else + { env.trust(USD(100'000), t.src); + } env.close(); env(pay(t.gw, t.src, USD(10'000))); @@ -2004,7 +2018,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(pay(gw, bob, USD(1))); env.close(); - bool const largeMantissa = features[featureSingleAssetVault] || features[featureLendingProtocol]; + bool const largeMantissa = + features[featureSingleAssetVault] || features[featureLendingProtocol]; // alice cannot create escrow for 1/10 iou - precision loss env(escrow::create(alice, bob, USD(1)), @@ -2050,7 +2065,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.fund(XRP(5000), bob); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2104,11 +2120,16 @@ struct EscrowToken_test : public beast::unit_test::suite Json::Value jv = escrow::create(alice, bob, XRP(1)); jv.removeMember(jss::Amount); - jv[jss::Amount][jss::mpt_issuance_id] = "00000004A407AF5856CCF3C42619DAA925813FC955C72983"; + jv[jss::Amount][jss::mpt_issuance_id] = + "00000004A407AF5856CCF3C42619DAA925813FC955C72983"; jv[jss::Amount][jss::value] = "-1"; auto const result = withMPT ? ter(temBAD_AMOUNT) : ter(temDISABLED); - env(jv, escrow::condition(escrow::cb1), escrow::finish_time(env.now() + 1s), fee(baseFee * 150), result); + env(jv, + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + result); env.close(); } @@ -2121,7 +2142,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2153,7 +2175,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2179,7 +2202,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const mpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); Json::Value jv = escrow::create(alice, bob, mpt(2)); - jv[jss::Amount][jss::mpt_issuance_id] = "00000004A407AF5856CCF3C42619DAA925813FC955C72983"; + jv[jss::Amount][jss::mpt_issuance_id] = + "00000004A407AF5856CCF3C42619DAA925813FC955C72983"; env(jv, escrow::condition(escrow::cb1), escrow::finish_time(env.now() + 1s), @@ -2222,7 +2246,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); auto const MPT = mptGw["MPT"]; env(escrow::create(alice, bob, MPT(4)), @@ -2243,7 +2268,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = gw, .holder = alice}); auto const MPT = mptGw["MPT"]; @@ -2271,7 +2298,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = gw, .holder = alice}); mptGw.authorize({.account = bob}); @@ -2302,7 +2331,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2331,7 +2362,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2384,7 +2417,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2408,7 +2442,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2442,7 +2477,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = gw, .holder = alice}); mptGw.authorize({.account = bob}); @@ -2511,7 +2548,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2560,7 +2599,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2593,7 +2633,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2627,7 +2668,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2667,7 +2709,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = gw, .holder = alice}); mptGw.authorize({.account = bob}); @@ -2735,7 +2779,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.fund(XRP(5000), bob); MPTTester mptGw(env, gw, {.holders = {alice, carol}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = carol}); auto const MPT = mptGw["MPT"]; @@ -2909,7 +2954,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -2943,7 +2989,8 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice, MPT) == preAliceMPT - MPT(1)); BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 0); - BEAST_EXPECT(!env.le(keylet::mptoken(MPT.mpt(), alice))->isFieldPresent(sfLockedAmount)); + BEAST_EXPECT( + !env.le(keylet::mptoken(MPT.mpt(), alice))->isFieldPresent(sfLockedAmount)); BEAST_EXPECT(env.balance(bob, MPT) == preBobMPT + MPT(1)); BEAST_EXPECT(mptEscrowed(env, bob, MPT) == 0); BEAST_EXPECT(env.balance(gw, MPT) == outstandingMPT); @@ -2960,7 +3007,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3032,7 +3080,8 @@ struct EscrowToken_test : public beast::unit_test::suite Env env{*this, features}; MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3045,7 +3094,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::create(alice, alice, MPT(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 500s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const aa = env.le(keylet::escrow(alice.id(), aseq)); BEAST_EXPECT(aa); @@ -3064,7 +3114,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::create(bob, bob, MPT(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const bb = env.le(keylet::escrow(bob.id(), bseq)); BEAST_EXPECT(bb); @@ -3079,7 +3130,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::finish(alice, alice, aseq)); { BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1); @@ -3094,7 +3146,8 @@ struct EscrowToken_test : public beast::unit_test::suite env(escrow::cancel(bob, bob, bseq)); { BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), bseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir bod(*env.current(), keylet::ownerDir(bob.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1); @@ -3107,7 +3160,8 @@ struct EscrowToken_test : public beast::unit_test::suite Env env{*this, features}; MPTTester mptGw(env, gw, {.holders = {alice, bob, carol}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); mptGw.authorize({.account = carol}); @@ -3120,12 +3174,14 @@ struct EscrowToken_test : public beast::unit_test::suite auto const bseq = env.seq(bob); env(escrow::create(alice, bob, MPT(1'000)), escrow::finish_time(env.now() + 1s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); env(escrow::create(bob, carol, MPT(1'000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const ab = env.le(keylet::escrow(alice.id(), aseq)); @@ -3204,7 +3260,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -3266,7 +3323,10 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.transferFee = 25000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + {.transferFee = 25000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3318,7 +3378,10 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.transferFee = 25000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + {.transferFee = 25000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3361,7 +3424,10 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.transferFee = 25000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + {.transferFee = 25000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3414,7 +3480,9 @@ struct EscrowToken_test : public beast::unit_test::suite MPTTester mptGw(env, gw, {.holders = {alice, bob}}); mptGw.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = gw, .holder = alice}); mptGw.authorize({.account = bob}); @@ -3454,7 +3522,10 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); + mptGw.create( + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3570,7 +3641,8 @@ struct EscrowToken_test : public beast::unit_test::suite auto const gw = Account("gw"); MPTTester mptGw(env, gw, {.holders = {alice, bob}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); mptGw.authorize({.account = bob}); auto const MPT = mptGw["MPT"]; @@ -3618,7 +3690,8 @@ struct EscrowToken_test : public beast::unit_test::suite env.close(); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -3637,7 +3710,8 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice, MPT) == MPT(0)); BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 10); - mptGw.authorize({.account = alice, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS}); + mptGw.authorize( + {.account = alice, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS}); env(escrow::finish(bob, alice, seq1), escrow::condition(escrow::cb1), @@ -3700,7 +3774,8 @@ public: { using namespace test::jtx; FeatureBitset const all{testable_amendments()}; - for (FeatureBitset const& feats : {all - featureSingleAssetVault - featureLendingProtocol, all}) + for (FeatureBitset const& feats : + {all - featureSingleAssetVault - featureLendingProtocol, all}) { testIOUWithFeats(feats); testMPTWithFeats(feats); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 2f1dc47c31..40d7fb297b 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1,12 +1,11 @@ #include -#include - #include #include #include #include #include +#include #include #include @@ -91,7 +90,9 @@ struct Escrow_test : public beast::unit_test::suite auto const ts = env.now() + 117s; auto const seq = env.seq("alice"); - env(escrow::create("alice", "bob", XRP(1000)), escrow::condition(escrow::cb1), escrow::cancel_time(ts)); + env(escrow::create("alice", "bob", XRP(1000)), + escrow::condition(escrow::cb1), + escrow::cancel_time(ts)); // Advance the ledger, verifying that the cancel won't complete // prematurely. @@ -121,7 +122,9 @@ struct Escrow_test : public beast::unit_test::suite auto const cts = env.now() + 192s; auto const seq = env.seq("alice"); - env(escrow::create("alice", "bob", XRP(1000)), escrow::finish_time(fts), escrow::cancel_time(cts)); + env(escrow::create("alice", "bob", XRP(1000)), + escrow::finish_time(fts), + escrow::cancel_time(cts)); // Advance the ledger, verifying that the finish and cancel won't // complete prematurely. @@ -150,7 +153,9 @@ struct Escrow_test : public beast::unit_test::suite auto const cts = env.now() + 184s; auto const seq = env.seq("alice"); - env(escrow::create("alice", "bob", XRP(1000)), escrow::finish_time(fts), escrow::cancel_time(cts)); + env(escrow::create("alice", "bob", XRP(1000)), + escrow::finish_time(fts), + escrow::cancel_time(cts)); // Advance the ledger, verifying that the finish and cancel won't // complete prematurely. @@ -192,12 +197,17 @@ struct Escrow_test : public beast::unit_test::suite // Check to make sure that we correctly detect if tags are really // required: env(fset(bob, asfRequireDest)); - env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(env.now() + 1s), ter(tecDST_TAG_NEEDED)); + env(escrow::create(alice, bob, XRP(1000)), + escrow::finish_time(env.now() + 1s), + ter(tecDST_TAG_NEEDED)); // set source and dest tags auto const seq = env.seq(alice); - env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(env.now() + 1s), stag(1), dtag(2)); + env(escrow::create(alice, bob, XRP(1000)), + escrow::finish_time(env.now() + 1s), + stag(1), + dtag(2)); auto const sle = env.le(keylet::escrow(alice.id(), seq)); BEAST_EXPECT(sle); @@ -298,7 +308,9 @@ struct Escrow_test : public beast::unit_test::suite ter(temINVALID_FLAG)); // Finish time is in the past - env(escrow::create("alice", "bob", XRP(1000)), escrow::finish_time(env.now() - 5s), ter(tecNO_PERMISSION)); + env(escrow::create("alice", "bob", XRP(1000)), + escrow::finish_time(env.now() - 5s), + ter(tecNO_PERMISSION)); // Cancel time is in the past env(escrow::create("alice", "bob", XRP(1000)), @@ -307,7 +319,9 @@ struct Escrow_test : public beast::unit_test::suite ter(tecNO_PERMISSION)); // no destination account - env(escrow::create("alice", "carol", XRP(1000)), escrow::finish_time(env.now() + 1s), ter(tecNO_DST)); + env(escrow::create("alice", "carol", XRP(1000)), + escrow::finish_time(env.now() + 1s), + ter(tecNO_DST)); env.fund(XRP(5000), "carol"); @@ -323,17 +337,25 @@ struct Escrow_test : public beast::unit_test::suite } // Sending zero or no XRP: - env(escrow::create("alice", "carol", XRP(0)), escrow::finish_time(env.now() + 1s), ter(temBAD_AMOUNT)); - env(escrow::create("alice", "carol", XRP(-1000)), escrow::finish_time(env.now() + 1s), ter(temBAD_AMOUNT)); + env(escrow::create("alice", "carol", XRP(0)), + escrow::finish_time(env.now() + 1s), + ter(temBAD_AMOUNT)); + env(escrow::create("alice", "carol", XRP(-1000)), + escrow::finish_time(env.now() + 1s), + ter(temBAD_AMOUNT)); // Fail if neither CancelAfter nor FinishAfter are specified: env(escrow::create("alice", "carol", XRP(1)), ter(temBAD_EXPIRATION)); // Fail if neither a FinishTime nor a condition are attached: - env(escrow::create("alice", "carol", XRP(1)), escrow::cancel_time(env.now() + 1s), ter(temMALFORMED)); + env(escrow::create("alice", "carol", XRP(1)), + escrow::cancel_time(env.now() + 1s), + ter(temMALFORMED)); // Fail if FinishAfter has already passed: - env(escrow::create("alice", "carol", XRP(1)), escrow::finish_time(env.now() - 1s), ter(tecNO_PERMISSION)); + env(escrow::create("alice", "carol", XRP(1)), + escrow::finish_time(env.now() - 1s), + ter(tecNO_PERMISSION)); // If both CancelAfter and FinishAfter are set, then CancelAfter must // be strictly later than FinishAfter. @@ -369,10 +391,14 @@ struct Escrow_test : public beast::unit_test::suite auto const accountIncrement = drops(env.current()->fees().increment); env.fund(accountReserve + accountIncrement + XRP(50), "daniel"); - env(escrow::create("daniel", "bob", XRP(51)), escrow::finish_time(env.now() + 1s), ter(tecUNFUNDED)); + env(escrow::create("daniel", "bob", XRP(51)), + escrow::finish_time(env.now() + 1s), + ter(tecUNFUNDED)); env.fund(accountReserve + accountIncrement + XRP(50), "evan"); - env(escrow::create("evan", "bob", XRP(50)), escrow::finish_time(env.now() + 1s), ter(tecUNFUNDED)); + env(escrow::create("evan", "bob", XRP(50)), + escrow::finish_time(env.now() + 1s), + ter(tecUNFUNDED)); env.fund(accountReserve, "frank"); env(escrow::create("frank", "bob", XRP(1)), @@ -383,7 +409,9 @@ struct Escrow_test : public beast::unit_test::suite { // Specify incorrect sequence number env.fund(XRP(5000), "hannah"); auto const seq = env.seq("hannah"); - env(escrow::create("hannah", "hannah", XRP(10)), escrow::finish_time(env.now() + 1s), fee(150 * baseFee)); + env(escrow::create("hannah", "hannah", XRP(10)), + escrow::finish_time(env.now() + 1s), + fee(150 * baseFee)); env.close(); env(escrow::finish("hannah", "hannah", seq + 7), fee(150 * baseFee), ter(tecNO_TARGET)); } @@ -1009,8 +1037,12 @@ struct Escrow_test : public beast::unit_test::suite // Assemble finish that is missing the Condition or the Fulfillment // since either both must be present, or neither can: - env(escrow::finish("bob", "alice", seq), escrow::condition(escrow::cb3), ter(temMALFORMED)); - env(escrow::finish("bob", "alice", seq), escrow::fulfillment(escrow::fb3), ter(temMALFORMED)); + env(escrow::finish("bob", "alice", seq), + escrow::condition(escrow::cb3), + ter(temMALFORMED)); + env(escrow::finish("bob", "alice", seq), + escrow::fulfillment(escrow::fb3), + ter(temMALFORMED)); // Now finish it. env(escrow::finish("bob", "alice", seq), @@ -1025,10 +1057,11 @@ struct Escrow_test : public beast::unit_test::suite Env env(*this, features); env.fund(XRP(5000), "alice", "bob"); - std::array cb = {{0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52, 0x92, 0x67, - 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29, 0xB2, 0x38, 0x2C, 0xED, - 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F, 0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07, - 0x81, 0x03, 0x06, 0x34, 0xD2, 0x82, 0x02, 0x03, 0xC8}}; + std::array cb = { + {0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52, 0x92, 0x67, + 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29, 0xB2, 0x38, 0x2C, 0xED, + 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F, 0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07, + 0x81, 0x03, 0x06, 0x34, 0xD2, 0x82, 0x02, 0x03, 0xC8}}; // FIXME: this transaction should, eventually, return temDISABLED // instead of temMALFORMED. @@ -1060,7 +1093,8 @@ struct Escrow_test : public beast::unit_test::suite env(escrow::create(alice, alice, XRP(1000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 500s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const aa = env.le(keylet::escrow(alice.id(), aseq)); BEAST_EXPECT(aa); @@ -1074,7 +1108,8 @@ struct Escrow_test : public beast::unit_test::suite env(escrow::create(bruce, bruce, XRP(1000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const bb = env.le(keylet::escrow(bruce.id(), bseq)); BEAST_EXPECT(bb); @@ -1089,7 +1124,8 @@ struct Escrow_test : public beast::unit_test::suite env(escrow::finish(alice, alice, aseq)); { BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir aod(*env.current(), keylet::ownerDir(alice.id())); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0); @@ -1104,7 +1140,8 @@ struct Escrow_test : public beast::unit_test::suite env(escrow::cancel(bruce, bruce, bseq)); { BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq))); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); xrpl::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0); @@ -1120,12 +1157,14 @@ struct Escrow_test : public beast::unit_test::suite auto const bseq = env.seq(bruce); env(escrow::create(alice, bruce, XRP(1000)), escrow::finish_time(env.now() + 1s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); env(escrow::create(bruce, carol, XRP(1000)), escrow::finish_time(env.now() + 1s), escrow::cancel_time(env.now() + 2s)); - BEAST_EXPECT((*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); + BEAST_EXPECT( + (*env.meta())[sfTransactionResult] == static_cast(tesSUCCESS)); env.close(5s); auto const ab = env.le(keylet::escrow(alice.id(), aseq)); @@ -1205,9 +1244,13 @@ struct Escrow_test : public beast::unit_test::suite { auto const jtx = env.jt( - escrow::create("alice", "carol", XRP(1000)), escrow::finish_time(env.now() + 1s), seq(1), fee(baseFee)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + escrow::create("alice", "carol", XRP(1000)), + escrow::finish_time(env.now() + 1s), + seq(1), + fee(baseFee)); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(1000)); @@ -1215,8 +1258,9 @@ struct Escrow_test : public beast::unit_test::suite { auto const jtx = env.jt(escrow::cancel("bob", "alice", 3), seq(1), fee(baseFee)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -1224,8 +1268,9 @@ struct Escrow_test : public beast::unit_test::suite { auto const jtx = env.jt(escrow::finish("bob", "alice", 3), seq(1), fee(baseFee)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -1272,7 +1317,9 @@ struct Escrow_test : public beast::unit_test::suite auto const ts = env.now() + 97s; std::uint32_t const escrowSeq = aliceTicket; - env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(ts), ticket::use(aliceTicket)); + env(escrow::create(alice, bob, XRP(1000)), + escrow::finish_time(ts), + ticket::use(aliceTicket)); BEAST_EXPECT(env.seq(alice) == aliceRootSeq); env.require(tickets(alice, 0)); env.require(tickets(bob, bobTicketCount)); @@ -1289,10 +1336,15 @@ struct Escrow_test : public beast::unit_test::suite } // bob tries to re-use a ticket, which is rejected. - env(escrow::finish(bob, alice, escrowSeq), fee(150 * baseFee), ticket::use(bobTicket), ter(tefNO_TICKET)); + env(escrow::finish(bob, alice, escrowSeq), + fee(150 * baseFee), + ticket::use(bobTicket), + ter(tefNO_TICKET)); // bob uses one of his remaining tickets. Success! - env(escrow::finish(bob, alice, escrowSeq), fee(150 * baseFee), ticket::use(--bobTicket)); + env(escrow::finish(bob, alice, escrowSeq), + fee(150 * baseFee), + ticket::use(--bobTicket)); env.close(); BEAST_EXPECT(env.seq(bob) == bobRootSeq); } @@ -1354,7 +1406,9 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT(env.seq(bob) == bobRootSeq); // Verify that the cancel succeeds. - env(escrow::cancel(bob, alice, escrowSeq), fee(150 * baseFee), ticket::use(bobTicket++)); + env(escrow::cancel(bob, alice, escrowSeq), + fee(150 * baseFee), + ticket::use(bobTicket++)); env.close(); BEAST_EXPECT(env.seq(bob) == bobRootSeq); @@ -1420,7 +1474,9 @@ struct Escrow_test : public beast::unit_test::suite env.close(); // Fail, credentials not accepted - env(escrow::finish(carol, alice, seq), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS)); + env(escrow::finish(carol, alice, seq), + credentials::ids({credIdx}), + ter(tecBAD_CREDENTIALS)); env.close(); @@ -1428,10 +1484,14 @@ struct Escrow_test : public beast::unit_test::suite env.close(); // Fail, credentials doesn’t belong to root account - env(escrow::finish(dillon, alice, seq), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS)); + env(escrow::finish(dillon, alice, seq), + credentials::ids({credIdx}), + ter(tecBAD_CREDENTIALS)); // Fail, no depositPreauth - env(escrow::finish(carol, alice, seq), credentials::ids({credIdx}), ter(tecNO_PERMISSION)); + env(escrow::finish(carol, alice, seq), + credentials::ids({credIdx}), + ter(tecNO_PERMISSION)); env(deposit::authCredentials(bob, {{zelda, credType}})); env.close(); @@ -1480,7 +1540,8 @@ struct Escrow_test : public beast::unit_test::suite env(credentials::accept(bob, zelda, credType2)); env.close(); auto const credIdxBob = - credentials::ledgerEntry(env, bob, zelda, credType2)[jss::result][jss::index].asString(); + credentials::ledgerEntry(env, bob, zelda, credType2)[jss::result][jss::index] + .asString(); auto const seq = env.seq(alice); env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(env.now() + 1s)); diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 5103671ce0..f65a00530b 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -2,15 +2,15 @@ #include #include -#include #include -#include +#include #include #include #include #include #include +#include #include @@ -32,7 +32,11 @@ struct FeeSettingsFields }; STTx -createFeeTx(Rules const& rules, std::uint32_t seq, FeeSettingsFields const& fields, bool forceAllFields = false) +createFeeTx( + Rules const& rules, + std::uint32_t seq, + FeeSettingsFields const& fields, + bool forceAllFields = false) { auto fill = [&](auto& obj) { obj.setAccountID(sfAccount, AccountID()); @@ -41,23 +45,32 @@ createFeeTx(Rules const& rules, std::uint32_t seq, FeeSettingsFields const& fiel if (rules.enabled(featureXRPFees)) { // New XRPFees format - all three fields are REQUIRED - obj.setFieldAmount(sfBaseFeeDrops, fields.baseFeeDrops ? *fields.baseFeeDrops : XRPAmount{0}); - obj.setFieldAmount(sfReserveBaseDrops, fields.reserveBaseDrops ? *fields.reserveBaseDrops : XRPAmount{0}); obj.setFieldAmount( - sfReserveIncrementDrops, fields.reserveIncrementDrops ? *fields.reserveIncrementDrops : XRPAmount{0}); + sfBaseFeeDrops, fields.baseFeeDrops ? *fields.baseFeeDrops : XRPAmount{0}); + obj.setFieldAmount( + sfReserveBaseDrops, + fields.reserveBaseDrops ? *fields.reserveBaseDrops : XRPAmount{0}); + obj.setFieldAmount( + sfReserveIncrementDrops, + fields.reserveIncrementDrops ? *fields.reserveIncrementDrops : XRPAmount{0}); } else { // Legacy format - all four fields are REQUIRED obj.setFieldU64(sfBaseFee, fields.baseFee ? *fields.baseFee : 0); obj.setFieldU32(sfReserveBase, fields.reserveBase ? *fields.reserveBase : 0); - obj.setFieldU32(sfReserveIncrement, fields.reserveIncrement ? *fields.reserveIncrement : 0); - obj.setFieldU32(sfReferenceFeeUnits, fields.referenceFeeUnits ? *fields.referenceFeeUnits : 0); + obj.setFieldU32( + sfReserveIncrement, fields.reserveIncrement ? *fields.reserveIncrement : 0); + obj.setFieldU32( + sfReferenceFeeUnits, fields.referenceFeeUnits ? *fields.referenceFeeUnits : 0); } if (rules.enabled(featureSmartEscrow) || forceAllFields) { - obj.setFieldU32(sfExtensionComputeLimit, fields.extensionComputeLimit ? *fields.extensionComputeLimit : 0); - obj.setFieldU32(sfExtensionSizeLimit, fields.extensionSizeLimit ? *fields.extensionSizeLimit : 0); + obj.setFieldU32( + sfExtensionComputeLimit, + fields.extensionComputeLimit ? *fields.extensionComputeLimit : 0); + obj.setFieldU32( + sfExtensionSizeLimit, fields.extensionSizeLimit ? *fields.extensionSizeLimit : 0); obj.setFieldU32(sfGasPrice, fields.gasPrice ? *fields.gasPrice : 0); } }; @@ -129,7 +142,10 @@ applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx) } bool -verifyFeeObject(std::shared_ptr const& ledger, Rules const& rules, FeeSettingsFields const& expected) +verifyFeeObject( + std::shared_ptr const& ledger, + Rules const& rules, + FeeSettingsFields const& expected) { auto const feeObject = ledger->read(keylet::fees()); if (!feeObject) @@ -144,19 +160,22 @@ verifyFeeObject(std::shared_ptr const& ledger, Rules const& rules, if (rules.enabled(featureXRPFees)) { if (feeObject->isFieldPresent(sfBaseFee) || feeObject->isFieldPresent(sfReserveBase) || - feeObject->isFieldPresent(sfReserveIncrement) || feeObject->isFieldPresent(sfReferenceFeeUnits)) + feeObject->isFieldPresent(sfReserveIncrement) || + feeObject->isFieldPresent(sfReferenceFeeUnits)) return false; if (!checkEquality(sfBaseFeeDrops, expected.baseFeeDrops.value_or(XRPAmount{0}))) return false; if (!checkEquality(sfReserveBaseDrops, expected.reserveBaseDrops.value_or(XRPAmount{0}))) return false; - if (!checkEquality(sfReserveIncrementDrops, expected.reserveIncrementDrops.value_or(XRPAmount{0}))) + if (!checkEquality( + sfReserveIncrementDrops, expected.reserveIncrementDrops.value_or(XRPAmount{0}))) return false; } else { - if (feeObject->isFieldPresent(sfBaseFeeDrops) || feeObject->isFieldPresent(sfReserveBaseDrops) || + if (feeObject->isFieldPresent(sfBaseFeeDrops) || + feeObject->isFieldPresent(sfReserveBaseDrops) || feeObject->isFieldPresent(sfReserveIncrementDrops)) return false; @@ -181,7 +200,8 @@ verifyFeeObject(std::shared_ptr const& ledger, Rules const& rules, } else { - if (feeObject->isFieldPresent(sfExtensionComputeLimit) || feeObject->isFieldPresent(sfExtensionSizeLimit) || + if (feeObject->isFieldPresent(sfExtensionComputeLimit) || + feeObject->isFieldPresent(sfExtensionSizeLimit) || feeObject->isFieldPresent(sfGasPrice)) return false; } @@ -264,6 +284,9 @@ class FeeVote_test : public beast::unit_test::suite "extension_compute_limit = -100", "extension_size_limit = -200", "gas_price = -300"}); + // Negative values wrap to large positive uint32_t values. + // reference_fee is uint64_t and has bounds checking, so it keeps + // the default. // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); @@ -274,8 +297,8 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(setup.gas_price == static_cast(-300)); } { - auto const big64 = - std::to_string(static_cast(std::numeric_limits::max()) + 1); + auto const big64 = std::to_string( + static_cast(std::numeric_limits::max()) + 1); Section config; config.append( {"reference_fee = " + big64, @@ -304,7 +327,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees - featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -312,7 +338,10 @@ class FeeVote_test : public beast::unit_test::suite // Test successful fee transaction with legacy fields FeeSettingsFields fields{ - .baseFee = 10, .reserveBase = 200000, .reserveIncrement = 50000, .referenceFeeUnits = 10}; + .baseFee = 10, + .reserveBase = 200000, + .reserveIncrement = 50000, + .referenceFeeUnits = 10}; auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields); OpenView accum(ledger.get()); @@ -327,7 +356,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -351,7 +383,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments()); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -379,7 +414,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -407,7 +445,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() - featureXRPFees - featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -425,7 +466,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() - featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -443,7 +487,10 @@ class FeeVote_test : public beast::unit_test::suite { jtx::Env env(*this, jtx::testable_amendments() | featureXRPFees | featureSmartEscrow); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // Create the next ledger to apply transaction to ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); @@ -593,7 +640,8 @@ class FeeVote_test : public beast::unit_test::suite ledger = std::make_shared(*ledger, env.app().timeKeeper().closeTime()); // Apply partial update (only some fields) - FeeSettingsFields fields2{.baseFeeDrops = XRPAmount{20}, .reserveBaseDrops = XRPAmount{200000}}; + FeeSettingsFields fields2{ + .baseFeeDrops = XRPAmount{20}, .reserveBaseDrops = XRPAmount{200000}}; auto feeTx2 = createFeeTx(ledger->rules(), ledger->seq(), fields2); { @@ -650,7 +698,10 @@ class FeeVote_test : public beast::unit_test::suite auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); auto sec = randomSecretKey(); auto pub = derivePublicKey(KeyType::secp256k1, sec); @@ -676,7 +727,10 @@ class FeeVote_test : public beast::unit_test::suite auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); auto sec = randomSecretKey(); auto pub = derivePublicKey(KeyType::secp256k1, sec); @@ -775,7 +829,8 @@ class FeeVote_test : public beast::unit_test::suite // Check the values BEAST_EXPECT(feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.reference_fee}); BEAST_EXPECT(feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.account_reserve}); - BEAST_EXPECT(feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.owner_reserve}); + BEAST_EXPECT( + feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.owner_reserve}); } void @@ -795,10 +850,14 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(env.current()->fees().extensionSizeLimit == 0); BEAST_EXPECT(env.current()->fees().gasPrice == 0); - auto const createFeeTxFromVoting = [&](FeeSetup const& setup) -> std::pair> { + auto const createFeeTxFromVoting = + [&](FeeSetup const& setup) -> std::pair> { auto feeVote = make_FeeVote(setup, env.app().journal("FeeVote")); auto ledger = std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); // doVoting requires a flag ledger (every 256th ledger) // We need to create a ledger at sequence 256 to make it a flag @@ -833,7 +892,8 @@ class FeeVote_test : public beast::unit_test::suite validations.push_back(val); } - auto txSet = std::make_shared(SHAMapType::TRANSACTION, env.app().getNodeFamily()); + auto txSet = + std::make_shared(SHAMapType::TRANSACTION, env.app().getNodeFamily()); // This should not throw since we have a flag ledger feeVote->doVoting(ledger, validations, txSet); @@ -864,11 +924,17 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECTS(!feeTx.isFieldPresent(sfReferenceFeeUnits), line); // Check the values - BEAST_EXPECTS(feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.reference_fee}, line); - BEAST_EXPECTS(feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.account_reserve}, line); - BEAST_EXPECTS(feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.owner_reserve}, line); - BEAST_EXPECTS(feeTx.getFieldU32(sfExtensionComputeLimit) == setup.extension_compute_limit, line); - BEAST_EXPECTS(feeTx.getFieldU32(sfExtensionSizeLimit) == setup.extension_size_limit, line); + BEAST_EXPECTS( + feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.reference_fee}, line); + BEAST_EXPECTS( + feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.account_reserve}, line); + BEAST_EXPECTS( + feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.owner_reserve}, + line); + BEAST_EXPECTS( + feeTx.getFieldU32(sfExtensionComputeLimit) == setup.extension_compute_limit, line); + BEAST_EXPECTS( + feeTx.getFieldU32(sfExtensionSizeLimit) == setup.extension_size_limit, line); BEAST_EXPECTS(feeTx.getFieldU32(sfGasPrice) == setup.gas_price, line); }; diff --git a/src/test/app/FixNFTokenPageLinks_test.cpp b/src/test/app/FixNFTokenPageLinks_test.cpp index 88f368e9cf..8022c52a5d 100644 --- a/src/test/app/FixNFTokenPageLinks_test.cpp +++ b/src/test/app/FixNFTokenPageLinks_test.cpp @@ -1,10 +1,9 @@ #include -#include -#include - #include #include +#include +#include namespace xrpl { @@ -40,7 +39,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // creation of NFT pages that are completely full. This lambda // tells us the taxon value we should pass in in order for the // internal representation to match the passed in value. - auto internalTaxon = [this, &env](Account const& acct, std::uint32_t taxon) -> std::uint32_t { + auto internalTaxon = [this, &env]( + Account const& acct, std::uint32_t taxon) -> std::uint32_t { std::uint32_t tokenSeq = [this, &env, &acct]() { auto const le = env.le(acct); if (BEAST_EXPECT(le)) @@ -86,7 +86,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite for (Json::UInt i = 0; i < acctObjs.size(); ++i) { if (BEAST_EXPECT( - acctObjs[i].isMember(sfNFTokens.jsonName) && acctObjs[i][sfNFTokens.jsonName].isArray())) + acctObjs[i].isMember(sfNFTokens.jsonName) && + acctObjs[i][sfNFTokens.jsonName].isArray())) { BEAST_EXPECT(acctObjs[i][sfNFTokens.jsonName].size() == 32); ++pageCount; @@ -138,7 +139,10 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // Invalid flags. auto const linkFixFee = drops(env.current()->fees().increment); - env(ledgerStateFix::nftPageLinks(alice, alice), fee(linkFixFee), txflags(tfPassive), ter(temINVALID_FLAG)); + env(ledgerStateFix::nftPageLinks(alice, alice), + fee(linkFixFee), + txflags(tfPassive), + ter(temINVALID_FLAG)); { // ledgerStateFix::nftPageLinks requires an Owner field. @@ -303,7 +307,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // bob's "middle" page is still present, but has lost the // NextPageMin field. { - auto bobMiddleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(bob), bobMiddleNFTokenPageIndex)); + auto bobMiddleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(bob), bobMiddleNFTokenPageIndex)); if (!BEAST_EXPECT(bobMiddleNFTokenPage)) return; @@ -352,7 +357,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // carol's "middle" page is still present, but has lost the // NextPageMin field. - auto carolMiddleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(carol), carolMiddleNFTokenPageIndex)); + auto carolMiddleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(carol), carolMiddleNFTokenPageIndex)); if (!BEAST_EXPECT(carolMiddleNFTokenPage)) return; @@ -453,7 +459,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite } // alice's middle page should be gone. - BEAST_EXPECT(!env.le(keylet::nftpage(keylet::nftpage_min(alice), aliceMiddleNFTokenPageIndex))); + BEAST_EXPECT( + !env.le(keylet::nftpage(keylet::nftpage_min(alice), aliceMiddleNFTokenPageIndex))); BEAST_EXPECT(nftCount(env, alice) == 32); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -469,7 +476,8 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite // bob's "middle" page is still present and missing NextPageMin. { - auto bobMiddleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(bob), bobMiddleNFTokenPageIndex)); + auto bobMiddleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(bob), bobMiddleNFTokenPageIndex)); if (!BEAST_EXPECT(bobMiddleNFTokenPage)) return; @@ -493,8 +501,9 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite BEAST_EXPECT(bobLastNFTokenPage->at(sfPreviousPageMin) != bobMiddleNFTokenPageIndex); BEAST_EXPECT(!bobLastNFTokenPage->isFieldPresent(sfNextPageMin)); - auto const bobNewFirstNFTokenPage = - env.le(keylet::nftpage(keylet::nftpage_min(bob), bobLastNFTokenPage->at(sfPreviousPageMin))); + auto const bobNewFirstNFTokenPage = env.le( + keylet::nftpage( + keylet::nftpage_min(bob), bobLastNFTokenPage->at(sfPreviousPageMin))); if (!BEAST_EXPECT(bobNewFirstNFTokenPage)) return; @@ -562,8 +571,9 @@ class FixNFTokenPageLinks_test : public beast::unit_test::suite BEAST_EXPECT(!carolLastNFTokenPage->isFieldPresent(sfNextPageMin)); // carol also has a "first" page that includes a NextPageMin field. - auto carolFirstNFTokenPage = - env.le(keylet::nftpage(keylet::nftpage_min(carol), carolMiddleNFTokenPage->at(sfPreviousPageMin))); + auto carolFirstNFTokenPage = env.le( + keylet::nftpage( + keylet::nftpage_min(carol), carolMiddleNFTokenPage->at(sfPreviousPageMin))); if (!BEAST_EXPECT(carolFirstNFTokenPage)) return; diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index 551bd117d8..1e9514f756 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -1,20 +1,25 @@ #include #include -#include -#include #include #include #include #include +#include #include +#include +#include namespace xrpl { namespace test { bool -getNoRippleFlag(jtx::Env const& env, jtx::Account const& src, jtx::Account const& dst, Currency const& cur) +getNoRippleFlag( + jtx::Env const& env, + jtx::Account const& src, + jtx::Account const& dst, + Currency const& cur) { if (auto sle = env.le(keylet::line(src, dst, cur))) { @@ -104,7 +109,10 @@ struct Flow_test : public beast::unit_test::suite // alice will redeem to bob; a transfer fee will be charged env(pay(bob, alice, USDB(6))); - env(pay(alice, dan, USDC(5)), path(bob, carol), sendmax(USDA(6)), txflags(tfNoRippleDirect)); + env(pay(alice, dan, USDC(5)), + path(bob, carol), + sendmax(USDA(6)), + txflags(tfNoRippleDirect)); env.require(balance(dan, USDC(5))); env.require(balance(alice, USDB(0.5))); } @@ -120,7 +128,10 @@ struct Flow_test : public beast::unit_test::suite env.trust(USDC(10), dan); env(rate(bob, 1.1)); - env(pay(alice, dan, USDC(5)), path(bob, carol), sendmax(USDA(6)), txflags(tfNoRippleDirect)); + env(pay(alice, dan, USDC(5)), + path(bob, carol), + sendmax(USDA(6)), + txflags(tfNoRippleDirect)); env.require(balance(dan, USDC(5))); env.require(balance(bob, USDA(5))); } @@ -140,7 +151,10 @@ struct Flow_test : public beast::unit_test::suite // Pay alice so she redeems to carol and a transfer fee is charged env(pay(carol, alice, USDC(10))); - env(pay(alice, erin, USDD(5)), path(carol, dan), path(bob, dan), txflags(tfNoRippleDirect)); + env(pay(alice, erin, USDD(5)), + path(carol, dan), + path(bob, dan), + txflags(tfNoRippleDirect)); env.require(balance(erin, USDD(5))); env.require(balance(dan, USDB(5))); @@ -183,6 +197,7 @@ struct Flow_test : public beast::unit_test::suite // Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut for (auto bobDanQIn : {80, 100, 120}) + { for (auto bobAliceQOut : {80, 100, 120}) { Env env(*this, features); @@ -194,14 +209,23 @@ struct Flow_test : public beast::unit_test::suite env(pay(alice, bob, USDA(100))); env.require(balance(bob, USDA(100))); - env(pay(dan, carol, USDA(10)), path(bob), sendmax(USDD(100)), txflags(tfNoRippleDirect)); + env(pay(dan, carol, USDA(10)), + path(bob), + sendmax(USDD(100)), + txflags(tfNoRippleDirect)); env.require(balance(bob, USDA(90))); if (bobAliceQOut > bobDanQIn) - env.require(balance(bob, USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn)))); + { + env.require( + balance(bob, USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn)))); + } else + { env.require(balance(bob, USDD(10))); + } env.require(balance(carol, USDA(10))); } + } // bob -> alice -> carol; vary carolAliceQIn for (auto carolAliceQIn : {80, 100, 120}) @@ -455,8 +479,10 @@ struct Flow_test : public beast::unit_test::suite return false; Sandbox sb(&view, tapNONE); for (auto const& o : flowResult.removableOffers) + { if (auto ok = sb.peek(keylet::offer(o))) offerDelete(sb, ok, flowJournal); + } sb.apply(view); return true; }); @@ -519,7 +545,8 @@ struct Flow_test : public beast::unit_test::suite bookDirStr.erase(0, 48); return std::stoull(bookDirStr, nullptr, 16); }(); - std::uint64_t const actualRate = getRate(usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays)); + std::uint64_t const actualRate = + getRate(usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays)); // We expect the actual rate of the offer to be worse // (larger) than the rate of the book page holding the @@ -824,7 +851,10 @@ struct Flow_test : public beast::unit_test::suite // Consuming the offer changes the owner count, which could also cause // liquidity to decrease in the forward pass auto const toSend = consumeOffer ? USD(10) : USD(9); - env(pay(alice, alice, toSend), path(~USD), sendmax(XRP(20000)), txflags(tfPartialPayment | tfNoRippleDirect)); + env(pay(alice, alice, toSend), + path(~USD), + sendmax(XRP(20000)), + txflags(tfPartialPayment | tfNoRippleDirect)); } void @@ -850,7 +880,10 @@ struct Flow_test : public beast::unit_test::suite STAmount tinyAmt3{USD.issue(), 9000000000000003ll, -17, false, STAmount::unchecked{}}; env(offer(gw, drops(9000000000), tinyAmt3)); - env(pay(alice, bob, tinyAmt1), path(~USD), sendmax(drops(9000000000)), txflags(tfNoRippleDirect)); + env(pay(alice, bob, tinyAmt1), + path(~USD), + sendmax(drops(9000000000)), + txflags(tfNoRippleDirect)); BEAST_EXPECT(!isOffer(env, gw, XRP(0), USD(0))); } @@ -873,7 +906,10 @@ struct Flow_test : public beast::unit_test::suite env(pay(gw, alice, tinyAmt1)); env(offer(gw, tinyAmt3, drops(9000000000))); - env(pay(alice, bob, drops(9000000000)), path(~XRP), sendmax(USD(1)), txflags(tfNoRippleDirect)); + env(pay(alice, bob, drops(9000000000)), + path(~XRP), + sendmax(USD(1)), + txflags(tfNoRippleDirect)); BEAST_EXPECT(!isOffer(env, gw, USD(0), XRP(0))); } @@ -930,7 +966,10 @@ struct Flow_test : public beast::unit_test::suite STAmount{USD.issue(), std::uint64_t(1700000000000000ull), -14, false}, XRP(.001))); - env(pay(alice, bob, XRP(10000)), path(~XRP), sendmax(USD(100)), txflags(tfPartialPayment | tfNoRippleDirect)); + env(pay(alice, bob, XRP(10000)), + path(~XRP), + sendmax(USD(100)), + txflags(tfPartialPayment | tfNoRippleDirect)); } void diff --git a/src/test/app/Freeze_test.cpp b/src/test/app/Freeze_test.cpp index 9d7db52e56..6f89163c04 100644 --- a/src/test/app/Freeze_test.cpp +++ b/src/test/app/Freeze_test.cpp @@ -73,7 +73,8 @@ class Freeze_test : public beast::unit_test::suite if (!BEAST_EXPECT(checkArraySize(affected, 2u))) return; auto ff = affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName]; - BEAST_EXPECT(ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); BEAST_EXPECT(ff[jss::Flags].asUInt() & lsfLowFreeze); BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze)); env.close(); @@ -87,8 +88,10 @@ class Freeze_test : public beast::unit_test::suite if (!BEAST_EXPECT(checkArraySize(affected, 5u))) return; auto ff = affected[3u][sfModifiedNode.fieldName][sfFinalFields.fieldName]; - BEAST_EXPECT(ff[sfHighLimit.fieldName] == bob["USD"](100).value().getJson(JsonOptions::none)); - auto amt = STAmount{Issue{to_currency("USD"), noAccount()}, -15}.value().getJson(JsonOptions::none); + BEAST_EXPECT( + ff[sfHighLimit.fieldName] == bob["USD"](100).value().getJson(JsonOptions::none)); + auto amt = STAmount{Issue{to_currency("USD"), noAccount()}, -15}.value().getJson( + JsonOptions::none); BEAST_EXPECT(ff[sfBalance.fieldName] == amt); env.close(); } @@ -149,7 +152,8 @@ class Freeze_test : public beast::unit_test::suite if (!BEAST_EXPECT(checkArraySize(affected, 2u))) return; auto ff = affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName]; - BEAST_EXPECT(ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + ff[sfLowLimit.fieldName] == G1["USD"](0).value().getJson(JsonOptions::none)); BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfLowFreeze)); BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze)); env.close(); @@ -320,7 +324,8 @@ class Freeze_test : public beast::unit_test::suite env(trust(G1, A1["USD"](0), tfSetFreeze | tfClearFreeze), ter(tecNO_PERMISSION)); env(trust(G1, A1["USD"](0), tfSetFreeze | tfClearDeepFreeze), ter(tecNO_PERMISSION)); env(trust(G1, A1["USD"](0), tfSetDeepFreeze | tfClearFreeze), ter(tecNO_PERMISSION)); - env(trust(G1, A1["USD"](0), tfSetDeepFreeze | tfClearDeepFreeze), ter(tecNO_PERMISSION)); + env(trust(G1, A1["USD"](0), tfSetDeepFreeze | tfClearDeepFreeze), + ter(tecNO_PERMISSION)); } else { @@ -388,7 +393,8 @@ class Freeze_test : public beast::unit_test::suite { // Account without GlobalFreeze (proving operations normally work) // test: visible offers where taker_pays is unfrozen issuer - auto offers = env.rpc("book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; + auto offers = env.rpc( + "book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 2u))) return; std::set accounts; @@ -400,7 +406,8 @@ class Freeze_test : public beast::unit_test::suite BEAST_EXPECT(accounts.find(G1.human()) != std::end(accounts)); // test: visible offers where taker_gets is unfrozen issuer - offers = env.rpc("book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; + offers = env.rpc( + "book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 2u))) return; accounts.clear(); @@ -458,11 +465,13 @@ class Freeze_test : public beast::unit_test::suite // test: book_offers shows offers // (should these actually be filtered?) - offers = env.rpc("book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; + offers = env.rpc( + "book_offers", "XRP", std::string("USD/") + G1.human())[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 2u))) return; - offers = env.rpc("book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; + offers = env.rpc( + "book_offers", std::string("USD/") + G1.human(), "XRP")[jss::result][jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 2u))) return; } @@ -629,7 +638,8 @@ class Freeze_test : public beast::unit_test::suite auto offers = getAccountOffers(env, A3)[jss::offers]; if (!BEAST_EXPECT(checkArraySize(offers, 1u))) return; - BEAST_EXPECT(offers[0u][jss::taker_gets] == G1["USD"](999).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + offers[0u][jss::taker_gets] == G1["USD"](999).value().getJson(JsonOptions::none)); // test: someone else creates an offer providing liquidity env(offer(A4, XRP(999), G1["USD"](999))); @@ -950,11 +960,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: A1 cannot send USD using XRP through A2 offer - env(pay(A1, G1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(A1, G1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); // test: G1 cannot send USD using XRP through A2 offer - env(pay(G1, A1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(G1, A1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); env(trust(G1, A2["USD"](0), tfClearFreeze)); @@ -968,11 +986,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: A1 cannot send USD using XRP through A2 offer - env(pay(A1, G1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(A1, G1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); // test: G1 cannot send USD using XRP through A2 offer - env(pay(G1, A1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(G1, A1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze)); @@ -1005,11 +1031,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: A1 cannot send USD using XRP through A2 offer - env(pay(A1, G1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(A1, G1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); // test: G1 cannot send USD using XRP through A2 offer - env(pay(G1, A1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(G1, A1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze)); @@ -1049,11 +1083,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: A1 cannot send XRP using USD through A2 offer - env(pay(A1, G1, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(A1, G1, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); // test: G1 cannot send XRP using USD through A2 offer - env(pay(G1, A1, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(G1, A1, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze)); @@ -1086,11 +1128,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: A1 cannot send XRP using USD through A2 offer - env(pay(A1, G1, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(A1, G1, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); // test: G1 cannot send XRP using USD through A2 offer - env(pay(G1, A1, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_PARTIAL)); + env(pay(G1, A1, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_PARTIAL)); env.close(); env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze)); @@ -1556,7 +1606,11 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: cannot use USD to make payment - env(pay(A1, A2, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_DRY)); + env(pay(A1, A2, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_DRY)); env.close(); // test: can still receive USD payments. @@ -1583,11 +1637,19 @@ class Freeze_test : public beast::unit_test::suite env.close(); // test: cannot use USD to make payment - env(pay(A1, A2, XRP(10)), path(~XRP), sendmax(USD(11)), txflags(tfNoRippleDirect), ter(tecPATH_DRY)); + env(pay(A1, A2, XRP(10)), + path(~XRP), + sendmax(USD(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_DRY)); env.close(); // test: cannot receive USD payments. - env(pay(A2, A1, USD(10)), path(~USD), sendmax(XRP(11)), txflags(tfNoRippleDirect), ter(tecPATH_DRY)); + env(pay(A2, A1, USD(10)), + path(~USD), + sendmax(XRP(11)), + txflags(tfNoRippleDirect), + ter(tecPATH_DRY)); env.close(); // test: can still receive XRP payments. @@ -1784,7 +1846,9 @@ class Freeze_test : public beast::unit_test::suite env(token::createOffer(A1, nftID, USD(11)), token::owner(A2)); env.close(); - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecFROZEN)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecFROZEN)); env.close(); } @@ -1819,7 +1883,11 @@ class Freeze_test : public beast::unit_test::suite // Helper function to extract trustline flags from open ledger uint32_t - getTrustlineFlags(test::jtx::Env& env, size_t expectedArraySize, size_t expectedArrayIndex, bool modified = true) + getTrustlineFlags( + test::jtx::Env& env, + size_t expectedArraySize, + size_t expectedArrayIndex, + bool modified = true) { using namespace test::jtx; auto const affected = env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName]; @@ -1828,10 +1896,14 @@ class Freeze_test : public beast::unit_test::suite if (modified) { - return affected[expectedArrayIndex][sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::Flags].asUInt(); + return affected[expectedArrayIndex][sfModifiedNode.fieldName][sfFinalFields.fieldName] + [jss::Flags] + .asUInt(); } - return affected[expectedArrayIndex][sfCreatedNode.fieldName][sfNewFields.fieldName][jss::Flags].asUInt(); + return affected[expectedArrayIndex][sfCreatedNode.fieldName][sfNewFields.fieldName] + [jss::Flags] + .asUInt(); } // Helper function that returns the index of the next check on account @@ -1842,7 +1914,10 @@ class Freeze_test : public beast::unit_test::suite } uint256 - createNFTSellOffer(test::jtx::Env& env, test::jtx::Account const& account, test::jtx::PrettyAmount const& currency) + createNFTSellOffer( + test::jtx::Env& env, + test::jtx::Account const& account, + test::jtx::PrettyAmount const& currency) { using namespace test::jtx; uint256 const nftID{token::getNextID(env, account, 0u, tfTransferable)}; diff --git a/src/test/app/HashRouter_test.cpp b/src/test/app/HashRouter_test.cpp index 03bb28e35a..58bace3ef2 100644 --- a/src/test/app/HashRouter_test.cpp +++ b/src/test/app/HashRouter_test.cpp @@ -1,8 +1,9 @@ -#include +#include #include #include #include +#include namespace xrpl { namespace test { @@ -230,7 +231,7 @@ class HashRouter_test : public beast::unit_test::suite ++stopwatch; // Confirm that peers list is empty. peers = router.shouldRelay(key1); - BEAST_EXPECT(peers && peers->size() == 0); + BEAST_EXPECT(peers && peers->empty()); } void @@ -242,7 +243,7 @@ class HashRouter_test : public beast::unit_test::suite HashRouter router(getSetup(5s, 1s), stopwatch); uint256 const key(1); HashRouter::PeerShortID peer = 1; - HashRouterFlags flags; + HashRouterFlags flags = HashRouterFlags::UNDEFINED; BEAST_EXPECT(router.shouldProcess(key, peer, flags, 1s)); BEAST_EXPECT(!router.shouldProcess(key, peer, flags, 1s)); diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index d786c4498a..a64031bd36 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include namespace xrpl { namespace test { @@ -76,7 +75,10 @@ createApplyContext( } static ApplyContext -createApplyContext(test::jtx::Env& env, OpenView& ov, STTx const& tx = STTx(ttESCROW_FINISH, [](STObject&) {})) +createApplyContext( + test::jtx::Env& env, + OpenView& ov, + STTx const& tx = STTx(ttESCROW_FINISH, [](STObject&) {})) { return createApplyContext(env, ov, env.journal, tx); } @@ -115,7 +117,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite WasmHostFunctionsImpl hfs(ac, dummyEscrow); auto const result = hfs.getParentLedgerTime(); if (BEAST_EXPECT(result.has_value())) - BEAST_EXPECT(result.value() == env.current()->parentCloseTime().time_since_epoch().count()); + BEAST_EXPECT( + result.value() == env.current()->parentCloseTime().time_since_epoch().count()); } } @@ -216,9 +219,15 @@ struct HostFuncImpl_test : public beast::unit_test::suite { WasmHostFunctionsImpl hfs(ac, dummyEscrow); - BEAST_EXPECT(hfs.cacheLedgerObj(accountKeylet.key, -1).error() == HostFunctionError::SLOT_OUT_RANGE); - BEAST_EXPECT(hfs.cacheLedgerObj(accountKeylet.key, 257).error() == HostFunctionError::SLOT_OUT_RANGE); - BEAST_EXPECT(hfs.cacheLedgerObj(dummyEscrow.key, 0).error() == HostFunctionError::LEDGER_OBJ_NOT_FOUND); + BEAST_EXPECT( + hfs.cacheLedgerObj(accountKeylet.key, -1).error() == + HostFunctionError::SLOT_OUT_RANGE); + BEAST_EXPECT( + hfs.cacheLedgerObj(accountKeylet.key, 257).error() == + HostFunctionError::SLOT_OUT_RANGE); + BEAST_EXPECT( + hfs.cacheLedgerObj(dummyEscrow.key, 0).error() == + HostFunctionError::LEDGER_OBJ_NOT_FOUND); BEAST_EXPECT(hfs.cacheLedgerObj(accountKeylet.key, 0).value() == 1); for (int i = 1; i <= 256; ++i) @@ -226,7 +235,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const result = hfs.cacheLedgerObj(accountKeylet.key, i); BEAST_EXPECT(result.has_value() && result.value() == i); } - BEAST_EXPECT(hfs.cacheLedgerObj(accountKeylet.key, 0).error() == HostFunctionError::SLOTS_FULL); + BEAST_EXPECT( + hfs.cacheLedgerObj(accountKeylet.key, 0).error() == HostFunctionError::SLOTS_FULL); } { @@ -237,7 +247,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const result = hfs.cacheLedgerObj(accountKeylet.key, 0); BEAST_EXPECT(result.has_value() && result.value() == i); } - BEAST_EXPECT(hfs.cacheLedgerObj(accountKeylet.key, 0).error() == HostFunctionError::SLOTS_FULL); + BEAST_EXPECT( + hfs.cacheLedgerObj(accountKeylet.key, 0).error() == HostFunctionError::SLOTS_FULL); } } @@ -247,7 +258,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite testcase("getTxField"); using namespace test::jtx; - std::string const credIdHex = "0011223344556677889900112233445566778899001122334455667788990011"; + std::string const credIdHex = + "0011223344556677889900112233445566778899001122334455667788990011"; uint256 credId; BEAST_EXPECT(credId.parseHex(credIdHex)); @@ -404,7 +416,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should return nullopt for a field not present auto const notPresent = hfs.getCurrentLedgerObjField(sfOwner); - BEAST_EXPECT(!notPresent.has_value() && notPresent.error() == HostFunctionError::FIELD_NOT_FOUND); + BEAST_EXPECT( + !notPresent.has_value() && notPresent.error() == HostFunctionError::FIELD_NOT_FOUND); { auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5); @@ -454,7 +467,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should return error for slot out of range auto const outOfRange = hfs.getLedgerObjField(0, sfAccount); - BEAST_EXPECT(!outOfRange.has_value() && outOfRange.error() == HostFunctionError::SLOT_OUT_RANGE); + BEAST_EXPECT( + !outOfRange.has_value() && outOfRange.error() == HostFunctionError::SLOT_OUT_RANGE); auto const tooHigh = hfs.getLedgerObjField(257, sfAccount); BEAST_EXPECT(!tooHigh.has_value() && tooHigh.error() == HostFunctionError::SLOT_OUT_RANGE); @@ -465,7 +479,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should return error for field not present auto const notPresent = hfs.getLedgerObjField(1, sfOwner); - BEAST_EXPECT(!notPresent.has_value() && notPresent.error() == HostFunctionError::FIELD_NOT_FOUND); + BEAST_EXPECT( + !notPresent.has_value() && notPresent.error() == HostFunctionError::FIELD_NOT_FOUND); } void @@ -477,7 +492,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite Env env{*this}; OpenView ov{*env.current()}; - std::string const credIdHex = "0011223344556677889900112233445566778899001122334455667788990011"; + std::string const credIdHex = + "0011223344556677889900112233445566778899001122334455667788990011"; uint256 credId; BEAST_EXPECT(credId.parseHex(credIdHex)); @@ -504,7 +520,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator is a sequence of int32_t codes: // [sfMemos.fieldCode, 0, sfMemoData.fieldCode] std::vector locatorVec = {sfMemos.fieldCode, 0, sfMemoData.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getTxNestedField(locator); if (BEAST_EXPECTS(result.has_value(), std::to_string(static_cast(result.error())))) @@ -517,7 +535,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite { // Locator for sfCredentialIDs[0] std::vector locatorVec = {sfCredentialIDs.fieldCode, 0}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getTxNestedField(locator); if (BEAST_EXPECTS(result.has_value(), std::to_string(static_cast(result.error())))) @@ -530,10 +550,13 @@ struct HostFuncImpl_test : public beast::unit_test::suite { // can use the nested locator for base fields too std::vector locatorVec = {sfAccount.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const account = hfs.getTxNestedField(locator); - if (BEAST_EXPECTS(account.has_value(), std::to_string(static_cast(account.error())))) + if (BEAST_EXPECTS( + account.has_value(), std::to_string(static_cast(account.error())))) { BEAST_EXPECT(std::ranges::equal(*account, env.master.id())); } @@ -546,17 +569,23 @@ struct HostFuncImpl_test : public beast::unit_test::suite Slice locator(reinterpret_cast(locatorVec.data() + 1), sizeof(int32_t)); auto const account = hfs.getTxNestedField(locator); - if (BEAST_EXPECTS(account.has_value(), std::to_string(static_cast(account.error())))) + if (BEAST_EXPECTS( + account.has_value(), std::to_string(static_cast(account.error())))) { BEAST_EXPECT(std::ranges::equal(*account, env.master.id())); } } - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError) { - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError) { + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getTxNestedField(locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Locator for non-existent base field expectError( @@ -577,6 +606,18 @@ struct HostFuncImpl_test : public beast::unit_test::suite {sfCredentialIDs.fieldCode, 1}, // index 1 does not exist HostFunctionError::INDEX_OUT_OF_BOUNDS); + // Locator for negative index (STArray) + expectError( + {sfMemos.fieldCode, + -1, // negative index + sfMemoData.fieldCode}, + HostFunctionError::INDEX_OUT_OF_BOUNDS); + + // Locator for negative index (STVector256) + expectError( + {sfCredentialIDs.fieldCode, -1}, // negative index + HostFunctionError::INDEX_OUT_OF_BOUNDS); + // Locator for non-existent nested field expectError( {sfMemos.fieldCode, 0, sfURI.fieldCode}, // sfURI does not exist in the memo @@ -596,6 +637,31 @@ struct HostFuncImpl_test : public beast::unit_test::suite field_code(20000, 20000)}, HostFunctionError::INVALID_FIELD); + // Locator for negative base sfield code (-1 = sfInvalid, exists in map but not in tx) + expectError( + {-1, // sfInvalid's field code + 0, + sfAccount.fieldCode}, + HostFunctionError::FIELD_NOT_FOUND); + + // Locator for zero base sfield code (0 = sfGeneric, exists in map but not in tx) + expectError( + {0, // sfGeneric's field code + 0, + sfAccount.fieldCode}, + HostFunctionError::FIELD_NOT_FOUND); + + // Locator for very negative base sfield code (not in knownCodeToField map) + expectError( + {std::numeric_limits::min(), 0, sfAccount.fieldCode}, + HostFunctionError::INVALID_FIELD); + + // Locator for negative nested sfield code in STObject context + // (sfMemos[0] is an STObject, then -1 is looked up as SField) + expectError( + {sfMemos.fieldCode, 0, -1}, // -1 = sfInvalid, exists in map but not in memo object + HostFunctionError::FIELD_NOT_FOUND); + // Locator for STArray expectError({sfMemos.fieldCode}, HostFunctionError::NOT_LEAF_FIELD); @@ -618,7 +684,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite Slice malformedLocator(reinterpret_cast(locatorVec.data()), 3); auto const malformedResult = hfs.getTxNestedField(malformedLocator); BEAST_EXPECT( - !malformedResult.has_value() && malformedResult.error() == HostFunctionError::LOCATOR_MALFORMED); + !malformedResult.has_value() && + malformedResult.error() == HostFunctionError::LOCATOR_MALFORMED); } } @@ -646,18 +713,25 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for base field std::vector baseLocator = {sfSignerQuorum.fieldCode}; Slice baseLocatorSlice( - reinterpret_cast(baseLocator.data()), baseLocator.size() * sizeof(int32_t)); + reinterpret_cast(baseLocator.data()), + baseLocator.size() * sizeof(int32_t)); auto const signerQuorum = hfs.getCurrentLedgerObjNestedField(baseLocatorSlice); - if (BEAST_EXPECTS(signerQuorum.has_value(), std::to_string(static_cast(signerQuorum.error())))) + if (BEAST_EXPECTS( + signerQuorum.has_value(), std::to_string(static_cast(signerQuorum.error())))) { BEAST_EXPECT(*signerQuorum == toBytes(static_cast(2))); } - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError) { - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError) { + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getCurrentLedgerObjNestedField(locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Locator for non-existent base field expectError( @@ -675,19 +749,25 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for empty locator Slice emptyLocator(nullptr, 0); auto const emptyResult = hfs.getCurrentLedgerObjNestedField(emptyLocator); - BEAST_EXPECT(!emptyResult.has_value() && emptyResult.error() == HostFunctionError::LOCATOR_MALFORMED); + BEAST_EXPECT( + !emptyResult.has_value() && + emptyResult.error() == HostFunctionError::LOCATOR_MALFORMED); // Locator for malformed locator (not multiple of 4) std::vector malformedLocatorVec = {sfMemos.fieldCode}; Slice malformedLocator(reinterpret_cast(malformedLocatorVec.data()), 3); auto const malformedResult = hfs.getCurrentLedgerObjNestedField(malformedLocator); - BEAST_EXPECT(!malformedResult.has_value() && malformedResult.error() == HostFunctionError::LOCATOR_MALFORMED); + BEAST_EXPECT( + !malformedResult.has_value() && + malformedResult.error() == HostFunctionError::LOCATOR_MALFORMED); { auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5); WasmHostFunctionsImpl dummyHfs(ac, dummyEscrow); std::vector const locatorVec = {sfAccount.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = dummyHfs.getCurrentLedgerObjNestedField(locator); if (BEAST_EXPECT(!result.has_value())) BEAST_EXPECTS( @@ -722,8 +802,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for sfSignerEntries[0].sfAccount { - std::vector const locatorVec = {sfSignerEntries.fieldCode, 0, sfAccount.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + std::vector const locatorVec = { + sfSignerEntries.fieldCode, 0, sfAccount.fieldCode}; + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getLedgerObjNestedField(1, locator); if (BEAST_EXPECTS(result.has_value(), std::to_string(static_cast(result.error())))) @@ -734,11 +817,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for sfSignerEntries[1].sfAccount { - std::vector const locatorVec = {sfSignerEntries.fieldCode, 1, sfAccount.fieldCode}; - Slice const locator = - Slice(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + std::vector const locatorVec = { + sfSignerEntries.fieldCode, 1, sfAccount.fieldCode}; + Slice const locator = Slice( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result2 = hfs.getLedgerObjNestedField(1, locator); - if (BEAST_EXPECTS(result2.has_value(), std::to_string(static_cast(result2.error())))) + if (BEAST_EXPECTS( + result2.has_value(), std::to_string(static_cast(result2.error())))) { BEAST_EXPECT(std::ranges::equal(*result2, becky.id())); } @@ -746,11 +832,15 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for sfSignerEntries[0].sfSignerWeight { - std::vector const locatorVec = {sfSignerEntries.fieldCode, 0, sfSignerWeight.fieldCode}; - Slice const locator = - Slice(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + std::vector const locatorVec = { + sfSignerEntries.fieldCode, 0, sfSignerWeight.fieldCode}; + Slice const locator = Slice( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const weightResult = hfs.getLedgerObjNestedField(1, locator); - if (BEAST_EXPECTS(weightResult.has_value(), std::to_string(static_cast(weightResult.error())))) + if (BEAST_EXPECTS( + weightResult.has_value(), + std::to_string(static_cast(weightResult.error())))) { // Should be 1 auto const expected = toBytes(static_cast(1)); @@ -761,10 +851,13 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for base field sfSignerQuorum { std::vector const locatorVec = {sfSignerQuorum.fieldCode}; - Slice const locator = - Slice(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice const locator = Slice( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const quorumResult = hfs.getLedgerObjNestedField(1, locator); - if (BEAST_EXPECTS(quorumResult.has_value(), std::to_string(static_cast(quorumResult.error())))) + if (BEAST_EXPECTS( + quorumResult.has_value(), + std::to_string(static_cast(quorumResult.error())))) { auto const expected = toBytes(static_cast(2)); BEAST_EXPECT(*quorumResult == expected); @@ -772,12 +865,17 @@ struct HostFuncImpl_test : public beast::unit_test::suite } // Helper for error checks - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError, int slot = 1) { + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError, + int slot = 1) { Slice const locator( - reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getLedgerObjNestedField(slot, locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Error: base field not found @@ -804,10 +902,13 @@ struct HostFuncImpl_test : public beast::unit_test::suite HostFunctionError::FIELD_NOT_FOUND); // Error: invalid field code - expectError({field_code(99999, 99999), 0, sfAccount.fieldCode}, HostFunctionError::INVALID_FIELD); + expectError( + {field_code(99999, 99999), 0, sfAccount.fieldCode}, HostFunctionError::INVALID_FIELD); // Error: invalid nested field code - expectError({sfSignerEntries.fieldCode, 0, field_code(99999, 99999)}, HostFunctionError::INVALID_FIELD); + expectError( + {sfSignerEntries.fieldCode, 0, field_code(99999, 99999)}, + HostFunctionError::INVALID_FIELD); // Error: slot out of range expectError({sfSignerQuorum.fieldCode}, HostFunctionError::SLOT_OUT_RANGE, 0); @@ -820,7 +921,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite expectError({sfSignerEntries.fieldCode}, HostFunctionError::NOT_LEAF_FIELD); // Error: nesting into non-array/object field - expectError({sfSignerQuorum.fieldCode, 0, sfAccount.fieldCode}, HostFunctionError::LOCATOR_MALFORMED); + expectError( + {sfSignerQuorum.fieldCode, 0, sfAccount.fieldCode}, + HostFunctionError::LOCATOR_MALFORMED); // Error: empty locator expectError({}, HostFunctionError::LOCATOR_MALFORMED); @@ -829,7 +932,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite std::vector const locatorVec = {sfSignerEntries.fieldCode}; Slice const locator = Slice(reinterpret_cast(locatorVec.data()), 3); auto const malformed = hfs.getLedgerObjNestedField(1, locator); - BEAST_EXPECT(!malformed.has_value() && malformed.error() == HostFunctionError::LOCATOR_MALFORMED); + BEAST_EXPECT( + !malformed.has_value() && malformed.error() == HostFunctionError::LOCATOR_MALFORMED); } void @@ -838,7 +942,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite testcase("getTxArrayLen"); using namespace test::jtx; - std::string const credIdHex = "0011223344556677889900112233445566778899001122334455667788990011"; + std::string const credIdHex = + "0011223344556677889900112233445566778899001122334455667788990011"; uint256 credId; BEAST_EXPECT(credId.parseHex(credIdHex)); @@ -1010,18 +1115,25 @@ struct HostFuncImpl_test : public beast::unit_test::suite WasmHostFunctionsImpl hfs(ac, dummyEscrow); // Helper for error checks - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError, int slot = 1) { + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError, + int slot = 1) { Slice const locator( - reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getTxNestedArrayLen(locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Locator for sfMemos { std::vector locatorVec = {sfMemos.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const arrLen = hfs.getTxNestedArrayLen(locator); BEAST_EXPECT(arrLen.has_value() && arrLen.value() == 1); } @@ -1053,18 +1165,25 @@ struct HostFuncImpl_test : public beast::unit_test::suite WasmHostFunctionsImpl hfs(ac, signerKeylet); // Helper for error checks - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError, int slot = 1) { + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError, + int slot = 1) { Slice const locator( - reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getCurrentLedgerObjNestedArrayLen(locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Locator for sfSignerEntries { std::vector locatorVec = {sfSignerEntries.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const arrLen = hfs.getCurrentLedgerObjNestedArrayLen(locator); BEAST_EXPECT(arrLen.has_value() && arrLen.value() == 2); } @@ -1080,7 +1199,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite WasmHostFunctionsImpl dummyHfs(ac, dummyEscrow); std::vector locatorVec = {sfAccount.fieldCode}; Slice const locator( - reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = dummyHfs.getCurrentLedgerObjNestedArrayLen(locator); if (BEAST_EXPECT(!result.has_value())) BEAST_EXPECTS( @@ -1113,18 +1233,25 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Locator for sfSignerEntries std::vector locatorVec = {sfSignerEntries.fieldCode}; - Slice locator(reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const arrLen = hfs.getLedgerObjNestedArrayLen(1, locator); if (BEAST_EXPECT(arrLen.has_value())) BEAST_EXPECT(arrLen.value() == 2); // Helper for error checks - auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError, int slot = 1) { + auto expectError = [&](std::vector const& locatorVec, + HostFunctionError expectedError, + int slot = 1) { Slice const locator( - reinterpret_cast(locatorVec.data()), locatorVec.size() * sizeof(int32_t)); + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); auto const result = hfs.getLedgerObjNestedArrayLen(slot, locator); if (BEAST_EXPECT(!result.has_value())) - BEAST_EXPECTS(result.error() == expectedError, std::to_string(static_cast(result.error()))); + BEAST_EXPECTS( + result.error() == expectedError, + std::to_string(static_cast(result.error()))); }; // Error: non-array field @@ -1146,10 +1273,13 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Error: locator malformed (not multiple of 4) Slice malformedLocator(reinterpret_cast(locator.data()), 3); auto const malformed = hfs.getLedgerObjNestedArrayLen(1, malformedLocator); - BEAST_EXPECT(!malformed.has_value() && malformed.error() == HostFunctionError::LOCATOR_MALFORMED); + BEAST_EXPECT( + !malformed.has_value() && malformed.error() == HostFunctionError::LOCATOR_MALFORMED); // Error: locator for non-STArray field - expectError({sfSignerQuorum.fieldCode, 0, sfAccount.fieldCode}, HostFunctionError::LOCATOR_MALFORMED); + expectError( + {sfSignerQuorum.fieldCode, 0, sfAccount.fieldCode}, + HostFunctionError::LOCATOR_MALFORMED); } void @@ -1159,7 +1289,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - env(escrow::create(env.master, env.master, XRP(100)), escrow::finish_time(env.now() + std::chrono::seconds(1))); + env(escrow::create(env.master, env.master, XRP(100)), + escrow::finish_time(env.now() + std::chrono::seconds(1))); env.close(); OpenView ov{*env.current()}; @@ -1203,7 +1334,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should succeed for valid signature { auto const result = hfs.checkSignature( - Slice(message.data(), message.size()), Slice(sig.data(), sig.size()), Slice(pk.data(), pk.size())); + Slice(message.data(), message.size()), + Slice(sig.data(), sig.size()), + Slice(pk.data(), pk.size())); BEAST_EXPECT(result.has_value()); BEAST_EXPECT(result.value() == 1); } @@ -1233,7 +1366,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should fail for empty public key { auto const result = hfs.checkSignature( - Slice(message.data(), message.size()), Slice(sig.data(), sig.size()), Slice(nullptr, 0)); + Slice(message.data(), message.size()), + Slice(sig.data(), sig.size()), + Slice(nullptr, 0)); BEAST_EXPECT(!result.has_value()); BEAST_EXPECT(result.error() == HostFunctionError::INVALID_PARAMS); } @@ -1241,15 +1376,17 @@ struct HostFuncImpl_test : public beast::unit_test::suite // Should fail for empty signature { auto const result = hfs.checkSignature( - Slice(message.data(), message.size()), Slice(nullptr, 0), Slice(pk.data(), pk.size())); + Slice(message.data(), message.size()), + Slice(nullptr, 0), + Slice(pk.data(), pk.size())); BEAST_EXPECT(result.has_value()); BEAST_EXPECT(result.value() == 0); } // Should fail for empty message { - auto const result = - hfs.checkSignature(Slice(nullptr, 0), Slice(sig.data(), sig.size()), Slice(pk.data(), pk.size())); + auto const result = hfs.checkSignature( + Slice(nullptr, 0), Slice(sig.data(), sig.size()), Slice(pk.data(), pk.size())); BEAST_EXPECT(result.has_value()); BEAST_EXPECT(result.value() == 0); } @@ -1303,13 +1440,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite BEAST_EXPECT(compareKeylet(actual.value(), expected)); \ } \ } -#define COMPARE_KEYLET_FAIL(hfsFunc, expected, ...) \ - { \ - auto actual = hfs.hfsFunc(__VA_ARGS__); \ - if (BEAST_EXPECT(!actual.has_value())) \ - { \ - BEAST_EXPECTS(actual.error() == expected, std::to_string(HfErrorToInt(actual.error()))); \ - } \ +#define COMPARE_KEYLET_FAIL(hfsFunc, expected, ...) \ + { \ + auto actual = hfs.hfsFunc(__VA_ARGS__); \ + if (BEAST_EXPECT(!actual.has_value())) \ + { \ + BEAST_EXPECTS( \ + actual.error() == expected, std::to_string(HfErrorToInt(actual.error()))); \ + } \ } COMPARE_KEYLET(accountKeylet, keylet::account, env.master.id()); @@ -1317,7 +1455,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite COMPARE_KEYLET(ammKeylet, keylet::amm, xrpIssue(), env.master["USD"].issue()); COMPARE_KEYLET_FAIL(ammKeylet, HostFunctionError::INVALID_PARAMS, xrpIssue(), xrpIssue()); - COMPARE_KEYLET_FAIL(ammKeylet, HostFunctionError::INVALID_PARAMS, makeMptID(1, env.master.id()), xrpIssue()); + COMPARE_KEYLET_FAIL( + ammKeylet, + HostFunctionError::INVALID_PARAMS, + makeMptID(1, env.master.id()), + xrpIssue()); COMPARE_KEYLET(checkKeylet, keylet::check, env.master.id(), 1); COMPARE_KEYLET_FAIL(checkKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); @@ -1358,25 +1500,47 @@ struct HostFuncImpl_test : public beast::unit_test::suite COMPARE_KEYLET_FAIL(didKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount()); COMPARE_KEYLET(delegateKeylet, keylet::delegate, env.master.id(), alice.id()); - COMPARE_KEYLET_FAIL(delegateKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id()); - COMPARE_KEYLET_FAIL(delegateKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount()); - COMPARE_KEYLET_FAIL(delegateKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id()); + COMPARE_KEYLET_FAIL( + delegateKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id()); + COMPARE_KEYLET_FAIL( + delegateKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount()); + COMPARE_KEYLET_FAIL( + delegateKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id()); COMPARE_KEYLET(depositPreauthKeylet, keylet::depositPreauth, env.master.id(), alice.id()); - COMPARE_KEYLET_FAIL(depositPreauthKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id()); - COMPARE_KEYLET_FAIL(depositPreauthKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount()); - COMPARE_KEYLET_FAIL(depositPreauthKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id()); + COMPARE_KEYLET_FAIL( + depositPreauthKeylet, + HostFunctionError::INVALID_PARAMS, + env.master.id(), + env.master.id()); + COMPARE_KEYLET_FAIL( + depositPreauthKeylet, + HostFunctionError::INVALID_ACCOUNT, + env.master.id(), + xrpAccount()); + COMPARE_KEYLET_FAIL( + depositPreauthKeylet, + HostFunctionError::INVALID_ACCOUNT, + xrpAccount(), + env.master.id()); COMPARE_KEYLET(escrowKeylet, keylet::escrow, env.master.id(), 1); COMPARE_KEYLET_FAIL(escrowKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); Currency usd = to_currency("USD"); COMPARE_KEYLET(lineKeylet, keylet::line, env.master.id(), alice.id(), usd); - COMPARE_KEYLET_FAIL(lineKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id(), usd); - COMPARE_KEYLET_FAIL(lineKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount(), usd); - COMPARE_KEYLET_FAIL(lineKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id(), usd); COMPARE_KEYLET_FAIL( - lineKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), alice.id(), to_currency("")); + lineKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id(), usd); + COMPARE_KEYLET_FAIL( + lineKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount(), usd); + COMPARE_KEYLET_FAIL( + lineKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id(), usd); + COMPARE_KEYLET_FAIL( + lineKeylet, + HostFunctionError::INVALID_PARAMS, + env.master.id(), + alice.id(), + to_currency("")); { auto actual = hfs.mptIssuanceKeylet(env.master.id(), 1); @@ -1395,7 +1559,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const sampleMPTID = makeMptID(1, env.master.id()); COMPARE_KEYLET(mptokenKeylet, keylet::mptoken, sampleMPTID, alice.id()); COMPARE_KEYLET_FAIL(mptokenKeylet, HostFunctionError::INVALID_PARAMS, MPTID{}, alice.id()); - COMPARE_KEYLET_FAIL(mptokenKeylet, HostFunctionError::INVALID_ACCOUNT, sampleMPTID, xrpAccount()); + COMPARE_KEYLET_FAIL( + mptokenKeylet, HostFunctionError::INVALID_ACCOUNT, sampleMPTID, xrpAccount()); COMPARE_KEYLET(nftOfferKeylet, keylet::nftoffer, env.master.id(), 1); COMPARE_KEYLET_FAIL(nftOfferKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); @@ -1407,12 +1572,16 @@ struct HostFuncImpl_test : public beast::unit_test::suite COMPARE_KEYLET_FAIL(oracleKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); COMPARE_KEYLET(paychanKeylet, keylet::payChan, env.master.id(), alice.id(), 1); - COMPARE_KEYLET_FAIL(paychanKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id(), 1); - COMPARE_KEYLET_FAIL(paychanKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount(), 1); - COMPARE_KEYLET_FAIL(paychanKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id(), 1); + COMPARE_KEYLET_FAIL( + paychanKeylet, HostFunctionError::INVALID_PARAMS, env.master.id(), env.master.id(), 1); + COMPARE_KEYLET_FAIL( + paychanKeylet, HostFunctionError::INVALID_ACCOUNT, env.master.id(), xrpAccount(), 1); + COMPARE_KEYLET_FAIL( + paychanKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), env.master.id(), 1); COMPARE_KEYLET(permissionedDomainKeylet, keylet::permissionedDomain, env.master.id(), 1); - COMPARE_KEYLET_FAIL(permissionedDomainKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); + COMPARE_KEYLET_FAIL( + permissionedDomainKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount(), 1); COMPARE_KEYLET(signersKeylet, keylet::signers, env.master.id()); COMPARE_KEYLET_FAIL(signersKeylet, HostFunctionError::INVALID_ACCOUNT, xrpAccount()); @@ -1956,12 +2125,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatFromInt(std::numeric_limits::min(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatFromInt(std::numeric_limits::min(), 4); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { @@ -1994,12 +2165,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatFromUint(std::numeric_limits::min(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatFromUint(std::numeric_limits::min(), 4); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { @@ -2028,17 +2201,20 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatSet(1, 0, -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatSet(1, 0, 4); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatSet(1, wasmMaxExponent + normalExp + 1, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { @@ -2075,6 +2251,12 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const result = hfs.floatSet(10, -1, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); } + + { + auto const result = hfs.floatSet(1, Number::maxExponent + normalExp + 1, 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + } } void @@ -2091,12 +2273,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatCompare(Slice(), Slice()); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatCompare(makeSlice(floatInvalidZero), Slice()); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { @@ -2143,22 +2327,26 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatAdd(Slice(), Slice(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatAdd(Slice(), Slice(), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatAdd(makeSlice(float1), makeSlice(invalid), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatAdd(makeSlice(floatMaxIOU), makeSlice(floatMaxExp), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { @@ -2186,26 +2374,32 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatSubtract(Slice(), Slice(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatSubtract(Slice(), Slice(), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatSubtract(makeSlice(float1), makeSlice(invalid), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { - auto const result = hfs.floatSubtract(makeSlice(floatMaxIOU), makeSlice(floatMinusMaxExp), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + auto const result = + hfs.floatSubtract(makeSlice(floatMaxIOU), makeSlice(floatMinusMaxExp), 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { - auto const result = hfs.floatSubtract(makeSlice(floatIntMin), makeSlice(floatIntZero), 0); + auto const result = + hfs.floatSubtract(makeSlice(floatIntMin), makeSlice(floatIntZero), 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntMin); } @@ -2229,22 +2423,26 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatMultiply(Slice(), Slice(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatMultiply(Slice(), Slice(), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatMultiply(makeSlice(float1), makeSlice(invalid), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatMultiply(makeSlice(floatMaxIOU), makeSlice(float1More), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { @@ -2253,7 +2451,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { - auto const result = hfs.floatMultiply(makeSlice(floatIntZero), makeSlice(floatMaxIOU), 0); + auto const result = + hfs.floatMultiply(makeSlice(floatIntZero), makeSlice(floatMaxIOU), 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); } @@ -2277,22 +2476,26 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatDivide(Slice(), Slice(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatDivide(Slice(), Slice(), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatDivide(makeSlice(float1), makeSlice(invalid), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatDivide(makeSlice(float1), makeSlice(floatIntZero), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { @@ -2300,7 +2503,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite if (BEAST_EXPECT(y)) { auto const result = hfs.floatDivide(makeSlice(floatMaxIOU), makeSlice(*y), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } } @@ -2329,17 +2533,20 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatRoot(Slice(), 2, -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatRoot(makeSlice(invalid), 3, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatRoot(makeSlice(float1), -2, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { @@ -2395,32 +2602,38 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatPower(Slice(), 2, -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatPower(makeSlice(invalid), 3, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatPower(makeSlice(float1), -2, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 2, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 81, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 2, 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { @@ -2467,12 +2680,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatLog(Slice(), -1); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { auto const result = hfs.floatLog(makeSlice(invalid), 0); - BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } // perf test logs diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 7558c951b0..8372bb340b 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -2,11 +2,11 @@ #include #include -#include -#include - #include #include +#include +#include +#include #include #include #include @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include @@ -29,17 +31,20 @@ class Invariants_test : public beast::unit_test::suite // on the ledger after creating two accounts, but before closing it, and // before the Precheck function. These should only be valid functions, and // not direct manipulations. Preclose is not commonly used. - using Preclose = std::function; + using Preclose = std::function< + bool(test::jtx::Account const& a, test::jtx::Account const& b, test::jtx::Env& env)>; // this is common setup/method for running a failing invariant check. The // precheck function is used to manipulate the ApplyContext with view // changes that will cause the check to fail. - using Precheck = std::function; + using Precheck = std::function< + bool(test::jtx::Account const& a, test::jtx::Account const& b, ApplyContext& ac)>; static FeatureBitset defaultAmendments() { - return xrpl::test::jtx::testable_amendments() | featureInvariantsV1_1 | featureSingleAssetVault; + return xrpl::test::jtx::testable_amendments() | featureInvariantsV1_1 | + featureSingleAssetVault; } /** Run a specific test case to put the ledger into a state that will be @@ -69,8 +74,15 @@ class Invariants_test : public beast::unit_test::suite Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None) { - return doInvariantCheck( - test::jtx::Env(*this, defaultAmendments()), expect_logs, precheck, fee, tx, ters, preclose, setTxAccount); + doInvariantCheck( + test::jtx::Env(*this, defaultAmendments()), + expect_logs, + precheck, + fee, + tx, + ters, + preclose, + setTxAccount); } void @@ -96,11 +108,12 @@ class Invariants_test : public beast::unit_test::suite if (setTxAccount != TxAccount::None) tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id()); - return doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters); + doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters); } void doInvariantCheck( + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) test::jtx::Env&& env, test::jtx::Account const& A1, test::jtx::Account const& A2, @@ -130,7 +143,7 @@ class Invariants_test : public beast::unit_test::suite BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual))); auto const messages = sink.messages().str(); - if (terActual != tesSUCCESS) + if (!isTesSuccess(terActual)) { BEAST_EXPECTS( messages.starts_with("Invariant failed:") || @@ -152,7 +165,8 @@ class Invariants_test : public beast::unit_test::suite using namespace test::jtx; testcase << "XRP created"; doInvariantCheck( - {{"XRP net change was positive: 500"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"XRP net change was positive: 500"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // put a single account in the view and "manufacture" some XRP auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -171,18 +185,20 @@ class Invariants_test : public beast::unit_test::suite testcase << "account root removed"; // An account was deleted, but not by an AccountDelete transaction. - doInvariantCheck({{"an account root was deleted"}}, [](Account const& A1, Account const&, ApplyContext& ac) { - // remove an account from the view - auto sle = ac.view().peek(keylet::account(A1.id())); - if (!sle) - return false; - // Clear the balance so the "account deletion left behind a - // non-zero balance" check doesn't trip earlier than the desired - // check. - sle->at(sfBalance) = beast::zero; - ac.view().erase(sle); - return true; - }); + doInvariantCheck( + {{"an account root was deleted"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { + // remove an account from the view + auto sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + // Clear the balance so the "account deletion left behind a + // non-zero balance" check doesn't trip earlier than the desired + // check. + sle->at(sfBalance) = beast::zero; + ac.view().erase(sle); + return true; + }); // Successful AccountDelete transaction that didn't delete an account. // @@ -379,20 +395,24 @@ class Invariants_test : public beast::unit_test::suite BEAST_EXPECT(sle->at(~sfAMMID)); BEAST_EXPECT(sle->at(~sfAMMID) == ammKey); - for (auto const& trustKeylet : {keylet::line(ammAcctID, A1["USD"]), keylet::line(A1, ammIssue)}) + for (auto const& trustKeylet : + {keylet::line(ammAcctID, A1["USD"]), keylet::line(A1, ammIssue)}) { - if (auto const line = ac.view().peek(trustKeylet); !line) + auto const line = ac.view().peek(trustKeylet); + if (!line) { return false; } - else - { - STAmount const lowLimit = line->at(sfLowLimit); - STAmount const highLimit = line->at(sfHighLimit); - BEAST_EXPECT( - trustDelete(ac.view(), line, lowLimit.getIssuer(), highLimit.getIssuer(), ac.journal) == - tesSUCCESS); - } + + STAmount const lowLimit = line->at(sfLowLimit); + STAmount const highLimit = line->at(sfHighLimit); + BEAST_EXPECT( + trustDelete( + ac.view(), + line, + lowLimit.getIssuer(), + highLimit.getIssuer(), + ac.journal) == tesSUCCESS); } auto const ammSle = ac.view().peek(keylet::amm(ammKey)); @@ -400,8 +420,10 @@ class Invariants_test : public beast::unit_test::suite return false; auto const ownerDirKeylet = keylet::ownerDir(ammAcctID); - BEAST_EXPECT(ac.view().dirRemove(ownerDirKeylet, ammSle->at(sfOwnerNode), ammKey, false)); - BEAST_EXPECT(!ac.view().exists(ownerDirKeylet) || ac.view().emptyDirDelete(ownerDirKeylet)); + BEAST_EXPECT( + ac.view().dirRemove(ownerDirKeylet, ammSle->at(sfOwnerNode), ammKey, false)); + BEAST_EXPECT( + !ac.view().exists(ownerDirKeylet) || ac.view().emptyDirDelete(ownerDirKeylet)); // Clear the balance so the "account deletion left behind a // non-zero balance" check doesn't trip earlier than the desired @@ -444,7 +466,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"invalid ledger entry type added"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"invalid ledger entry type added"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // add an entry in the table with an SLE of an invalid type auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -453,7 +476,8 @@ class Invariants_test : public beast::unit_test::suite // make a dummy escrow ledger entry, then change the type to an // unsupported value so that the valid type invariant check // will fail. - auto const sleNew = std::make_shared(keylet::escrow(A1, (*sle)[sfSequence] + 2)); + auto const sleNew = + std::make_shared(keylet::escrow(A1, (*sle)[sfSequence] + 2)); // We don't use ltNICKNAME directly since it's marked deprecated // to prevent accidental use elsewhere. @@ -469,9 +493,11 @@ class Invariants_test : public beast::unit_test::suite using namespace test::jtx; testcase << "trust lines with XRP not allowed"; doInvariantCheck( - {{"an XRP trust line was created"}}, [](Account const& A1, Account const& A2, ApplyContext& ac) { + {{"an XRP trust line was created"}}, + [](Account const& A1, Account const& A2, ApplyContext& ac) { // create simple trust SLE with xrp currency - auto const sleNew = std::make_shared(keylet::line(A1, A2, xrpIssue().currency)); + auto const sleNew = + std::make_shared(keylet::line(A1, A2, xrpIssue().currency)); ac.view().insert(sleNew); return true; }); @@ -594,17 +620,20 @@ class Invariants_test : public beast::unit_test::suite return true; }; - auto const changeBalances = - [&](Account const& A1, Account const& A2, ApplyContext& ac, int A1Balance, int A2Balance) { - auto const sleA1 = ac.view().peek(keylet::line(A1, G1["USD"])); - auto const sleA2 = ac.view().peek(keylet::line(A2, G1["USD"])); + auto const changeBalances = [&](Account const& A1, + Account const& A2, + ApplyContext& ac, + int A1Balance, + int A2Balance) { + auto const sleA1 = ac.view().peek(keylet::line(A1, G1["USD"])); + auto const sleA2 = ac.view().peek(keylet::line(A2, G1["USD"])); - sleA1->setFieldAmount(sfBalance, G1["USD"](A1Balance)); - sleA2->setFieldAmount(sfBalance, G1["USD"](A2Balance)); + sleA1->setFieldAmount(sfBalance, G1["USD"](A1Balance)); + sleA2->setFieldAmount(sfBalance, G1["USD"](A2Balance)); - ac.view().update(sleA1); - ac.view().update(sleA2); - }; + ac.view().update(sleA1); + ac.view().update(sleA2); + }; // test: imitating frozen A1 making a payment to A2. doInvariantCheck( @@ -678,7 +707,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"incorrect account XRP balance"}, {"XRP net change of -1000000001 doesn't match fee 0"}}, + {{"incorrect account XRP balance"}, + {"XRP net change of -1000000001 doesn't match fee 0"}}, [this](Account const& A1, Account const&, ApplyContext& ac) { // balance is negative auto const sle = ac.view().peek(keylet::account(A1.id())); @@ -710,7 +740,8 @@ class Invariants_test : public beast::unit_test::suite XRPAmount{INITIAL_XRP}); doInvariantCheck( - {{"fee paid is 20 exceeds fee specified in transaction."}, {"XRP net change of 0 doesn't match fee 20"}}, + {{"fee paid is 20 exceeds fee specified in transaction."}, + {"XRP net change of 0 doesn't match fee 20"}}, [](Account const&, Account const&, ApplyContext&) { return true; }, XRPAmount{20}, STTx{ttACCOUNT_SET, [](STObject& tx) { tx.setFieldAmount(sfFee, XRPAmount{10}); }}); @@ -722,46 +753,49 @@ class Invariants_test : public beast::unit_test::suite using namespace test::jtx; testcase << "no bad offers"; - doInvariantCheck({{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { - // offer with negative takerpays - auto const sle = ac.view().peek(keylet::account(A1.id())); - if (!sle) - return false; - auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); - sleNew->setAccountID(sfAccount, A1.id()); - sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); - sleNew->setFieldAmount(sfTakerPays, XRP(-1)); - ac.view().insert(sleNew); - return true; - }); + doInvariantCheck( + {{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + // offer with negative takerpays + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); + sleNew->setAccountID(sfAccount, A1.id()); + sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); + sleNew->setFieldAmount(sfTakerPays, XRP(-1)); + ac.view().insert(sleNew); + return true; + }); - doInvariantCheck({{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { - // offer with negative takergets - auto const sle = ac.view().peek(keylet::account(A1.id())); - if (!sle) - return false; - auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); - sleNew->setAccountID(sfAccount, A1.id()); - sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); - sleNew->setFieldAmount(sfTakerPays, A1["USD"](10)); - sleNew->setFieldAmount(sfTakerGets, XRP(-1)); - ac.view().insert(sleNew); - return true; - }); + doInvariantCheck( + {{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + // offer with negative takergets + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); + sleNew->setAccountID(sfAccount, A1.id()); + sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); + sleNew->setFieldAmount(sfTakerPays, A1["USD"](10)); + sleNew->setFieldAmount(sfTakerGets, XRP(-1)); + ac.view().insert(sleNew); + return true; + }); - doInvariantCheck({{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { - // offer XRP to XRP - auto const sle = ac.view().peek(keylet::account(A1.id())); - if (!sle) - return false; - auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); - sleNew->setAccountID(sfAccount, A1.id()); - sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); - sleNew->setFieldAmount(sfTakerPays, XRP(10)); - sleNew->setFieldAmount(sfTakerGets, XRP(11)); - ac.view().insert(sleNew); - return true; - }); + doInvariantCheck( + {{"offer with a bad amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + // offer XRP to XRP + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + auto sleNew = std::make_shared(keylet::offer(A1.id(), (*sle)[sfSequence])); + sleNew->setAccountID(sfAccount, A1.id()); + sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]); + sleNew->setFieldAmount(sfTakerPays, XRP(10)); + sleNew->setFieldAmount(sfTakerGets, XRP(11)); + ac.view().insert(sleNew); + return true; + }); } void @@ -771,7 +805,8 @@ class Invariants_test : public beast::unit_test::suite testcase << "no zero escrow"; doInvariantCheck( - {{"XRP net change of -1000000 doesn't match fee 0"}, {"escrow specifies invalid amount"}}, + {{"XRP net change of -1000000 doesn't match fee 0"}, + {"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { // escrow with negative amount auto const sle = ac.view().peek(keylet::account(A1.id())); @@ -784,7 +819,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"XRP net change was positive: 100000000000000001"}, {"escrow specifies invalid amount"}}, + {{"XRP net change was positive: 100000000000000001"}, + {"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { // escrow with too-large amount auto const sle = ac.view().peek(keylet::account(A1.id())); @@ -800,7 +836,8 @@ class Invariants_test : public beast::unit_test::suite // IOU < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // escrow with too-little iou auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -816,7 +853,8 @@ class Invariants_test : public beast::unit_test::suite // IOU bad currency doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // escrow with bad iou currency auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -832,7 +870,8 @@ class Invariants_test : public beast::unit_test::suite // MPT < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // escrow with too-little mpt auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -848,7 +887,8 @@ class Invariants_test : public beast::unit_test::suite // MPT OutstandingAmount < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // mptissuance outstanding is negative auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -863,7 +903,8 @@ class Invariants_test : public beast::unit_test::suite // MPT LockedAmount < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // mptissuance locked is less than locked auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -878,7 +919,8 @@ class Invariants_test : public beast::unit_test::suite // MPT OutstandingAmount < LockedAmount doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // mptissuance outstanding is less than locked auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -894,7 +936,8 @@ class Invariants_test : public beast::unit_test::suite // MPT MPTAmount < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // mptoken amount is negative auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -909,7 +952,8 @@ class Invariants_test : public beast::unit_test::suite // MPT LockedAmount < 0 doInvariantCheck( - {{"escrow specifies invalid amount"}}, [](Account const& A1, Account const&, ApplyContext& ac) { + {{"escrow specifies invalid amount"}}, + [](Account const& A1, Account const&, ApplyContext& ac) { // mptoken locked amount is negative auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) @@ -929,15 +973,17 @@ class Invariants_test : public beast::unit_test::suite using namespace test::jtx; testcase << "valid new account root"; - doInvariantCheck({{"account root created illegally"}}, [](Account const&, Account const&, ApplyContext& ac) { - // Insert a new account root created by a non-payment into - // the view. - Account const A3{"A3"}; - Keylet const acctKeylet = keylet::account(A3); - auto const sleNew = std::make_shared(acctKeylet); - ac.view().insert(sleNew); - return true; - }); + doInvariantCheck( + {{"account root created illegally"}}, + [](Account const&, Account const&, ApplyContext& ac) { + // Insert a new account root created by a non-payment into + // the view. + Account const A3{"A3"}; + Keylet const acctKeylet = keylet::account(A3); + auto const sleNew = std::make_shared(acctKeylet); + ac.view().insert(sleNew); + return true; + }); doInvariantCheck( {{"multiple accounts created in a single transaction"}}, @@ -980,7 +1026,8 @@ class Invariants_test : public beast::unit_test::suite auto const sleNew = std::make_shared(acctKeylet); sleNew->setFieldU32(sfSequence, 0); sleNew->setFieldH256(sfAMMID, uint256(1)); - sleNew->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDefaultRipple); + sleNew->setFieldU32( + sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDefaultRipple); ac.view().insert(sleNew); return true; }, @@ -1025,7 +1072,9 @@ class Invariants_test : public beast::unit_test::suite auto const sleNew = std::make_shared(acctKeylet); sleNew->setFieldU32(sfSequence, 0); sleNew->setFieldH256(sfAMMID, uint256(1)); - sleNew->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth | lsfRequireDestTag); + sleNew->setFieldU32( + sfFlags, + lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth | lsfRequireDestTag); ac.view().insert(sleNew); return true; }, @@ -1040,9 +1089,11 @@ class Invariants_test : public beast::unit_test::suite testcase << "NFTokenPage"; // lambda that returns an STArray of NFTokenIDs. - uint256 const firstNFTID("0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000"); + uint256 const firstNFTID( + "0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000"); auto makeNFTokenIDs = [&firstNFTID](unsigned int nftCount) { - SOTemplate const* nfTokenTemplate = InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); + SOTemplate const* nfTokenTemplate = + InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); uint256 nftID(firstNFTID); STArray ret; @@ -1058,7 +1109,8 @@ class Invariants_test : public beast::unit_test::suite }; doInvariantCheck( - {{"NFT page has invalid size"}}, [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { + {{"NFT page has invalid size"}}, + [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { auto nftPage = std::make_shared(keylet::nftpage_max(A1)); nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(0)); @@ -1067,7 +1119,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"NFT page has invalid size"}}, [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { + {{"NFT page has invalid size"}}, + [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { auto nftPage = std::make_shared(keylet::nftpage_max(A1)); nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(33)); @@ -1076,7 +1129,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"NFTs on page are not sorted"}}, [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { + {{"NFTs on page are not sorted"}}, + [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { STArray nfTokens = makeNFTokenIDs(2); std::iter_swap(nfTokens.begin(), nfTokens.begin() + 1); @@ -1088,7 +1142,8 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"NFT contains empty URI"}}, [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { + {{"NFT contains empty URI"}}, + [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { STArray nfTokens = makeNFTokenIDs(1); nfTokens[0].setFieldVL(sfURI, Blob{}); @@ -1136,8 +1191,8 @@ class Invariants_test : public beast::unit_test::suite {{"NFT page is improperly linked"}}, [&makeNFTokenIDs](Account const& A1, Account const& A2, ApplyContext& ac) { STArray nfTokens = makeNFTokenIDs(1); - auto nftPage = std::make_shared( - keylet::nftpage(keylet::nftpage_max(A1), ++(nfTokens[0].getFieldH256(sfNFTokenID)))); + auto nftPage = std::make_shared(keylet::nftpage( + keylet::nftpage_max(A1), ++(nfTokens[0].getFieldH256(sfNFTokenID)))); nftPage->setFieldArray(sfNFTokens, std::move(nfTokens)); nftPage->setFieldH256(sfNextPageMin, keylet::nftpage_max(A2).key); @@ -1146,10 +1201,11 @@ class Invariants_test : public beast::unit_test::suite }); doInvariantCheck( - {{"NFT found in incorrect page"}}, [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { + {{"NFT found in incorrect page"}}, + [&makeNFTokenIDs](Account const& A1, Account const&, ApplyContext& ac) { STArray nfTokens = makeNFTokenIDs(2); - auto nftPage = std::make_shared( - keylet::nftpage(keylet::nftpage_max(A1), (nfTokens[1].getFieldH256(sfNFTokenID)))); + auto nftPage = std::make_shared(keylet::nftpage( + keylet::nftpage_max(A1), (nfTokens[1].getFieldH256(sfNFTokenID)))); nftPage->setFieldArray(sfNFTokens, std::move(nfTokens)); ac.view().insert(nftPage); @@ -1384,12 +1440,14 @@ class Invariants_test : public beast::unit_test::suite std::initializer_list goodTers = {tesSUCCESS, tesSUCCESS}; - std::vector badMoreThan1{{"transaction affected more than 1 permissioned domain entry."}}; + std::vector badMoreThan1{ + {"transaction affected more than 1 permissioned domain entry."}}; std::vector emptyV; std::vector badNoDomains{{"no domain objects affected by"}}; std::vector badNotDeleted{{"domain object modified, but not deleted by "}}; std::vector badDeleted{{"domain object deleted by"}}; - std::vector badTx{{"domain object(s) affected by an unauthorized transaction."}}; + std::vector badTx{ + {"domain object(s) affected by an unauthorized transaction."}}; { testcase << "PermissionedDomain set 2 domains "; @@ -1961,7 +2019,8 @@ class Invariants_test : public beast::unit_test::suite for (auto const& mod : mods) { doInvariantCheck( - {{"changed an unchangeable field"}}, [&](Account const& A1, Account const&, ApplyContext& ac) { + {{"changed an unchangeable field"}}, + [&](Account const& A1, Account const&, ApplyContext& ac) { auto sle = ac.view().peek(keylet::account(A1.id())); if (!sle) return false; @@ -1988,7 +2047,8 @@ class Invariants_test : public beast::unit_test::suite // Initialize with a placeholder value because there's no default // ctor Keylet loanBrokerKeylet = keylet::amendments(); - Preclose createLoanBroker = [&, this](Account const& alice, Account const& issuer, Env& env) { + Preclose createLoanBroker = [&, this]( + Account const& alice, Account const& issuer, Env& env) { PrettyAsset const asset = [&]() { switch (assetType) { @@ -2002,7 +2062,8 @@ class Invariants_test : public beast::unit_test::suite case Asset::MPT: { MPTTester mptt{env, issuer, mptInitNoFund}; - mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + mptt.create( + {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); PrettyAsset const mptAsset = mptt.issuanceID(); mptt.authorize({.account = alice}); env(pay(issuer, alice, mptAsset(1000))); @@ -2051,7 +2112,8 @@ class Invariants_test : public beast::unit_test::suite { // Create the directory BEAST_EXPECT( - ::xrpl::directory::createRoot(ac.view(), dirKeylet, loanBrokerKeylet.key, describe) == 0); + ::xrpl::directory::createRoot( + ac.view(), dirKeylet, loanBrokerKeylet.key, describe) == 0); sleDir = ac.view().peek(dirKeylet); } @@ -2155,7 +2217,8 @@ class Invariants_test : public beast::unit_test::suite // failure. STVector256 indexes; - ::xrpl::directory::insertKey(ac.view(), sleDir, 0, false, indexes, slePseudo->key()); + ::xrpl::directory::insertKey( + ac.view(), sleDir, 0, false, indexes, slePseudo->key()); return true; }, @@ -2201,14 +2264,16 @@ class Invariants_test : public beast::unit_test::suite }; struct Adjustments { - std::optional assetsTotal = {}; - std::optional assetsAvailable = {}; - std::optional lossUnrealized = {}; - std::optional assetsMaximum = {}; - std::optional sharesTotal = {}; - std::optional vaultAssets = {}; - std::optional accountAssets = {}; - std::optional accountShares = {}; + // NOLINTBEGIN(readability-redundant-member-init) + std::optional assetsTotal = std::nullopt; + std::optional assetsAvailable = std::nullopt; + std::optional lossUnrealized = std::nullopt; + std::optional assetsMaximum = std::nullopt; + std::optional sharesTotal = std::nullopt; + std::optional vaultAssets = std::nullopt; + std::optional accountAssets = std::nullopt; + std::optional accountShares = std::nullopt; + // NOLINTEND(readability-redundant-member-init) }; auto constexpr adjust = [&](ApplyView& ac, xrpl::Keylet keylet, Adjustments args) { auto sleVault = ac.peek(keylet); @@ -2230,12 +2295,16 @@ class Invariants_test : public beast::unit_test::suite if (args.assetsTotal) (*sleVault)[sfAssetsTotal] = *(*sleVault)[sfAssetsTotal] + *args.assetsTotal; if (args.assetsAvailable) - (*sleVault)[sfAssetsAvailable] = *(*sleVault)[sfAssetsAvailable] + *args.assetsAvailable; + { + (*sleVault)[sfAssetsAvailable] = + *(*sleVault)[sfAssetsAvailable] + *args.assetsAvailable; + } ac.update(sleVault); if (args.sharesTotal) { - (*sleShares)[sfOutstandingAmount] = *(*sleShares)[sfOutstandingAmount] + *args.sharesTotal; + (*sleShares)[sfOutstandingAmount] = + *(*sleShares)[sfOutstandingAmount] + *args.sharesTotal; ac.update(sleShares); } @@ -2248,7 +2317,8 @@ class Invariants_test : public beast::unit_test::suite auto slePseudoAccount = ac.peek(keylet::account(pseudoId)); if (!slePseudoAccount) return false; - (*slePseudoAccount)[sfBalance] = *(*slePseudoAccount)[sfBalance] + *args.vaultAssets; + (*slePseudoAccount)[sfBalance] = + *(*slePseudoAccount)[sfBalance] + *args.vaultAssets; ac.update(slePseudoAccount); } else if (assets.holds()) @@ -2261,7 +2331,9 @@ class Invariants_test : public beast::unit_test::suite ac.update(sleMPToken); } else + { return false; // Not supporting testing with IOU + } } if (args.accountAssets) @@ -2285,7 +2357,9 @@ class Invariants_test : public beast::unit_test::suite ac.update(sleMPToken); } else + { return false; // Not supporting testing with IOU + } } if (args.accountShares) @@ -2395,8 +2469,8 @@ class Invariants_test : public beast::unit_test::suite auto const sequence = ac.view().seq(); auto const vaultKeylet = keylet::vault(A1.id(), sequence); auto sleVault = std::make_shared(vaultKeylet); - auto const vaultPage = - ac.view().dirInsert(keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); + auto const vaultPage = ac.view().dirInsert( + keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); sleVault->setFieldU64(sfOwnerNode, *vaultPage); ac.view().insert(sleVault); return true; @@ -2467,8 +2541,8 @@ class Invariants_test : public beast::unit_test::suite auto const insertVault = [&](Account const A) { auto const vaultKeylet = keylet::vault(A.id(), sequence); auto sleVault = std::make_shared(vaultKeylet); - auto const vaultPage = - ac.view().dirInsert(keylet::ownerDir(A.id()), sleVault->key(), describeOwnerDir(A.id())); + auto const vaultPage = ac.view().dirInsert( + keylet::ownerDir(A.id()), sleVault->key(), describeOwnerDir(A.id())); sleVault->setFieldU64(sfOwnerNode, *vaultPage); ac.view().insert(sleVault); }; @@ -2750,7 +2824,8 @@ class Invariants_test : public beast::unit_test::suite precloseXrp); doInvariantCheck( - {"vault transaction must not change loss unrealized", "set must not change assets outstanding"}, + {"vault transaction must not change loss unrealized", + "set must not change assets outstanding"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); return adjust(ac.view(), keylet, args(A2.id(), 0, [&](Adjustments& sample) { @@ -2770,11 +2845,13 @@ class Invariants_test : public beast::unit_test::suite "vault transaction must not change loss unrealized"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 100, [&](Adjustments& sample) { sample.lossUnrealized = 13; })); + return adjust(ac.view(), keylet, args(A2.id(), 100, [&](Adjustments& sample) { + sample.lossUnrealized = 13; + })); }, XRPAmount{}, - STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, + STTx{ + ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -2783,8 +2860,9 @@ class Invariants_test : public beast::unit_test::suite {"set assets outstanding must not exceed assets maximum"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 0, [&](Adjustments& sample) { sample.assetsMaximum = 1; })); + return adjust(ac.view(), keylet, args(A2.id(), 0, [&](Adjustments& sample) { + sample.assetsMaximum = 1; + })); }, XRPAmount{}, STTx{ttVAULT_SET, [](STObject& tx) {}}, @@ -2796,8 +2874,9 @@ class Invariants_test : public beast::unit_test::suite {"assets maximum must be positive"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 0, [&](Adjustments& sample) { sample.assetsMaximum = -1; })); + return adjust(ac.view(), keylet, args(A2.id(), 0, [&](Adjustments& sample) { + sample.assetsMaximum = -1; + })); }, XRPAmount{}, STTx{ttVAULT_SET, [](STObject& tx) {}}, @@ -3039,8 +3118,8 @@ class Invariants_test : public beast::unit_test::suite auto const sequence = ac.view().seq(); auto const vaultKeylet = keylet::vault(A1.id(), sequence); auto sleVault = std::make_shared(vaultKeylet); - auto const vaultPage = - ac.view().dirInsert(keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); + auto const vaultPage = ac.view().dirInsert( + keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); sleVault->setFieldU64(sfOwnerNode, *vaultPage); auto pseudoId = pseudoAccountAddress(ac.view(), vaultKeylet.key); @@ -3053,15 +3132,16 @@ class Invariants_test : public beast::unit_test::suite ? 0 // : sequence; sleAccount->setFieldU32(sfSequence, seqno); - sleAccount->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + sleAccount->setFieldU32( + sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); sleAccount->setFieldH256(sfVaultID, vaultKeylet.key); ac.view().insert(sleAccount); auto const sharesMptId = makeMptID(sequence, pseudoId); auto const sharesKeylet = keylet::mptIssuance(sharesMptId); auto sleShares = std::make_shared(sharesKeylet); - auto const sharesPage = - ac.view().dirInsert(keylet::ownerDir(pseudoId), sharesKeylet, describeOwnerDir(pseudoId)); + auto const sharesPage = ac.view().dirInsert( + keylet::ownerDir(pseudoId), sharesKeylet, describeOwnerDir(pseudoId)); sleShares->setFieldU64(sfOwnerNode, *sharesPage); sleShares->at(sfFlags) = 0; @@ -3094,8 +3174,8 @@ class Invariants_test : public beast::unit_test::suite auto const sequence = ac.view().seq(); auto const vaultKeylet = keylet::vault(A1.id(), sequence); auto sleVault = std::make_shared(vaultKeylet); - auto const vaultPage = - ac.view().dirInsert(keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); + auto const vaultPage = ac.view().dirInsert( + keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); sleVault->setFieldU64(sfOwnerNode, *vaultPage); auto pseudoId = pseudoAccountAddress(ac.view(), vaultKeylet.key); @@ -3108,7 +3188,8 @@ class Invariants_test : public beast::unit_test::suite ? 0 // : sequence; sleAccount->setFieldU32(sfSequence, seqno); - sleAccount->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); + sleAccount->setFieldU32( + sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth); // sleAccount->setFieldH256(sfVaultID, vaultKeylet.key); // Setting wrong vault key sleAccount->setFieldH256(sfVaultID, uint256(42)); @@ -3117,8 +3198,8 @@ class Invariants_test : public beast::unit_test::suite auto const sharesMptId = makeMptID(sequence, pseudoId); auto const sharesKeylet = keylet::mptIssuance(sharesMptId); auto sleShares = std::make_shared(sharesKeylet); - auto const sharesPage = - ac.view().dirInsert(keylet::ownerDir(pseudoId), sharesKeylet, describeOwnerDir(pseudoId)); + auto const sharesPage = ac.view().dirInsert( + keylet::ownerDir(pseudoId), sharesKeylet, describeOwnerDir(pseudoId)); sleShares->setFieldU64(sfOwnerNode, *sharesPage); sleShares->at(sfFlags) = 0; @@ -3152,15 +3233,15 @@ class Invariants_test : public beast::unit_test::suite auto const sequence = ac.view().seq(); auto const vaultKeylet = keylet::vault(A1.id(), sequence); auto sleVault = std::make_shared(vaultKeylet); - auto const vaultPage = - ac.view().dirInsert(keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); + auto const vaultPage = ac.view().dirInsert( + keylet::ownerDir(A1.id()), sleVault->key(), describeOwnerDir(A1.id())); sleVault->setFieldU64(sfOwnerNode, *vaultPage); auto const sharesMptId = makeMptID(sequence, A2.id()); auto const sharesKeylet = keylet::mptIssuance(sharesMptId); auto sleShares = std::make_shared(sharesKeylet); - auto const sharesPage = - ac.view().dirInsert(keylet::ownerDir(A2.id()), sharesKeylet, describeOwnerDir(A2.id())); + auto const sharesPage = ac.view().dirInsert( + keylet::ownerDir(A2.id()), sharesKeylet, describeOwnerDir(A2.id())); sleShares->setFieldU64(sfOwnerNode, *sharesPage); sleShares->at(sfFlags) = 0; @@ -3192,8 +3273,9 @@ class Invariants_test : public beast::unit_test::suite {"deposit must change vault balance"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 0, [](Adjustments& sample) { sample.vaultAssets.reset(); })); + return adjust(ac.view(), keylet, args(A2.id(), 0, [](Adjustments& sample) { + sample.vaultAssets.reset(); + })); }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject&) {}}, @@ -3204,11 +3286,13 @@ class Invariants_test : public beast::unit_test::suite {"deposit assets outstanding must not exceed assets maximum"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 200, [&](Adjustments& sample) { sample.assetsMaximum = 1; })); + return adjust(ac.view(), keylet, args(A2.id(), 200, [&](Adjustments& sample) { + sample.assetsMaximum = 1; + })); }, XRPAmount{}, - STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, + STTx{ + ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -3296,8 +3380,9 @@ class Invariants_test : public beast::unit_test::suite {"deposit must change depositor shares"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 10, [&](Adjustments& sample) { sample.accountShares.reset(); })); + return adjust(ac.view(), keylet, args(A2.id(), 10, [&](Adjustments& sample) { + sample.accountShares.reset(); + })); }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, @@ -3310,8 +3395,9 @@ class Invariants_test : public beast::unit_test::suite [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 10, [](Adjustments& sample) { sample.sharesTotal = 0; })); + return adjust(ac.view(), keylet, args(A2.id(), 10, [](Adjustments& sample) { + sample.sharesTotal = 0; + })); }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, @@ -3345,8 +3431,9 @@ class Invariants_test : public beast::unit_test::suite ac.view().update(sleA3); auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 10, [&](Adjustments& sample) { sample.assetsTotal = 11; })); + return adjust(ac.view(), keylet, args(A2.id(), 10, [&](Adjustments& sample) { + sample.assetsTotal = 11; + })); }, XRPAmount{2000}, STTx{ @@ -3361,7 +3448,8 @@ class Invariants_test : public beast::unit_test::suite TxAccount::A2); doInvariantCheck( - {"deposit and assets outstanding must add up", "deposit and assets available must add up"}, + {"deposit and assets outstanding must add up", + "deposit and assets available must add up"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); return adjust(ac.view(), keylet, args(A2.id(), 10, [&](Adjustments& sample) { @@ -3380,8 +3468,9 @@ class Invariants_test : public beast::unit_test::suite {"withdrawal must change vault balance"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), 0, [](Adjustments& sample) { sample.vaultAssets.reset(); })); + return adjust(ac.view(), keylet, args(A2.id(), 0, [](Adjustments& sample) { + sample.vaultAssets.reset(); + })); }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, @@ -3421,12 +3510,13 @@ class Invariants_test : public beast::unit_test::suite precloseXrp); doInvariantCheck( - {"withdrawal must change vault and destination balance by " - "equal amount", - "withdrawal must decrease vault balance", - "withdrawal must increase destination balance", - "withdrawal and assets outstanding must add up", - "withdrawal and assets available must add up"}, + { + "withdrawal must change vault and destination balance by equal amount", + "withdrawal must decrease vault balance", + "withdrawal must increase destination balance", + "withdrawal and assets outstanding must add up", + "withdrawal and assets available must add up", + }, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); @@ -3452,8 +3542,9 @@ class Invariants_test : public beast::unit_test::suite {"withdrawal must change one destination balance"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - if (!adjust( - ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { *sample.vaultAssets -= 5; }))) + if (!adjust(ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { + *sample.vaultAssets -= 5; + }))) return false; auto sleA3 = ac.view().peek(keylet::account(A3.id())); if (!sleA3) @@ -3472,8 +3563,9 @@ class Invariants_test : public beast::unit_test::suite {"withdrawal must change depositor shares"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { sample.accountShares.reset(); })); + return adjust(ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { + sample.accountShares.reset(); + })); }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, @@ -3485,8 +3577,9 @@ class Invariants_test : public beast::unit_test::suite {"withdrawal must change vault shares"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), -10, [](Adjustments& sample) { sample.sharesTotal = 0; })); + return adjust(ac.view(), keylet, args(A2.id(), -10, [](Adjustments& sample) { + sample.sharesTotal = 0; + })); }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, @@ -3512,7 +3605,8 @@ class Invariants_test : public beast::unit_test::suite TxAccount::A2); doInvariantCheck( - {"withdrawal and assets outstanding must add up", "withdrawal and assets available must add up"}, + {"withdrawal and assets outstanding must add up", + "withdrawal and assets available must add up"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq()); return adjust(ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { @@ -3534,8 +3628,9 @@ class Invariants_test : public beast::unit_test::suite ac.view().update(sleA3); auto const keylet = keylet::vault(A1.id(), ac.view().seq()); - return adjust( - ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { sample.assetsTotal = -7; })); + return adjust(ac.view(), keylet, args(A2.id(), -10, [&](Adjustments& sample) { + sample.assetsTotal = -7; + })); }, XRPAmount{2000}, STTx{ @@ -3616,8 +3711,9 @@ class Invariants_test : public beast::unit_test::suite {"clawback must change vault balance"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq() - 2); - return adjust( - ac.view(), keylet, args(A2.id(), -1, [&](Adjustments& sample) { sample.vaultAssets.reset(); })); + return adjust(ac.view(), keylet, args(A2.id(), -1, [&](Adjustments& sample) { + sample.vaultAssets.reset(); + })); }, XRPAmount{}, STTx{ttVAULT_CLAWBACK, [&](STObject& tx) { tx[sfAccount] = A3.id(); }}, @@ -3654,8 +3750,9 @@ class Invariants_test : public beast::unit_test::suite "clawback must change vault shares"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq() - 2); - return adjust( - ac.view(), keylet, args(A4.id(), 10, [&](Adjustments& sample) { sample.sharesTotal = 0; })); + return adjust(ac.view(), keylet, args(A4.id(), 10, [&](Adjustments& sample) { + sample.sharesTotal = 0; + })); }, XRPAmount{}, STTx{ @@ -3671,8 +3768,9 @@ class Invariants_test : public beast::unit_test::suite {"clawback must change holder shares"}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const keylet = keylet::vault(A1.id(), ac.view().seq() - 2); - return adjust( - ac.view(), keylet, args(A4.id(), -10, [&](Adjustments& sample) { sample.accountShares.reset(); })); + return adjust(ac.view(), keylet, args(A4.id(), -10, [&](Adjustments& sample) { + sample.accountShares.reset(); + })); }, XRPAmount{}, STTx{ diff --git a/src/test/app/LPTokenTransfer_test.cpp b/src/test/app/LPTokenTransfer_test.cpp index 055b72ebe6..265ff7d2ef 100644 --- a/src/test/app/LPTokenTransfer_test.cpp +++ b/src/test/app/LPTokenTransfer_test.cpp @@ -97,7 +97,10 @@ class LPTokenTransfer_test : public jtx::AMMTest { // with fixFrozenLPTokenTransfer, alice fails to consume carol's // offer since carol's USD is frozen - env(pay(alice, bob, STAmount{lpIssue, 10}), txflags(tfPartialPayment), sendmax(XRP(10)), ter(tecPATH_DRY)); + env(pay(alice, bob, STAmount{lpIssue, 10}), + txflags(tfPartialPayment), + sendmax(XRP(10)), + ter(tecPATH_DRY)); env.close(); BEAST_EXPECT(expectOffers(env, carol, 1)); @@ -106,7 +109,9 @@ class LPTokenTransfer_test : public jtx::AMMTest env.close(); // alice successfully consumes carol's offer - env(pay(alice, bob, STAmount{lpIssue, 10}), txflags(tfPartialPayment), sendmax(XRP(10))); + env(pay(alice, bob, STAmount{lpIssue, 10}), + txflags(tfPartialPayment), + sendmax(XRP(10))); env.close(); BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -114,7 +119,9 @@ class LPTokenTransfer_test : public jtx::AMMTest { // without fixFrozenLPTokenTransfer, alice can consume carol's offer // even when carol's USD is frozen - env(pay(alice, bob, STAmount{lpIssue, 10}), txflags(tfPartialPayment), sendmax(XRP(10))); + env(pay(alice, bob, STAmount{lpIssue, 10}), + txflags(tfPartialPayment), + sendmax(XRP(10))); env.close(); BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -136,7 +143,9 @@ class LPTokenTransfer_test : public jtx::AMMTest env.close(); // alice successfully consumes carol's offer - env(pay(alice, bob, XRP(10)), txflags(tfPartialPayment), sendmax(STAmount{lpIssue, 10})); + env(pay(alice, bob, XRP(10)), + txflags(tfPartialPayment), + sendmax(STAmount{lpIssue, 10})); env.close(); BEAST_EXPECT(expectOffers(env, carol, 0)); } @@ -169,7 +178,9 @@ class LPTokenTransfer_test : public jtx::AMMTest // sell lptoken when one of the assets is frozen // carol can't create an offer to sell lptoken - env(offer(carol, XRP(10), STAmount{lpIssue, 10}), txflags(tfPassive), ter(tecUNFUNDED_OFFER)); + env(offer(carol, XRP(10), STAmount{lpIssue, 10}), + txflags(tfPassive), + ter(tecUNFUNDED_OFFER)); env.close(); BEAST_EXPECT(expectOffers(env, carol, 0)); @@ -288,9 +299,13 @@ class LPTokenTransfer_test : public jtx::AMMTest // with fixFrozenLPTokenTransfer enabled, bob fails to cash the check if (features[fixFrozenLPTokenTransfer]) + { env(check::cash(bob, carolChkId, STAmount{lpIssue, 10}), ter(tecPATH_PARTIAL)); + } else + { env(check::cash(bob, carolChkId, STAmount{lpIssue, 10})); + } env.close(); @@ -361,7 +376,9 @@ class LPTokenTransfer_test : public jtx::AMMTest // bob fails to create a buy offer with lptoken for carol's nft // since bob's USD is frozen - env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}), token::owner(carol), ter(tecUNFUNDED_OFFER)); + env(token::createOffer(bob, nftID, STAmount{lpIssue, 10}), + token::owner(carol), + ter(tecUNFUNDED_OFFER)); env.close(); // gateway unfreezes bob's USD diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index eeca551f35..93ab4a6210 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -3,11 +3,11 @@ #include #include -#include #include #include #include +#include #include #include @@ -37,7 +37,10 @@ public: { assert(!stx); return std::make_shared( - create_genesis, env.app().config(), std::vector{}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{}, + env.app().getNodeFamily()); } auto res = std::make_shared(*prev, prev->header().closeTime + closeOffset); @@ -56,7 +59,10 @@ public: res->unshare(); // Accept ledger - res->setAccepted(res->header().closeTime, res->header().closeTimeResolution, true /* close time correct*/); + res->setAccepted( + res->header().closeTime, + res->header().closeTimeResolution, + true /* close time correct*/); lh.insert(res, false); return res; } @@ -84,7 +90,10 @@ public: // Close time mismatch { bool found = false; - Env env{*this, envconfig(), std::make_unique("MISMATCH on close time", &found)}; + Env env{ + *this, + envconfig(), + std::make_unique("MISMATCH on close time", &found)}; LedgerHistory lh{beast::insight::NullCollector::New(), env.app()}; auto const genesis = makeLedger({}, env, lh, 0s); auto const ledgerA = makeLedger(genesis, env, lh, 4s); @@ -100,7 +109,10 @@ public: // Prior ledger mismatch { bool found = false; - Env env{*this, envconfig(), std::make_unique("MISMATCH on prior ledger", &found)}; + Env env{ + *this, + envconfig(), + std::make_unique("MISMATCH on prior ledger", &found)}; LedgerHistory lh{beast::insight::NullCollector::New(), env.app()}; auto const genesis = makeLedger({}, env, lh, 0s); auto const ledgerA = makeLedger(genesis, env, lh, 4s); @@ -119,8 +131,8 @@ public: // somehow generate different ledgers for (bool const txBug : {true, false}) { - std::string const msg = - txBug ? "MISMATCH with same consensus transaction set" : "MISMATCH on consensus transaction set"; + std::string const msg = txBug ? "MISMATCH with same consensus transaction set" + : "MISMATCH on consensus transaction set"; bool found = false; Env env{*this, envconfig(), std::make_unique(msg, &found)}; LedgerHistory lh{beast::insight::NullCollector::New(), env.app()}; @@ -141,7 +153,8 @@ public: lh.builtLedger(ledgerA, txAlice.stx->getTransactionID(), {}); // Simulate the bug by claiming ledgerB had the same consensus hash // as ledgerA, but somehow generated different ledgers - lh.validatedLedger(ledgerB, txBug ? txAlice.stx->getTransactionID() : txBob.stx->getTransactionID()); + lh.validatedLedger( + ledgerB, txBug ? txAlice.stx->getTransactionID() : txBob.stx->getTransactionID()); BEAST_EXPECT(found); } diff --git a/src/test/app/LedgerLoad_test.cpp b/src/test/app/LedgerLoad_test.cpp index f27edadd58..46c372923e 100644 --- a/src/test/app/LedgerLoad_test.cpp +++ b/src/test/app/LedgerLoad_test.cpp @@ -21,7 +21,7 @@ class LedgerLoad_test : public beast::unit_test::suite std::unique_ptr cfg, std::string const& dbPath, std::string const& ledger, - Config::StartUpType type, + StartUpType type, std::optional trapTxHash) { cfg->START_LEDGER = ledger; @@ -36,17 +36,19 @@ class LedgerLoad_test : public beast::unit_test::suite struct SetupData { std::string const dbPath; - std::string ledgerFile{}; - Json::Value ledger{}; - Json::Value hashes{}; - uint256 trapTxHash{}; + // NOLINTBEGIN(readability-redundant-member-init) + std::string ledgerFile = {}; + Json::Value ledger = {}; + Json::Value hashes = {}; + uint256 trapTxHash = {}; + // NOLINTEND(readability-redundant-member-init) }; SetupData setupLedger(beast::temp_dir const& td) { using namespace test::jtx; - SetupData retval = {td.path()}; + SetupData retval = {.dbPath = td.path()}; retval.ledgerFile = td.file("ledgerdata.json"); @@ -58,10 +60,11 @@ class LedgerLoad_test : public beast::unit_test::suite Account acct{"A" + std::to_string(i)}; env.fund(XRP(10000), acct); env.close(); - if (i > 0 && BEAST_EXPECT(prev)) + if (i > 0 && BEAST_EXPECT(prev.has_value())) { - env.trust(acct["USD"](1000), *prev); - env(pay(acct, *prev, acct["USD"](5))); + env.trust(acct["USD"](1000), *prev); // NOLINT(bugprone-unchecked-optional-access) + env(pay( + acct, *prev, acct["USD"](5))); // NOLINT(bugprone-unchecked-optional-access) } env(offer(acct, XRP(100), acct["USD"](1))); env.close(); @@ -82,7 +85,8 @@ class LedgerLoad_test : public beast::unit_test::suite BEAST_EXPECT(retval.hashes.size() == 41); retval.trapTxHash = [&]() { - auto const txs = env.rpc("ledger", std::to_string(41), "tx")[jss::result][jss::ledger][jss::transactions]; + auto const txs = env.rpc( + "ledger", std::to_string(41), "tx")[jss::result][jss::ledger][jss::transactions]; BEAST_EXPECT(txs.isArray() && txs.size() > 0); uint256 tmp; BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString())); @@ -105,11 +109,13 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger file specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; - BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + sd.ledger[jss::ledger][jss::accountState].size() == + jrb[jss::ledger][jss::accountState].size()); } void @@ -123,7 +129,7 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "", Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "", StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -132,7 +138,8 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "badfile.json", Config::LOAD_FILE, std::nullopt), + envconfig( + ledgerConfig, sd.dbPath, "badfile.json", StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -153,7 +160,12 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerFileCorrupt.string(), Config::LOAD_FILE, std::nullopt), + envconfig( + ledgerConfig, + sd.dbPath, + ledgerFileCorrupt.string(), + StartUpType::LOAD_FILE, + std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -170,12 +182,14 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98); - BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + jrb[jss::ledger][jss::accountState].size() <= + sd.ledger[jss::ledger][jss::accountState].size()); } void @@ -189,7 +203,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, std::nullopt), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -199,7 +213,9 @@ class LedgerLoad_test : public beast::unit_test::suite env.close(); auto const closed = env.rpc("ledger", "current", "full")[jss::result]; BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98); - BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + closed[jss::ledger][jss::accountState].size() <= + sd.ledger[jss::ledger][jss::accountState].size()); } void @@ -213,7 +229,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, sd.trapTxHash), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -223,7 +239,9 @@ class LedgerLoad_test : public beast::unit_test::suite env.close(); auto const closed = env.rpc("ledger", "current", "full")[jss::result]; BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98); - BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + closed[jss::ledger][jss::accountState].size() <= + sd.ledger[jss::ledger][jss::accountState].size()); } void @@ -241,7 +259,7 @@ class LedgerLoad_test : public beast::unit_test::suite // replay when trapTxHash is set to an invalid transaction Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, ~sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, ~sd.trapTxHash), nullptr, beast::severities::kDisabled); BEAST_EXPECT(false); @@ -265,11 +283,13 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger "latest" specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "latest", Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "latest", StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; - BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + sd.ledger[jss::ledger][jss::accountState].size() == + jrb[jss::ledger][jss::accountState].size()); } void @@ -281,11 +301,13 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with specific ledger index at startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "43", Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "43", StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; - BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size()); + BEAST_EXPECT( + sd.ledger[jss::ledger][jss::accountState].size() == + jrb[jss::ledger][jss::accountState].size()); } public: diff --git a/src/test/app/LedgerMaster_test.cpp b/src/test/app/LedgerMaster_test.cpp index eb346288c2..b48579043e 100644 --- a/src/test/app/LedgerMaster_test.cpp +++ b/src/test/app/LedgerMaster_test.cpp @@ -78,18 +78,22 @@ class LedgerMaster_test : public beast::unit_test::suite uint32_t txnIndex = metas[0]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq, txnIndex); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *result == - uint256("277F4FD89C20B92457FEF05FF63F6405563AD0563C73D967A29727" - "72679ADC65")); + uint256( + "277F4FD89C20B92457FEF05FF63F6405563AD0563C73D967A29727" + "72679ADC65")); } // success (second tx) { uint32_t txnIndex = metas[1]->getFieldU32(sfTransactionIndex); auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq + 1, txnIndex); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *result == - uint256("293DF7335EBBAF4420D52E70ABF470EB4C5792CAEA2F91F76193C2" - "819F538FDE")); + uint256( + "293DF7335EBBAF4420D52E70ABF470EB4C5792CAEA2F91F76193C2" + "819F538FDE")); } } diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 229cad21c9..54c12b465a 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -41,7 +41,8 @@ struct LedgerReplay_test : public beast::unit_test::suite auto const lastClosed = ledgerMaster.getClosedLedger(); auto const lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash); - auto const replayed = buildLedger(LedgerReplay(lastClosedParent, lastClosed), tapNONE, env.app(), env.journal); + auto const replayed = buildLedger( + LedgerReplay(lastClosedParent, lastClosed), tapNONE, env.app(), env.journal); BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash); } @@ -60,7 +61,10 @@ enum class InboundLedgersBehavior { class MagicInboundLedgers : public InboundLedgers { public: - MagicInboundLedgers(LedgerMaster& ledgerSource, LedgerMaster& ledgerSink, InboundLedgersBehavior bhvr) + MagicInboundLedgers( + LedgerMaster& ledgerSource, + LedgerMaster& ledgerSink, + InboundLedgersBehavior bhvr) : ledgerSource(ledgerSource), ledgerSink(ledgerSink), bhvr(bhvr) { } @@ -92,7 +96,10 @@ public: } virtual bool - gotLedgerData(LedgerHash const& ledgerHash, std::shared_ptr, std::shared_ptr) override + gotLedgerData( + LedgerHash const& ledgerHash, + std::shared_ptr, + std::shared_ptr) override { return false; } @@ -174,7 +181,8 @@ class TestPeer : public Peer { public: TestPeer(bool enableLedgerReplay) - : ledgerReplayEnabled_(enableLedgerReplay), nodePublicKey_(derivePublicKey(KeyType::ed25519, randomSecretKey())) + : ledgerReplayEnabled_(enableLedgerReplay) + , nodePublicKey_(derivePublicKey(KeyType::ed25519, randomSecretKey())) { } @@ -322,7 +330,10 @@ struct TestPeerSet : public PeerSet LedgerReplayMsgHandler& other, PeerSetBehavior bhvr, bool enableLedgerReplay) - : local(me), remote(other), dummyPeer(std::make_shared(enableLedgerReplay)), behavior(bhvr) + : local(me) + , remote(other) + , dummyPeer(std::make_shared(enableLedgerReplay)) + , behavior(bhvr) { } @@ -337,14 +348,20 @@ struct TestPeerSet : public PeerSet } void - sendRequest(::google::protobuf::Message const& msg, protocol::MessageType type, std::shared_ptr const& peer) - override + sendRequest( + ::google::protobuf::Message const& msg, + protocol::MessageType type, + std::shared_ptr const& peer) override { int dropRate = 0; if (behavior == PeerSetBehavior::Drop50) + { dropRate = 50; + } else if (behavior == PeerSetBehavior::DropAll) + { dropRate = 100; + } if ((rand() % 100 + 1) <= dropRate) return; @@ -356,7 +373,8 @@ struct TestPeerSet : public PeerSet return; auto request = std::make_shared( dynamic_cast(msg)); - auto reply = std::make_shared(remote.processProofPathRequest(request)); + auto reply = std::make_shared( + remote.processProofPathRequest(request)); local.processProofPathResponse(reply); if (behavior == PeerSetBehavior::Repeat) local.processProofPathResponse(reply); @@ -367,8 +385,8 @@ struct TestPeerSet : public PeerSet return; auto request = std::make_shared( dynamic_cast(msg)); - auto reply = - std::make_shared(remote.processReplayDeltaRequest(request)); + auto reply = std::make_shared( + remote.processReplayDeltaRequest(request)); local.processReplayDeltaResponse(reply); if (behavior == PeerSetBehavior::Repeat) local.processReplayDeltaResponse(reply); @@ -403,7 +421,10 @@ public: LedgerReplayMsgHandler& other, PeerSetBehavior bhvr, PeerFeature peerFeature) - : local(me), remote(other), behavior(bhvr), enableLedgerReplay(peerFeature == PeerFeature::LedgerReplayEnabled) + : local(me) + , remote(other) + , behavior(bhvr) + , enableLedgerReplay(peerFeature == PeerFeature::LedgerReplayEnabled) { } @@ -480,7 +501,7 @@ struct LedgerServer auto updateIdx = [&]() { assert(fundedAccounts > senders.size()); fromIdx = (fromIdx + r) % fundedAccounts; - while (senders.count(fromIdx) != 0) + while (senders.contains(fromIdx)) fromIdx = (fromIdx + 1) % fundedAccounts; senders.insert(fromIdx); toIdx = (toIdx + r * 2) % fundedAccounts; @@ -493,7 +514,8 @@ struct LedgerServer updateIdx(); env(pay(accounts[fromIdx], accounts[toIdx], - jtx::drops(ledgerMaster.getClosedLedger()->fees().base) + jtx::XRP(param.txAmount)), + jtx::drops(ledgerMaster.getClosedLedger()->fees().base) + + jtx::XRP(param.txAmount)), jtx::seq(jtx::autofill), jtx::fee(jtx::autofill), jtx::sig(jtx::autofill)); @@ -553,7 +575,11 @@ public: , replayer( env.app(), inboundLedgers, - std::make_unique(clientMsgHandler, serverMsgHandler, behavior, peerFeature)) + std::make_unique( + clientMsgHandler, + serverMsgHandler, + behavior, + peerFeature)) { } @@ -778,7 +804,10 @@ public: using namespace beast::severities; void -logAll(LedgerServer& server, LedgerReplayClient& client, beast::severities::Severity level = Severity::kTrace) +logAll( + LedgerServer& server, + LedgerReplayClient& client, + beast::severities::Severity level = Severity::kTrace) { server.app.logs().threshold(level); client.app.logs().threshold(level); @@ -843,8 +872,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite auto request = std::make_shared(); request->set_ledgerhash(l->header().hash.data(), l->header().hash.size()); request->set_type(protocol::TMLedgerMapType::lmACCOUNT_STATE); - auto reply = - std::make_shared(server.msgHandler.processProofPathRequest(request)); + auto reply = std::make_shared( + server.msgHandler.processProofPathRequest(request)); BEAST_EXPECT(reply->has_error()); BEAST_EXPECT(!server.msgHandler.processProofPathResponse(reply)); } @@ -855,8 +884,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite request->set_key(keylet::skip().key.data(), keylet::skip().key.size()); uint256 hash(1234567); request->set_ledgerhash(hash.data(), hash.size()); - auto reply = - std::make_shared(server.msgHandler.processProofPathRequest(request)); + auto reply = std::make_shared( + server.msgHandler.processProofPathRequest(request)); BEAST_EXPECT(reply->has_error()); } @@ -867,8 +896,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite request->set_type(protocol::TMLedgerMapType::lmACCOUNT_STATE); request->set_key(keylet::skip().key.data(), keylet::skip().key.size()); // generate response - auto reply = - std::make_shared(server.msgHandler.processProofPathRequest(request)); + auto reply = std::make_shared( + server.msgHandler.processProofPathRequest(request)); BEAST_EXPECT(!reply->has_error()); BEAST_EXPECT(server.msgHandler.processProofPathResponse(reply)); @@ -899,15 +928,15 @@ struct LedgerReplayer_test : public beast::unit_test::suite { // request, missing hash auto request = std::make_shared(); - auto reply = - std::make_shared(server.msgHandler.processReplayDeltaRequest(request)); + auto reply = std::make_shared( + server.msgHandler.processReplayDeltaRequest(request)); BEAST_EXPECT(reply->has_error()); BEAST_EXPECT(!server.msgHandler.processReplayDeltaResponse(reply)); // request, wrong hash uint256 hash(1234567); request->set_ledgerhash(hash.data(), hash.size()); - reply = - std::make_shared(server.msgHandler.processReplayDeltaRequest(request)); + reply = std::make_shared( + server.msgHandler.processReplayDeltaRequest(request)); BEAST_EXPECT(reply->has_error()); BEAST_EXPECT(!server.msgHandler.processReplayDeltaResponse(reply)); } @@ -916,8 +945,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite // good request auto request = std::make_shared(); request->set_ledgerhash(l->header().hash.data(), l->header().hash.size()); - auto reply = - std::make_shared(server.msgHandler.processReplayDeltaRequest(request)); + auto reply = std::make_shared( + server.msgHandler.processReplayDeltaRequest(request)); BEAST_EXPECT(!reply->has_error()); BEAST_EXPECT(server.msgHandler.processReplayDeltaResponse(reply)); @@ -943,7 +972,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite { testcase("TaskParameter"); - auto makeSkipList = [](int count) -> std::vector const { + auto makeSkipList = [](int count) -> std::vector { std::vector sList; for (int i = 0; i < count; ++i) sList.emplace_back(i); @@ -1034,7 +1063,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite beast::IP::Address addr = boost::asio::ip::make_address("172.1.1.100"); jtx::Env serverEnv(*this); serverEnv.app().config().LEDGER_REPLAY = server; - auto http_resp = xrpl::makeResponse(true, http_request, addr, addr, uint256{1}, 1, {1, 0}, serverEnv.app()); + auto http_resp = xrpl::makeResponse( + true, http_request, addr, addr, uint256{1}, 1, {1, 0}, serverEnv.app()); auto const clientResult = peerFeatureEnabled(http_resp, FEATURE_LEDGER_REPLAY, client); if (clientResult != expecting) return false; @@ -1069,7 +1099,9 @@ struct LedgerReplayer_test : public beast::unit_test::suite l = net.server.ledgerMaster.getLedgerByHash(l->header().parentHash); } else + { break; + } } net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); @@ -1088,7 +1120,11 @@ struct LedgerReplayer_test : public beast::unit_test::suite { testcase("all the ledgers from InboundLedgers"); NetworkOfTwo net( - *this, {totalReplay + 1}, PeerSetBehavior::DropAll, InboundLedgersBehavior::Good, PeerFeature::None); + *this, + {totalReplay + 1}, + PeerSetBehavior::DropAll, + InboundLedgersBehavior::Good, + PeerFeature::None); auto l = net.server.ledgerMaster.getClosedLedger(); uint256 finalHash = l->header().hash; @@ -1098,9 +1134,27 @@ struct LedgerReplayer_test : public beast::unit_test::suite BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash, totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); - // sweep - net.client.replayer.sweep(); - BEAST_EXPECT(net.client.countsAsExpected(0, 0, 0)); + // sweep() cleans up skipLists_ and deltas_ by removing entries whose + // weak_ptr can no longer be locked. Those weak_ptrs expire only when the + // last shared_ptr holder releases the sub-task. The sole owner is the + // LedgerReplayTask, but a JobQueue worker thread may still hold a + // temporary shared_ptr to a sub-task (from wptr.lock()) while executing + // the timer job that drove the task to completion. If sweep() runs before + // that thread unwinds, the weak_ptr is still lockable and the map entry + // is not removed. We retry until the worker thread finishes. + auto waitForSweep = [&net]() { + for (auto numAttempts = 0; numAttempts < 20; ++numAttempts) + { + net.client.replayer.sweep(); + if (net.client.countsAsExpected(0, 0, 0)) + { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; + }; + BEAST_EXPECT(waitForSweep()); } void @@ -1166,8 +1220,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay); std::vector deltaStatuses; - BEAST_EXPECT( - net.client.checkStatus(finalHash, totalReplay, TaskStatus::NotDone, TaskStatus::NotDone, deltaStatuses)); + BEAST_EXPECT(net.client.checkStatus( + finalHash, totalReplay, TaskStatus::NotDone, TaskStatus::NotDone, deltaStatuses)); BEAST_EXPECT(net.client.countsAsExpected(1, 1, 0)); net.client.replayer.stop(); @@ -1229,11 +1283,15 @@ struct LedgerReplayer_test : public beast::unit_test::suite l->header(), // wrong ledger info std::map>()); BEAST_EXPECT(net.client.taskStatus(delta) == TaskStatus::Failed); - BEAST_EXPECT(net.client.taskStatus(net.client.findTask(finalHash, totalReplay)) == TaskStatus::Failed); + BEAST_EXPECT( + net.client.taskStatus(net.client.findTask(finalHash, totalReplay)) == + TaskStatus::Failed); // add another task net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash, totalReplay + 1); - BEAST_EXPECT(net.client.taskStatus(net.client.findTask(finalHash, totalReplay + 1)) == TaskStatus::Failed); + BEAST_EXPECT( + net.client.taskStatus(net.client.findTask(finalHash, totalReplay + 1)) == + TaskStatus::Failed); } void @@ -1281,7 +1339,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite // partial overlap l = net.server.ledgerMaster.getLedgerByHash(l->header().parentHash); auto finalHash_moreEarly = l->header().parentHash; - net.client.replayer.replay(InboundLedger::Reason::GENERIC, finalHash_moreEarly, totalReplay); + net.client.replayer.replay( + InboundLedger::Reason::GENERIC, finalHash_moreEarly, totalReplay); BEAST_EXPECT(net.client.waitAndCheckStatus( finalHash_moreEarly, totalReplay, @@ -1424,14 +1483,19 @@ struct LedgerReplayerLong_test : public beast::unit_test::suite for (int i = 0; i < rounds; ++i) { - net.client.replayer.replay(InboundLedger::Reason::GENERIC, finishHashes[i], totalReplay); + net.client.replayer.replay( + InboundLedger::Reason::GENERIC, finishHashes[i], totalReplay); } std::vector deltaStatuses(totalReplay - 1, TaskStatus::Completed); for (int i = 0; i < rounds; ++i) { BEAST_EXPECT(net.client.waitAndCheckStatus( - finishHashes[i], totalReplay, TaskStatus::Completed, TaskStatus::Completed, deltaStatuses)); + finishHashes[i], + totalReplay, + TaskStatus::Completed, + TaskStatus::Completed, + deltaStatuses)); } BEAST_EXPECT(net.client.waitForLedgers(finishHashes[0], totalReplay * rounds)); diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index 58e4c5aaa4..24661c18fb 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -5,13 +5,12 @@ #include #include -#include -#include -#include -#include - #include #include +#include +#include +#include +#include #include #include @@ -64,7 +63,8 @@ class LendingHelpers_test : public beast::unit_test::suite { testcase("computeRaisedRate: " + tc.name); - auto const computedRaisedRate = computeRaisedRate(tc.periodicRate, tc.paymentsRemaining); + auto const computedRaisedRate = + computeRaisedRate(tc.periodicRate, tc.paymentsRemaining); BEAST_EXPECTS( computedRaisedRate == tc.expectedRaisedRate, "Raised rate mismatch: expected " + to_string(tc.expectedRaisedRate) + ", got " + @@ -116,11 +116,12 @@ class LendingHelpers_test : public beast::unit_test::suite { testcase("computePaymentFactor: " + tc.name); - auto const computedPaymentFactor = computePaymentFactor(tc.periodicRate, tc.paymentsRemaining); + auto const computedPaymentFactor = + computePaymentFactor(tc.periodicRate, tc.paymentsRemaining); BEAST_EXPECTS( computedPaymentFactor == tc.expectedPaymentFactor, - "Payment factor mismatch: expected " + to_string(tc.expectedPaymentFactor) + ", got " + - to_string(computedPaymentFactor)); + "Payment factor mismatch: expected " + to_string(tc.expectedPaymentFactor) + + ", got " + to_string(computedPaymentFactor)); } } @@ -178,8 +179,8 @@ class LendingHelpers_test : public beast::unit_test::suite loanPeriodicPayment(tc.principalOutstanding, tc.periodicRate, tc.paymentsRemaining); BEAST_EXPECTS( computedPeriodicPayment == tc.expectedPeriodicPayment, - "Periodic payment mismatch: expected " + to_string(tc.expectedPeriodicPayment) + ", got " + - to_string(computedPeriodicPayment)); + "Periodic payment mismatch: expected " + to_string(tc.expectedPeriodicPayment) + + ", got " + to_string(computedPeriodicPayment)); } } @@ -233,11 +234,12 @@ class LendingHelpers_test : public beast::unit_test::suite { testcase("loanPrincipalFromPeriodicPayment: " + tc.name); - auto const computedPrincipalOutstanding = - loanPrincipalFromPeriodicPayment(tc.periodicPayment, tc.periodicRate, tc.paymentsRemaining); + auto const computedPrincipalOutstanding = loanPrincipalFromPeriodicPayment( + tc.periodicPayment, tc.periodicRate, tc.paymentsRemaining); BEAST_EXPECTS( computedPrincipalOutstanding == tc.expectedPrincipalOutstanding, - "Principal outstanding mismatch: expected " + to_string(tc.expectedPrincipalOutstanding) + ", got " + + "Principal outstanding mismatch: expected " + + to_string(tc.expectedPrincipalOutstanding) + ", got " + to_string(computedPrincipalOutstanding)); } } @@ -264,7 +266,12 @@ class LendingHelpers_test : public beast::unit_test::suite auto const expectedPrincipalPortion = Number{400}; // 1,000 - 100 - 500 auto const components = detail::computeOverpaymentComponents( - IOU, loanScale, overpayment, overpaymentInterestRate, overpaymentFeeRate, managementFeeRate); + IOU, + loanScale, + overpayment, + overpaymentInterestRate, + overpaymentFeeRate, + managementFeeRate); BEAST_EXPECT(components.untrackedManagementFee == expectedOverpaymentFee); @@ -275,11 +282,12 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECT(components.trackedManagementFeeDelta == expectedOverpaymentManagementFee); BEAST_EXPECT(components.trackedPrincipalDelta == expectedPrincipalPortion); BEAST_EXPECT( - components.trackedManagementFeeDelta + components.untrackedInterest == expectedOverpaymentInterestGross); + components.trackedManagementFeeDelta + components.untrackedInterest == + expectedOverpaymentInterestGross); BEAST_EXPECT( - components.trackedManagementFeeDelta + components.untrackedInterest + components.trackedPrincipalDelta + - components.untrackedManagementFee == + components.trackedManagementFeeDelta + components.untrackedInterest + + components.trackedPrincipalDelta + components.untrackedManagementFee == overpayment); } @@ -328,11 +336,12 @@ class LendingHelpers_test : public beast::unit_test::suite computeInterestAndFeeParts(IOU, tc.interest, tc.managementFeeRate, loanScale); BEAST_EXPECTS( computedInterestPart == tc.expectedInterestPart, - "Interest part mismatch: expected " + to_string(tc.expectedInterestPart) + ", got " + - to_string(computedInterestPart)); + "Interest part mismatch: expected " + to_string(tc.expectedInterestPart) + + ", got " + to_string(computedInterestPart)); BEAST_EXPECTS( computedFeePart == tc.expectedFeePart, - "Fee part mismatch: expected " + to_string(tc.expectedFeePart) + ", got " + to_string(computedFeePart)); + "Fee part mismatch: expected " + to_string(tc.expectedFeePart) + ", got " + + to_string(computedFeePart)); } } @@ -399,11 +408,14 @@ class LendingHelpers_test : public beast::unit_test::suite testcase("loanLatePaymentInterest: " + tc.name); auto const computedLateInterest = loanLatePaymentInterest( - tc.principalOutstanding, tc.lateInterestRate, tc.parentCloseTime, tc.nextPaymentDueDate); + tc.principalOutstanding, + tc.lateInterestRate, + tc.parentCloseTime, + tc.nextPaymentDueDate); BEAST_EXPECTS( computedLateInterest == tc.expectedLateInterest, - "Late interest mismatch: expected " + to_string(tc.expectedLateInterest) + ", got " + - to_string(computedLateInterest)); + "Late interest mismatch: expected " + to_string(tc.expectedLateInterest) + + ", got " + to_string(computedLateInterest)); } } @@ -490,8 +502,8 @@ class LendingHelpers_test : public beast::unit_test::suite tc.paymentInterval); BEAST_EXPECTS( computedAccruedInterest == tc.expectedAccruedInterest, - "Accrued interest mismatch: expected " + to_string(tc.expectedAccruedInterest) + ", got " + - to_string(computedAccruedInterest)); + "Accrued interest mismatch: expected " + to_string(tc.expectedAccruedInterest) + + ", got " + to_string(computedAccruedInterest)); } } @@ -566,7 +578,8 @@ class LendingHelpers_test : public beast::unit_test::suite tc.closeInterestRate); BEAST_EXPECTS( computedFullPaymentInterest == tc.expectedFullPaymentInterest, - "Full payment interest mismatch: expected " + to_string(tc.expectedFullPaymentInterest) + ", got " + + "Full payment interest mismatch: expected " + + to_string(tc.expectedFullPaymentInterest) + ", got " + to_string(computedFullPaymentInterest)); } } @@ -596,7 +609,13 @@ class LendingHelpers_test : public beast::unit_test::suite asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -625,7 +644,8 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.interestPaid == 0, - " interestPaid mismatch: expected 0, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 0, got " + + to_string(actualPaymentParts.interestPaid)); BEAST_EXPECTS( actualPaymentParts.principalPaid == overpaymentAmount, @@ -647,8 +667,9 @@ class LendingHelpers_test : public beast::unit_test::suite actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); } void @@ -679,7 +700,13 @@ class LendingHelpers_test : public beast::unit_test::suite managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -708,11 +735,13 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.principalPaid == 45, - " principalPaid mismatch: expected 45, got `" + to_string(actualPaymentParts.principalPaid)); + " principalPaid mismatch: expected 45, got `" + + to_string(actualPaymentParts.principalPaid)); BEAST_EXPECTS( actualPaymentParts.interestPaid == 0, - " interestPaid mismatch: expected 0, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 0, got " + + to_string(actualPaymentParts.interestPaid)); // =========== VALIDATE STATE CHANGES =========== // With no Loan interest, interest outstanding should not change @@ -731,8 +760,9 @@ class LendingHelpers_test : public beast::unit_test::suite actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); } void @@ -763,7 +793,13 @@ class LendingHelpers_test : public beast::unit_test::suite managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -796,23 +832,27 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.principalPaid == 50, - " principalPaid mismatch: expected 50, got `" + to_string(actualPaymentParts.principalPaid)); + " principalPaid mismatch: expected 50, got `" + + to_string(actualPaymentParts.principalPaid)); // with no interest portion, interest paid should be zero BEAST_EXPECTS( actualPaymentParts.interestPaid == 0, - " interestPaid mismatch: expected 0, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 0, got " + + to_string(actualPaymentParts.interestPaid)); // =========== VALIDATE STATE CHANGES =========== BEAST_EXPECTS( actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); BEAST_EXPECTS( - actualPaymentParts.valueChange == newState.interestDue - loanProperties.loanState.interestDue, + actualPaymentParts.valueChange == + newState.interestDue - loanProperties.loanState.interestDue, " valueChange mismatch: expected " + to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " + to_string(actualPaymentParts.valueChange)); @@ -852,7 +892,13 @@ class LendingHelpers_test : public beast::unit_test::suite managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -874,15 +920,17 @@ class LendingHelpers_test : public beast::unit_test::suite // with overpayment interest portion, interest paid should be 5 BEAST_EXPECTS( actualPaymentParts.interestPaid == 5, - " interestPaid mismatch: expected 5, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 5, got " + + to_string(actualPaymentParts.interestPaid)); // With overpayment interest portion, value change should equal the // interest decrease plus overpayment interest portion BEAST_EXPECTS( - (actualPaymentParts.valueChange == Number{-205922, -5} + actualPaymentParts.interestPaid), + (actualPaymentParts.valueChange == + Number{-205922, -5} + actualPaymentParts.interestPaid), " valueChange mismatch: expected " + - to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid) + ", got " + - to_string(actualPaymentParts.valueChange)); + to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid) + + ", got " + to_string(actualPaymentParts.valueChange)); // with no fee portion, fee paid should be zero BEAST_EXPECTS( @@ -891,15 +939,17 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.principalPaid == 45, - " principalPaid mismatch: expected 45, got `" + to_string(actualPaymentParts.principalPaid)); + " principalPaid mismatch: expected 45, got `" + + to_string(actualPaymentParts.principalPaid)); // =========== VALIDATE STATE CHANGES =========== BEAST_EXPECTS( actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); // The change in interest is equal to the value change sans the // overpayment interest @@ -908,7 +958,8 @@ class LendingHelpers_test : public beast::unit_test::suite newState.interestDue - loanProperties.loanState.interestDue, " valueChange mismatch: expected " + to_string( - newState.interestDue - loanProperties.loanState.interestDue + actualPaymentParts.interestPaid) + + newState.interestDue - loanProperties.loanState.interestDue + + actualPaymentParts.interestPaid) + ", got " + to_string(actualPaymentParts.valueChange)); // With no Loan management fee, management fee due should not change @@ -948,7 +999,13 @@ class LendingHelpers_test : public beast::unit_test::suite managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -972,14 +1029,17 @@ class LendingHelpers_test : public beast::unit_test::suite // overpayment interest portion first, so interest paid remains 4.5 BEAST_EXPECTS( (actualPaymentParts.interestPaid == Number{45, -1}), - " interestPaid mismatch: expected 4.5, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 4.5, got " + + to_string(actualPaymentParts.interestPaid)); // With overpayment interest portion, value change should equal the // interest decrease plus overpayment interest portion BEAST_EXPECTS( - (actualPaymentParts.valueChange == Number{-18533, -4} + actualPaymentParts.interestPaid), - " valueChange mismatch: expected " + to_string(Number{-18533, -4} + actualPaymentParts.interestPaid) + - ", got " + to_string(actualPaymentParts.valueChange)); + (actualPaymentParts.valueChange == + Number{-18533, -4} + actualPaymentParts.interestPaid), + " valueChange mismatch: expected " + + to_string(Number{-18533, -4} + actualPaymentParts.interestPaid) + ", got " + + to_string(actualPaymentParts.valueChange)); // While there is no overpayment fee, fee paid should equal the // management fee charged against the overpayment interest portion @@ -989,21 +1049,25 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.principalPaid == 45, - " principalPaid mismatch: expected 45, got `" + to_string(actualPaymentParts.principalPaid)); + " principalPaid mismatch: expected 45, got `" + + to_string(actualPaymentParts.principalPaid)); // =========== VALIDATE STATE CHANGES =========== BEAST_EXPECTS( actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); // Note that the management fee value change is not captured, as this // value is not needed to correctly update the Vault state. BEAST_EXPECTS( - (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-20592, -5}), - " management fee change mismatch: expected " + to_string(Number{-20592, -5}) + ", got " + + (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == + Number{-20592, -5}), + " management fee change mismatch: expected " + to_string(Number{-20592, -5}) + + ", got " + to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue)); BEAST_EXPECTS( @@ -1042,7 +1106,13 @@ class LendingHelpers_test : public beast::unit_test::suite managementFeeRate); auto const loanProperties = computeLoanProperties( - asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale); + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); auto const ret = tryOverpayment( asset, @@ -1066,14 +1136,17 @@ class LendingHelpers_test : public beast::unit_test::suite // overpayment interest portion first, so interest paid remains 4.5 BEAST_EXPECTS( (actualPaymentParts.interestPaid == Number{45, -1}), - " interestPaid mismatch: expected 4.5, got " + to_string(actualPaymentParts.interestPaid)); + " interestPaid mismatch: expected 4.5, got " + + to_string(actualPaymentParts.interestPaid)); // With overpayment interest portion, value change should equal the // interest decrease plus overpayment interest portion BEAST_EXPECTS( - (actualPaymentParts.valueChange == Number{-164737, -5} + actualPaymentParts.interestPaid), - " valueChange mismatch: expected " + to_string(Number{-164737, -5} + actualPaymentParts.interestPaid) + - ", got " + to_string(actualPaymentParts.valueChange)); + (actualPaymentParts.valueChange == + Number{-164737, -5} + actualPaymentParts.interestPaid), + " valueChange mismatch: expected " + + to_string(Number{-164737, -5} + actualPaymentParts.interestPaid) + ", got " + + to_string(actualPaymentParts.valueChange)); // While there is no overpayment fee, fee paid should equal the // management fee charged against the overpayment interest portion @@ -1083,7 +1156,8 @@ class LendingHelpers_test : public beast::unit_test::suite BEAST_EXPECTS( actualPaymentParts.principalPaid == 40, - " principalPaid mismatch: expected 40, got `" + to_string(actualPaymentParts.principalPaid)); + " principalPaid mismatch: expected 40, got `" + + to_string(actualPaymentParts.principalPaid)); // =========== VALIDATE STATE CHANGES =========== @@ -1091,14 +1165,17 @@ class LendingHelpers_test : public beast::unit_test::suite actualPaymentParts.principalPaid == loanProperties.loanState.principalOutstanding - newState.principalOutstanding, " principalPaid mismatch: expected " + - to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " + - to_string(actualPaymentParts.principalPaid)); + to_string( + loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); // Note that the management fee value change is not captured, as this // value is not needed to correctly update the Vault state. BEAST_EXPECTS( - (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-18304, -5}), - " management fee change mismatch: expected " + to_string(Number{-18304, -5}) + ", got " + + (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == + Number{-18304, -5}), + " management fee change mismatch: expected " + to_string(Number{-18304, -5}) + + ", got " + to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue)); BEAST_EXPECTS( diff --git a/src/test/app/LoadFeeTrack_test.cpp b/src/test/app/LoadFeeTrack_test.cpp index 61174fc9e9..fed76288ef 100644 --- a/src/test/app/LoadFeeTrack_test.cpp +++ b/src/test/app/LoadFeeTrack_test.cpp @@ -1,8 +1,8 @@ -#include #include #include #include +#include namespace xrpl { diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 58052e3fb1..361f70209f 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include namespace xrpl { namespace test { @@ -12,7 +11,8 @@ class LoanBroker_test : public beast::unit_test::suite // Ensure that all the features needed for Lending Protocol are included, // even if they are set to unsupported. FeatureBitset const all{ - jtx::testable_amendments() | featureMPTokensV1 | featureSingleAssetVault | featureLendingProtocol}; + jtx::testable_amendments() | featureMPTokensV1 | featureSingleAssetVault | + featureLendingProtocol}; void testDisabled() @@ -50,7 +50,10 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(alice), ter(temDISABLED)); env(coverClawback(alice), loanBrokerID(brokerKeylet.key), ter(temDISABLED)); env(coverClawback(alice), amount(asset(0)), ter(temDISABLED)); - env(coverClawback(alice), loanBrokerID(brokerKeylet.key), amount(asset(1000)), ter(temDISABLED)); + env(coverClawback(alice), + loanBrokerID(brokerKeylet.key), + amount(asset(1000)), + ter(temDISABLED)); // 4. LoanBrokerDelete env(del(alice, brokerKeylet.key), ter(temDISABLED)); }; @@ -88,12 +91,24 @@ class LoanBroker_test : public beast::unit_test::suite { { auto const& asset = vault.asset.raw(); - testcase << "Lifecycle: " - << (asset.native() ? "XRP " - : asset.holds() ? "IOU " - : asset.holds() ? "MPT " - : "Unknown ") - << label; + std::string_view assetLabel; + if (asset.native()) + { + assetLabel = "XRP "; + } + else if (asset.holds()) + { + assetLabel = "IOU "; + } + else if (asset.holds()) + { + assetLabel = "MPT "; + } + else + { + assetLabel = "Unknown "; + } + testcase << "Lifecycle: " << assetLabel << label; } using namespace jtx; @@ -166,7 +181,8 @@ class LoanBroker_test : public beast::unit_test::suite // log << "Pseudo-account after create: " // << to_string(pseudo->getJson()) << std::endl // << std::endl; - BEAST_EXPECT(pseudo->at(sfFlags) == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth)); + BEAST_EXPECT( + pseudo->at(sfFlags) == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth)); BEAST_EXPECT(pseudo->at(sfSequence) == 0); BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero); BEAST_EXPECT(pseudo->at(sfOwnerCount) == (vault.asset.raw().native() ? 0 : 1)); @@ -209,16 +225,17 @@ class LoanBroker_test : public beast::unit_test::suite } } - auto verifyCoverAmount = [&env, &vault, &pseudoAccount, &broker, &keylet, this](auto n) { - using namespace jtx; + auto verifyCoverAmount = + [&env, &vault, &pseudoAccount, &broker, &keylet, this](auto n) { + using namespace jtx; - if (BEAST_EXPECT(broker = env.le(keylet))) - { - auto const amount = vault.asset(n); - BEAST_EXPECT(broker->at(sfCoverAvailable) == amount.number()); - env.require(balance(pseudoAccount, amount)); - } - }; + if (BEAST_EXPECT(broker = env.le(keylet))) + { + auto const amount = vault.asset(n); + BEAST_EXPECT(broker->at(sfCoverAvailable) == amount.number()); + env.require(balance(pseudoAccount, amount)); + } + }; // Test Cover funding before allowing alterations env(coverDeposit(alice, uint256(0), vault.asset(10)), ter(temINVALID)); @@ -254,7 +271,9 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(alice), amount(ghostIouAsset(1)), ter(tecNO_ENTRY)); env(coverClawback(alice), amount(badIouAsset(1)), ter(tecOBJECT_NOT_FOUND)); // Pseudo-account is not for a broker - env(coverClawback(alice), amount(vaultPseudoIouAsset(1)), ter(tecOBJECT_NOT_FOUND)); + env(coverClawback(alice), + amount(vaultPseudoIouAsset(1)), + ter(tecOBJECT_NOT_FOUND)); // If we specify a pseudo-account as the IOU amount, it // needs to match the loan broker env(coverClawback(issuer), @@ -300,11 +319,15 @@ class LoanBroker_test : public beast::unit_test::suite if (!vault.asset.raw().native()) { TER const expected = vault.asset.raw().holds() ? tecNO_AUTH : tecNO_LINE; - env(coverWithdraw(alice, keylet.key, vault.asset(1)), destination(bystander), ter(expected)); + env(coverWithdraw(alice, keylet.key, vault.asset(1)), + destination(bystander), + ter(expected)); } // Can not withdraw to the zero address - env(coverWithdraw(alice, keylet.key, vault.asset(1)), destination(AccountID{}), ter(temMALFORMED)); + env(coverWithdraw(alice, keylet.key, vault.asset(1)), + destination(AccountID{}), + ter(temMALFORMED)); // Withdraw some of the cover amount env(coverWithdraw(alice, keylet.key, vault.asset(7))); @@ -343,7 +366,12 @@ class LoanBroker_test : public beast::unit_test::suite // Issuer claws it all back in various different ways for (auto const& tx : { // defer autofills until submission time - env.json(coverClawback(issuer), loanBrokerID(keylet.key), fee(none), seq(none), sig(none)), + env.json( + coverClawback(issuer), + loanBrokerID(keylet.key), + fee(none), + seq(none), + sig(none)), env.json( coverClawback(issuer), loanBrokerID(keylet.key), @@ -397,7 +425,10 @@ class LoanBroker_test : public beast::unit_test::suite // Verify that fields get removed when set to default values // Debt maximum: explicit 0 // Data: explicit empty - env(set(alice, vault.vaultID), loanBrokerID(broker->key()), debtMaximum(Number(0)), data("")); + env(set(alice, vault.vaultID), + loanBrokerID(broker->key()), + debtMaximum(Number(0)), + data("")); env.close(); // Check the updated fields @@ -445,7 +476,8 @@ class LoanBroker_test : public beast::unit_test::suite BEAST_EXPECT(!pseudo); } auto const expectedBalance = aliceBalance + coverFunds - - (aliceBalance.value().native() ? STAmount(env.current()->fees().base.value()) : vault.asset(0)); + (aliceBalance.value().native() ? STAmount(env.current()->fees().base.value()) + : vault.asset(0)); env.require(balance(alice, expectedBalance)); env.require(balance(pseudoAccount, vault.asset(none))); } @@ -569,13 +601,21 @@ class LoanBroker_test : public beast::unit_test::suite env(set(evan, vault.vaultID, ~tfUniversal), ter(temINVALID_FLAG)); // field length validation // sfData: good length, bad account - env(set(evan, vault.vaultID), data(std::string(maxDataPayloadLength, 'X')), ter(tecNO_PERMISSION)); + env(set(evan, vault.vaultID), + data(std::string(maxDataPayloadLength, 'X')), + ter(tecNO_PERMISSION)); // sfData: too long - env(set(evan, vault.vaultID), data(std::string(maxDataPayloadLength + 1, 'Y')), ter(temINVALID)); + env(set(evan, vault.vaultID), + data(std::string(maxDataPayloadLength + 1, 'Y')), + ter(temINVALID)); // sfManagementFeeRate: good value, bad account - env(set(evan, vault.vaultID), managementFeeRate(maxManagementFeeRate), ter(tecNO_PERMISSION)); + env(set(evan, vault.vaultID), + managementFeeRate(maxManagementFeeRate), + ter(tecNO_PERMISSION)); // sfManagementFeeRate: too big - env(set(evan, vault.vaultID), managementFeeRate(maxManagementFeeRate + TenthBips16(10)), ter(temINVALID)); + env(set(evan, vault.vaultID), + managementFeeRate(maxManagementFeeRate + TenthBips16(10)), + ter(temINVALID)); // sfCoverRateMinimum and sfCoverRateLiquidation are linked // Cover: good value, bad account env(set(evan, vault.vaultID), @@ -646,29 +686,28 @@ class LoanBroker_test : public beast::unit_test::suite // fields that can't be changed // LoanBrokerID - env(set(alice, vault.vaultID), loanBrokerID(nextKeylet.key), ter(tecNO_ENTRY), THISLINE); + env(set(alice, vault.vaultID), loanBrokerID(nextKeylet.key), ter(tecNO_ENTRY)); // VaultID - env(set(alice, nextKeylet.key), loanBrokerID(broker->key()), ter(tecNO_ENTRY), THISLINE); + env(set(alice, nextKeylet.key), loanBrokerID(broker->key()), ter(tecNO_ENTRY)); // Owner - env(set(evan, vault.vaultID), loanBrokerID(broker->key()), ter(tecNO_PERMISSION), THISLINE); + env(set(evan, vault.vaultID), + loanBrokerID(broker->key()), + ter(tecNO_PERMISSION)); // ManagementFeeRate env(set(alice, vault.vaultID), loanBrokerID(broker->key()), managementFeeRate(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // CoverRateMinimum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), coverRateMinimum(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // CoverRateLiquidation env(set(alice, vault.vaultID), loanBrokerID(broker->key()), coverRateLiquidation(maxManagementFeeRate), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // fields that can be changed testData = "Test Data 1234"; @@ -676,15 +715,13 @@ class LoanBroker_test : public beast::unit_test::suite env(set(alice, vault.vaultID), loanBrokerID(broker->key()), data(std::string(maxDataPayloadLength + 1, 'W')), - ter(temINVALID), - THISLINE); + ter(temINVALID)); // Bad debt maximum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), debtMaximum(Number(-175, -1)), - ter(temINVALID), - THISLINE); + ter(temINVALID)); Number debtMax{175, -1}; if (vault.asset.integral()) { @@ -692,16 +729,14 @@ class LoanBroker_test : public beast::unit_test::suite loanBrokerID(broker->key()), data(testData), debtMaximum(debtMax), - ter(tecPRECISION_LOSS), - THISLINE); + ter(tecPRECISION_LOSS)); roundToAsset(vault.asset, debtMax); } // Data & Debt maximum env(set(alice, vault.vaultID), loanBrokerID(broker->key()), data(testData), - debtMaximum(debtMax), - THISLINE); + debtMaximum(debtMax)); }, [&](SLE::const_ref broker) { // Check the updated fields @@ -709,7 +744,8 @@ class LoanBroker_test : public beast::unit_test::suite Number const expected = STAmount{vault.asset, Number(175, -1)}; auto const actual = broker->at(sfDebtMaximum); BEAST_EXPECTS( - actual == expected, "Expected: " + to_string(expected) + ", Actual: " + to_string(actual)); + actual == expected, + "Expected: " + to_string(expected) + ", Actual: " + to_string(actual)); }); lifecycle( @@ -743,7 +779,10 @@ class LoanBroker_test : public beast::unit_test::suite }, [&](SLE::const_ref broker) { // Reset Data & Debt maximum to default values - env(set(alice, vault.vaultID), loanBrokerID(broker->key()), data(""), debtMaximum(Number(0))); + env(set(alice, vault.vaultID), + loanBrokerID(broker->key()), + data(""), + debtMaximum(Number(0))); }, [&](SLE::const_ref broker) { // Check the updated fields @@ -759,7 +798,8 @@ class LoanBroker_test : public beast::unit_test::suite void testLoanBroker( - std::function getAsset, + std::function + getAsset, LoanBrokerTest brokerTest) { using namespace jtx; @@ -775,16 +815,16 @@ class LoanBroker_test : public beast::unit_test::suite PrettyAsset const asset = [&]() { if (getAsset) return getAsset(env, issuer, alice); - env(trust(alice, issuer["IOU"](1'000'000)), THISLINE); + env(trust(alice, issuer["IOU"](1'000'000))); env.close(); return PrettyAsset(issuer["IOU"]); }(); - env(pay(issuer, alice, asset(100'000)), THISLINE); + env(pay(issuer, alice, asset(100'000))); env.close(); auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); - env(tx, THISLINE); + env(tx); env.close(); auto const le = env.le(vaultKeylet); VaultInfo vaultInfo = [&]() { @@ -795,11 +835,11 @@ class LoanBroker_test : public beast::unit_test::suite if (vaultInfo.vaultID == uint256{}) return; - env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}), THISLINE); + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)})); env.close(); auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); - env(set(alice, vaultInfo.vaultID), THISLINE); + env(set(alice, vaultInfo.vaultID)); env.close(); auto broker = env.le(brokerKeylet); @@ -810,23 +850,23 @@ class LoanBroker_test : public beast::unit_test::suite auto jv = getTxJv(); // empty broker ID jv[sfLoanBrokerID] = ""; - env(jv, ter(temINVALID), THISLINE); + env(jv, ter(temINVALID)); // zero broker ID jv[sfLoanBrokerID] = to_string(uint256{}); // needs a flag to distinguish the parsed STTx from the prior // test - env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID), THISLINE); + env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID)); }; auto testZeroVaultID = [&](auto&& getTxJv) { auto jv = getTxJv(); // empty broker ID jv[sfVaultID] = ""; - env(jv, ter(temINVALID), THISLINE); + env(jv, ter(temINVALID)); // zero broker ID jv[sfVaultID] = to_string(uint256{}); // needs a flag to distinguish the parsed STTx from the prior // test - env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID), THISLINE); + env(jv, txflags(tfFullyCanonicalSig), ter(temINVALID)); }; if (brokerTest == CoverDeposit) @@ -835,21 +875,24 @@ class LoanBroker_test : public beast::unit_test::suite testZeroBrokerID([&]() { return coverDeposit(alice, brokerKeylet.key, asset(10)); }); // preclaim: tecWRONG_ASSET - env(coverDeposit(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET), THISLINE); + env(coverDeposit(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET)); // preclaim: tecINSUFFICIENT_FUNDS - env(pay(alice, issuer, asset(100'000 - 50)), THISLINE); + env(pay(alice, issuer, asset(100'000 - 50))); env.close(); - env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), ter(tecINSUFFICIENT_FUNDS)); + env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), + ter(tecINSUFFICIENT_FUNDS)); // preclaim: tecFROZEN - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(fset(issuer, asfGlobalFreeze)); env.close(); - env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), ter(tecFROZEN), THISLINE); + env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), ter(tecFROZEN)); } else + { // Fund the cover deposit - env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), THISLINE); + env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10))); + } env.close(); if (brokerTest == CoverWithdraw) @@ -858,49 +901,61 @@ class LoanBroker_test : public beast::unit_test::suite testZeroBrokerID([&]() { return coverWithdraw(alice, brokerKeylet.key, asset(10)); }); // preclaim: tecWRONG_ASSET - env(coverWithdraw(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET), THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, issuer["BAD"](10)), ter(tecWRONG_ASSET)); // preclaim: tecNO_DST Account const bogus{"bogus"}; - env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(bogus), ter(tecNO_DST), THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, asset(10)), + destination(bogus), + ter(tecNO_DST)); // preclaim: tecDST_TAG_NEEDED Account const dest{"dest"}; env.fund(XRP(1'000), dest); - env(fset(dest, asfRequireDest), THISLINE); + env(fset(dest, asfRequireDest)); env.close(); - env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), ter(tecDST_TAG_NEEDED), THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, asset(10)), + destination(dest), + ter(tecDST_TAG_NEEDED)); // preclaim: tecNO_PERMISSION - env(fclear(dest, asfRequireDest), THISLINE); - env(fset(dest, asfDepositAuth), THISLINE); + env(fclear(dest, asfRequireDest)); + env(fset(dest, asfDepositAuth)); env.close(); - env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), ter(tecNO_PERMISSION), THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, asset(10)), + destination(dest), + ter(tecNO_PERMISSION)); // preclaim: tecFROZEN - env(trust(dest, asset(1'000)), THISLINE); - env(fclear(dest, asfDepositAuth), THISLINE); - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(trust(dest, asset(1'000))); + env(fclear(dest, asfDepositAuth)); + env(fset(issuer, asfGlobalFreeze)); env.close(); - env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), ter(tecFROZEN), THISLINE); + env(coverWithdraw(alice, brokerKeylet.key, asset(10)), + destination(dest), + ter(tecFROZEN)); // preclaim:: tecFROZEN (deep frozen) - env(fclear(issuer, asfGlobalFreeze), THISLINE); - env(trust(issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze), THISLINE); - env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(dest), ter(tecFROZEN), THISLINE); + env(fclear(issuer, asfGlobalFreeze)); + env(trust(issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze)); + env(coverWithdraw(alice, brokerKeylet.key, asset(10)), + destination(dest), + ter(tecFROZEN)); // preclaim: tecPSEUDO_ACCOUNT env(coverWithdraw(alice, brokerKeylet.key, asset(10)), destination(vaultInfo.pseudoAccount), - ter(tecPSEUDO_ACCOUNT), - THISLINE); + ter(tecPSEUDO_ACCOUNT)); } if (brokerTest == CoverClawback) { // preflight: temINVALID (empty/zero broker id) testZeroBrokerID([&]() { - return env.json(coverClawback(alice), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2))); + return env.json( + coverClawback(alice), + loanBrokerID(brokerKeylet.key), + amount(vaultInfo.asset(2))); }); if (asset.holds()) @@ -909,17 +964,15 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); // preclaim: NoFreeze is set - env(fset(issuer, asfAllowTrustLineClawback | asfNoFreeze), THISLINE); + env(fset(issuer, asfAllowTrustLineClawback | asfNoFreeze)); env.close(); env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } else { @@ -927,8 +980,7 @@ class LoanBroker_test : public beast::unit_test::suite env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(2)), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } env.close(); } @@ -939,54 +991,60 @@ class LoanBroker_test : public beast::unit_test::suite env.fund(XRP(1'000), borrower); env(loan::set(borrower, brokerKeylet.key, asset(50).value()), sig(sfCounterpartySignature, alice), - fee(env.current()->fees().base * 2), - THISLINE); + fee(env.current()->fees().base * 2)); // preflight: temINVALID (empty/zero broker id) testZeroBrokerID([&]() { return del(alice, brokerKeylet.key); }); // preclaim: tecHAS_OBLIGATIONS - env(del(alice, brokerKeylet.key), ter(tecHAS_OBLIGATIONS), THISLINE); + env(del(alice, brokerKeylet.key), ter(tecHAS_OBLIGATIONS)); // Repay and delete the loan auto const loanKeylet = keylet::loan(brokerKeylet.key, 1); - env(loan::pay(borrower, loanKeylet.key, asset(50).value()), THISLINE); - env(loan::del(alice, loanKeylet.key), THISLINE); + env(loan::pay(borrower, loanKeylet.key, asset(50).value())); + env(loan::del(alice, loanKeylet.key)); - env(trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze), THISLINE); + env(trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze)); // preclaim: tecFROZEN (deep frozen) - env(del(alice, brokerKeylet.key), ter(tecFROZEN), THISLINE); - env(trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze), THISLINE); + env(del(alice, brokerKeylet.key), ter(tecFROZEN)); + env(trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze)); // successful delete the loan broker object - env(del(alice, brokerKeylet.key), ter(tesSUCCESS), THISLINE); + env(del(alice, brokerKeylet.key), ter(tesSUCCESS)); } else - env(del(alice, brokerKeylet.key), THISLINE); + { + env(del(alice, brokerKeylet.key)); + } if (brokerTest == Set) { // preflight: temINVALID (empty/zero broker id) - testZeroBrokerID([&]() { return env.json(set(alice, vaultInfo.vaultID), loanBrokerID(brokerKeylet.key)); }); + testZeroBrokerID([&]() { + return env.json(set(alice, vaultInfo.vaultID), loanBrokerID(brokerKeylet.key)); + }); // preflight: temINVALID (empty/zero vault id) - testZeroVaultID([&]() { return env.json(set(alice, vaultInfo.vaultID), loanBrokerID(brokerKeylet.key)); }); + testZeroVaultID([&]() { + return env.json(set(alice, vaultInfo.vaultID), loanBrokerID(brokerKeylet.key)); + }); if (asset.holds()) { - env(fclear(issuer, asfDefaultRipple), THISLINE); + env(fclear(issuer, asfDefaultRipple)); env.close(); // preclaim: DefaultRipple is not set - env(set(alice, vaultInfo.vaultID), ter(terNO_RIPPLE), THISLINE); + env(set(alice, vaultInfo.vaultID), ter(terNO_RIPPLE)); - env(fset(issuer, asfDefaultRipple), THISLINE); + env(fset(issuer, asfDefaultRipple)); env.close(); } - auto const amt = env.balance(alice) - env.current()->fees().accountReserve(env.ownerCount(alice)); - env(pay(alice, issuer, amt), THISLINE); + auto const amt = + env.balance(alice) - env.current()->fees().accountReserve(env.ownerCount(alice)); + env(pay(alice, issuer, amt)); // preclaim:: tecINSUFFICIENT_RESERVE - env(set(alice, vaultInfo.vaultID), ter(tecINSUFFICIENT_RESERVE), THISLINE); + env(set(alice, vaultInfo.vaultID), ter(tecINSUFFICIENT_RESERVE)); } } @@ -1009,7 +1067,7 @@ class LoanBroker_test : public beast::unit_test::suite auto jtx = env.jt(coverClawback(alice), amount(USD(100))); // holder == account - env(jtx, ter(temINVALID), THISLINE); + env(jtx, ter(temINVALID)); // holder == beast::zero STAmount bad(Issue{USD.currency, beast::zero}, 100); @@ -1196,7 +1254,10 @@ class LoanBroker_test : public beast::unit_test::suite // Can't unauthorize Vault pseudo-account asset.authorize( - {.account = issuer, .holder = vaultInfo.pseudoAccount, .flags = tfMPTUnauthorize, .err = tecNO_PERMISSION}); + {.account = issuer, + .holder = vaultInfo.pseudoAccount, + .flags = tfMPTUnauthorize, + .err = tecNO_PERMISSION}); auto forUnauthAuth = [&](auto&& doTx) { for (auto const flag : {tfMPTUnauthorize, 0u}) @@ -1211,13 +1272,15 @@ class LoanBroker_test : public beast::unit_test::suite // Can't deposit into Vault if the vault owner is not authorized forUnauthAuth([&](bool authorized) { auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS); - env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}), err); + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}), + err); }); // Can't withdraw from Vault if the vault owner is not authorized forUnauthAuth([&](bool authorized) { auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS); - env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}), err); + env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}), + err); }); auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); @@ -1231,7 +1294,10 @@ class LoanBroker_test : public beast::unit_test::suite // Can't unauthorize LoanBroker pseudo-account asset.authorize( - {.account = issuer, .holder = brokerPseudo, .flags = tfMPTUnauthorize, .err = tecNO_PERMISSION}); + {.account = issuer, + .holder = brokerPseudo, + .flags = tfMPTUnauthorize, + .err = tecNO_PERMISSION}); // Can't cover deposit into Vault if the vault owner is not authorized forUnauthAuth([&](bool authorized) { @@ -1246,8 +1312,9 @@ class LoanBroker_test : public beast::unit_test::suite }); // Issuer can always cover clawback. The holder authorization is n/a. - forUnauthAuth( - [&](bool) { env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(1))); }); + forUnauthAuth([&](bool) { + env(coverClawback(issuer), loanBrokerID(brokerKeylet.key), amount(vaultInfo.asset(1))); + }); } void @@ -1274,11 +1341,11 @@ class LoanBroker_test : public beast::unit_test::suite return mptAsset; }(); - env(pay(issuer, alice, asset(100'000)), THISLINE); + env(pay(issuer, alice, asset(100'000))); env.close(); auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); - env(tx, THISLINE); + env(tx); env.close(); auto const le = env.le(vaultKeylet); VaultInfo vaultInfo = [&]() { @@ -1289,19 +1356,18 @@ class LoanBroker_test : public beast::unit_test::suite if (vaultInfo.vaultID == uint256{}) return; - env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}), THISLINE); + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)})); env.close(); auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); - env(set(alice, vaultInfo.vaultID), THISLINE); + env(set(alice, vaultInfo.vaultID)); env.close(); Account const borrower{"borrower"}; env.fund(XRP(1'000), borrower); env(loan::set(borrower, brokerKeylet.key, asset(50).value()), sig(sfCounterpartySignature, alice), - fee(env.current()->fees().base * 2), - THISLINE); + fee(env.current()->fees().base * 2)); auto const broker = env.le(brokerKeylet); if (!BEAST_EXPECT(broker)) return; @@ -1312,47 +1378,47 @@ class LoanBroker_test : public beast::unit_test::suite auto tx2 = set(alice, vaultInfo.vaultID); tx2[sfLoanBrokerID] = to_string(brokerKeylet.key); tx2[sfDebtMaximum] = debtTotal - 1; - env(tx2, ter(tecLIMIT_EXCEEDED), THISLINE); + env(tx2, ter(tecLIMIT_EXCEEDED)); tx2[sfDebtMaximum] = debtTotal + 1; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); tx2[sfDebtMaximum] = 0; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); tx2[sfDebtMaximum] = Json::Value::maxInt; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); { auto const dm = power(2, 64) - 1; BEAST_EXPECT(dm > maxMPTokenAmount); tx2[sfDebtMaximum] = dm; - env(tx2, ter(temINVALID), THISLINE); + env(tx2, ter(temINVALID)); } { auto const dm = power(2, 63) - 1; BEAST_EXPECTS(dm > maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(temINVALID), THISLINE); + env(tx2, ter(temINVALID)); } { auto const dm = power(2, 63) - 3; BEAST_EXPECTS(dm == maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } { auto const dm = 2 * (power(2, 62) - 1) + 1; BEAST_EXPECTS(dm == maxMPTokenAmount, to_string(dm)); tx2[sfDebtMaximum] = dm; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } tx2[sfDebtMaximum] = Number{9223372036854775807, 0}; - env(tx2, ter(tesSUCCESS), THISLINE); + env(tx2, ter(tesSUCCESS)); } void @@ -1377,7 +1443,8 @@ class LoanBroker_test : public beast::unit_test::suite env(tx); env.close(); - env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = deposit}), ter(err)); + env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = deposit}), + ter(err)); env.close(); auto const brokerKeylet = keylet::loanbroker(broker, env.seq(broker)); @@ -1618,7 +1685,8 @@ class LoanBroker_test : public beast::unit_test::suite .authHolder = true, .maxAmt = 5'000}); // unauthorize dest - tester.authorize({.account = issuer, .holder = dest, .flags = tfMPTUnauthorize}); + tester.authorize( + {.account = issuer, .holder = dest, .flags = tfMPTUnauthorize}); return tester; } case ReachedMAX: { @@ -1668,9 +1736,10 @@ class LoanBroker_test : public beast::unit_test::suite BEAST_EXPECT(env.ter() == err); env.close(); - if (err != tesSUCCESS) + if (!isTesSuccess(err)) { - env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)})); + env(vault.withdraw( + {.depositor = broker, .id = keylet.key, .amount = token(1'000)})); } // Test LoanBroker withdraw diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 5deb1f179c..171e2eddb5 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -3,14 +3,12 @@ #include #include -#include -#include -#include -#include - #include #include -// cspell: words LOANTODO +#include +#include +#include +#include #include @@ -23,7 +21,8 @@ protected: // Ensure that all the features needed for Lending Protocol are included, // even if they are set to unsupported. FeatureBitset const all{ - jtx::testable_amendments() | featureMPTokensV1 | featureSingleAssetVault | featureLendingProtocol}; + jtx::testable_amendments() | featureMPTokensV1 | featureSingleAssetVault | + featureLendingProtocol}; std::string const iouCurrency{"IOU"}; @@ -82,7 +81,7 @@ protected: int coverDeposit = 1000; TenthBips16 managementFeeRate{100}; TenthBips32 coverRateLiquidation = percentageToTenthBips(25); - std::string data{}; + std::string data = {}; // NOLINT(readability-redundant-member-init) std::uint32_t flags = 0; Number @@ -151,20 +150,22 @@ protected: // only signs. bool counterpartyExplicit = true; Number principalRequest; - std::optional setFee{}; - std::optional originationFee{}; - std::optional serviceFee{}; - std::optional lateFee{}; - std::optional closeFee{}; - std::optional overFee{}; - std::optional interest{}; - std::optional lateInterest{}; - std::optional closeInterest{}; - std::optional overpaymentInterest{}; - std::optional payTotal{}; - std::optional payInterval{}; - std::optional gracePd{}; - std::optional flags{}; + // NOLINTBEGIN(readability-redundant-member-init) + std::optional setFee = std::nullopt; + std::optional originationFee = std::nullopt; + std::optional serviceFee = std::nullopt; + std::optional lateFee = std::nullopt; + std::optional closeFee = std::nullopt; + std::optional overFee = std::nullopt; + std::optional interest = std::nullopt; + std::optional lateInterest = std::nullopt; + std::optional closeInterest = std::nullopt; + std::optional overpaymentInterest = std::nullopt; + std::optional payTotal = std::nullopt; + std::optional payInterval = std::nullopt; + std::optional gracePd = std::nullopt; + std::optional flags = std::nullopt; + // NOLINTEND(readability-redundant-member-init) template jtx::JTx @@ -173,7 +174,11 @@ protected: using namespace jtx; using namespace jtx::loan; - JTx jt{loan::set(account, broker.brokerID, broker.asset(principalRequest).number(), flags.value_or(0))}; + JTx jt{loan::set( + account, + broker.brokerID, + broker.asset(principalRequest).number(), + flags.value_or(0))}; sig(sfCounterpartySignature, counter)(env, jt); @@ -229,7 +234,7 @@ protected: struct LoanState { std::uint32_t previousPaymentDate = 0; - NetClock::time_point startDate = {}; + NetClock::time_point startDate; std::uint32_t nextPaymentDate = 0; std::uint32_t paymentRemaining = 0; std::int32_t const loanScale = 0; @@ -274,21 +279,25 @@ protected: std::uint32_t ownerCount) const { using namespace jtx; - if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); env.test.BEAST_EXPECT(brokerSle)) + if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + env.test.BEAST_EXPECT(brokerSle)) { TenthBips16 const managementFeeRate{brokerSle->at(sfManagementFeeRate)}; auto const brokerDebt = brokerSle->at(sfDebtTotal); auto const expectedDebt = principalOutstanding + interestOwed; env.test.BEAST_EXPECT(brokerDebt == expectedDebt); env.test.BEAST_EXPECT( - env.balance(pseudoAccount, broker.asset).number() == brokerSle->at(sfCoverAvailable)); + env.balance(pseudoAccount, broker.asset).number() == + brokerSle->at(sfCoverAvailable)); env.test.BEAST_EXPECT(brokerSle->at(sfOwnerCount) == ownerCount); - if (auto vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); env.test.BEAST_EXPECT(vaultSle)) + if (auto vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); + env.test.BEAST_EXPECT(vaultSle)) { Account const vaultPseudo{"vaultPseudoAccount", vaultSle->at(sfAccount)}; env.test.BEAST_EXPECT( - vaultSle->at(sfAssetsAvailable) == env.balance(vaultPseudo, broker.asset).number()); + vaultSle->at(sfAssetsAvailable) == + env.balance(vaultPseudo, broker.asset).number()); if (ownerCount == 0) { // Allow some slop for rounding IOUs @@ -318,10 +327,12 @@ protected: auto const borrowerScale = std::max(loanScale, balanceBefore.number().exponent()); STAmount const balanceChangeAmount{ - broker.asset, roundToAsset(broker.asset, expectedPayment + adjustment, borrowerScale)}; + broker.asset, + roundToAsset(broker.asset, expectedPayment + adjustment, borrowerScale)}; { auto const difference = roundToScale( - env.balance(account, broker.asset) - (balanceBefore - balanceChangeAmount), borrowerScale); + env.balance(account, broker.asset) - (balanceBefore - balanceChangeAmount), + borrowerScale); env.test.expect( roundToScale(difference, loanScale) >= beast::zero, "Balance before: " + to_string(balanceBefore.value()) + @@ -354,7 +365,8 @@ protected: env.test.BEAST_EXPECT(loan->at(sfLoanScale) == loanScale); env.test.BEAST_EXPECT(loan->at(sfTotalValueOutstanding) == totalValue); env.test.BEAST_EXPECT(loan->at(sfPrincipalOutstanding) == principalOutstanding); - env.test.BEAST_EXPECT(loan->at(sfManagementFeeOutstanding) == managementFeeOutstanding); + env.test.BEAST_EXPECT( + loan->at(sfManagementFeeOutstanding) == managementFeeOutstanding); env.test.BEAST_EXPECT(loan->at(sfPeriodicPayment) == periodicPayment); env.test.BEAST_EXPECT(loan->at(sfFlags) == flags); @@ -362,9 +374,16 @@ protected: auto const interestRate = TenthBips32{loan->at(sfInterestRate)}; auto const paymentInterval = loan->at(sfPaymentInterval); - checkBroker(principalOutstanding, ls.interestDue, interestRate, paymentInterval, paymentRemaining, 1); + checkBroker( + principalOutstanding, + ls.interestDue, + interestRate, + paymentInterval, + paymentRemaining, + 1); - if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); env.test.BEAST_EXPECT(brokerSle)) + if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + env.test.BEAST_EXPECT(brokerSle)) { if (auto vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); env.test.BEAST_EXPECT(vaultSle)) @@ -372,7 +391,8 @@ protected: if ((flags & lsfLoanImpaired) && !(flags & lsfLoanDefault)) { env.test.BEAST_EXPECT( - vaultSle->at(sfLossUnrealized) == totalValue - managementFeeOutstanding); + vaultSle->at(sfLossUnrealized) == + totalValue - managementFeeOutstanding); } else { @@ -495,13 +515,16 @@ protected: BEAST_EXPECT(state.principalOutstanding == broker.asset(1000).value()); BEAST_EXPECT( state.loanScale >= - (broker.asset.integral() ? 0 : std::max(broker.vaultScale(env), state.principalOutstanding.exponent()))); + (broker.asset.integral() + ? 0 + : std::max(broker.vaultScale(env), state.principalOutstanding.exponent()))); BEAST_EXPECT(state.paymentInterval == 600); { NumberRoundModeGuard mg(Number::upward); BEAST_EXPECT( state.totalValue == - roundToAsset(broker.asset, state.periodicPayment * state.paymentRemaining, state.loanScale)); + roundToAsset( + broker.asset, state.periodicPayment * state.paymentRemaining, state.loanScale)); } BEAST_EXPECT( state.managementFeeOutstanding == @@ -519,14 +542,17 @@ protected: bool canImpairLoan(jtx::Env const& env, BrokerInfo const& broker, LoanState const& state) { - if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { - if (auto const vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); BEAST_EXPECT(vaultSle)) + if (auto const vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); + BEAST_EXPECT(vaultSle)) { // log << vaultSle->getJson() << std::endl; - auto const assetsUnavailable = vaultSle->at(sfAssetsTotal) - vaultSle->at(sfAssetsAvailable); - auto const unrealizedLoss = - vaultSle->at(sfLossUnrealized) + state.totalValue - state.managementFeeOutstanding; + auto const assetsUnavailable = + vaultSle->at(sfAssetsTotal) - vaultSle->at(sfAssetsAvailable); + auto const unrealizedLoss = vaultSle->at(sfLossUnrealized) + state.totalValue - + state.managementFeeOutstanding; if (!BEAST_EXPECT(unrealizedLoss <= assetsUnavailable)) { @@ -560,7 +586,8 @@ protected: case AssetType::IOU: { PrettyAsset const asset{issuer[iouCurrency]}; - auto const limit = asset(100 * (brokerParams.vaultDeposit + brokerParams.coverDeposit)); + auto const limit = + asset(100 * (brokerParams.vaultDeposit + brokerParams.coverDeposit)); if (lender != issuer) env(trust(lender, limit)); if (borrower != issuer) @@ -618,7 +645,13 @@ protected: auto const total = loanParams.payTotal.value_or(LoanSet::defaultPaymentTotal); auto const feeRate = brokerParams.managementFeeRate; auto const props = computeLoanProperties( - asset, principal, interest, interval, total, feeRate, asset(brokerParams.vaultDeposit).number().exponent()); + asset, + principal, + interest, + interval, + total, + feeRate, + asset(brokerParams.vaultDeposit).number().exponent()); log << "Loan properties:\n" << "\tPrincipal: " << principal << std::endl << "\tInterest rate: " << interest << std::endl @@ -667,10 +700,12 @@ protected: env.close(); if (asset.native() || lender != issuer) + { env( pay((asset.native() ? env.master : issuer), lender, asset(brokerParams.vaultDeposit + brokerParams.coverDeposit))); + } // Fund the borrower later once we know the total loan // size @@ -733,15 +768,21 @@ protected: // Add extra for transaction fees and reserves, if appropriate, or a // tiny amount for the extra paid in each transaction auto const totalNeeded = state.totalValue + (serviceFee * state.paymentRemaining) + - (broker.asset.native() ? Number( - baseFee * state.paymentRemaining + - env.current()->fees().accountReserve(env.ownerCount(borrower))) - : broker.asset(15).number()); + (broker.asset.native() + ? Number( + baseFee * state.paymentRemaining + + env.current()->fees().accountReserve(env.ownerCount(borrower))) + : broker.asset(15).number()); auto const shortage = totalNeeded - borrowerBalance.number(); if (shortage > beast::zero && (broker.asset.native() || issuer != borrower)) - env(pay((broker.asset.native() ? env.master : issuer), borrower, STAmount{broker.asset, shortage})); + { + env( + pay((broker.asset.native() ? env.master : issuer), + borrower, + STAmount{broker.asset, shortage})); + } } void @@ -787,9 +828,11 @@ protected: // the below BEAST_EXPECTs may not hold across assets. auto const periodicRate = loanPeriodicRate(state.interestRate, state.paymentInterval); STAmount const roundedPeriodicPayment{ - broker.asset, roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; + broker.asset, + roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; if (!showStepBalances) + { log << currencyLabel << " Payment components: " << "Payments remaining, " << "rawInterest, rawPrincipal, " @@ -797,15 +840,20 @@ protected: << "trackedValueDelta, trackedPrincipalDelta, " "trackedInterestDelta, trackedMgmtFeeDelta, special" << std::endl; + } // Include the service fee - STAmount const totalDue = roundToScale(roundedPeriodicPayment + serviceFee, state.loanScale, Number::upward); + STAmount const totalDue = + roundToScale(roundedPeriodicPayment + serviceFee, state.loanScale, Number::upward); - auto currentRoundedState = - constructLoanState(state.totalValue, state.principalOutstanding, state.managementFeeOutstanding); + auto currentRoundedState = constructLoanState( + state.totalValue, state.principalOutstanding, state.managementFeeOutstanding); { auto const raw = computeTheoreticalLoanState( - state.periodicPayment, periodicRate, state.paymentRemaining, broker.params.managementFeeRate); + state.periodicPayment, + periodicRate, + state.paymentRemaining, + broker.params.managementFeeRate); if (showStepBalances) { @@ -813,15 +861,17 @@ protected: << "\n\tTotal value: " << currentRoundedState.valueOutstanding << "\n\tPrincipal: " << currentRoundedState.principalOutstanding << "\n\tInterest: " << currentRoundedState.interestDue - << "\n\tMgmt fee: " << currentRoundedState.managementFeeDue << "\n\tPayments remaining " - << state.paymentRemaining << std::endl; + << "\n\tMgmt fee: " << currentRoundedState.managementFeeDue + << "\n\tPayments remaining " << state.paymentRemaining << std::endl; } else { - log << currencyLabel << " Loan starting state: " << state.paymentRemaining << ", " << raw.interestDue - << ", " << raw.principalOutstanding << ", " << raw.managementFeeDue << ", " - << currentRoundedState.valueOutstanding << ", " << currentRoundedState.principalOutstanding << ", " - << currentRoundedState.interestDue << ", " << currentRoundedState.managementFeeDue << std::endl; + log << currencyLabel << " Loan starting state: " << state.paymentRemaining << ", " + << raw.interestDue << ", " << raw.principalOutstanding << ", " + << raw.managementFeeDue << ", " << currentRoundedState.valueOutstanding << ", " + << currentRoundedState.principalOutstanding << ", " + << currentRoundedState.interestDue << ", " + << currentRoundedState.managementFeeDue << std::endl; } } @@ -843,7 +893,10 @@ protected: std::size_t totalPaymentsMade = 0; xrpl::LoanState currentTrueState = computeTheoreticalLoanState( - state.periodicPayment, periodicRate, state.paymentRemaining, broker.params.managementFeeRate); + state.periodicPayment, + periodicRate, + state.paymentRemaining, + broker.params.managementFeeRate); auto validateBorrowerBalance = [&]() { if (borrower == issuer || !paymentParams.validateBalances) @@ -851,7 +904,9 @@ protected: auto const totalSpent = (totalPaid.trackedValueDelta + totalFeesPaid + (broker.asset.native() ? Number(baseFee) * totalPaymentsMade : numZero)); - BEAST_EXPECT(env.balance(borrower, broker.asset).number() == borrowerInitialBalance - totalSpent); + BEAST_EXPECT( + env.balance(borrower, broker.asset).number() == + borrowerInitialBalance - totalSpent); }; auto const defaultRound = broker.asset.integral() ? 3 : 0; @@ -887,27 +942,37 @@ protected: paymentComponents.trackedManagementFeeDelta); xrpl::LoanState const nextTrueState = computeTheoreticalLoanState( - state.periodicPayment, periodicRate, state.paymentRemaining - 1, broker.params.managementFeeRate); + state.periodicPayment, + periodicRate, + state.paymentRemaining - 1, + broker.params.managementFeeRate); detail::LoanStateDeltas const deltas = currentTrueState - nextTrueState; - BEAST_EXPECT(deltas.total() == deltas.principal + deltas.interest + deltas.managementFee); + BEAST_EXPECT( + deltas.total() == deltas.principal + deltas.interest + deltas.managementFee); BEAST_EXPECT( paymentComponents.specialCase == detail::PaymentSpecialCase::final || deltas.total() == state.periodicPayment || (state.loanScale - (deltas.total() - state.periodicPayment).exponent()) > 14); if (!showStepBalances) + { log << currencyLabel << " Payment components: " << state.paymentRemaining << ", " - << deltas.interest << ", " << deltas.principal << ", " << deltas.managementFee << ", " - << paymentComponents.trackedValueDelta << ", " << paymentComponents.trackedPrincipalDelta << ", " - << paymentComponents.trackedInterestPart() << ", " << paymentComponents.trackedManagementFeeDelta - << ", " - << (paymentComponents.specialCase == detail::PaymentSpecialCase::final ? "final" - : paymentComponents.specialCase == detail::PaymentSpecialCase::extra ? "extra" - : "none") - << std::endl; + << deltas.interest << ", " << deltas.principal << ", " << deltas.managementFee + << ", " << paymentComponents.trackedValueDelta << ", " + << paymentComponents.trackedPrincipalDelta << ", " + << paymentComponents.trackedInterestPart() << ", " + << paymentComponents.trackedManagementFeeDelta << ", " << [&]() -> char const* { + if (paymentComponents.specialCase == detail::PaymentSpecialCase::final) + return "final"; + if (paymentComponents.specialCase == detail::PaymentSpecialCase::extra) + return "extra"; + return "none"; + }() << std::endl; + } - auto const totalDueAmount = STAmount{broker.asset, paymentComponents.trackedValueDelta + serviceFee}; + auto const totalDueAmount = + STAmount{broker.asset, paymentComponents.trackedValueDelta + serviceFee}; if (paymentParams.validateBalances) { @@ -919,7 +984,8 @@ protected: // IOUs, the difference should be dust. Number const diff = totalDue - totalDueAmount; BEAST_EXPECT( - paymentComponents.specialCase == detail::PaymentSpecialCase::final || diff == beast::zero || + paymentComponents.specialCase == detail::PaymentSpecialCase::final || + diff == beast::zero || (diff > beast::zero && ((broker.asset.integral() && (static_cast(diff) < 3)) || (state.loanScale - diff.exponent() > 13)))); @@ -950,25 +1016,34 @@ protected: // Check the result verifyLoanStatus.checkPayment( - state.loanScale, borrower, borrowerBalanceBeforePayment, totalDueAmount, adjustment); + state.loanScale, + borrower, + borrowerBalanceBeforePayment, + totalDueAmount, + adjustment); } if (showStepBalances) { auto const loanSle = env.le(loanKeylet); if (!BEAST_EXPECT(loanSle)) + { // No reason for this not to exist return; + } auto const current = constructRoundedLoanState(loanSle); auto const errors = nextTrueState - current; log << currencyLabel << " Loan balances: " << "\n\tAmount taken: " << paymentComponents.trackedValueDelta << "\n\tTotal value: " << current.valueOutstanding - << " (true: " << truncate(nextTrueState.valueOutstanding) << ", error: " << truncate(errors.total()) + << " (true: " << truncate(nextTrueState.valueOutstanding) + << ", error: " << truncate(errors.total()) << ")\n\tPrincipal: " << current.principalOutstanding << " (true: " << truncate(nextTrueState.principalOutstanding) - << ", error: " << truncate(errors.principal) << ")\n\tInterest: " << current.interestDue - << " (true: " << truncate(nextTrueState.interestDue) << ", error: " << truncate(errors.interest) + << ", error: " << truncate(errors.principal) + << ")\n\tInterest: " << current.interestDue + << " (true: " << truncate(nextTrueState.interestDue) + << ", error: " << truncate(errors.interest) << ")\n\tMgmt fee: " << current.managementFeeDue << " (true: " << truncate(nextTrueState.managementFeeDue) << ", error: " << truncate(errors.managementFee) << ")\n\tPayments remaining " @@ -1010,14 +1085,15 @@ protected: BEAST_EXPECT(state.paymentRemaining == 0); BEAST_EXPECT(state.principalOutstanding == 0); - auto const initialInterestDue = - initialState.totalValue - (initialState.principalOutstanding + initialState.managementFeeOutstanding); + auto const initialInterestDue = initialState.totalValue - + (initialState.principalOutstanding + initialState.managementFeeOutstanding); if (paymentParams.validateBalances) { // Make sure all the payments add up BEAST_EXPECT(totalPaid.trackedValueDelta == initialState.totalValue); BEAST_EXPECT(totalPaid.trackedPrincipalDelta == initialState.principalOutstanding); - BEAST_EXPECT(totalPaid.trackedManagementFeeDelta == initialState.managementFeeOutstanding); + BEAST_EXPECT( + totalPaid.trackedManagementFeeDelta == initialState.managementFeeOutstanding); // This is almost a tautology given the previous checks, but // check it anyway for completeness. BEAST_EXPECT(totalInterestPaid == initialInterestDue); @@ -1028,26 +1104,33 @@ protected: { auto const loanSle = env.le(loanKeylet); if (!BEAST_EXPECT(loanSle)) + { // No reason for this not to exist return; + } log << currencyLabel << " Total amounts paid: " << "\n\tTotal value: " << totalPaid.trackedValueDelta << " (initial: " << truncate(initialState.totalValue) << ", error: " << truncate(initialState.totalValue - totalPaid.trackedValueDelta) << ")\n\tPrincipal: " << totalPaid.trackedPrincipalDelta - << " (initial: " << truncate(initialState.principalOutstanding) - << ", error: " << truncate(initialState.principalOutstanding - totalPaid.trackedPrincipalDelta) - << ")\n\tInterest: " << totalInterestPaid << " (initial: " << truncate(initialInterestDue) + << " (initial: " << truncate(initialState.principalOutstanding) << ", error: " + << truncate(initialState.principalOutstanding - totalPaid.trackedPrincipalDelta) + << ")\n\tInterest: " << totalInterestPaid + << " (initial: " << truncate(initialInterestDue) << ", error: " << truncate(initialInterestDue - totalInterestPaid) << ")\n\tMgmt fee: " << totalPaid.trackedManagementFeeDelta - << " (initial: " << truncate(initialState.managementFeeOutstanding) - << ", error: " << truncate(initialState.managementFeeOutstanding - totalPaid.trackedManagementFeeDelta) + << " (initial: " << truncate(initialState.managementFeeOutstanding) << ", error: " + << truncate( + initialState.managementFeeOutstanding - totalPaid.trackedManagementFeeDelta) << ")\n\tTotal payments made: " << totalPaymentsMade << std::endl; } } void - runLoan(AssetType assetType, BrokerParameters const& brokerParams, LoanParameters const& loanParams) + runLoan( + AssetType assetType, + BrokerParameters const& brokerParams, + LoanParameters const& loanParams) { using namespace jtx; @@ -1057,8 +1140,9 @@ protected: Env env(*this, all); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -1104,13 +1188,16 @@ protected: std::uint32_t flags, // The end of life callback is expected to take the loan to 0 payments // remaining, one way or another - std::function toEndOfLife) + std::function + toEndOfLife) { auto const [keylet, loanSequence] = [&]() { auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); if (!BEAST_EXPECT(brokerSle)) + { // will be invalid return std::make_pair(keylet::loan(broker.brokerID), std::uint32_t(0)); + } // Broker has no loans BEAST_EXPECT(brokerSle->at(sfOwnerCount) == 0); @@ -1194,7 +1281,8 @@ protected: auto const startDate = env.current()->header().parentCloseTime.time_since_epoch().count(); - if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { BEAST_EXPECT(brokerSle->at(sfOwnerCount) == 1); } @@ -1209,16 +1297,19 @@ protected: BEAST_EXPECT( env.balance(borrower, broker.asset).value() == - borrowerStartbalance.value() + principalRequestAmount - originationFeeAmount - adjustment.value()); + borrowerStartbalance.value() + principalRequestAmount - originationFeeAmount - + adjustment.value()); } - auto const loanFlags = createJtx.stx->isFlag(tfLoanOverpayment) ? lsfLoanOverpayment : LedgerSpecificFlags(0); + auto const loanFlags = + createJtx.stx->isFlag(tfLoanOverpayment) ? lsfLoanOverpayment : LedgerSpecificFlags(0); if (auto loan = env.le(keylet); BEAST_EXPECT(loan)) { // log << "loan after create: " << to_string(loan->getJson()) // << std::endl; - BEAST_EXPECT(loan->isFlag(lsfLoanOverpayment) == createJtx.stx->isFlag(tfLoanOverpayment)); + BEAST_EXPECT( + loan->isFlag(lsfLoanOverpayment) == createJtx.stx->isFlag(tfLoanOverpayment)); BEAST_EXPECT(loan->at(sfLoanSequence) == loanSequence); BEAST_EXPECT(loan->at(sfBorrower) == borrower.id()); BEAST_EXPECT(loan->at(sfLoanBrokerID) == broker.brokerID); @@ -1239,7 +1330,9 @@ protected: BEAST_EXPECT(loan->at(sfPaymentRemaining) == *loanParams.payTotal); BEAST_EXPECT( loan->at(sfLoanScale) >= - (broker.asset.integral() ? 0 : std::max(broker.vaultScale(env), principalRequestAmount.exponent()))); + (broker.asset.integral() + ? 0 + : std::max(broker.vaultScale(env), principalRequestAmount.exponent()))); BEAST_EXPECT(loan->at(sfPrincipalOutstanding) == principalRequestAmount); } @@ -1282,7 +1375,8 @@ protected: env(manage(lender, keylet.key, tfLoanUnimpair | tfLoanImpair), ter(temINVALID_FLAG)); env(manage(lender, keylet.key, tfLoanImpair | tfLoanDefault), ter(temINVALID_FLAG)); env(manage(lender, keylet.key, tfLoanUnimpair | tfLoanDefault), ter(temINVALID_FLAG)); - env(manage(lender, keylet.key, tfLoanUnimpair | tfLoanImpair | tfLoanDefault), ter(temINVALID_FLAG)); + env(manage(lender, keylet.key, tfLoanUnimpair | tfLoanImpair | tfLoanDefault), + ter(temINVALID_FLAG)); // invalid loan ID env(manage(lender, broker.brokerID, tfLoanImpair), ter(tecNO_ENTRY)); // Loan is unimpaired, can't unimpair it again @@ -1294,9 +1388,11 @@ protected: // Check the vault bool const canImpair = canImpairLoan(env, broker, state); // Impair the loan, if possible - env(manage(lender, keylet.key, tfLoanImpair), canImpair ? ter(tesSUCCESS) : ter(tecLIMIT_EXCEEDED)); + env(manage(lender, keylet.key, tfLoanImpair), + canImpair ? ter(tesSUCCESS) : ter(tecLIMIT_EXCEEDED)); // Unimpair the loan - env(manage(lender, keylet.key, tfLoanUnimpair), canImpair ? ter(tesSUCCESS) : ter(tecNO_PERMISSION)); + env(manage(lender, keylet.key, tfLoanUnimpair), + canImpair ? ter(tesSUCCESS) : ter(tecNO_PERMISSION)); auto const nextDueDate = startDate + *loanParams.payInterval; @@ -1360,10 +1456,13 @@ protected: // No loans left verifyLoanStatus.checkBroker(0, 0, *loanParams.interest, 1, 0, 0); - BEAST_EXPECT(env.balance(borrower, broker.asset).value() == borrowerStartingBalance.value() - adjustment); + BEAST_EXPECT( + env.balance(borrower, broker.asset).value() == + borrowerStartingBalance.value() - adjustment); BEAST_EXPECT(env.ownerCount(borrower) == borrowerOwnerCount); - if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { BEAST_EXPECT(brokerSle->at(sfOwnerCount) == 0); } @@ -1372,7 +1471,13 @@ protected: std::string getCurrencyLabel(Asset const& asset) { - return (asset.native() ? "XRP" : asset.holds() ? "IOU" : asset.holds() ? "MPT" : "Unknown"); + if (asset.native()) + return "XRP"; + if (asset.holds()) + return "IOU"; + if (asset.holds()) + return "MPT"; + return "Unknown"; } /** Wrapper to run a series of lifecycle tests for a given asset and loan @@ -1400,8 +1505,8 @@ protected: auto const currencyLabel = getCurrencyLabel(asset); auto const caseLabel = [&]() { std::stringstream ss; - ss << "Lifecycle: " << loanAmount << " " << currencyLabel << " Scale interest to: " << interestExponent - << " "; + ss << "Lifecycle: " << loanAmount << " " << currencyLabel + << " Scale interest to: " << interestExponent << " "; return ss.str(); }(); testcase << caseLabel; @@ -1702,8 +1807,10 @@ protected: auto const vaultPseudo = [&]() { auto const vaultSle = env.le(keylet::vault(brokerSle->at(sfVaultID))); if (!BEAST_EXPECT(vaultSle)) + { // This will be wrong, but the test has failed anyway. return lender; + } auto const vaultPseudo = Account("Vault pseudo-account", vaultSle->at(sfAccount)); return vaultPseudo; }(); @@ -1721,7 +1828,7 @@ protected: // XRP can't be frozen return std::make_tuple(empty, empty, empty, tesSUCCESS); } - else if (broker.asset.holds()) + if (broker.asset.holds()) { auto freeze = [&](Account const& holder) { env(trust(issuer, holder[iouCurrency](0), tfSetFreeze)); @@ -1730,20 +1837,19 @@ protected: env(trust(issuer, holder[iouCurrency](0), tfSetFreeze | tfSetDeepFreeze)); }; auto unfreeze = [&](Account const& holder) { - env(trust(issuer, holder[iouCurrency](0), tfClearFreeze | tfClearDeepFreeze)); + env(trust( + issuer, holder[iouCurrency](0), tfClearFreeze | tfClearDeepFreeze)); }; return std::make_tuple(freeze, deepfreeze, unfreeze, tecFROZEN); } - else - { - auto freeze = [&](Account const& holder) { - mptt.set({.account = issuer, .holder = holder, .flags = tfMPTLock}); - }; - auto unfreeze = [&](Account const& holder) { - mptt.set({.account = issuer, .holder = holder, .flags = tfMPTUnlock}); - }; - return std::make_tuple(freeze, empty, unfreeze, tecLOCKED); - } + + auto freeze = [&](Account const& holder) { + mptt.set({.account = issuer, .holder = holder, .flags = tfMPTLock}); + }; + auto unfreeze = [&](Account const& holder) { + mptt.set({.account = issuer, .holder = holder, .flags = tfMPTUnlock}); + }; + return std::make_tuple(freeze, empty, unfreeze, tecLOCKED); }(); // Try freezing the accounts that can't be frozen @@ -1816,10 +1922,10 @@ protected: } // Finally! Create a loan - std::string testData; auto coverAvailable = [&env, this](uint256 const& brokerID, Number const& expected) { - if (auto const brokerSle = env.le(keylet::loanbroker(brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerID)); + BEAST_EXPECT(brokerSle)) { auto const available = brokerSle->at(sfCoverAvailable); BEAST_EXPECT(available == expected); @@ -1828,18 +1934,22 @@ protected: return Number{}; }; auto getDefaultInfo = [&env, this](LoanState const& state, BrokerInfo const& broker) { - if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { BEAST_EXPECT( - state.loanScale >= (broker.asset.integral() - ? 0 - : std::max(broker.vaultScale(env), state.principalOutstanding.exponent()))); + state.loanScale >= + (broker.asset.integral() + ? 0 + : std::max( + broker.vaultScale(env), state.principalOutstanding.exponent()))); NumberRoundModeGuard mg(Number::upward); auto const defaultAmount = roundToAsset( broker.asset, std::min( tenthBipsOfValue( - tenthBipsOfValue(brokerSle->at(sfDebtTotal), broker.params.coverRateMin), + tenthBipsOfValue( + brokerSle->at(sfDebtTotal), broker.params.coverRateMin), broker.params.coverRateLiquidation), state.totalValue - state.managementFeeOutstanding), state.loanScale); @@ -1853,13 +1963,15 @@ protected: Number const& startingCoverAvailable, Number const& amountToBeCovered) { coverAvailable(broker.brokerID, startingCoverAvailable - amountToBeCovered); - env(loanBroker::coverDeposit(brokerAcct, broker.brokerID, STAmount{broker.asset, amountToBeCovered})); + env(loanBroker::coverDeposit( + brokerAcct, broker.brokerID, STAmount{broker.asset, amountToBeCovered})); coverAvailable(broker.brokerID, startingCoverAvailable); env.close(); }; auto defaultImmediately = [&](std::uint32_t baseFlag, bool impair = true) { - return [&, impair, baseFlag](Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { + return [&, impair, baseFlag]( + Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { // toEndOfLife // // Default the loan @@ -1869,8 +1981,8 @@ protected: BEAST_EXPECT(state.flags == baseFlag); auto const& broker = verifyLoanStatus.broker; - auto const startingCoverAvailable = - coverAvailable(broker.brokerID, broker.asset(broker.params.coverDeposit).number()); + auto const startingCoverAvailable = coverAvailable( + broker.brokerID, broker.asset(broker.params.coverDeposit).number()); if (impair) { @@ -1939,7 +2051,8 @@ protected: verifyLoanStatus(state); // Send some bogus pay transactions - env(pay(borrower, keylet::loan(uint256(0)).key, broker.asset(10), txFlags), ter(temINVALID)); + env(pay(borrower, keylet::loan(uint256(0)).key, broker.asset(10), txFlags), + ter(temINVALID)); // broker.asset(80) is less than a single payment, but all these // checks fail before that matters env(pay(borrower, loanKeylet.key, broker.asset(-80), txFlags), ter(temBAD_AMOUNT)); @@ -1985,12 +2098,14 @@ protected: ter(temINVALID_FLAG)); { - auto const otherAsset = broker.asset.raw() == assets[0].raw() ? assets[1] : assets[0]; + auto const otherAsset = + broker.asset.raw() == assets[0].raw() ? assets[1] : assets[0]; env(pay(borrower, loanKeylet.key, otherAsset(100), txFlags), ter(tecWRONG_ASSET)); } // Amount doesn't cover a single payment - env(pay(borrower, loanKeylet.key, STAmount{broker.asset, 1}, txFlags), ter(tecINSUFFICIENT_PAYMENT)); + env(pay(borrower, loanKeylet.key, STAmount{broker.asset, 1}, txFlags), + ter(tecINSUFFICIENT_PAYMENT)); // Get the balance after these failed transactions take // fees @@ -2005,7 +2120,9 @@ protected: // balance XRPAmount const badFee{ baseFee * - (borrowerBalanceBeforePayment.number() * 2 / state.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; + (borrowerBalanceBeforePayment.number() * 2 / state.periodicPayment / + loanPaymentsPerFeeIncrement + + 1)}; env(pay(borrower, loanKeylet.key, STAmount{broker.asset, borrowerBalanceBeforePayment.number() * 2}, @@ -2031,7 +2148,8 @@ protected: state.principalOutstanding = 0; state.totalValue = 0; state.managementFeeOutstanding = 0; - state.previousPaymentDate = state.nextPaymentDate + state.paymentInterval * (numPayments - 1); + state.previousPaymentDate = + state.nextPaymentDate + state.paymentInterval * (numPayments - 1); state.nextPaymentDate = 0; verifyLoanStatus(state); @@ -2044,7 +2162,8 @@ protected: }; auto fullPayment = [&](std::uint32_t baseFlag) { - return [&, baseFlag](Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { + return [&, baseFlag]( + Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { // toEndOfLife // auto state = getCurrentState(env, broker, loanKeylet, verifyLoanStatus); @@ -2062,63 +2181,92 @@ protected: // the below BEAST_EXPECTs may not hold across assets. Number const interval = state.paymentInterval; auto const periodicRate = interval * Number(12, -2) / secondsInYear; - BEAST_EXPECT(periodicRate == Number(2283105022831050228ULL, -24, Number::normalized{})); + BEAST_EXPECT( + periodicRate == Number(2283105022831050228ULL, -24, Number::normalized{})); STAmount const principalOutstanding{broker.asset, state.principalOutstanding}; STAmount const accruedInterest{ broker.asset, state.principalOutstanding * periodicRate * loanAge / interval}; BEAST_EXPECT(accruedInterest == broker.asset(Number(1141552511415525, -19))); - STAmount const prepaymentPenalty{broker.asset, state.principalOutstanding * Number(36, -3)}; + STAmount const prepaymentPenalty{ + broker.asset, state.principalOutstanding * Number(36, -3)}; BEAST_EXPECT(prepaymentPenalty == broker.asset(36)); STAmount const closePaymentFee = broker.asset(4); auto const payoffAmount = roundToScale( - principalOutstanding + accruedInterest + prepaymentPenalty + closePaymentFee, state.loanScale); + principalOutstanding + accruedInterest + prepaymentPenalty + closePaymentFee, + state.loanScale); BEAST_EXPECT( payoffAmount == - roundToAsset(broker.asset, broker.asset(Number(1040000114155251, -12)).number(), state.loanScale)); + roundToAsset( + broker.asset, + broker.asset(Number(1040000114155251, -12)).number(), + state.loanScale)); // The terms of this loan actually make the early payoff // more expensive than just making payments - BEAST_EXPECT(payoffAmount > state.paymentRemaining * (state.periodicPayment + broker.asset(2).value())); + BEAST_EXPECT( + payoffAmount > + state.paymentRemaining * (state.periodicPayment + broker.asset(2).value())); - singlePayment(loanKeylet, verifyLoanStatus, state, payoffAmount, 1, baseFlag, tfLoanFullPayment); + singlePayment( + loanKeylet, + verifyLoanStatus, + state, + payoffAmount, + 1, + baseFlag, + tfLoanFullPayment); }; }; auto combineAllPayments = [&](std::uint32_t baseFlag) { - return [&, baseFlag](Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { - // toEndOfLife - // + return + [&, baseFlag](Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { + // toEndOfLife + // - auto state = getCurrentState(env, broker, loanKeylet, verifyLoanStatus); - env.close(); + auto state = getCurrentState(env, broker, loanKeylet, verifyLoanStatus); + env.close(); - BEAST_EXPECT( - STAmount(broker.asset, state.periodicPayment) == broker.asset(Number(8333457002039338267, -17))); + BEAST_EXPECT( + STAmount(broker.asset, state.periodicPayment) == + broker.asset(Number(8333457002039338267, -17))); - // Make all the payments in one transaction - // service fee is 2 - auto const startingPayments = state.paymentRemaining; - STAmount const payoffAmount = [&]() { - NumberRoundModeGuard mg(Number::upward); - auto const rawPayoff = startingPayments * (state.periodicPayment + broker.asset(2).value()); - STAmount payoffAmount{broker.asset, rawPayoff}; - BEAST_EXPECTS(payoffAmount == broker.asset(Number(1024014840244721, -12)), to_string(payoffAmount)); - BEAST_EXPECT(payoffAmount > state.principalOutstanding); + // Make all the payments in one transaction + // service fee is 2 + auto const startingPayments = state.paymentRemaining; + STAmount const payoffAmount = [&]() { + NumberRoundModeGuard mg(Number::upward); + auto const rawPayoff = + startingPayments * (state.periodicPayment + broker.asset(2).value()); + STAmount payoffAmount{broker.asset, rawPayoff}; + BEAST_EXPECTS( + payoffAmount == broker.asset(Number(1024014840244721, -12)), + to_string(payoffAmount)); + BEAST_EXPECT(payoffAmount > state.principalOutstanding); - payoffAmount = roundToScale(payoffAmount, state.loanScale); + payoffAmount = roundToScale(payoffAmount, state.loanScale); - return payoffAmount; - }(); + return payoffAmount; + }(); - auto const totalPayoffValue = state.totalValue + startingPayments * broker.asset(2).value(); - STAmount const totalPayoffAmount{broker.asset, totalPayoffValue}; + auto const totalPayoffValue = + state.totalValue + startingPayments * broker.asset(2).value(); + STAmount const totalPayoffAmount{broker.asset, totalPayoffValue}; - BEAST_EXPECTS( - totalPayoffAmount == payoffAmount, - "Payoff amount: " + to_string(payoffAmount) + ". Total Value: " + to_string(totalPayoffAmount)); + BEAST_EXPECTS( + totalPayoffAmount == payoffAmount, + "Payoff amount: " + to_string(payoffAmount) + + ". Total Value: " + to_string(totalPayoffAmount)); - singlePayment(loanKeylet, verifyLoanStatus, state, payoffAmount, state.paymentRemaining, baseFlag, 0); - }; + singlePayment( + loanKeylet, + verifyLoanStatus, + state, + payoffAmount, + state.paymentRemaining, + baseFlag, + 0); + }; }; // There are a lot of fields that can be set on a loan, but most @@ -2273,9 +2421,11 @@ protected: // the below BEAST_EXPECTs may not hold across assets. Number const interval = state.paymentInterval; auto const periodicRate = interval * Number(12, -2) / secondsInYear; - BEAST_EXPECT(periodicRate == Number(2283105022831050228, -24, Number::normalized{})); + BEAST_EXPECT( + periodicRate == Number(2283105022831050228, -24, Number::normalized{})); STAmount const roundedPeriodicPayment{ - broker.asset, roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; + broker.asset, + roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; testcase << currencyLabel << " Payment components: " << "Payments remaining, rawInterest, rawPrincipal, " @@ -2292,8 +2442,8 @@ protected: Number::upward)); // 83334570.01162141 // Include the service fee - STAmount const totalDue = - roundToScale(roundedPeriodicPayment + serviceFee, state.loanScale, Number::upward); + STAmount const totalDue = roundToScale( + roundedPeriodicPayment + serviceFee, state.loanScale, Number::upward); // Only check the first payment since the rounding // may drift as payments are made BEAST_EXPECT( @@ -2305,33 +2455,47 @@ protected: { auto const raw = computeTheoreticalLoanState( - state.periodicPayment, periodicRate, state.paymentRemaining, broker.params.managementFeeRate); + state.periodicPayment, + periodicRate, + state.paymentRemaining, + broker.params.managementFeeRate); auto const rounded = constructLoanState( - state.totalValue, state.principalOutstanding, state.managementFeeOutstanding); - testcase << currencyLabel << " Loan starting state: " << state.paymentRemaining << ", " - << raw.interestDue << ", " << raw.principalOutstanding << ", " << raw.managementFeeDue - << ", " << rounded.valueOutstanding << ", " << rounded.principalOutstanding << ", " - << rounded.interestDue << ", " << rounded.managementFeeDue; + state.totalValue, + state.principalOutstanding, + state.managementFeeOutstanding); + testcase << currencyLabel << " Loan starting state: " << state.paymentRemaining + << ", " << raw.interestDue << ", " << raw.principalOutstanding << ", " + << raw.managementFeeDue << ", " << rounded.valueOutstanding << ", " + << rounded.principalOutstanding << ", " << rounded.interestDue << ", " + << rounded.managementFeeDue; } // Try to pay a little extra to show that it's _not_ // taken - STAmount const transactionAmount = STAmount{broker.asset, totalDue} + broker.asset(10); + STAmount const transactionAmount = + STAmount{broker.asset, totalDue} + broker.asset(10); // Only check the first payment since the rounding // may drift as payments are made BEAST_EXPECT( transactionAmount == roundToScale( - broker.asset(Number(9533457002039400, -14), Number::upward), state.loanScale, Number::upward)); + broker.asset(Number(9533457002039400, -14), Number::upward), + state.loanScale, + Number::upward)); auto const initialState = state; detail::PaymentComponents totalPaid{ - .trackedValueDelta = 0, .trackedPrincipalDelta = 0, .trackedManagementFeeDelta = 0}; + .trackedValueDelta = 0, + .trackedPrincipalDelta = 0, + .trackedManagementFeeDelta = 0}; Number totalInterestPaid = 0; std::size_t totalPaymentsMade = 0; xrpl::LoanState currentTrueState = computeTheoreticalLoanState( - state.periodicPayment, periodicRate, state.paymentRemaining, broker.params.managementFeeRate); + state.periodicPayment, + periodicRate, + state.paymentRemaining, + broker.params.managementFeeRate); while (state.paymentRemaining > 0) { @@ -2360,17 +2524,22 @@ protected: broker.params.managementFeeRate); detail::LoanStateDeltas const deltas = currentTrueState - nextTrueState; - testcase << currencyLabel << " Payment components: " << state.paymentRemaining << ", " - << deltas.interest << ", " << deltas.principal << ", " << deltas.managementFee << ", " - << paymentComponents.trackedValueDelta << ", " << paymentComponents.trackedPrincipalDelta - << ", " << paymentComponents.trackedInterestPart() << ", " + testcase << currencyLabel << " Payment components: " << state.paymentRemaining + << ", " << deltas.interest << ", " << deltas.principal << ", " + << deltas.managementFee << ", " << paymentComponents.trackedValueDelta + << ", " << paymentComponents.trackedPrincipalDelta << ", " + << paymentComponents.trackedInterestPart() << ", " << paymentComponents.trackedManagementFeeDelta << ", " - << (paymentComponents.specialCase == detail::PaymentSpecialCase::final ? "final" - : paymentComponents.specialCase == detail::PaymentSpecialCase::extra ? "extra" - : "none"); + << [&]() -> char const* { + if (paymentComponents.specialCase == detail::PaymentSpecialCase::final) + return "final"; + if (paymentComponents.specialCase == detail::PaymentSpecialCase::extra) + return "extra"; + return "none"; + }(); - auto const totalDueAmount = - STAmount{broker.asset, paymentComponents.trackedValueDelta + serviceFee.number()}; + auto const totalDueAmount = STAmount{ + broker.asset, paymentComponents.trackedValueDelta + serviceFee.number()}; // Due to the rounding algorithms to keep the interest and // principal in sync with "true" values, the computed amount @@ -2380,14 +2549,16 @@ protected: // IOUs, the difference should be after the 8th digit. Number const diff = totalDue - totalDueAmount; BEAST_EXPECT( - paymentComponents.specialCase == detail::PaymentSpecialCase::final || diff == beast::zero || + paymentComponents.specialCase == detail::PaymentSpecialCase::final || + diff == beast::zero || (diff > beast::zero && ((broker.asset.integral() && (static_cast(diff) < 3)) || (state.loanScale - diff.exponent() > 13)))); BEAST_EXPECT( paymentComponents.trackedValueDelta == - paymentComponents.trackedPrincipalDelta + paymentComponents.trackedInterestPart() + + paymentComponents.trackedPrincipalDelta + + paymentComponents.trackedInterestPart() + paymentComponents.trackedManagementFeeDelta); BEAST_EXPECT( paymentComponents.specialCase == detail::PaymentSpecialCase::final || @@ -2395,7 +2566,8 @@ protected: BEAST_EXPECT( state.paymentRemaining < 12 || - roundToAsset(broker.asset, deltas.principal, state.loanScale, Number::upward) == + roundToAsset( + broker.asset, deltas.principal, state.loanScale, Number::upward) == roundToScale( broker.asset(Number(8333228691531218890, -17), Number::upward), state.loanScale, @@ -2409,14 +2581,17 @@ protected: BEAST_EXPECT( paymentComponents.specialCase == detail::PaymentSpecialCase::final || (state.periodicPayment.exponent() - - (deltas.principal + deltas.interest + deltas.managementFee - state.periodicPayment) + (deltas.principal + deltas.interest + deltas.managementFee - + state.periodicPayment) .exponent()) > 14); auto const borrowerBalanceBeforePayment = env.balance(borrower, broker.asset); if (canImpairLoan(env, broker, state)) + { // Making a payment will unimpair the loan env(manage(lender, loanKeylet.key, tfLoanImpair)); + } env.close(); @@ -2434,7 +2609,11 @@ protected: // Check the result verifyLoanStatus.checkPayment( - state.loanScale, borrower, borrowerBalanceBeforePayment, totalDueAmount, adjustment); + state.loanScale, + borrower, + borrowerBalanceBeforePayment, + totalDueAmount, + adjustment); --state.paymentRemaining; state.previousPaymentDate = state.nextPaymentDate; @@ -2455,7 +2634,8 @@ protected: totalPaid.trackedValueDelta += paymentComponents.trackedValueDelta; totalPaid.trackedPrincipalDelta += paymentComponents.trackedPrincipalDelta; - totalPaid.trackedManagementFeeDelta += paymentComponents.trackedManagementFeeDelta; + totalPaid.trackedManagementFeeDelta += + paymentComponents.trackedManagementFeeDelta; totalInterestPaid += paymentComponents.trackedInterestPart(); ++totalPaymentsMade; @@ -2469,13 +2649,15 @@ protected: // Make sure all the payments add up BEAST_EXPECT(totalPaid.trackedValueDelta == initialState.totalValue); BEAST_EXPECT(totalPaid.trackedPrincipalDelta == initialState.principalOutstanding); - BEAST_EXPECT(totalPaid.trackedManagementFeeDelta == initialState.managementFeeOutstanding); + BEAST_EXPECT( + totalPaid.trackedManagementFeeDelta == initialState.managementFeeOutstanding); // This is almost a tautology given the previous checks, but // check it anyway for completeness. BEAST_EXPECT( totalInterestPaid == initialState.totalValue - - (initialState.principalOutstanding + initialState.managementFeeOutstanding)); + (initialState.principalOutstanding + + initialState.managementFeeOutstanding)); BEAST_EXPECT(totalPaymentsMade == initialState.paymentRemaining); // Can't impair or default a paid off loan @@ -2503,7 +2685,8 @@ protected: auto const start = clock_type::now(); timed(); - auto const duration = std::chrono::duration_cast(clock_type::now() - start); + auto const duration = + std::chrono::duration_cast(clock_type::now() - start); log << label << " took " << duration.count() << "ms" << std::endl; @@ -2532,7 +2715,8 @@ protected: STAmount const totalDue{ broker.asset, - roundPeriodicPayment(broker.asset, state.periodicPayment + serviceFee, state.loanScale)}; + roundPeriodicPayment( + broker.asset, state.periodicPayment + serviceFee, state.loanScale)}; // Make a single payment time("single payment", [&]() { env(pay(borrower, loanKeylet.key, totalDue)); }); @@ -2542,7 +2726,9 @@ protected: auto const numPayments = (state.paymentRemaining - 2); STAmount const bigPayment{broker.asset, totalDue * numPayments}; XRPAmount const bigFee{baseFee * (numPayments / loanPaymentsPerFeeIncrement + 1)}; - time("ten payments", [&]() { env(pay(borrower, loanKeylet.key, bigPayment), fee(bigFee)); }); + time("ten payments", [&]() { + env(pay(borrower, loanKeylet.key, bigPayment), fee(bigFee)); + }); env.close(); time("final payment", [&]() { @@ -2645,7 +2831,9 @@ protected: MPTTester mptt{env, issuer, mptInitNoFund}; auto const none = LedgerSpecificFlags(0); - mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | (args.requireAuth ? tfMPTRequireAuth : none)}); + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanLock | + (args.requireAuth ? tfMPTRequireAuth : none)}); env.close(); PrettyAsset mptAsset = mptt.issuanceID(); mptt.authorize({.account = lender}); @@ -3137,8 +3325,7 @@ protected: interestRate(TenthBips32(10'000)), sig(sfCounterpartySignature, lender), fee(env.current()->fees().base * 5), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); }, nullptr); @@ -3148,7 +3335,8 @@ protected: Number const principalRequest = broker.asset(1'000).value(); Vault vault{env}; auto tx = vault.set({.owner = lender, .id = broker.vaultID}); - tx[sfAssetsMaximum] = BrokerParameters::defaults().vaultDeposit + broker.asset(1).number(); + tx[sfAssetsMaximum] = + BrokerParameters::defaults().vaultDeposit + broker.asset(1).number(); env(tx); env.close(); @@ -3160,8 +3348,7 @@ protected: fee(env.current()->fees().base * 5), paymentTotal(2), paymentInterval(3600 * 24), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); }, nullptr); } @@ -3223,8 +3410,8 @@ protected: std::vector brokers; for (auto const& asset : assets) { - brokers.emplace_back( - createVaultAndBroker(env, asset, lender, BrokerParameters{.data = "spam spam spam spam"})); + brokers.emplace_back(createVaultAndBroker( + env, asset, lender, BrokerParameters{.data = "spam spam spam spam"})); } // Create and update Loans @@ -3239,13 +3426,15 @@ protected: } } - if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { BEAST_EXPECT(brokerSle->at(sfOwnerCount) == 0); BEAST_EXPECT(brokerSle->at(sfDebtTotal) == 0); auto const coverAvailable = brokerSle->at(sfCoverAvailable); - env(loanBroker::coverWithdraw(lender, broker.brokerID, STAmount(broker.asset, coverAvailable))); + env(loanBroker::coverWithdraw( + lender, broker.brokerID, STAmount(broker.asset, coverAvailable))); env.close(); brokerSle = env.le(keylet::loanbroker(broker.brokerID)); @@ -3291,8 +3480,8 @@ protected: // The LoanSet json can be created without a counterparty signature, // but it will not pass preflight - auto createJson = - env.json(set(lender, broker.brokerID, broker.asset(principalRequest).value()), fee(loanSetFee)); + auto createJson = env.json( + set(lender, broker.brokerID, broker.asset(principalRequest).value()), fee(loanSetFee)); env(createJson, ter(temBAD_SIGNER)); // Adding an empty counterparty signature object also fails, but @@ -3309,7 +3498,9 @@ protected: BEAST_EXPECT(jr.isMember(jss::result)); auto const jResult = jr[jss::result]; BEAST_EXPECT(jResult[jss::error] == "invalidTransaction"); - BEAST_EXPECT(jResult[jss::error_exception] == "fails local checks: Transaction has bad signature."); + BEAST_EXPECT( + jResult[jss::error_exception] == + "fails local checks: Transaction has bad signature."); } // Copy the transaction signature into the counterparty signature. @@ -3396,8 +3587,8 @@ protected: // From FIND-001 testcase << "Batch Bypass Counterparty"; - bool const lendingBatchEnabled = - !std::any_of(Batch::disabledTxTypes.begin(), Batch::disabledTxTypes.end(), [](auto const& disabled) { + bool const lendingBatchEnabled = !std::any_of( + Batch::disabledTxTypes.begin(), Batch::disabledTxTypes.end(), [](auto const& disabled) { return disabled == ttLOAN_BROKER_SET; }); @@ -3479,7 +3670,8 @@ protected: BrokerInfo broker{createVaultAndBroker(env, xrpAsset, lender, brokerParams)}; - if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(broker.brokerID)); + BEAST_EXPECT(brokerSle)) { BEAST_EXPECT(brokerSle->at(sfDebtMaximum) == 0); } @@ -3555,7 +3747,7 @@ protected: createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); // Fails in preclaim because principal requested can't be // represented as XRP - env(createJson, ter(tecPRECISION_LOSS), THISLINE); + env(createJson, ter(tecPRECISION_LOSS)); env.close(); BEAST_EXPECT(!env.le(keylet)); @@ -3567,7 +3759,7 @@ protected: createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); // Fails in doApply because the payment is too small to be // represented as XRP. - env(createJson, ter(tecPRECISION_LOSS), THISLINE); + env(createJson, ter(tecPRECISION_LOSS)); env.close(); } @@ -3592,10 +3784,8 @@ protected: Account const alice{"alice"}; std::string const borrowerPass = "borrower"; - std::string const borrowerSeed = "ssBRAsLpH4778sLNYC4ik1JBJsBVf"; Account borrower{borrowerPass, KeyType::ed25519}; auto const lenderPass = "lender"; - std::string const lenderSeed = "shPTCZGwTEhJrYT8NbcNkeaa8pzPM"; Account lender{lenderPass, KeyType::ed25519}; env.fund(XRP(1'000'000), alice, lender, borrower); @@ -3632,7 +3822,8 @@ protected: lowerFee(); auto const jSubmit = env.rpc("submit", txSignBlob); BEAST_EXPECT( - jSubmit.isMember(jss::result) && jSubmit[jss::result].isMember(jss::engine_result) && + jSubmit.isMember(jss::result) && + jSubmit[jss::result].isMember(jss::engine_result) && jSubmit[jss::result][jss::engine_result].asString() == "tesSUCCESS"); lowerFee(); @@ -3656,7 +3847,8 @@ protected: }(); auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams)); BEAST_EXPECT( - jSignBorrower.isMember(jss::result) && jSignBorrower[jss::result].isMember(jss::error) && + jSignBorrower.isMember(jss::result) && + jSignBorrower[jss::result].isMember(jss::error) && jSignBorrower[jss::result][jss::error] == "invalidParams" && jSignBorrower[jss::result].isMember(jss::error_message) && jSignBorrower[jss::result][jss::error_message] == "Destination"); @@ -3689,7 +3881,8 @@ protected: }(); auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams)); BEAST_EXPECTS( - jSignBorrower.isMember(jss::result) && jSignBorrower[jss::result].isMember(jss::tx_json), + jSignBorrower.isMember(jss::result) && + jSignBorrower[jss::result].isMember(jss::tx_json), to_string(jSignBorrower)); auto const txBorrowerSignResult = jSignBorrower[jss::result][jss::tx_json]; auto const txBorrowerSignBlob = jSignBorrower[jss::result][jss::tx_blob].asString(); @@ -3720,7 +3913,9 @@ protected: return params; }(); auto const jSignLender = env.rpc("json", "sign", to_string(lenderSignParams)); - BEAST_EXPECT(jSignLender.isMember(jss::result) && jSignLender[jss::result].isMember(jss::tx_json)); + BEAST_EXPECT( + jSignLender.isMember(jss::result) && + jSignLender[jss::result].isMember(jss::tx_json)); auto const txLenderSignResult = jSignLender[jss::result][jss::tx_json]; auto const txLenderSignBlob = jSignLender[jss::result][jss::tx_blob].asString(); @@ -3739,7 +3934,8 @@ protected: jSubmitBlobResult[jss::engine_result].asString() == "tecNO_ENTRY", to_string(jSubmitBlobResult)); - BEAST_EXPECT(!jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error)); + BEAST_EXPECT( + !jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error)); // 4-alt. Lender submits the transaction json originally // received from the Borrower. It gets signed, but is now a @@ -3758,7 +3954,8 @@ protected: jSubmitJsonResult[jss::engine_result].asString() == "tefPAST_SEQ", to_string(jSubmitJsonResult)); - BEAST_EXPECT(!jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error)); + BEAST_EXPECT( + !jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error)); BEAST_EXPECT(jSubmitBlobTx == jSubmitJsonTx); } @@ -3790,7 +3987,9 @@ protected: return params; }(); auto const jSignLender = env.rpc("json", "sign", to_string(lenderSignParams)); - BEAST_EXPECT(jSignLender.isMember(jss::result) && jSignLender[jss::result].isMember(jss::tx_json)); + BEAST_EXPECT( + jSignLender.isMember(jss::result) && + jSignLender[jss::result].isMember(jss::tx_json)); auto const txLenderSignResult = jSignLender[jss::result][jss::tx_json]; auto const txLenderSignBlob = jSignLender[jss::result][jss::tx_blob].asString(); @@ -3820,7 +4019,9 @@ protected: return params; }(); auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams)); - BEAST_EXPECT(jSignBorrower.isMember(jss::result) && jSignBorrower[jss::result].isMember(jss::tx_json)); + BEAST_EXPECT( + jSignBorrower.isMember(jss::result) && + jSignBorrower[jss::result].isMember(jss::tx_json)); auto const txBorrowerSignResult = jSignBorrower[jss::result][jss::tx_json]; auto const txBorrowerSignBlob = jSignBorrower[jss::result][jss::tx_blob].asString(); @@ -3839,7 +4040,8 @@ protected: jSubmitBlobResult[jss::engine_result].asString() == "tecNO_ENTRY", to_string(jSubmitBlobResult)); - BEAST_EXPECT(!jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error)); + BEAST_EXPECT( + !jSubmitBlob.isMember(jss::error) && !jSubmitBlobResult.isMember(jss::error)); // 4-alt. Borrower submits the transaction json originally // received from the Lender. It gets signed, but is now a @@ -3858,7 +4060,8 @@ protected: jSubmitJsonResult[jss::engine_result].asString() == "tefPAST_SEQ", to_string(jSubmitJsonResult)); - BEAST_EXPECT(!jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error)); + BEAST_EXPECT( + !jSubmitJson.isMember(jss::error) && !jSubmitJsonResult.isMember(jss::error)); BEAST_EXPECT(jSubmitBlobTx == jSubmitJsonTx); } @@ -3880,10 +4083,12 @@ protected: Env env(*this); auto getCoverBalance = [&](BrokerInfo const& brokerInfo, auto const& accountField) { - if (auto const le = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(le)) + if (auto const le = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(le)) { auto const account = le->at(accountField); - if (auto const sleLine = env.le(keylet::line(account, IOU)); BEAST_EXPECT(sleLine)) + if (auto const sleLine = env.le(keylet::line(account, IOU)); + BEAST_EXPECT(sleLine)) { STAmount balance = sleLine->at(sfBalance); if (account > issuer.id()) @@ -4032,8 +4237,8 @@ protected: // preclaim Env env(*this); env.fund(XRP(1'000), lender, issuer, borrower); - env(trust(lender, IOU(10'000'000)), THISLINE); - env(pay(issuer, lender, IOU(5'000'000)), THISLINE); + env(trust(lender, IOU(10'000'000))); + env(pay(issuer, lender, IOU(5'000'000))); BrokerInfo brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)}; auto const loanSetFee = fee(env.current()->fees().base * 2); @@ -4041,60 +4246,59 @@ protected: env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), sig(sfCounterpartySignature, lender), - loanSetFee, - THISLINE); + loanSetFee); env.close(); std::uint32_t const loanSequence = 1; auto const loanKeylet = keylet::loan(brokerInfo.brokerID, loanSequence); - env(fset(issuer, asfGlobalFreeze), THISLINE); + env(fset(issuer, asfGlobalFreeze)); env.close(); // preclaim: tecFROZEN - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN)); env.close(); - env(fclear(issuer, asfGlobalFreeze), THISLINE); + env(fclear(issuer, asfGlobalFreeze)); env.close(); auto const pseudoBroker = [&]() -> std::optional { - if (auto brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { return Account{"pseudo", brokerSle->at(sfAccount)}; } - else - { - return std::nullopt; - } + + return std::nullopt; }(); if (!pseudoBroker) return; // Lender and pseudoaccount must both be frozen - env(trust(issuer, lender["IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze), THISLINE); - env(trust(issuer, (*pseudoBroker)["IOU"](1'000), *pseudoBroker, tfSetFreeze | tfSetDeepFreeze), THISLINE); + env(trust(issuer, lender["IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze)); + env(trust( + issuer, (*pseudoBroker)["IOU"](1'000), *pseudoBroker, tfSetFreeze | tfSetDeepFreeze)); env.close(); // preclaim: tecFROZEN due to deep frozen - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN)); env.close(); // Only one needs to be unfrozen - env(trust(issuer, lender["IOU"](1'000), tfClearFreeze | tfClearDeepFreeze), THISLINE); + env(trust(issuer, lender["IOU"](1'000), tfClearFreeze | tfClearDeepFreeze)); env.close(); // The payment is late by this point - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecEXPIRED), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecEXPIRED)); env.close(); - env(pay(borrower, loanKeylet.key, debtMaximumRequest, tfLoanLatePayment), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest, tfLoanLatePayment)); env.close(); // preclaim: tecKILLED // note that tecKILLED in loanMakePayment() // doesn't happen because of the preclaim check. - env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecKILLED), THISLINE); + env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecKILLED)); } void @@ -4121,108 +4325,120 @@ protected: }; // preflight: - testWrapper( - [&](Env& env, BrokerInfo const& brokerInfo, jtx::fee const& loanSetFee, Number const& debtMaximumRequest) { - // first temBAD_SIGNER: TODO - // invalid grace period - { - // zero grace period - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - gracePeriod(0), - loanSetFee, - ter(temINVALID)); - - // grace period less than default minimum - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - gracePeriod(LoanSet::defaultGracePeriod - 1), - loanSetFee, - ter(temINVALID)); - - // grace period greater than payment interval - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - paymentInterval(120), - gracePeriod(121), - loanSetFee, - ter(temINVALID)); - } - // empty/zero broker ID - { - auto jv = set(borrower, uint256{}, debtMaximumRequest); - - auto testZeroBrokerID = [&](std::string const& id, std::uint32_t flags = 0) { - // empty broker ID - jv[sfLoanBrokerID] = id; - env(jv, sig(sfCounterpartySignature, lender), loanSetFee, txflags(flags), ter(temINVALID)); - }; - // empty broker ID - testZeroBrokerID(std::string("")); - // zero broker ID - // needs a flag to distinguish the parsed STTx from the prior - // test - testZeroBrokerID(to_string(uint256{}), tfFullyCanonicalSig); - } - - // preflightCheckSigningKey() failure: - // can it happen? the signature is checked before transactor - // executes - - JTx tx = env.jt( - set(borrower, brokerInfo.brokerID, debtMaximumRequest), + testWrapper([&](Env& env, + BrokerInfo const& brokerInfo, + jtx::fee const& loanSetFee, + Number const& debtMaximumRequest) { + // first temBAD_SIGNER: TODO + // invalid grace period + { + // zero grace period + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), sig(sfCounterpartySignature, lender), - loanSetFee); - STTx local = *(tx.stx); - auto counterpartySig = local.getFieldObject(sfCounterpartySignature); - auto badPubKey = counterpartySig.getFieldVL(sfSigningPubKey); - badPubKey[20] ^= 0xAA; - counterpartySig.setFieldVL(sfSigningPubKey, badPubKey); - local.setFieldObject(sfCounterpartySignature, counterpartySig); - Json::Value jvResult; - jvResult[jss::tx_blob] = strHex(local.getSerializer().slice()); - auto res = env.rpc("json", "submit", to_string(jvResult))["result"]; - BEAST_EXPECT( - res[jss::error] == "invalidTransaction" && - res[jss::error_exception] == "fails local checks: Counterparty: Invalid signature."); - }); + gracePeriod(0), + loanSetFee, + ter(temINVALID)); + + // grace period less than default minimum + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + gracePeriod(LoanSet::defaultGracePeriod - 1), + loanSetFee, + ter(temINVALID)); + + // grace period greater than payment interval + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + paymentInterval(120), + gracePeriod(121), + loanSetFee, + ter(temINVALID)); + } + // empty/zero broker ID + { + auto jv = set(borrower, uint256{}, debtMaximumRequest); + + auto testZeroBrokerID = [&](std::string const& id, std::uint32_t flags = 0) { + // empty broker ID + jv[sfLoanBrokerID] = id; + env(jv, + sig(sfCounterpartySignature, lender), + loanSetFee, + txflags(flags), + ter(temINVALID)); + }; + // empty broker ID + testZeroBrokerID(std::string("")); + // zero broker ID + // needs a flag to distinguish the parsed STTx from the prior + // test + testZeroBrokerID(to_string(uint256{}), tfFullyCanonicalSig); + } + + // preflightCheckSigningKey() failure: + // can it happen? the signature is checked before transactor + // executes + + JTx tx = env.jt( + set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + loanSetFee); + STTx local = *(tx.stx); + auto counterpartySig = local.getFieldObject(sfCounterpartySignature); + auto badPubKey = counterpartySig.getFieldVL(sfSigningPubKey); + badPubKey[20] ^= 0xAA; + counterpartySig.setFieldVL(sfSigningPubKey, badPubKey); + local.setFieldObject(sfCounterpartySignature, counterpartySig); + Json::Value jvResult; + jvResult[jss::tx_blob] = strHex(local.getSerializer().slice()); + auto res = env.rpc("json", "submit", to_string(jvResult))["result"]; + BEAST_EXPECT( + res[jss::error] == "invalidTransaction" && + res[jss::error_exception] == + "fails local checks: Counterparty: Invalid signature."); + }); // preclaim: - testWrapper( - [&](Env& env, BrokerInfo const& brokerInfo, jtx::fee const& loanSetFee, Number const& debtMaximumRequest) { - // canAddHoldingFailure (IOU only, if MPT doesn't have - // MPTCanTransfer set, then can't create Vault/LoanBroker, - // and LoanSet will fail with different error - env(fclear(issuer, asfDefaultRipple)); - env.close(); - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - loanSetFee, - ter(terNO_RIPPLE)); - }); + testWrapper([&](Env& env, + BrokerInfo const& brokerInfo, + jtx::fee const& loanSetFee, + Number const& debtMaximumRequest) { + // canAddHoldingFailure (IOU only, if MPT doesn't have + // MPTCanTransfer set, then can't create Vault/LoanBroker, + // and LoanSet will fail with different error + env(fclear(issuer, asfDefaultRipple)); + env.close(); + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + loanSetFee, + ter(terNO_RIPPLE)); + }); // doApply: - testWrapper( - [&](Env& env, BrokerInfo const& brokerInfo, jtx::fee const& loanSetFee, Number const& debtMaximumRequest) { - auto const amt = env.balance(borrower) - env.current()->fees().accountReserve(env.ownerCount(borrower)); - env(pay(borrower, issuer, amt)); + testWrapper([&](Env& env, + BrokerInfo const& brokerInfo, + jtx::fee const& loanSetFee, + Number const& debtMaximumRequest) { + auto const amt = env.balance(borrower) - + env.current()->fees().accountReserve(env.ownerCount(borrower)); + env(pay(borrower, issuer, amt)); - // tecINSUFFICIENT_RESERVE - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - loanSetFee, - ter(tecINSUFFICIENT_RESERVE)); + // tecINSUFFICIENT_RESERVE + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + loanSetFee, + ter(tecINSUFFICIENT_RESERVE)); - // addEmptyHolding failure - env(pay(issuer, borrower, amt)); - env(fset(issuer, asfGlobalFreeze)); - env.close(); + // addEmptyHolding failure + env(pay(issuer, borrower, amt)); + env(fset(issuer, asfGlobalFreeze)); + env.close(); - env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), - sig(sfCounterpartySignature, lender), - loanSetFee, - ter(tecFROZEN)); - }); + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + loanSetFee, + ter(tecFROZEN)); + }); } void @@ -4360,17 +4576,22 @@ protected: verifyLoanStatus(originalState); Number const payment{3'269'349'176'470'588, -12}; - XRPAmount const payFee{baseFee * ((payment / originalState.periodicPayment) / loanPaymentsPerFeeIncrement + 1)}; - auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, payment}), fee(payFee)); + XRPAmount const payFee{ + baseFee * + ((payment / originalState.periodicPayment) / loanPaymentsPerFeeIncrement + 1)}; + auto loanPayTx = + env.json(pay(borrower, keylet.key, STAmount{broker.asset, payment}), fee(payFee)); BEAST_EXPECT(to_string(payment) == "3269.349176470588"); env(loanPayTx, ter(tesSUCCESS)); env.close(); auto const newState = getCurrentState(env, broker, keylet); - BEAST_EXPECT(isRounded(broker.asset, newState.managementFeeOutstanding, originalState.loanScale)); + BEAST_EXPECT( + isRounded(broker.asset, newState.managementFeeOutstanding, originalState.loanScale)); BEAST_EXPECT(newState.managementFeeOutstanding < originalState.managementFeeOutstanding); BEAST_EXPECT(isRounded(broker.asset, newState.totalValue, originalState.loanScale)); - BEAST_EXPECT(isRounded(broker.asset, newState.principalOutstanding, originalState.loanScale)); + BEAST_EXPECT( + isRounded(broker.asset, newState.principalOutstanding, originalState.loanScale)); } void @@ -4429,12 +4650,12 @@ protected: auto const keylet = keylet::loan(broker.brokerID, loanSequence); createJson = env.json(createJson, sig(sfCounterpartySignature, lender)); - env(createJson, THISLINE); + env(createJson); env.close(); auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, Number{}})); loanPayTx["Amount"]["value"] = "0.000281284125490196"; - env(loanPayTx, ter(tecINSUFFICIENT_PAYMENT), THISLINE); + env(loanPayTx, ter(tecINSUFFICIENT_PAYMENT)); env.close(); } @@ -4504,12 +4725,15 @@ protected: Number const amount{395937, -2}; loanPayTx["Amount"]["value"] = to_string(amount); XRPAmount const payFee{ - baseFee * std::int64_t(amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; + baseFee * + std::int64_t(amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; env(loanPayTx, ter(tesSUCCESS), fee(payFee)); env.close(); auto const stateAfter = getCurrentState(env, broker, keylet); - BEAST_EXPECT(stateAfter.paymentRemaining == stateBefore.paymentRemaining - loanMaximumPaymentsPerTransaction); + BEAST_EXPECT( + stateAfter.paymentRemaining == + stateBefore.paymentRemaining - loanMaximumPaymentsPerTransaction); } void @@ -4580,7 +4804,8 @@ protected: auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, Number{}})); Number const amount{3074'745'058'823'529, -12}; BEAST_EXPECT(to_string(amount) == "3074.745058823529"); - XRPAmount const payFee{baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; + XRPAmount const payFee{ + baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; loanPayTx["Amount"]["value"] = to_string(amount); env(loanPayTx, fee(payFee), ter(tesSUCCESS)); env.close(); @@ -4590,7 +4815,8 @@ protected: auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, Number{}})); Number const amount{6732'118'170'944'051, -12}; BEAST_EXPECT(to_string(amount) == "6732.118170944051"); - XRPAmount const payFee{baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; + XRPAmount const payFee{ + baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; loanPayTx["Amount"]["value"] = to_string(amount); env(loanPayTx, fee(payFee), ter(tesSUCCESS)); env.close(); @@ -4683,13 +4909,16 @@ protected: auto loanPayTx = env.json(pay(borrower, keylet.key, STAmount{broker.asset, Number{}})); Number const amount{9924'81, -2}; BEAST_EXPECT(to_string(amount) == "9924.81"); - XRPAmount const payFee{baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; + XRPAmount const payFee{ + baseFee * (amount / stateBefore.periodicPayment / loanPaymentsPerFeeIncrement + 1)}; loanPayTx["Amount"]["value"] = to_string(amount); env(loanPayTx, fee(payFee), ter(tesSUCCESS)); env.close(); auto const stateAfter = getCurrentState(env, broker, keylet); - BEAST_EXPECT(stateAfter.paymentRemaining == stateBefore.paymentRemaining - loanMaximumPaymentsPerTransaction); + BEAST_EXPECT( + stateAfter.paymentRemaining == + stateBefore.paymentRemaining - loanMaximumPaymentsPerTransaction); } void @@ -4752,7 +4981,9 @@ protected: auto const baseFee = env.current()->fees().base; - auto parentCloseTime = [&]() { return env.current()->parentCloseTime().time_since_epoch().count(); }; + auto parentCloseTime = [&]() { + return env.current()->parentCloseTime().time_since_epoch().count(); + }; auto maxLoanTime = [&]() { auto const startDate = parentCloseTime(); @@ -4786,7 +5017,8 @@ protected: auto const interval = maxLoanTime() + 1; auto const total = 1; auto const grace = interval; - auto createJson = env.json(baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); + auto createJson = env.json( + baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); // The grace period can't be larger than the interval. env(createJson, sig(sfCounterpartySignature, lender), ter(tecKILLED)); @@ -4817,7 +5049,8 @@ protected: auto const total = 60; auto const interval = (maxLoanTime() - total) / total; auto const grace = interval; - auto createJson = env.json(baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); + auto createJson = env.json( + baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); env(createJson, sig(sfCounterpartySignature, lender), ter(tecKILLED)); env.close(); @@ -4831,7 +5064,8 @@ protected: auto const grace = 100; auto const interval = maxLoanTime() - grace; auto const total = 1; - auto createJson = env.json(baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); + auto createJson = env.json( + baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); env(createJson, sig(sfCounterpartySignature, lender), ter(tesSUCCESS)); env.close(); @@ -4858,7 +5092,8 @@ protected: auto const grace = 5'000; auto const interval = maxTime - closeStartDate - grace; auto const total = 1; - auto createJson = env.json(baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); + auto createJson = env.json( + baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); env(createJson, sig(sfCounterpartySignature, lender), ter(tesSUCCESS)); env.close(); @@ -4903,7 +5138,8 @@ protected: auto const keylet = keylet::loan(broker.brokerID, loanSequence); auto const interval = maxLoanTime / total; - auto createJson = env.json(baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); + auto createJson = env.json( + baseJson, paymentInterval(interval), paymentTotal(total), gracePeriod(grace)); env(createJson, sig(sfCounterpartySignature, lender), ter(tesSUCCESS)); env.close(); @@ -4920,7 +5156,8 @@ protected: NumberRoundModeGuard mg{Number::upward}; Number const payment = beforeState.periodicPayment * (total - 1); XRPAmount const payFee{baseFee * ((total - 1) / loanPaymentsPerFeeIncrement + 1)}; - STAmount const paymentAmount = roundToScale(STAmount{broker.asset, payment}, beforeState.loanScale); + STAmount const paymentAmount = + roundToScale(STAmount{broker.asset, payment}, beforeState.loanScale); auto loanPayTx = env.json(pay(borrower, keylet.key, paymentAmount), fee(payFee)); env(loanPayTx, ter(tesSUCCESS)); env.close(); @@ -5119,7 +5356,7 @@ protected: auto const loanSetFee = fee(env.current()->fees().base * 2); auto const brokerPreLoan = env.le(keylet::loanbroker(broker.brokerID)); - if (!BEAST_EXPECT(brokerPreLoan)) + if (BEAST_EXPECT(brokerPreLoan); !brokerPreLoan.has_value()) return; auto const loanSequence = brokerPreLoan->at(sfLoanSequence); @@ -5167,18 +5404,20 @@ protected: Number const closePaymentFeeRounded = roundToAsset(broker.asset, loanSle->at(sfClosePaymentFee), state.loanScale); - Number const latePaymentFeeRounded = roundToAsset(broker.asset, loanSle->at(sfLatePaymentFee), state.loanScale); + Number const latePaymentFeeRounded = + roundToAsset(broker.asset, loanSle->at(sfLatePaymentFee), state.loanScale); - auto const roundedLoanState = - constructLoanState(state.totalValue, state.principalOutstanding, state.managementFeeOutstanding); + auto const roundedLoanState = constructLoanState( + state.totalValue, state.principalOutstanding, state.managementFeeOutstanding); Number const totalInterestOutstanding = roundedLoanState.interestDue; auto const periodicRate = loanPeriodicRate(interestRateValue, state.paymentInterval); - auto const rawLoanState = - computeTheoreticalLoanState(state.periodicPayment, periodicRate, state.paymentRemaining, managementFeeRate); + auto const rawLoanState = computeTheoreticalLoanState( + state.periodicPayment, periodicRate, state.paymentRemaining, managementFeeRate); auto const parentCloseTime = env.current()->parentCloseTime(); - auto const startDateSeconds = static_cast(state.startDate.time_since_epoch().count()); + auto const startDateSeconds = + static_cast(state.startDate.time_since_epoch().count()); Number const fullPaymentInterest = computeFullPaymentInterest( rawLoanState.principalOutstanding, @@ -5189,9 +5428,10 @@ protected: startDateSeconds, closeInterestRateValue); - Number const roundedFullInterestAmount = roundToAsset(broker.asset, fullPaymentInterest, state.loanScale); - Number const roundedFullManagementFee = - computeManagementFee(broker.asset, roundedFullInterestAmount, managementFeeRate, state.loanScale); + Number const roundedFullInterestAmount = + roundToAsset(broker.asset, fullPaymentInterest, state.loanScale); + Number const roundedFullManagementFee = computeManagementFee( + broker.asset, roundedFullInterestAmount, managementFeeRate, state.loanScale); Number const roundedFullInterest = roundedFullInterestAmount - roundedFullManagementFee; Number const trackedValueDelta = @@ -5203,16 +5443,19 @@ protected: Number const baseFullDue = trackedValueDelta + untrackedInterest + untrackedManagementFee; BEAST_EXPECT(baseFullDue == roundToAsset(broker.asset, baseFullDue, state.loanScale)); - auto const overdueSeconds = parentCloseTime.time_since_epoch().count() - state.nextPaymentDate; + auto const overdueSeconds = + parentCloseTime.time_since_epoch().count() - state.nextPaymentDate; if (!BEAST_EXPECT(overdueSeconds > 0)) return; Number const overdueRate = loanPeriodicRate(lateInterestRateValue, overdueSeconds); Number const lateInterestRaw = state.principalOutstanding * overdueRate; - Number const lateInterestRounded = roundToAsset(broker.asset, lateInterestRaw, state.loanScale); - Number const lateManagementFeeRounded = - computeManagementFee(broker.asset, lateInterestRounded, managementFeeRate, state.loanScale); - Number const penaltyDue = lateInterestRounded + lateManagementFeeRounded + latePaymentFeeRounded; + Number const lateInterestRounded = + roundToAsset(broker.asset, lateInterestRaw, state.loanScale); + Number const lateManagementFeeRounded = computeManagementFee( + broker.asset, lateInterestRounded, managementFeeRate, state.loanScale); + Number const penaltyDue = + lateInterestRounded + lateManagementFeeRounded + latePaymentFeeRounded; BEAST_EXPECT(penaltyDue > Number{}); auto const balanceBefore = env.balance(borrower, broker.asset).number(); @@ -5289,7 +5532,8 @@ protected: BEAST_EXPECT(debtOutstanding > Number{}); BEAST_EXPECT(coverAvailableBefore > Number{}); - log << "debt=" << to_string(debtOutstanding) << " cover_available=" << to_string(coverAvailableBefore); + log << "debt=" << to_string(debtOutstanding) + << " cover_available=" << to_string(coverAvailableBefore); env(coverClawback(issuer, 0), loanBrokerID(broker.brokerID)); env.close(); @@ -5421,14 +5665,17 @@ protected: BEAST_EXPECT(brokerSle2); auto const closePaymentFee = loanSle ? loanSle->at(sfClosePaymentFee) : Number{}; - auto const closeInterestRate = loanSle ? TenthBips32{loanSle->at(sfCloseInterestRate)} : TenthBips32{}; - auto const managementFeeRate = brokerSle2 ? TenthBips16{brokerSle2->at(sfManagementFeeRate)} : TenthBips16{}; + auto const closeInterestRate = + loanSle ? TenthBips32{loanSle->at(sfCloseInterestRate)} : TenthBips32{}; + auto const managementFeeRate = + brokerSle2 ? TenthBips16{brokerSle2->at(sfManagementFeeRate)} : TenthBips16{}; Number const periodicRate2 = loanPeriodicRate(after.interestRate, after.paymentInterval); // Accrued + prepayment-penalty interest based on current periodic // schedule auto const fullPaymentInterest = computeFullPaymentInterest( - detail::loanPrincipalFromPeriodicPayment(after.periodicPayment, periodicRate2, after.paymentRemaining), + detail::loanPrincipalFromPeriodicPayment( + after.periodicPayment, periodicRate2, after.paymentRemaining), periodicRate2, env.current()->parentCloseTime(), after.paymentInterval, @@ -5437,38 +5684,47 @@ protected: closeInterestRate); // Round to asset scale and split interest/fee parts - auto const roundedInterest = roundToAsset(asset.raw(), fullPaymentInterest, after.loanScale); + auto const roundedInterest = + roundToAsset(asset.raw(), fullPaymentInterest, after.loanScale); Number const roundedFullMgmtFee = computeManagementFee(asset.raw(), roundedInterest, managementFeeRate, after.loanScale); Number const roundedFullInterest = roundedInterest - roundedFullMgmtFee; // Show both signed and unsigned deltas to highlight the underflow. - auto const nowSecs = static_cast(env.current()->parentCloseTime().time_since_epoch().count()); - auto const startSecs = static_cast(after.startDate.time_since_epoch().count()); + auto const nowSecs = + static_cast(env.current()->parentCloseTime().time_since_epoch().count()); + auto const startSecs = + static_cast(after.startDate.time_since_epoch().count()); auto const lastPaymentDate = std::max(after.previousPaymentDate, startSecs); - auto const signedDelta = static_cast(nowSecs) - static_cast(lastPaymentDate); + auto const signedDelta = + static_cast(nowSecs) - static_cast(lastPaymentDate); auto const unsignedDelta = static_cast(nowSecs - lastPaymentDate); - log << "PoC window: prev=" << after.previousPaymentDate << " start=" << startSecs << " now=" << nowSecs - << " signedDelta=" << signedDelta << " unsignedDelta=" << unsignedDelta << std::endl; + log << "PoC window: prev=" << after.previousPaymentDate << " start=" << startSecs + << " now=" << nowSecs << " signedDelta=" << signedDelta + << " unsignedDelta=" << unsignedDelta << std::endl; // Reference (clamped) computation: emulate a non-negative accrual // window by clamping prevPaymentDate to 'now' for the full-pay path. auto const prevClamped = std::min(after.previousPaymentDate, nowSecs); auto const fullPaymentInterestClamped = computeFullPaymentInterest( - detail::loanPrincipalFromPeriodicPayment(after.periodicPayment, periodicRate2, after.paymentRemaining), + detail::loanPrincipalFromPeriodicPayment( + after.periodicPayment, periodicRate2, after.paymentRemaining), periodicRate2, env.current()->parentCloseTime(), after.paymentInterval, prevClamped, startSecs, closeInterestRate); - auto const roundedInterestClamped = roundToAsset(asset.raw(), fullPaymentInterestClamped, after.loanScale); - Number const roundedFullMgmtFeeClamped = - computeManagementFee(asset.raw(), roundedInterestClamped, managementFeeRate, after.loanScale); - Number const roundedFullInterestClamped = roundedInterestClamped - roundedFullMgmtFeeClamped; + auto const roundedInterestClamped = + roundToAsset(asset.raw(), fullPaymentInterestClamped, after.loanScale); + Number const roundedFullMgmtFeeClamped = computeManagementFee( + asset.raw(), roundedInterestClamped, managementFeeRate, after.loanScale); + Number const roundedFullInterestClamped = + roundedInterestClamped - roundedFullMgmtFeeClamped; STAmount const fullDueClamped{ asset, - after.principalOutstanding + roundedFullInterestClamped + roundedFullMgmtFeeClamped + closePaymentFee}; + after.principalOutstanding + roundedFullInterestClamped + roundedFullMgmtFeeClamped + + closePaymentFee}; // Collect vault NAV before closing payment auto const vaultId2 = brokerSle2 ? brokerSle2->at(sfVaultID) : uint256{}; @@ -5478,11 +5734,14 @@ protected: Number const assetsTotalBefore = vaultBefore ? vaultBefore->at(sfAssetsTotal) : Number{}; STAmount const fullDue{ - asset, after.principalOutstanding + roundedFullInterest + roundedFullMgmtFee + closePaymentFee}; + asset, + after.principalOutstanding + roundedFullInterest + roundedFullMgmtFee + + closePaymentFee}; log << "PoC payoff: principalOutstanding=" << after.principalOutstanding - << " roundedFullInterest=" << roundedFullInterest << " roundedFullMgmtFee=" << roundedFullMgmtFee - << " closeFee=" << closePaymentFee << " fullDue=" << to_string(fullDue.getJson()) << std::endl; + << " roundedFullInterest=" << roundedFullInterest + << " roundedFullMgmtFee=" << roundedFullMgmtFee << " closeFee=" << closePaymentFee + << " fullDue=" << to_string(fullDue.getJson()) << std::endl; log << "PoC reference (clamped): roundedFullInterestClamped=" << roundedFullInterestClamped << " roundedFullMgmtFeeClamped=" << roundedFullMgmtFeeClamped << " fullDueClamped=" << to_string(fullDueClamped.getJson()) << std::endl; @@ -5500,7 +5759,8 @@ protected: if (vaultAfter) { auto const assetsTotalAfter = vaultAfter->at(sfAssetsTotal); - log << "PoC NAV: assetsTotalBefore=" << assetsTotalBefore << " assetsTotalAfter=" << assetsTotalAfter + log << "PoC NAV: assetsTotalBefore=" << assetsTotalBefore + << " assetsTotalAfter=" << assetsTotalAfter << " delta=" << (assetsTotalAfter - assetsTotalBefore) << std::endl; // Value-based proof: underflowed window yields a payoff larger than @@ -5632,8 +5892,12 @@ protected: // after loan creation the assets total and available should // reflect the value of the loan BEAST_EXPECT(assetsAvail < assetsTotal); - BEAST_EXPECT(assetsAvail == broker.asset(brokerParams.vaultDeposit - loanParams.principalRequest).number()); - BEAST_EXPECT(assetsTotal == broker.asset(brokerParams.vaultDeposit + state.interestDue).number()); + BEAST_EXPECT( + assetsAvail == + broker.asset(brokerParams.vaultDeposit - loanParams.principalRequest).number()); + BEAST_EXPECT( + assetsTotal == + broker.asset(brokerParams.vaultDeposit + state.interestDue).number()); } // Step 7: Trigger default (dust adjustment will occur) @@ -5686,9 +5950,10 @@ protected: Env env(*this, all); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -5756,9 +6021,10 @@ protected: Env env(*this, all); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -5818,7 +6084,8 @@ protected: env(tx, txfee); env.close(); - env(vault.deposit({.depositor = depositor, .id = vaultKeyLet.key, .amount = XRP(1'000)}), txfee); + env(vault.deposit({.depositor = depositor, .id = vaultKeyLet.key, .amount = XRP(1'000)}), + txfee); env.close(); auto const brokerKeyLet = keylet::loanbroker(lender.id(), env.seq(lender)); @@ -5844,7 +6111,9 @@ protected: if (auto loan = env.le(loanKeylet); env.test.BEAST_EXPECT(loan)) { - env(loan::pay(borrower, loanKeylet.key, XRPAmount(150'001)), txflags(tfLoanOverpayment), txfee); + env(loan::pay(borrower, loanKeylet.key, XRPAmount(150'001)), + txflags(tfLoanOverpayment), + txfee); env.close(); } } @@ -5893,7 +6162,8 @@ protected: env.close(); // Verify DebtTotal is exactly 804 - if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { log << *brokerSle << std::endl; BEAST_EXPECT(brokerSle->at(sfDebtTotal) == Number(804)); @@ -5901,7 +6171,8 @@ protected: // Attempt to withdraw 2 XRP to self, leaving 80 XRP CoverAvailable. // The minimum is 80.4 XRP, which rounds up to 81 XRP, so this fails. - env(coverWithdraw(lender, brokerInfo.brokerID, xrpAsset(2).value()), ter(tecINSUFFICIENT_FUNDS)); + env(coverWithdraw(lender, brokerInfo.brokerID, xrpAsset(2).value()), + ter(tecINSUFFICIENT_FUNDS)); BEAST_EXPECT(env.ter() == tecINSUFFICIENT_FUNDS); env.close(); @@ -5912,7 +6183,8 @@ protected: env.close(); // Validate CoverAvailable == 80 XRP and DebtTotal remains 804 - if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { log << *brokerSle << std::endl; BEAST_EXPECT(brokerSle->at(sfCoverAvailable) == xrpAsset(81).value()); @@ -5955,9 +6227,10 @@ protected: Env env(*this, all); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -5994,9 +6267,13 @@ protected: Vault vault(env); if (borrower == broker) + { env.fund(XRP(10'000), broker, issuer, depositor); + } else + { env.fund(XRP(10'000), broker, borrower, issuer, depositor); + } env.close(); auto const xrpFee = XRP(100); @@ -6011,7 +6288,9 @@ protected: env(tx, txFee); env.close(); - env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = initialVault}), txFee); + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = initialVault}), + txFee); env.close(); auto const brokerKeylet = keylet::loanbroker(broker.id(), env.seq(broker)); @@ -6063,7 +6342,8 @@ protected: // service fee transfer in both cases. for (auto const& borrowerAcct : {broker, borrower_}) { - testLoanAsset([&](Env&) -> STAmount { return STAmount{XRPAmount{200'000}}; }, borrowerAcct); + testLoanAsset( + [&](Env&) -> STAmount { return STAmount{XRPAmount{200'000}}; }, borrowerAcct); testLoanAsset( [&](Env& env) -> STAmount { auto const IOU = issuer["USD"]; @@ -6077,7 +6357,11 @@ protected: borrowerAcct); testLoanAsset( [&](Env& env) -> STAmount { - MPTTester mpt({.env = env, .issuer = issuer, .holders = {broker, depositor}, .pay = 100'000'000}); + MPTTester mpt( + {.env = env, + .issuer = issuer, + .holders = {broker, depositor}, + .pay = 100'000'000}); return mpt(200'000); }, borrowerAcct); @@ -6100,15 +6384,17 @@ protected: .coverRateMin = TenthBips32{0}, .managementFeeRate = TenthBips16{0}, .coverRateLiquidation = TenthBips32{0}}; - LoanParameters const loanParams{.account = lender, .counter = issuer, .principalRequest = Number{10000}}; + LoanParameters const loanParams{ + .account = lender, .counter = issuer, .principalRequest = Number{10000}}; auto const assetType = AssetType::IOU; Env env(*this, all); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, issuer); + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, issuer); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -6161,9 +6447,10 @@ protected: Env env(*this, makeConfig(), all, nullptr, beast::severities::Severity::kWarning); - auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); + auto loanResult = + createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); - if (!BEAST_EXPECT(loanResult)) + if (BEAST_EXPECT(loanResult); !loanResult.has_value()) return; auto broker = std::get(*loanResult); @@ -6175,7 +6462,10 @@ protected: auto const state = getCurrentState(env, broker, loanKeylet); env(loan::pay( - borrower, loanKeylet.key, STAmount{broker.asset, state.periodicPayment * 3 / 2 + 1}, tfLoanOverpayment)); + borrower, + loanKeylet.key, + STAmount{broker.asset, state.periodicPayment * 3 / 2 + 1}, + tfLoanOverpayment)); env.close(); PaymentParameters paymentParams{ @@ -6184,7 +6474,15 @@ protected: }; makeLoanPayments( - env, broker, loanParams, loanKeylet, verifyLoanStatus, issuer, lender, borrower, paymentParams); + env, + broker, + loanParams, + loanKeylet, + verifyLoanStatus, + issuer, + lender, + borrower, + paymentParams); } void @@ -6215,9 +6513,10 @@ protected: auto const loanSetFee = fee(env.current()->fees().base * 2); - auto const loanKeylet = - keylet::loan(result.brokerKeylet().key, (env.le(result.brokerKeylet()))->at(sfLoanSequence)); - env(loan::set(borrower, result.brokerKeylet().key, asset(10'000).value(), tfLoanOverpayment), + auto const loanKeylet = keylet::loan( + result.brokerKeylet().key, (env.le(result.brokerKeylet()))->at(sfLoanSequence)); + env(loan::set( + borrower, result.brokerKeylet().key, asset(10'000).value(), tfLoanOverpayment), sig(sfCounterpartySignature, lender), loan::paymentInterval(86400 * 30), loan::paymentTotal(3), @@ -6234,7 +6533,8 @@ protected: BEAST_EXPECTS( env.balance(lender) - loanBrokerBalanceBefore == expectedOverpaymentManagementFee, - "overpayment management fee missmatch; expected:" + to_string(expectedOverpaymentManagementFee) + + "overpayment management fee missmatch; expected:" + + to_string(expectedOverpaymentManagementFee) + " got: " + to_string(env.balance(lender) - loanBrokerBalanceBefore)); } @@ -6296,7 +6596,8 @@ protected: // Verify trustline is still deleted BEAST_EXPECT(env.le(brokerTrustline) == nullptr); // Verify the service fee went to the broker pseudo-account - if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { Account const pseudo("pseudo-account", brokerSle->at(sfAccount)); auto const balance = env.balance(pseudo, IOU); @@ -6376,7 +6677,8 @@ protected: // Verify the MPT is still unauthorized. BEAST_EXPECT(env.le(brokerMpt) == nullptr); // Verify the service fee went to the broker pseudo-account - if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { Account const pseudo("pseudo-account", brokerSle->at(sfAccount)); auto const balance = env.balance(pseudo, MPT); @@ -6478,7 +6780,8 @@ protected: // Verify broker is still not authorized env(pay(issuer, broker, MPT(1'000)), ter(tecNO_AUTH)); // Verify the service fee went to the broker pseudo-account - if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); BEAST_EXPECT(brokerSle)) + if (auto const brokerSle = env.le(keylet::loanbroker(brokerInfo.brokerID)); + BEAST_EXPECT(brokerSle)) { Account const pseudo("pseudo-account", brokerSle->at(sfAccount)); auto const balance = env.balance(pseudo, MPT); @@ -6579,7 +6882,8 @@ protected: env.close(); PrettyAsset const asset = xrpIssue(); - auto const vaultDepositAmount = asset(200'000); // Enough for 2 x 50k loans plus interest/fees + auto const vaultDepositAmount = + asset(200'000); // Enough for 2 x 50k loans plus interest/fees auto const brokerInfo = createVaultAndBroker( env, @@ -6801,14 +7105,13 @@ public: void run() override { - auto const argument = arg(); auto const numIterations = [s = arg()]() -> int { int defaultNum = 5; if (s.empty()) return defaultNum; try { - std::size_t pos; + std::size_t pos = 0; auto const r = stoi(s, &pos); if (pos != s.size()) return defaultNum; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 965dd86475..5a90a3a71e 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -42,7 +42,11 @@ class MPToken_test : public beast::unit_test::suite // tries to set a txfee while not enabling in the flag mptAlice.create( - {.maxAmt = 100, .assetScale = 0, .transferFee = 1, .metadata = "test", .err = temMALFORMED}); + {.maxAmt = 100, + .assetScale = 0, + .transferFee = 1, + .metadata = "test", + .err = temMALFORMED}); if (!features[featureSingleAssetVault]) { @@ -70,7 +74,11 @@ class MPToken_test : public beast::unit_test::suite { // tries to set DomainID when RequireAuth is not set mptAlice.create( - {.maxAmt = 100, .assetScale = 0, .metadata = "test", .domainID = uint256(42), .err = temMALFORMED}); + {.maxAmt = 100, + .assetScale = 0, + .metadata = "test", + .domainID = uint256(42), + .err = temMALFORMED}); // tries to set zero DomainID mptAlice.create( @@ -100,10 +108,20 @@ class MPToken_test : public beast::unit_test::suite .err = temMALFORMED}); // empty metadata returns error - mptAlice.create({.maxAmt = 100, .assetScale = 0, .transferFee = 0, .metadata = "", .err = temMALFORMED}); + mptAlice.create( + {.maxAmt = 100, + .assetScale = 0, + .transferFee = 0, + .metadata = "", + .err = temMALFORMED}); // MaximumAmount of 0 returns error - mptAlice.create({.maxAmt = 0, .assetScale = 1, .transferFee = 1, .metadata = "test", .err = temMALFORMED}); + mptAlice.create( + {.maxAmt = 0, + .assetScale = 1, + .transferFee = 1, + .metadata = "test", + .err = temMALFORMED}); // MaximumAmount larger than 63 bit returns error mptAlice.create( @@ -140,8 +158,8 @@ class MPToken_test : public beast::unit_test::suite .transferFee = 10, .metadata = "123", .ownerCount = 1, - .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | - tfMPTCanClawback}); + .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | + tfMPTCanTransfer | tfMPTCanClawback}); // Get the hash for the most recent transaction. std::string const txHash{env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; @@ -175,13 +193,14 @@ class MPToken_test : public beast::unit_test::suite .transferFee = 10, .metadata = "123", .ownerCount = 1, - .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | - tfMPTCanClawback, + .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | + tfMPTCanTransfer | tfMPTCanClawback, .domainID = domainId1, }); // Get the hash for the most recent transaction. - std::string const txHash{env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; + std::string const txHash{ + env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; Json::Value const result = env.rpc("tx", txHash)[jss::result]; BEAST_EXPECT(result[sfMaximumAmount.getJsonName()] == "9223372036854775807"); @@ -214,7 +233,10 @@ class MPToken_test : public beast::unit_test::suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.destroy({.id = makeMptID(env.seq(alice), alice), .ownerCount = 0, .err = tecOBJECT_NOT_FOUND}); + mptAlice.destroy( + {.id = makeMptID(env.seq(alice), alice), + .ownerCount = 0, + .err = tecOBJECT_NOT_FOUND}); mptAlice.create({.ownerCount = 1}); @@ -267,7 +289,8 @@ class MPToken_test : public beast::unit_test::suite Env env{*this, features - featureMPTokensV1}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.authorize({.account = bob, .id = makeMptID(env.seq(alice), alice), .err = temDISABLED}); + mptAlice.authorize( + {.account = bob, .id = makeMptID(env.seq(alice), alice), .err = temDISABLED}); } // Validate fields in MPTokenAuthorize (preflight) @@ -329,7 +352,8 @@ class MPToken_test : public beast::unit_test::suite // bob tries to delete his MPToken, but fails since he still // holds tokens - mptAlice.authorize({.account = bob, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS}); + mptAlice.authorize( + {.account = bob, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS}); // bob pays back alice 100 tokens mptAlice.pay(bob, alice, 100); @@ -341,7 +365,10 @@ class MPToken_test : public beast::unit_test::suite // bob receives error when he tries to delete his MPToken that has // already been deleted mptAlice.authorize( - {.account = bob, .holderCount = 0, .flags = tfMPTUnauthorize, .err = tecOBJECT_NOT_FOUND}); + {.account = bob, + .holderCount = 0, + .flags = tfMPTUnauthorize, + .err = tecOBJECT_NOT_FOUND}); } // Test bad scenarios with allow-listing in MPTokenAuthorize (preclaim) @@ -390,7 +417,8 @@ class MPToken_test : public beast::unit_test::suite // 1 drop BEAST_EXPECT(incReserve > XRPAmount(1)); - MPTTester mptAlice1(env, alice, {.holders = {bob}, .xrpHolders = acctReserve + (incReserve - 1)}); + MPTTester mptAlice1( + env, alice, {.holders = {bob}, .xrpHolders = acctReserve + (incReserve - 1)}); mptAlice1.create(); MPTTester mptAlice2(env, alice, {.fund = false}); @@ -456,7 +484,8 @@ class MPToken_test : public beast::unit_test::suite mptAlice.authorize({.account = alice, .holder = bob}); // Unauthorize bob's mptoken - mptAlice.authorize({.account = alice, .holder = bob, .holderCount = 1, .flags = tfMPTUnauthorize}); + mptAlice.authorize( + {.account = alice, .holder = bob, .holderCount = 1, .flags = tfMPTUnauthorize}); mptAlice.authorize({.account = bob, .holderCount = 0, .flags = tfMPTUnauthorize}); } @@ -495,7 +524,8 @@ class MPToken_test : public beast::unit_test::suite Env env{*this, features - featureMPTokensV1}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.set({.account = bob, .id = makeMptID(env.seq(alice), alice), .err = temDISABLED}); + mptAlice.set( + {.account = bob, .id = makeMptID(env.seq(alice), alice), .err = temDISABLED}); env.enableFeature(featureMPTokensV1); @@ -512,7 +542,11 @@ class MPToken_test : public beast::unit_test::suite // test invalid flags - nothing is being changed mptAlice.set({.account = alice, .flags = 0x00000000, .err = tecNO_PERMISSION}); - mptAlice.set({.account = alice, .holder = bob, .flags = 0x00000000, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, + .holder = bob, + .flags = 0x00000000, + .err = tecNO_PERMISSION}); // cannot set DomainID since SAV is not enabled mptAlice.set({.account = alice, .domainID = uint256(42), .err = temDISABLED}); @@ -522,7 +556,8 @@ class MPToken_test : public beast::unit_test::suite // test invalid flags - nothing is being changed mptAlice.set({.account = alice, .flags = 0x00000000, .err = temMALFORMED}); - mptAlice.set({.account = alice, .holder = bob, .flags = 0x00000000, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = 0x00000000, .err = temMALFORMED}); if (!features[featurePermissionedDomains] || !features[featureSingleAssetVault]) { @@ -532,16 +567,22 @@ class MPToken_test : public beast::unit_test::suite else if (features[featureSingleAssetVault]) { // cannot set DomainID since Holder is set - mptAlice.set({.account = alice, .holder = bob, .domainID = uint256(42), .err = temMALFORMED}); + mptAlice.set( + {.account = alice, + .holder = bob, + .domainID = uint256(42), + .err = temMALFORMED}); } } // set both lock and unlock flags at the same time will fail - mptAlice.set({.account = alice, .flags = tfMPTLock | tfMPTUnlock, .err = temINVALID_FLAG}); + mptAlice.set( + {.account = alice, .flags = tfMPTLock | tfMPTUnlock, .err = temINVALID_FLAG}); // if the holder is the same as the acct that submitted the tx, // tx fails - mptAlice.set({.account = alice, .holder = alice, .flags = tfMPTLock, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, .holder = alice, .flags = tfMPTLock, .err = temMALFORMED}); } // Validate fields in MPTokenIssuanceSet (preclaim) @@ -561,11 +602,13 @@ class MPToken_test : public beast::unit_test::suite // issuer tries to lock a bob's mptoken that has disabled // locking - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); // issuer tries to unlock a bob's mptoken that has disabled // locking - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); } // Validate fields in MPTokenIssuanceSet (preclaim) @@ -576,7 +619,10 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); // alice trying to set when the mptissuance doesn't exist yet - mptAlice.set({.id = makeMptID(env.seq(alice), alice), .flags = tfMPTLock, .err = tecOBJECT_NOT_FOUND}); + mptAlice.set( + {.id = makeMptID(env.seq(alice), alice), + .flags = tfMPTLock, + .err = tecOBJECT_NOT_FOUND}); // create a mptokenissuance with locking mptAlice.create({.ownerCount = 1, .flags = tfMPTCanLock}); @@ -621,9 +667,11 @@ class MPToken_test : public beast::unit_test::suite mptAlice.set({.domainID = uint256(42), .err = tecOBJECT_NOT_FOUND}); // Trying to lock but locking is disabled - mptAlice.set({.flags = tfMPTUnlock, .domainID = uint256(42), .err = tecNO_PERMISSION}); + mptAlice.set( + {.flags = tfMPTUnlock, .domainID = uint256(42), .err = tecNO_PERMISSION}); - mptAlice.set({.flags = tfMPTUnlock, .domainID = beast::zero, .err = tecNO_PERMISSION}); + mptAlice.set( + {.flags = tfMPTUnlock, .domainID = beast::zero, .err = tecNO_PERMISSION}); } } } @@ -673,13 +721,18 @@ class MPToken_test : public beast::unit_test::suite // Delete bob's mptoken even though it is locked mptAlice.authorize({.account = bob, .flags = tfMPTUnauthorize}); - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecOBJECT_NOT_FOUND}); + mptAlice.set( + {.account = alice, + .holder = bob, + .flags = tfMPTUnlock, + .err = tecOBJECT_NOT_FOUND}); return; } // Cannot delete locked MPToken - mptAlice.authorize({.account = bob, .flags = tfMPTUnauthorize, .err = tecNO_PERMISSION}); + mptAlice.authorize( + {.account = bob, .flags = tfMPTUnauthorize, .err = tecNO_PERMISSION}); // alice unlocks mptissuance mptAlice.set({.account = alice, .flags = tfMPTUnlock}); @@ -706,7 +759,8 @@ class MPToken_test : public beast::unit_test::suite Account const credIssuer1{"credIssuer1"}; env.fund(XRP(1000), credIssuer1); - pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}}; + pdomain::Credentials const credentials1{ + {.issuer = credIssuer1, .credType = credType}}; env(pdomain::setTx(credIssuer1, credentials1)); return [&]() { @@ -719,7 +773,8 @@ class MPToken_test : public beast::unit_test::suite Account const credIssuer2{"credIssuer2"}; env.fund(XRP(1000), credIssuer2); - pdomain::Credentials const credentials2{{.issuer = credIssuer2, .credType = credType}}; + pdomain::Credentials const credentials2{ + {.issuer = credIssuer2, .credType = credType}}; env(pdomain::setTx(credIssuer2, credentials2)); return [&]() { @@ -861,7 +916,9 @@ class MPToken_test : public beast::unit_test::suite payment[jss::build_path] = true; auto jrr = env.rpc("json", "submit", to_string(payment)); BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::result][jss::error_message] == "Field 'build_path' not allowed in this context."); + BEAST_EXPECT( + jrr[jss::result][jss::error_message] == + "Field 'build_path' not allowed in this context."); } // Can't pay negative amount @@ -925,7 +982,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); mptAlice.authorize({.account = bob}); @@ -939,7 +997,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); // bob creates an empty MPToken mptAlice.authorize({.account = bob}); @@ -968,7 +1027,8 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1000), credIssuer1, bob); auto const domainId1 = [&]() { - pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}}; + pdomain::Credentials const credentials1{ + {.issuer = credIssuer1, .credType = credType}}; env(pdomain::setTx(credIssuer1, credentials1)); return [&]() { @@ -1009,7 +1069,8 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1000), credIssuer1, bob); auto const domainId1 = [&]() { - pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}}; + pdomain::Credentials const credentials1{ + {.issuer = credIssuer1, .credType = credType}}; env(pdomain::setTx(credIssuer1, credentials1)); return [&]() { @@ -1065,7 +1126,8 @@ class MPToken_test : public beast::unit_test::suite env.fund(XRP(1000), credIssuer1, credIssuer2, bob, carol); auto const domainId1 = [&]() { - pdomain::Credentials const credentials{{.issuer = credIssuer1, .credType = credType}}; + pdomain::Credentials const credentials{ + {.issuer = credIssuer1, .credType = credType}}; env(pdomain::setTx(credIssuer1, credentials)); return [&]() { @@ -1076,7 +1138,8 @@ class MPToken_test : public beast::unit_test::suite auto const domainId2 = [&]() { pdomain::Credentials const credentials{ - {.issuer = credIssuer1, .credType = credType}, {.issuer = credIssuer2, .credType = credType}}; + {.issuer = credIssuer1, .credType = credType}, + {.issuer = credIssuer2, .credType = credType}}; env(pdomain::setTx(credIssuer2, credentials)); return [&]() { @@ -1254,7 +1317,11 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); // Transfer fee is 10% - mptAlice.create({.transferFee = 10'000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + mptAlice.create( + {.transferFee = 10'000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer}); // Holders create MPToken mptAlice.authorize({.account = bob}); @@ -1343,7 +1410,10 @@ class MPToken_test : public beast::unit_test::suite txflags(tfPartialPayment), ter(tecPATH_PARTIAL)); // Payment succeeds if deliver amount >= deliverMin - env(pay(bob, alice, MPT(100)), sendmax(MPT(99)), deliver_min(MPT(99)), txflags(tfPartialPayment)); + env(pay(bob, alice, MPT(100)), + sendmax(MPT(99)), + deliver_min(MPT(99)), + txflags(tfPartialPayment)); } // Issuer fails trying to send more than the maximum amount allowed @@ -1352,7 +1422,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.maxAmt = 100, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + mptAlice.create( + {.maxAmt = 100, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); mptAlice.authorize({.account = bob}); @@ -1403,7 +1474,11 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); mptAlice.create( - {.maxAmt = 10'000, .transferFee = 100, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + {.maxAmt = 10'000, + .transferFee = 100, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer}); auto const MPT = mptAlice["MPT"]; mptAlice.authorize({.account = bob}); @@ -1418,13 +1493,18 @@ class MPToken_test : public beast::unit_test::suite auto const meta = env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName]; // Issuer got 10 in the transfer fees BEAST_EXPECT( - meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName][sfOutstandingAmount.fieldName] == "9990"); + meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName] + [sfOutstandingAmount.fieldName] == "9990"); // Destination account got 9'990 - BEAST_EXPECT(meta[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName][sfMPTAmount.fieldName] == "9990"); + BEAST_EXPECT( + meta[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName] + [sfMPTAmount.fieldName] == "9990"); // Source account spent 10'000 BEAST_EXPECT( - meta[2u][sfModifiedNode.fieldName][sfPreviousFields.fieldName][sfMPTAmount.fieldName] == "10000"); - BEAST_EXPECT(!meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName].isMember(sfMPTAmount.fieldName)); + meta[2u][sfModifiedNode.fieldName][sfPreviousFields.fieldName] + [sfMPTAmount.fieldName] == "10000"); + BEAST_EXPECT(!meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName].isMember( + sfMPTAmount.fieldName)); // payment between the holders fails without // partial payment @@ -1437,7 +1517,11 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); - mptAlice.create({.maxAmt = maxMPTokenAmount, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + mptAlice.create( + {.maxAmt = maxMPTokenAmount, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer}); auto const MPT = mptAlice["MPT"]; mptAlice.authorize({.account = bob}); @@ -1565,7 +1649,8 @@ class MPToken_test : public beast::unit_test::suite env.close(); MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); env(pay(diana, bob, XRP(500))); env.close(); @@ -1640,7 +1725,8 @@ class MPToken_test : public beast::unit_test::suite env.close(); MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTRequireAuth | tfMPTCanTransfer}); env(pay(diana, bob, XRP(500))); env.close(); @@ -1754,11 +1840,17 @@ class MPToken_test : public beast::unit_test::suite if (withAmount) jv[field.fieldName] = USD(10).value().getJson(JsonOptions::none); if (field == sfAsset) + { jv[jss::Asset] = to_json(mpt.get()); + } else if (field == sfAsset2) + { jv[jss::Asset2] = to_json(mpt.get()); + } else + { jv[field.fieldName] = mpt.getJson(JsonOptions::none); + } }; // All transactions with sfAmount, which don't support MPT. // Transactions with amount fields, which can't be MPT. @@ -1769,10 +1861,12 @@ class MPToken_test : public beast::unit_test::suite Json::Value jv; jv[jss::TransactionType] = jss::AMMCreate; jv[jss::Account] = alice.human(); - jv[jss::Amount] = - (field.fieldName == sfAmount.fieldName) ? mpt.getJson(JsonOptions::none) : "100000000"; - jv[jss::Amount2] = - (field.fieldName == sfAmount2.fieldName) ? mpt.getJson(JsonOptions::none) : "100000000"; + jv[jss::Amount] = (field.fieldName == sfAmount.fieldName) + ? mpt.getJson(JsonOptions::none) + : "100000000"; + jv[jss::Amount2] = (field.fieldName == sfAmount2.fieldName) + ? mpt.getJson(JsonOptions::none) + : "100000000"; jv[jss::TradingFee] = 0; test(jv, field.fieldName); }; @@ -1821,7 +1915,10 @@ class MPToken_test : public beast::unit_test::suite test(jv, field.fieldName); }; for (SField const& field : - {toSFieldRef(sfBidMin), toSFieldRef(sfBidMax), toSFieldRef(sfAsset), toSFieldRef(sfAsset2)}) + {toSFieldRef(sfBidMin), + toSFieldRef(sfBidMax), + toSFieldRef(sfAsset), + toSFieldRef(sfAsset2)}) ammBid(field); // AMMClawback auto ammClawback = [&](SField const& field) { @@ -1832,7 +1929,8 @@ class MPToken_test : public beast::unit_test::suite setMPTFields(field, jv); test(jv, field.fieldName); }; - for (SField const& field : {toSFieldRef(sfAmount), toSFieldRef(sfAsset), toSFieldRef(sfAsset2)}) + for (SField const& field : + {toSFieldRef(sfAmount), toSFieldRef(sfAsset), toSFieldRef(sfAsset2)}) ammClawback(field); // AMMDelete auto ammDelete = [&](SField const& field) { @@ -1965,13 +2063,14 @@ class MPToken_test : public beast::unit_test::suite } // XChainAddClaimAttestation { - Json::Value const jv = claim_attestation(alice, jvb, alice, mpt, alice, true, 1, alice, signer(alice)); + Json::Value const jv = + claim_attestation(alice, jvb, alice, mpt, alice, true, 1, alice, signer(alice)); test(jv, jss::Amount.c_str()); } // XChainAddAccountCreateAttestation { - Json::Value jv = - create_account_attestation(alice, jvb, alice, mpt, XRP(10), alice, false, 1, alice, signer(alice)); + Json::Value jv = create_account_attestation( + alice, jvb, alice, mpt, XRP(10), alice, false, 1, alice, signer(alice)); for (auto const& field : {sfAmount.fieldName, sfSignatureReward.fieldName}) { jv[field] = mpt.getJson(JsonOptions::none); @@ -1997,12 +2096,14 @@ class MPToken_test : public beast::unit_test::suite jv[jss::Account] = alice.human(); jv[sfXChainBridge.fieldName] = jvb; jv[sfSignatureReward.fieldName] = rewardAmount.getJson(JsonOptions::none); - jv[sfMinAccountCreateAmount.fieldName] = minAccountAmount.getJson(JsonOptions::none); + jv[sfMinAccountCreateAmount.fieldName] = + minAccountAmount.getJson(JsonOptions::none); test(jv, field); }; auto reward = STAmount{sfSignatureReward, mpt}; auto minAmount = STAmount{sfMinAccountCreateAmount, USD(10)}; - for (SField const& field : {std::ref(sfSignatureReward), std::ref(sfMinAccountCreateAmount)}) + for (SField const& field : + {std::ref(sfSignatureReward), std::ref(sfMinAccountCreateAmount)}) { bridgeTx(jss::XChainCreateBridge, reward, minAmount, field.fieldName); bridgeTx(jss::XChainModifyBridge, reward, minAmount, field.fieldName); @@ -2140,7 +2241,8 @@ class MPToken_test : public beast::unit_test::suite env.close(); MPTTester mptAlice(env, alice, {.holders = {bob}}); - auto const fakeMpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); + auto const fakeMpt = + xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); // issuer tries to clawback MPT where issuance doesn't exist env(claw(alice, fakeMpt(5), bob), ter(tecOBJECT_NOT_FOUND)); @@ -2228,7 +2330,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); // alice creates issuance - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); // bob creates a MPToken mptAlice.authorize({.account = bob}); @@ -2250,7 +2353,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); // alice creates issuance - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); // bob creates a MPToken mptAlice.authorize({.account = bob}); @@ -2272,7 +2376,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); // alice creates issuance - mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback | tfMPTRequireAuth}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback | tfMPTRequireAuth}); // bob creates a MPToken mptAlice.authorize({.account = bob}); @@ -2482,13 +2587,28 @@ class MPToken_test : public beast::unit_test::suite auto const mptID = makeMptID(env.seq(alice), alice); // Holder is not allowed when MutableFlags is present - mptAlice.set({.account = alice, .holder = bob, .id = mptID, .mutableFlags = 2, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .mutableFlags = 2, + .err = temMALFORMED}); // Holder is not allowed when MPTokenMetadata is present - mptAlice.set({.account = alice, .holder = bob, .id = mptID, .metadata = "test", .err = temMALFORMED}); + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .metadata = "test", + .err = temMALFORMED}); // Holder is not allowed when TransferFee is present - mptAlice.set({.account = alice, .holder = bob, .id = mptID, .transferFee = 100, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .transferFee = 100, + .err = temMALFORMED}); } // Can not set Flags when MutableFlags, MPTokenMetadata or @@ -2498,16 +2618,20 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); mptAlice.create( {.ownerCount = 1, - .mutableFlags = tmfMPTCanMutateMetadata | tmfMPTCanMutateCanLock | tmfMPTCanMutateTransferFee}); + .mutableFlags = tmfMPTCanMutateMetadata | tmfMPTCanMutateCanLock | + tmfMPTCanMutateTransferFee}); // Setting flags is not allowed when MutableFlags is present - mptAlice.set({.account = alice, .flags = tfMPTCanLock, .mutableFlags = 2, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, .flags = tfMPTCanLock, .mutableFlags = 2, .err = temMALFORMED}); // Setting flags is not allowed when MPTokenMetadata is present - mptAlice.set({.account = alice, .flags = tfMPTCanLock, .metadata = "test", .err = temMALFORMED}); + mptAlice.set( + {.account = alice, .flags = tfMPTCanLock, .metadata = "test", .err = temMALFORMED}); // setting flags is not allowed when TransferFee is present - mptAlice.set({.account = alice, .flags = tfMPTCanLock, .transferFee = 100, .err = temMALFORMED}); + mptAlice.set( + {.account = alice, .flags = tfMPTCanLock, .transferFee = 100, .err = temMALFORMED}); } // Flags being 0 or tfFullyCanonicalSig is fine @@ -2522,7 +2646,11 @@ class MPToken_test : public beast::unit_test::suite .mutableFlags = tmfMPTCanMutateTransferFee | tmfMPTCanMutateMetadata}); mptAlice.set({.account = alice, .flags = 0, .transferFee = 100, .metadata = "test"}); - mptAlice.set({.account = alice, .flags = tfFullyCanonicalSig, .transferFee = 200, .metadata = "test2"}); + mptAlice.set( + {.account = alice, + .flags = tfFullyCanonicalSig, + .transferFee = 200, + .metadata = "test2"}); } // Invalid MutableFlags @@ -2533,7 +2661,8 @@ class MPToken_test : public beast::unit_test::suite for (auto const flags : {10000, 0, 5000}) { - mptAlice.set({.account = alice, .id = mptID, .mutableFlags = flags, .err = temINVALID_FLAG}); + mptAlice.set( + {.account = alice, .id = mptID, .mutableFlags = flags, .err = temINVALID_FLAG}); } } @@ -2551,11 +2680,16 @@ class MPToken_test : public beast::unit_test::suite tmfMPTSetCanTransfer | tmfMPTClearCanTransfer, tmfMPTSetCanClawback | tmfMPTClearCanClawback, tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTClearCanTrade, - tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanEscrow | tmfMPTClearCanClawback}; + tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanEscrow | + tmfMPTClearCanClawback}; for (auto const& mutableFlags : flagCombinations) { - mptAlice.set({.account = alice, .id = mptID, .mutableFlags = mutableFlags, .err = temINVALID_FLAG}); + mptAlice.set( + {.account = alice, + .id = mptID, + .mutableFlags = mutableFlags, + .err = temINVALID_FLAG}); } } @@ -2582,7 +2716,8 @@ class MPToken_test : public beast::unit_test::suite for (auto const& mutableFlag : mutableFlags) { - mptAlice.set({.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); } } @@ -2615,7 +2750,10 @@ class MPToken_test : public beast::unit_test::suite mptAlice.create({.ownerCount = 1, .mutableFlags = tmfMPTCanMutateTransferFee}); mptAlice.set( - {.account = alice, .id = mptID, .transferFee = maxTransferFee + 1, .err = temBAD_TRANSFER_FEE}); + {.account = alice, + .id = mptID, + .transferFee = maxTransferFee + 1, + .err = temBAD_TRANSFER_FEE}); } // Test setting non-zero transfer fee and clearing MPTCanTransfer at the @@ -2633,12 +2771,16 @@ class MPToken_test : public beast::unit_test::suite // Can not set non-zero transfer fee and clear MPTCanTransfer at the // same time mptAlice.set( - {.account = alice, .mutableFlags = tmfMPTClearCanTransfer, .transferFee = 1, .err = temMALFORMED}); + {.account = alice, + .mutableFlags = tmfMPTClearCanTransfer, + .transferFee = 1, + .err = temMALFORMED}); // Can set transfer fee to zero and clear MPTCanTransfer at the same // time. tfMPTCanTransfer will be cleared and TransferFee field will // be removed. - mptAlice.set({.account = alice, .mutableFlags = tmfMPTClearCanTransfer, .transferFee = 0}); + mptAlice.set( + {.account = alice, .mutableFlags = tmfMPTClearCanTransfer, .transferFee = 0}); BEAST_EXPECT(!mptAlice.isTransferFeePresent()); } @@ -2647,7 +2789,9 @@ class MPToken_test : public beast::unit_test::suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .mutableFlags = tmfMPTCanMutateTransferFee | tmfMPTCanMutateCanTransfer}); + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = tmfMPTCanMutateTransferFee | tmfMPTCanMutateCanTransfer}); mptAlice.set({.account = alice, .transferFee = 100, .err = tecNO_PERMISSION}); @@ -2655,7 +2799,10 @@ class MPToken_test : public beast::unit_test::suite // at the same time. MPTCanTransfer must be set first, then transfer // fee can be set in a separate transaction. mptAlice.set( - {.account = alice, .mutableFlags = tmfMPTSetCanTransfer, .transferFee = 100, .err = tecNO_PERMISSION}); + {.account = alice, + .mutableFlags = tmfMPTSetCanTransfer, + .transferFee = 100, + .err = tecNO_PERMISSION}); } // Can not mutate transfer fee when it is not mutable @@ -2677,7 +2824,8 @@ class MPToken_test : public beast::unit_test::suite mptAlice.create( {.ownerCount = 1, - .mutableFlags = tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateMetadata}); + .mutableFlags = tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | + tmfMPTCanMutateMetadata}); // Can not mutate transfer fee mptAlice.set({.account = alice, .transferFee = 100, .err = tecNO_PERMISSION}); @@ -2695,7 +2843,8 @@ class MPToken_test : public beast::unit_test::suite // Can not mutate flags which are not mutable for (auto const& mutableFlag : invalidFlags) { - mptAlice.set({.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); } // Can mutate MPTCanTrade @@ -2724,7 +2873,8 @@ class MPToken_test : public beast::unit_test::suite { Env env{*this, features}; MPTTester mptAlice(env, alice); - mptAlice.create({.metadata = "test", .ownerCount = 1, .mutableFlags = tmfMPTCanMutateMetadata}); + mptAlice.create( + {.metadata = "test", .ownerCount = 1, .mutableFlags = tmfMPTCanMutateMetadata}); std::vector metadatas = { "mutate metadata", @@ -2774,7 +2924,9 @@ class MPToken_test : public beast::unit_test::suite // Test flag toggling { - auto testFlagToggle = [&](std::uint32_t createFlags, std::uint32_t setFlags, std::uint32_t clearFlags) { + auto testFlagToggle = [&](std::uint32_t createFlags, + std::uint32_t setFlags, + std::uint32_t clearFlags) { Env env{*this, features}; MPTTester mptAlice(env, alice); @@ -2793,11 +2945,14 @@ class MPToken_test : public beast::unit_test::suite }; testFlagToggle(tmfMPTCanMutateCanLock, tfMPTCanLock, tmfMPTClearCanLock); - testFlagToggle(tmfMPTCanMutateRequireAuth, tmfMPTSetRequireAuth, tmfMPTClearRequireAuth); + testFlagToggle( + tmfMPTCanMutateRequireAuth, tmfMPTSetRequireAuth, tmfMPTClearRequireAuth); testFlagToggle(tmfMPTCanMutateCanEscrow, tmfMPTSetCanEscrow, tmfMPTClearCanEscrow); testFlagToggle(tmfMPTCanMutateCanTrade, tmfMPTSetCanTrade, tmfMPTClearCanTrade); - testFlagToggle(tmfMPTCanMutateCanTransfer, tmfMPTSetCanTransfer, tmfMPTClearCanTransfer); - testFlagToggle(tmfMPTCanMutateCanClawback, tmfMPTSetCanClawback, tmfMPTClearCanClawback); + testFlagToggle( + tmfMPTCanMutateCanTransfer, tmfMPTSetCanTransfer, tmfMPTClearCanTransfer); + testFlagToggle( + tmfMPTCanMutateCanClawback, tmfMPTSetCanClawback, tmfMPTClearCanClawback); } } @@ -2818,7 +2973,8 @@ class MPToken_test : public beast::unit_test::suite {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanTransfer, - .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanTrade | tmfMPTCanMutateTransferFee}); + .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanTrade | + tmfMPTCanMutateTransferFee}); mptAlice.authorize({.account = bob, .holderCount = 1}); // Lock bob's mptoken @@ -2841,7 +2997,8 @@ class MPToken_test : public beast::unit_test::suite {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock, - .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata}); + .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanClawback | + tmfMPTCanMutateMetadata}); mptAlice.authorize({.account = bob, .holderCount = 1}); // Lock issuance @@ -2864,7 +3021,8 @@ class MPToken_test : public beast::unit_test::suite {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock, - .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata}); + .mutableFlags = tmfMPTCanMutateCanLock | tmfMPTCanMutateCanClawback | + tmfMPTCanMutateMetadata}); mptAlice.authorize({.account = bob, .holderCount = 1}); // Can lock and unlock @@ -2879,8 +3037,10 @@ class MPToken_test : public beast::unit_test::suite // Can not lock or unlock mptAlice.set({.account = alice, .flags = tfMPTLock, .err = tecNO_PERMISSION}); mptAlice.set({.account = alice, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); // Set MPTCanLock again mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanLock}); @@ -2904,7 +3064,10 @@ class MPToken_test : public beast::unit_test::suite Account const bob("bob"); MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .flags = tfMPTRequireAuth, .mutableFlags = tmfMPTCanMutateRequireAuth}); + mptAlice.create( + {.ownerCount = 1, + .flags = tfMPTRequireAuth, + .mutableFlags = tmfMPTCanMutateRequireAuth}); mptAlice.authorize({.account = bob}); mptAlice.authorize({.account = alice, .holder = bob}); @@ -2952,7 +3115,10 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {carol, bob}}); mptAlice.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer, .mutableFlags = tmfMPTCanMutateCanEscrow}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer, + .mutableFlags = tmfMPTCanMutateCanEscrow}); mptAlice.authorize({.account = carol}); mptAlice.authorize({.account = bob}); @@ -2998,7 +3164,9 @@ class MPToken_test : public beast::unit_test::suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); - mptAlice.create({.ownerCount = 1, .mutableFlags = tmfMPTCanMutateCanTransfer | tmfMPTCanMutateTransferFee}); + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = tmfMPTCanMutateCanTransfer | tmfMPTCanMutateTransferFee}); mptAlice.authorize({.account = bob}); mptAlice.authorize({.account = carol}); @@ -3015,7 +3183,10 @@ class MPToken_test : public beast::unit_test::suite // Can not set non-zero transfer fee even when trying to set // MPTCanTransfer at the same time mptAlice.set( - {.account = alice, .mutableFlags = tmfMPTSetCanTransfer, .transferFee = 100, .err = tecNO_PERMISSION}); + {.account = alice, + .mutableFlags = tmfMPTSetCanTransfer, + .transferFee = 100, + .err = tecNO_PERMISSION}); // Alice sets MPTCanTransfer mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanTransfer}); @@ -3076,7 +3247,8 @@ class MPToken_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .holderCount = 0, .mutableFlags = tmfMPTCanMutateCanClawback}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .mutableFlags = tmfMPTCanMutateCanClawback}); // Bob creates an MPToken mptAlice.authorize({.account = bob}); diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index 598949f662..609b7bdb89 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -1,15 +1,16 @@ #include +#include -#include -#include #include -#include #include #include #include #include #include +#include +#include +#include #include #include @@ -71,7 +72,7 @@ public: { setupDatabaseDir(getDatabasePath()); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } } @@ -81,20 +82,28 @@ public: { cleanupDatabaseDir(getDatabasePath()); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } } std::string - makeManifestString(PublicKey const& pk, SecretKey const& sk, PublicKey const& spk, SecretKey const& ssk, int seq) + makeManifestString( + PublicKey const& pk, + SecretKey const& sk, + PublicKey const& spk, + SecretKey const& ssk, + int seq) { STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -112,7 +121,8 @@ public: st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; - sign(st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); + sign( + st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature)); Serializer s; @@ -130,7 +140,8 @@ public: st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; - sign(st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); + sign( + st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature)); Serializer s; @@ -164,7 +175,8 @@ public: sign(st, HashPrefix::manifest, stype, ssk); BEAST_EXPECT(verify(st, HashPrefix::manifest, spk)); - sign(st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); + sign( + st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature); BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature)); Serializer s; @@ -199,7 +211,8 @@ public: auto dbCon = makeTestWalletDB(setup, dbName, env.journal); - auto getPopulatedManifests = [](ManifestCache const& cache) -> std::vector { + auto getPopulatedManifests = + [](ManifestCache const& cache) -> std::vector { std::vector result; result.reserve(32); cache.for_each_manifest([&result](Manifest const& man) { result.push_back(&man); }); @@ -220,14 +233,17 @@ public: { // save should not store untrusted master keys to db // except for revocations - m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) { return unl->listed(pubKey); }); + m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) { + return unl->listed(pubKey); + }); ManifestCache loaded; loaded.load(*dbCon, "ValidatorManifests"); // check that all loaded manifests are revocations - std::vector const loadedManifests(sort(getPopulatedManifests(loaded))); + std::vector const loadedManifests( + sort(getPopulatedManifests(loaded))); for (auto const& man : loadedManifests) BEAST_EXPECT(man->revoked()); @@ -236,25 +252,28 @@ public: // save should store all trusted master keys to db std::vector s1; std::vector keys; - std::string cfgManifest; for (auto const& man : inManifests) s1.push_back(toBase58(TokenType::NodePublic, man->masterKey)); unl->load({}, s1, keys); - m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) { return unl->listed(pubKey); }); + m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) { + return unl->listed(pubKey); + }); ManifestCache loaded; loaded.load(*dbCon, "ValidatorManifests"); // check that the manifest caches are the same - std::vector const loadedManifests(sort(getPopulatedManifests(loaded))); + std::vector const loadedManifests( + sort(getPopulatedManifests(loaded))); if (inManifests.size() == loadedManifests.size()) { - BEAST_EXPECT(std::equal( - inManifests.begin(), - inManifests.end(), - loadedManifests.begin(), - [](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; })); + BEAST_EXPECT( + std::equal( + inManifests.begin(), + inManifests.end(), + loadedManifests.begin(), + [](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; })); } else { @@ -267,7 +286,8 @@ public: std::vector const emptyRevocation; std::string const badManifest = "bad manifest"; - BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", badManifest, emptyRevocation)); + BEAST_EXPECT( + !loaded.load(*dbCon, "ValidatorManifests", badManifest, emptyRevocation)); auto const sk = randomSecretKey(); auto const pk = derivePublicKey(KeyType::ed25519, sk); @@ -275,7 +295,8 @@ public: std::string const cfgManifest = makeManifestString(pk, sk, kp.first, kp.second, 0); - BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", cfgManifest, emptyRevocation)); + BEAST_EXPECT( + loaded.load(*dbCon, "ValidatorManifests", cfgManifest, emptyRevocation)); } { // load config revocation @@ -283,23 +304,29 @@ public: std::string const emptyManifest; std::vector const badRevocation = {"bad revocation"}; - BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badRevocation)); + BEAST_EXPECT( + !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badRevocation)); auto const sk = randomSecretKey(); auto const keyType = KeyType::ed25519; auto const pk = derivePublicKey(keyType, sk); auto const kp = randomKeyPair(KeyType::secp256k1); - std::vector const nonRevocation = {makeManifestString(pk, sk, kp.first, kp.second, 0)}; + std::vector const nonRevocation = { + makeManifestString(pk, sk, kp.first, kp.second, 0)}; - BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, nonRevocation)); + BEAST_EXPECT( + !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, nonRevocation)); BEAST_EXPECT(!loaded.revoked(pk)); - std::vector const badSigRevocation = {makeRevocationString(sk, keyType, true)}; - BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badSigRevocation)); + std::vector const badSigRevocation = { + makeRevocationString(sk, keyType, true)}; + BEAST_EXPECT( + !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badSigRevocation)); BEAST_EXPECT(!loaded.revoked(pk)); std::vector const cfgRevocation = {makeRevocationString(sk, keyType)}; - BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", emptyManifest, cfgRevocation)); + BEAST_EXPECT( + loaded.load(*dbCon, "ValidatorManifests", emptyManifest, cfgRevocation)); BEAST_EXPECT(loaded.revoked(pk)); } @@ -324,7 +351,9 @@ public: ss.add32(HashPrefix::manifest); st.addWithoutSigningFields(ss); auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice()); - BEAST_EXPECT(strHex(sig) == strHex(*m.getSignature())); + BEAST_EXPECT( + strHex(sig) == + strHex(*m.getSignature())); // NOLINT(bugprone-unchecked-optional-access) auto const masterSig = sign(KeyType::ed25519, sk, ss.slice()); BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature())); @@ -349,7 +378,8 @@ public: auto const kp0 = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT( ManifestDisposition::accepted == - cache.applyManifest(makeManifest(sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0))); + cache.applyManifest( + makeManifest(sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0))); BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first); BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk); @@ -360,7 +390,8 @@ public: auto const kp1 = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT( ManifestDisposition::accepted == - cache.applyManifest(makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1))); + cache.applyManifest( + makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1))); BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first); BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk); BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first); @@ -369,7 +400,8 @@ public: // applied with the same signing key but a higher sequence BEAST_EXPECT( ManifestDisposition::badEphemeralKey == - cache.applyManifest(makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2))); + cache.applyManifest( + makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2))); BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first); BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk); BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first); @@ -377,7 +409,9 @@ public: // getSigningKey should return std::nullopt for a revoked master public // key getMasterKey should return std::nullopt for an ephemeral public // key from a revoked master public key - BEAST_EXPECT(ManifestDisposition::accepted == cache.applyManifest(makeRevocation(sk, KeyType::ed25519))); + BEAST_EXPECT( + ManifestDisposition::accepted == + cache.applyManifest(makeRevocation(sk, KeyType::ed25519))); BEAST_EXPECT(cache.revoked(pk)); BEAST_EXPECT(cache.getSigningKey(pk) == pk); BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first); @@ -390,29 +424,20 @@ public: testcase("validator token"); { - auto const valSecret = - parseBase58(TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi"); + auto const valSecret = parseBase58( + TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi"); // Format token string to test trim() std::vector const tokenBlob = { - " " - "eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3ND" - "diNT\n", - " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2Iiwib" - "WFuaWZl \n", - "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncx" - "L3ZDeE\n", - "\t " - "hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4" - "U0tG\t \t\n", - "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUz" - "ZQU2\n", - "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZ" - "eXd1\n", - "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZW" - "RGdj\n", - "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0i" - "fQ==\n"}; + " eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n", + " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n", + "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n", + "\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n", + "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n", + "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n", + "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n", + "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n", + }; auto const manifest = "JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/" @@ -427,7 +452,9 @@ public: auto const token = loadValidatorToken(tokenBlob); BEAST_EXPECT(token); - BEAST_EXPECT(token->validationSecret == *valSecret); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + BEAST_EXPECT(test::equal(token->validationSecret, *valSecret)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(token->manifest == manifest); } { @@ -481,13 +508,29 @@ public: std::uint32_t sequence = 0; // public key with invalid type - std::array const badKey{0x99, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B, - 0xA3, 0xF3, 0x17, 0xAE, 0x5B, 0xCF, 0x33, 0xB3, 0x29, 0x1B, 0xD6, - 0x3D, 0xB3, 0x26, 0x54, 0xA3, 0x13, 0x22, 0x2F, 0x7F, 0xD0, 0x20}; + std::array const badKey{ + 0x99, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B, + 0xA3, 0xF3, 0x17, 0xAE, 0x5B, 0xCF, 0x33, 0xB3, 0x29, 0x1B, 0xD6, + 0x3D, 0xB3, 0x26, 0x54, 0xA3, 0x13, 0x22, 0x2F, 0x7F, 0xD0, 0x20}; // Short public key: std::array const shortKey{ - 0x03, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B, 0xA3, 0xF3, 0x17, 0xAE, 0x5B}; + 0x03, + 0x30, + 0xE7, + 0xFC, + 0x9D, + 0x56, + 0xBB, + 0x25, + 0xD6, + 0x89, + 0x3B, + 0xA3, + 0xF3, + 0x17, + 0xAE, + 0x5B}; auto toString = [](STObject const& st) { Serializer s; @@ -529,8 +572,8 @@ public: }; { - testcase << "deserializeManifest: normal manifest (" << to_string(keyType) << " + " - << to_string(sKeyType) << ")"; + testcase << "deserializeManifest: normal manifest (" << to_string(keyType) + << " + " << to_string(sKeyType) << ")"; { // valid manifest without domain auto const st = buildManifestObject(++sequence, std::nullopt); @@ -539,12 +582,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain.empty()); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // invalid manifest (empty domain) @@ -576,12 +621,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain == "example.com"); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // valid manifest with invalid signature @@ -592,12 +639,14 @@ public: auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); BEAST_EXPECT(manifest->signingKey == spk); BEAST_EXPECT(manifest->sequence == sequence + 1); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->domain == "example.com"); BEAST_EXPECT(!manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // reject missing sequence @@ -669,44 +718,45 @@ public: } { - testcase << "deserializeManifest: revocation manifest (" << to_string(keyType) << " + " - << to_string(sKeyType) << ")"; + testcase << "deserializeManifest: revocation manifest (" << to_string(keyType) + << " + " << to_string(sKeyType) << ")"; // valid revocation { - auto const st = - buildManifestObject(std::numeric_limits::max(), std::nullopt, true, true); + auto const st = buildManifestObject( + std::numeric_limits::max(), std::nullopt, true, true); auto const m = toString(st); auto const manifest = deserializeManifest(m); BEAST_EXPECT(manifest); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(manifest->masterKey == pk); - // Since this manifest is revoked, it should not have - // a signingKey + // Since this manifest is revoked, it should not have a signingKey BEAST_EXPECT(!manifest->signingKey); BEAST_EXPECT(manifest->revoked()); BEAST_EXPECT(manifest->domain.empty()); BEAST_EXPECT(manifest->serialized == m); BEAST_EXPECT(manifest->verify()); + // NOLINTEND(bugprone-unchecked-optional-access) } { // can't specify an ephemeral signing key - auto const st = - buildManifestObject(std::numeric_limits::max(), std::nullopt, true, false); + auto const st = buildManifestObject( + std::numeric_limits::max(), std::nullopt, true, false); BEAST_EXPECT(!deserializeManifest(toString(st))); } { // can't specify an ephemeral signature - auto const st = - buildManifestObject(std::numeric_limits::max(), std::nullopt, false, true); + auto const st = buildManifestObject( + std::numeric_limits::max(), std::nullopt, false, true); BEAST_EXPECT(!deserializeManifest(toString(st))); } { // can't specify an ephemeral key & signature - auto const st = - buildManifestObject(std::numeric_limits::max(), std::nullopt, false, false); + auto const st = buildManifestObject( + std::numeric_limits::max(), std::nullopt, false, false); BEAST_EXPECT(!deserializeManifest(toString(st))); } @@ -804,16 +854,20 @@ public: auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a); auto const kp_a0 = randomKeyPair(KeyType::secp256k1); auto const kp_a1 = randomKeyPair(KeyType::secp256k1); - auto const s_a0 = makeManifest(sk_a, KeyType::ed25519, kp_a0.second, KeyType::secp256k1, 0); - auto const s_a1 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 1); - auto const s_a2 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 2); + auto const s_a0 = + makeManifest(sk_a, KeyType::ed25519, kp_a0.second, KeyType::secp256k1, 0); + auto const s_a1 = + makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 1); + auto const s_a2 = + makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 2); auto const s_aMax = makeRevocation(sk_a, KeyType::ed25519); auto const sk_b = randomSecretKey(); auto const kp_b0 = randomKeyPair(KeyType::secp256k1); auto const kp_b1 = randomKeyPair(KeyType::secp256k1); auto const kp_b2 = randomKeyPair(KeyType::secp256k1); - auto const s_b0 = makeManifest(sk_b, KeyType::ed25519, kp_b0.second, KeyType::secp256k1, 0); + auto const s_b0 = + makeManifest(sk_b, KeyType::ed25519, kp_b0.second, KeyType::secp256k1, 0); auto const s_b1 = makeManifest( sk_b, KeyType::ed25519, @@ -821,7 +875,8 @@ public: KeyType::secp256k1, 1, true); // invalidSig - auto const s_b2 = makeManifest(sk_b, KeyType::ed25519, kp_b2.second, KeyType::ed25519, 2); + auto const s_b2 = + makeManifest(sk_b, KeyType::ed25519, kp_b2.second, KeyType::ed25519, 2); auto const fake = s_b2.serialized + '\0'; @@ -858,7 +913,8 @@ public: BEAST_EXPECT(cache.applyManifest(clone(s_b1)) == ManifestDisposition::invalid); BEAST_EXPECT(cache.applyManifest(clone(s_b2)) == ManifestDisposition::accepted); - auto const s_c0 = makeManifest(kp_b2.second, KeyType::ed25519, randomSecretKey(), KeyType::ed25519, 47); + auto const s_c0 = makeManifest( + kp_b2.second, KeyType::ed25519, randomSecretKey(), KeyType::ed25519, 47); BEAST_EXPECT(cache.applyManifest(clone(s_c0)) == ManifestDisposition::badMasterKey); } diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index 3d732ee2e0..f96bfba2d2 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -84,7 +84,14 @@ public: Json::Value bigSigners = signers( alice, 1, - {{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1}, {shade, 1}, {spook, 1}}); + {{bogie, 1}, + {demon, 1}, + {ghost, 1}, + {haunt, 1}, + {jinni, 1}, + {phase, 1}, + {shade, 1}, + {spook, 1}}); env(bigSigners, ter(tecINSUFFICIENT_RESERVE)); env.close(); env.require(owners(alice, 1)); @@ -129,7 +136,14 @@ public: env(signers( alice, 1, - {{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1}, {demon, 1}, {spook, 1}}), + {{bogie, 1}, + {demon, 1}, + {ghost, 1}, + {haunt, 1}, + {jinni, 1}, + {phase, 1}, + {demon, 1}, + {spook, 1}}), ter(temBAD_SIGNER)); // Set a quorum of zero. Should fail. @@ -139,7 +153,14 @@ public: env(signers( alice, 9, - {{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1}, {shade, 1}, {spook, 1}}), + {{bogie, 1}, + {demon, 1}, + {ghost, 1}, + {haunt, 1}, + {jinni, 1}, + {phase, 1}, + {shade, 1}, + {spook, 1}}), ter(temBAD_QUORUM)); // clang-format off @@ -243,7 +264,14 @@ public: env(signers( alice, 1, - {{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1}, {shade, 1}, {spook, 1}})); + {{bogie, 1}, + {demon, 1}, + {ghost, 1}, + {haunt, 1}, + {jinni, 1}, + {phase, 1}, + {shade, 1}, + {spook, 1}})); env.close(); env.require(owners(alice, 1)); @@ -264,7 +292,9 @@ public: // This should work. aliceSeq = env.seq(alice); - env(noop(alice), msig(bogie, demon, ghost, haunt, jinni, phase, shade, spook), fee(9 * baseFee)); + env(noop(alice), + msig(bogie, demon, ghost, haunt, jinni, phase, shade, spook), + fee(9 * baseFee)); env.close(); BEAST_EXPECT(env.seq(alice) == aliceSeq + 1); @@ -300,7 +330,9 @@ public: msig phantoms{bogie, demon}; std::reverse(phantoms.signers.begin(), phantoms.signers.end()); std::uint32_t const aliceSeq = env.seq(alice); - env(noop(alice), phantoms, rpc("invalidTransaction", "fails local checks: Unsorted Signers array.")); + env(noop(alice), + phantoms, + rpc("invalidTransaction", "fails local checks: Unsorted Signers array.")); env.close(); BEAST_EXPECT(env.seq(alice) == aliceSeq); } @@ -454,7 +486,7 @@ public: env.close(); auto const baseFee = env.current()->fees().base; - std::uint32_t aliceSeq; + std::uint32_t aliceSeq = 0; // these represent oft-repeated setup for input json below auto setup_tx = [&]() -> Json::Value { @@ -510,7 +542,9 @@ public: auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result]; BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "When multi-signing 'tx_json.SigningPubKey' must be empty."); + BEAST_EXPECT( + jrr[jss::error_message] == + "When multi-signing 'tx_json.SigningPubKey' must be empty."); } { @@ -535,7 +569,8 @@ public: jrr = env.rpc("json", "submit_multisigned", to_string(jv_submit))[jss::result]; BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid Fee field. Fees must be greater than zero."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid Fee field. Fees must be greater than zero."); } { @@ -703,7 +738,11 @@ public: BEAST_EXPECT(env.seq(alice) == aliceSeq + 1); // Require all signers to sign. - env(signers(alice, 0x3FFFC, {{becky, 0xFFFF}, {cheri, 0xFFFF}, {daria, 0xFFFF}, {jinni, 0xFFFF}}), sig(alie)); + env(signers( + alice, + 0x3FFFC, + {{becky, 0xFFFF}, {cheri, 0xFFFF}, {daria, 0xFFFF}, {jinni, 0xFFFF}}), + sig(alie)); env.close(); env.require(owners(alice, 1)); @@ -743,7 +782,10 @@ public: // One signer short should fail. aliceSeq = env.seq(alice); - env(noop(alice), msig(becky, cheri, haunt, jinni, phase, shade, spook), fee(8 * baseFee), ter(tefBAD_QUORUM)); + env(noop(alice), + msig(becky, cheri, haunt, jinni, phase, shade, spook), + fee(8 * baseFee), + ter(tefBAD_QUORUM)); env.close(); BEAST_EXPECT(env.seq(alice) == aliceSeq); @@ -915,7 +957,10 @@ public: BEAST_EXPECT(env.seq(alice) == aliceSeq + 1); // Multisign a ttTRUST_SET - env(trust("alice", USD(100)), msig(becky, bogie), fee(3 * baseFee), require(lines("alice", 1))); + env(trust("alice", USD(100)), + msig(becky, bogie), + fee(3 * baseFee), + require(lines("alice", 1))); env.close(); env.require(owners(alice, 2)); @@ -940,7 +985,9 @@ public: } // Multisign a ttSIGNER_LIST_SET. - env(signers(alice, 3, {{becky, 1}, {bogie, 1}, {demon, 1}}), msig(becky, bogie), fee(3 * baseFee)); + env(signers(alice, 3, {{becky, 1}, {bogie, 1}, {demon, 1}}), + msig(becky, bogie), + fee(3 * baseFee)); env.close(); env.require(owners(alice, 2)); } @@ -974,7 +1021,9 @@ public: STTx local = *(tx.stx); local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Empty SigningPubKey."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Empty SigningPubKey."); } { // Single-sign, but invalidate the signature. @@ -986,7 +1035,9 @@ public: local.setFieldVL(sfTxnSignature, badSig); // Signature should fail. auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Invalid signature."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Invalid signature."); } { // Single-sign, but invalidate the sequence number. @@ -997,7 +1048,9 @@ public: local.setFieldU32(sfSequence, seq + 1); // Signature should fail. auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Invalid signature."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Invalid signature."); } { // Multisign, but leave a nonempty sfSigningPubKey. @@ -1006,7 +1059,8 @@ public: local[sfSigningPubKey] = alice.pk(); // Insert sfSigningPubKey auto const info = submitSTTx(local); BEAST_EXPECT( - info[jss::result][jss::error_exception] == "fails local checks: Cannot both single- and multi-sign."); + info[jss::result][jss::error_exception] == + "fails local checks: Cannot both single- and multi-sign."); } { // Both multi- and single-sign with an empty SigningPubKey. @@ -1016,7 +1070,8 @@ public: local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey auto const info = submitSTTx(local); BEAST_EXPECT( - info[jss::result][jss::error_exception] == "fails local checks: Cannot both single- and multi-sign."); + info[jss::result][jss::error_exception] == + "fails local checks: Cannot both single- and multi-sign."); } { // Multisign but invalidate one of the signatures. @@ -1030,8 +1085,8 @@ public: // Signature should fail. auto const info = submitSTTx(local); BEAST_EXPECT( - info[jss::result][jss::error_exception].asString().find("Invalid signature on account r") != - std::string::npos); + info[jss::result][jss::error_exception].asString().find( + "Invalid signature on account r") != std::string::npos); } { // Multisign with an empty signers array should fail. @@ -1039,7 +1094,9 @@ public: STTx local = *(tx.stx); local.peekFieldArray(sfSigners).clear(); // Empty Signers array. auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Invalid Signers array size."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Invalid Signers array size."); } { JTx tx = env.jt( @@ -1082,14 +1139,18 @@ public: bogie)); STTx local = *(tx.stx); auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Invalid Signers array size."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Invalid Signers array size."); } { // The account owner may not multisign for themselves. JTx tx = env.jt(noop(alice), fee(2 * baseFee), msig(alice)); STTx local = *(tx.stx); auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Invalid multisigner."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Invalid multisigner."); } { // No duplicate multisignatures allowed. @@ -1097,7 +1158,8 @@ public: STTx local = *(tx.stx); auto const info = submitSTTx(local); BEAST_EXPECT( - info[jss::result][jss::error_exception] == "fails local checks: Duplicate Signers not allowed."); + info[jss::result][jss::error_exception] == + "fails local checks: Duplicate Signers not allowed."); } { // Multisignatures must be submitted in sorted order. @@ -1108,7 +1170,9 @@ public: std::reverse(signers.begin(), signers.end()); // Signature should fail. auto const info = submitSTTx(local); - BEAST_EXPECT(info[jss::result][jss::error_exception] == "fails local checks: Unsorted Signers array."); + BEAST_EXPECT( + info[jss::result][jss::error_exception] == + "fails local checks: Unsorted Signers array."); } } @@ -1248,7 +1312,8 @@ public: BEAST_EXPECT(hash1 != hash2); // Submit the result of the two signatures. - Json::Value jvResult = env.rpc("json", "submit_multisigned", to_string(jvSubmit[jss::result])); + Json::Value jvResult = + env.rpc("json", "submit_multisigned", to_string(jvSubmit[jss::result])); BEAST_EXPECT(jvResult[jss::result][jss::status].asString() == "success"); BEAST_EXPECT(jvResult[jss::result][jss::engine_result].asString() == "tesSUCCESS"); @@ -1262,7 +1327,8 @@ public: Json::Value jvTx = env.rpc("tx", hash2); BEAST_EXPECT(jvTx[jss::result][jss::status].asString() == "success"); BEAST_EXPECT(jvTx[jss::result][jss::validated].asString() == "true"); - BEAST_EXPECT(jvTx[jss::result][jss::meta][sfTransactionResult.jsonName].asString() == "tesSUCCESS"); + BEAST_EXPECT( + jvTx[jss::result][jss::meta][sfTransactionResult.jsonName].asString() == "tesSUCCESS"); } void diff --git a/src/test/app/NFTokenAuth_test.cpp b/src/test/app/NFTokenAuth_test.cpp index 1e7afc9bb0..b973530f41 100644 --- a/src/test/app/NFTokenAuth_test.cpp +++ b/src/test/app/NFTokenAuth_test.cpp @@ -1,6 +1,6 @@ #include -#include +#include namespace xrpl { @@ -335,7 +335,9 @@ public: if (features[fixEnforceNFTokenTrustlineV2]) { // test: G1 requires authorization of broker, no trust line exists - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecNO_LINE)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecNO_LINE)); env.close(); // trust line created, but not authorized @@ -343,7 +345,9 @@ public: env.close(); // test: G1 requires authorization of broker - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecNO_AUTH)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecNO_AUTH)); env.close(); // test: can still be brokered without broker fee. @@ -413,7 +417,9 @@ public: if (features[fixEnforceNFTokenTrustlineV2]) { // test: G1 requires authorization of A2 - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecNO_AUTH)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecNO_AUTH)); env.close(); } } @@ -464,7 +470,9 @@ public: if (features[fixEnforceNFTokenTrustlineV2]) { // test: G1 requires authorization of broker, no trust line exists - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecNO_LINE)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecNO_LINE)); env.close(); // trust line created, but not authorized @@ -472,7 +480,9 @@ public: env.close(); // test: G1 requires authorization of A2 - env(token::brokerOffers(broker, buyIdx, sellIdx), token::brokerFee(USD(1)), ter(tecNO_AUTH)); + env(token::brokerOffers(broker, buyIdx, sellIdx), + token::brokerFee(USD(1)), + ter(tecNO_AUTH)); env.close(); // test: cannot be brokered even without broker fee. diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index 915bb6ec79..25f5a0f880 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -1,9 +1,8 @@ #include -#include - #include #include +#include #include @@ -33,7 +32,9 @@ class NFTokenBurn_test : public beast::unit_test::suite { using namespace test::jtx; uint256 const nftokenID = token::getNextID(env, owner, 0, tfTransferable); - env(token::mint(owner, 0), token::uri(std::string(maxTokenURILength, 'u')), txflags(tfTransferable)); + env(token::mint(owner, 0), + token::uri(std::string(maxTokenURILength, 'u')), + txflags(tfTransferable)); env.close(); offerIndexes.reserve(tokenCancelCount); @@ -81,10 +82,12 @@ class NFTokenBurn_test : public beast::unit_test::suite } for (Json::UInt i = 0; i < state.size(); ++i) { - if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) + if (state[i].isMember(sfNFTokens.jsonName) && + state[i][sfNFTokens.jsonName].isArray()) { std::uint32_t tokenCount = state[i][sfNFTokens.jsonName].size(); - std::cout << tokenCount << " NFtokens in page " << state[i][jss::index].asString() << std::endl; + std::cout << tokenCount << " NFtokens in page " + << state[i][jss::index].asString() << std::endl; if (vol == noisy) { @@ -93,10 +96,18 @@ class NFTokenBurn_test : public beast::unit_test::suite else { if (tokenCount > 0) - std::cout << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() << std::endl; + { + std::cout + << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() + << std::endl; + } if (tokenCount > 1) - std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() - << std::endl; + { + std::cout + << "last: " + << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() + << std::endl; + } } } } @@ -154,13 +165,15 @@ class NFTokenBurn_test : public beast::unit_test::suite // effect of random numbers, but we want the test to run the same // way each time. std::mt19937 engine; - std::uniform_int_distribution feeDist(decltype(maxTransferFee){}, maxTransferFee); + std::uniform_int_distribution feeDist( + decltype(maxTransferFee){}, maxTransferFee); alice.nfts.reserve(105); while (alice.nfts.size() < 105) { std::uint16_t const xferFee = feeDist(engine); - alice.nfts.push_back(token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee)); + alice.nfts.push_back( + token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee)); env(token::mint(alice), txflags(tfTransferable | tfBurnable), token::xferFee(xferFee)); env.close(); } @@ -169,7 +182,8 @@ class NFTokenBurn_test : public beast::unit_test::suite while (minter.nfts.size() < 105) { std::uint16_t const xferFee = feeDist(engine); - minter.nfts.push_back(token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee)); + minter.nfts.push_back( + token::getNextID(env, alice, 0u, tfTransferable | tfBurnable, xferFee)); env(token::mint(minter), txflags(tfTransferable | tfBurnable), token::xferFee(xferFee), @@ -214,8 +228,12 @@ class NFTokenBurn_test : public beast::unit_test::suite for (uint256 nft : owner.nfts) { // Create sell offers for owner. - env(token::createOffer(owner, nft, drops(1)), txflags(tfSellNFToken), token::destination(other1)); - env(token::createOffer(owner, nft, drops(1)), txflags(tfSellNFToken), token::destination(other2)); + env(token::createOffer(owner, nft, drops(1)), + txflags(tfSellNFToken), + token::destination(other1)); + env(token::createOffer(owner, nft, drops(1)), + txflags(tfSellNFToken), + token::destination(other2)); env.close(); // Create buy offers for other1 and other2. @@ -242,7 +260,7 @@ class NFTokenBurn_test : public beast::unit_test::suite std::uniform_int_distribution acctDist(0, 2); std::uniform_int_distribution mintDist(0, 1); - while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 || stats[2]->nfts.size() > 0) + while (!stats[0]->nfts.empty() || !stats[1]->nfts.empty() || !stats[2]->nfts.empty()) { // Pick an account to burn an nft. If there are no nfts left // pick again. @@ -259,14 +277,20 @@ class NFTokenBurn_test : public beast::unit_test::suite // Decide which of the accounts should burn the nft. If the // owner is becky then any of the three accounts can burn. // Otherwise either alice or minter can burn. - AcctStat& burner = owner.acct == becky.acct ? *(stats[acctDist(engine)]) - : mintDist(engine) ? alice - : minter; + AcctStat& burner = [&]() -> AcctStat& { + if (owner.acct == becky.acct) + return *(stats[acctDist(engine)]); + return mintDist(engine) ? alice : minter; + }(); if (owner.acct == burner.acct) + { env(token::burn(burner, nft)); + } else + { env(token::burn(burner, nft), token::owner(owner)); + } env.close(); // Every time we burn an nft, the number of nfts they hold should @@ -357,7 +381,8 @@ class NFTokenBurn_test : public beast::unit_test::suite int pageCount = 0; for (Json::UInt i = 0; i < state.size(); ++i) { - if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) + if (state[i].isMember(sfNFTokens.jsonName) && + state[i][sfNFTokens.jsonName].isArray()) { BEAST_EXPECT(state[i][sfNFTokens.jsonName].size() == 32); ++pageCount; @@ -418,12 +443,14 @@ class NFTokenBurn_test : public beast::unit_test::suite return; uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin); - auto middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + auto middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin); - auto firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + auto firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); if (!BEAST_EXPECT(firstNFTokenPage)) return; @@ -463,12 +490,14 @@ class NFTokenBurn_test : public beast::unit_test::suite BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32); // The "middle" page should be gone. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); BEAST_EXPECT(!middleNFTokenPage); // The "first" page should still be present and linked to // the last page. - firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); BEAST_EXPECT(firstNFTokenPage); BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin)); BEAST_EXPECT(firstNFTokenPage->at(~sfNextPageMin) == lastNFTokenPage->key()); @@ -484,7 +513,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // The "middle" page is still present, but has lost the // NextPageMin field. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin)); @@ -517,12 +547,14 @@ class NFTokenBurn_test : public beast::unit_test::suite return; uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin); - auto middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + auto middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin); - auto firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + auto firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); if (!BEAST_EXPECT(firstNFTokenPage)) return; @@ -537,15 +569,18 @@ class NFTokenBurn_test : public beast::unit_test::suite // Verify that middle page is gone and the links in the two // remaining pages are correct. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); BEAST_EXPECT(!middleNFTokenPage); lastNFTokenPage = env.le(keylet::nftpage_max(alice)); BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin)); BEAST_EXPECT(lastNFTokenPage->getFieldH256(sfPreviousPageMin) == firstNFTokenPageIndex); - firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); - BEAST_EXPECT(firstNFTokenPage->getFieldH256(sfNextPageMin) == keylet::nftpage_max(alice).key); + firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + BEAST_EXPECT( + firstNFTokenPage->getFieldH256(sfNextPageMin) == keylet::nftpage_max(alice).key); BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin)); // Burn the remaining nfts. @@ -573,12 +608,14 @@ class NFTokenBurn_test : public beast::unit_test::suite return; uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin); - auto middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + auto middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin); - auto firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + auto firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); if (!BEAST_EXPECT(firstNFTokenPage)) return; @@ -592,11 +629,13 @@ class NFTokenBurn_test : public beast::unit_test::suite } // Verify the first page is gone. - firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); BEAST_EXPECT(!firstNFTokenPage); // Check the links in the other two pages. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin)); @@ -630,11 +669,13 @@ class NFTokenBurn_test : public beast::unit_test::suite BEAST_EXPECT(lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32); // The "middle" page should be gone. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); BEAST_EXPECT(!middleNFTokenPage); // The "first" page should still be gone. - firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); BEAST_EXPECT(!firstNFTokenPage); } else @@ -647,7 +688,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // The "middle" page is still present, but has lost the // NextPageMin field. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin)); @@ -697,7 +739,8 @@ class NFTokenBurn_test : public beast::unit_test::suite STTx tx{ttACCOUNT_SET, [](STObject&) {}}; test::StreamSink sink{beast::severities::kWarning}; beast::Journal jlog{sink}; - ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; + ApplyContext ac{ + env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; // Verify that the last page is present and contains one NFT. auto lastNFTokenPage = ac.view().peek(keylet::nftpage_max(alice)); @@ -718,8 +761,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // uncomment to log the invariant failure message // log << " --> " << sink.messages().str() << std::endl; BEAST_EXPECT( - sink.messages().str().find("Last NFT page deleted with non-empty directory") != - std::string::npos); + sink.messages().str().find( + "Last NFT page deleted with non-empty directory") != std::string::npos); } } { @@ -729,12 +772,15 @@ class NFTokenBurn_test : public beast::unit_test::suite STTx tx{ttACCOUNT_SET, [](STObject&) {}}; test::StreamSink sink{beast::severities::kWarning}; beast::Journal jlog{sink}; - ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; + ApplyContext ac{ + env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; // Verify that the middle page is present. auto lastNFTokenPage = ac.view().peek(keylet::nftpage_max(alice)); auto middleNFTokenPage = ac.view().peek( - keylet::nftpage(keylet::nftpage_min(alice), lastNFTokenPage->getFieldH256(sfPreviousPageMin))); + keylet::nftpage( + keylet::nftpage_min(alice), + lastNFTokenPage->getFieldH256(sfPreviousPageMin))); BEAST_EXPECT(middleNFTokenPage); // Remove the NextMinPage link from the middle page to fire @@ -751,7 +797,8 @@ class NFTokenBurn_test : public beast::unit_test::suite BEAST_EXPECT(sink.messages().str().starts_with("Invariant failed:")); // uncomment to log the invariant failure message // log << " --> " << sink.messages().str() << std::endl; - BEAST_EXPECT(sink.messages().str().find("Lost NextMinPage link") != std::string::npos); + BEAST_EXPECT( + sink.messages().str().find("Lost NextMinPage link") != std::string::npos); } } } @@ -780,7 +827,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // When the token is burned, 498 sell offers and 1 buy offer are // removed. In total, 499 offers are removed std::vector offerIndexes; - auto const nftokenID = createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries - 2); + auto const nftokenID = + createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries - 2); // Verify all sell offers are present in the ledger. for (uint256 const& offerIndex : offerIndexes) @@ -826,7 +874,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // After we burn the token, 500 of the sell offers should be // removed, and one is left over std::vector offerIndexes; - auto const nftokenID = createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries + 1); + auto const nftokenID = + createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries + 1); // Verify all sell offers are present in the ledger. for (uint256 const& offerIndex : offerIndexes) @@ -869,7 +918,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // are removed. // In total, 500 offers are removed std::vector offerIndexes; - auto const nftokenID = createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries - 1); + auto const nftokenID = + createNftAndOffers(env, alice, offerIndexes, maxDeletableTokenOfferEntries - 1); // Verify all sell offers are present in the ledger. for (uint256 const& offerIndex : offerIndexes) @@ -988,7 +1038,8 @@ class NFTokenBurn_test : public beast::unit_test::suite int pageCount = 0; for (Json::UInt i = 0; i < state.size(); ++i) { - if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) + if (state[i].isMember(sfNFTokens.jsonName) && + state[i][sfNFTokens.jsonName].isArray()) { BEAST_EXPECT(state[i][sfNFTokens.jsonName].size() == 32); ++pageCount; @@ -1013,12 +1064,14 @@ class NFTokenBurn_test : public beast::unit_test::suite return; uint256 const middleNFTokenPageIndex = lastNFTokenPage->at(sfPreviousPageMin); - auto middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + auto middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; uint256 const firstNFTokenPageIndex = middleNFTokenPage->at(sfPreviousPageMin); - auto firstNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); + auto firstNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), firstNFTokenPageIndex)); if (!BEAST_EXPECT(firstNFTokenPage)) return; @@ -1048,7 +1101,8 @@ class NFTokenBurn_test : public beast::unit_test::suite // The "middle" page is still present, but has lost the // NextPageMin field. - middleNFTokenPage = env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); + middleNFTokenPage = + env.le(keylet::nftpage(keylet::nftpage_min(alice), middleNFTokenPageIndex)); if (!BEAST_EXPECT(middleNFTokenPage)) return; BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin)); diff --git a/src/test/app/NFTokenDir_test.cpp b/src/test/app/NFTokenDir_test.cpp index 65bd97ec32..73698f1f89 100644 --- a/src/test/app/NFTokenDir_test.cpp +++ b/src/test/app/NFTokenDir_test.cpp @@ -1,10 +1,9 @@ #include -#include - #include #include #include +#include #include @@ -44,10 +43,12 @@ class NFTokenDir_test : public beast::unit_test::suite } for (Json::UInt i = 0; i < state.size(); ++i) { - if (state[i].isMember(sfNFTokens.jsonName) && state[i][sfNFTokens.jsonName].isArray()) + if (state[i].isMember(sfNFTokens.jsonName) && + state[i][sfNFTokens.jsonName].isArray()) { std::uint32_t tokenCount = state[i][sfNFTokens.jsonName].size(); - std::cout << tokenCount << " NFtokens in page " << state[i][jss::index].asString() << std::endl; + std::cout << tokenCount << " NFtokens in page " + << state[i][jss::index].asString() << std::endl; if (vol == noisy) { @@ -56,10 +57,18 @@ class NFTokenDir_test : public beast::unit_test::suite else { if (tokenCount > 0) - std::cout << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() << std::endl; + { + std::cout + << "first: " << state[i][sfNFTokens.jsonName][0u].toStyledString() + << std::endl; + } if (tokenCount > 1) - std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() - << std::endl; + { + std::cout + << "last: " + << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() + << std::endl; + } } } } @@ -139,7 +148,8 @@ class NFTokenDir_test : public beast::unit_test::suite // with identical 96-low-bits are all kept on the same page. // Lambda that exercises the lopsided splits. - auto exerciseLopsided = [this, &features](std::initializer_list seeds) { + auto exerciseLopsided = [this, + &features](std::initializer_list seeds) { Env env{*this, features}; // Eventually all of the NFTokens will be owned by buyer. @@ -152,7 +162,8 @@ class NFTokenDir_test : public beast::unit_test::suite accounts.reserve(seeds.size()); for (std::string_view seed : seeds) { - Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); + Account const& account = + accounts.emplace_back(Account::base58Seed, std::string(seed)); env.fund(XRP(10000), account); // Do not close the ledger inside the loop. If accounts are @@ -170,13 +181,16 @@ class NFTokenDir_test : public beast::unit_test::suite for (Account const& account : accounts) { // Mint the NFT. - uint256 const& nftID = nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); + uint256 const& nftID = + nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); env(token::mint(account, 0), txflags(tfTransferable)); env.close(); // Create an offer to give the NFT to buyer for free. offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key); - env(token::createOffer(account, nftID, XRP(0)), token::destination(buyer), txflags((tfSellNFToken))); + env(token::createOffer(account, nftID, XRP(0)), + token::destination(buyer), + txflags((tfSellNFToken))); } env.close(); @@ -352,7 +366,8 @@ class NFTokenDir_test : public beast::unit_test::suite accounts.reserve(seeds.size()); for (std::string_view seed : seeds) { - Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); + Account const& account = + accounts.emplace_back(Account::base58Seed, std::string(seed)); env.fund(XRP(10000), account); // Do not close the ledger inside the loop. If accounts are @@ -370,13 +385,16 @@ class NFTokenDir_test : public beast::unit_test::suite for (Account const& account : accounts) { // Mint the NFT. - uint256 const& nftID = nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); + uint256 const& nftID = + nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); env(token::mint(account, 0), txflags(tfTransferable)); env.close(); // Create an offer to give the NFT to buyer for free. offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key); - env(token::createOffer(account, nftID, XRP(0)), token::destination(buyer), txflags((tfSellNFToken))); + env(token::createOffer(account, nftID, XRP(0)), + token::destination(buyer), + txflags((tfSellNFToken))); } env.close(); @@ -596,13 +614,16 @@ class NFTokenDir_test : public beast::unit_test::suite for (Account const& account : accounts) { // Mint the NFT. - uint256 const& nftID = nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); + uint256 const& nftID = + nftIDs.emplace_back(token::getNextID(env, account, 0, tfTransferable)); env(token::mint(account, 0), txflags(tfTransferable)); env.close(); // Create an offer to give the NFT to buyer for free. offers.emplace_back(keylet::nftoffer(account, env.seq(account)).key); - env(token::createOffer(account, nftID, XRP(0)), token::destination(buyer), txflags((tfSellNFToken))); + env(token::createOffer(account, nftID, XRP(0)), + token::destination(buyer), + txflags((tfSellNFToken))); } env.close(); @@ -764,14 +785,16 @@ class NFTokenDir_test : public beast::unit_test::suite // Mint the NFT. Tweak the taxon so zero is always stored. std::uint32_t taxon = toUInt32(nft::cipheredTaxon(i, nft::toTaxon(0))); - uint256 const& nftID = - nftIDsByPage[i].emplace_back(token::getNextID(env, account, taxon, tfTransferable)); + uint256 const& nftID = nftIDsByPage[i].emplace_back( + token::getNextID(env, account, taxon, tfTransferable)); env(token::mint(account, taxon), txflags(tfTransferable)); env.close(); // Create an offer to give the NFT to buyer for free. offers[i].emplace_back(keylet::nftoffer(account, env.seq(account)).key); - env(token::createOffer(account, nftID, XRP(0)), token::destination(buyer), txflags((tfSellNFToken))); + env(token::createOffer(account, nftID, XRP(0)), + token::destination(buyer), + txflags((tfSellNFToken))); } } env.close(); @@ -906,7 +929,8 @@ class NFTokenDir_test : public beast::unit_test::suite return env.rpc("json", "account_objects", to_string(params)); }(); BEAST_EXPECT( - remainingOffers.isMember(jss::result) && remainingOffers[jss::result].isMember(jss::account_objects) && + remainingOffers.isMember(jss::result) && + remainingOffers[jss::result].isMember(jss::account_objects) && remainingOffers[jss::result][jss::account_objects].size() == 0); } diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 1b9ce82d4a..ba61830758 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -1,10 +1,9 @@ #include -#include - #include #include #include +#include #include @@ -165,22 +164,27 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // A lambda that checks alice's ownerCount, mintedCount, and // burnedCount all in one fell swoop. - auto checkAliceOwnerMintedBurned = - [&env, this, &alice](std::uint32_t owners, std::uint32_t minted, std::uint32_t burned, int line) { - auto oneCheck = [line, this](char const* type, std::uint32_t found, std::uint32_t exp) { - if (found == exp) - pass(); - else - { - std::stringstream ss; - ss << "Wrong " << type << " count. Found: " << found << "; Expected: " << exp; - fail(ss.str(), __FILE__, line); - } - }; - oneCheck("owner", ownerCount(env, alice), owners); - oneCheck("minted", mintedCount(env, alice), minted); - oneCheck("burned", burnedCount(env, alice), burned); + auto checkAliceOwnerMintedBurned = [&env, this, &alice]( + std::uint32_t owners, + std::uint32_t minted, + std::uint32_t burned, + int line) { + auto oneCheck = [line, this](char const* type, std::uint32_t found, std::uint32_t exp) { + if (found == exp) + { + pass(); + } + else + { + std::stringstream ss; + ss << "Wrong " << type << " count. Found: " << found << "; Expected: " << exp; + fail(ss.str(), __FILE__, line); + } }; + oneCheck("owner", ownerCount(env, alice), owners); + oneCheck("minted", mintedCount(env, alice), minted); + oneCheck("burned", burnedCount(env, alice), burned); + }; // alice still does not have enough XRP for the reserve of an NFT // page. @@ -264,16 +268,20 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite std::uint32_t minterMinted, std::uint32_t minterBurned, int line) { - auto oneCheck = [this](char const* type, std::uint32_t found, std::uint32_t exp, int line) { - if (found == exp) - pass(); - else - { - std::stringstream ss; - ss << "Wrong " << type << " count. Found: " << found << "; Expected: " << exp; - fail(ss.str(), __FILE__, line); - } - }; + auto oneCheck = + [this](char const* type, std::uint32_t found, std::uint32_t exp, int line) { + if (found == exp) + { + pass(); + } + else + { + std::stringstream ss; + ss << "Wrong " << type << " count. Found: " << found + << "; Expected: " << exp; + fail(ss.str(), __FILE__, line); + } + }; oneCheck("alice owner", ownerCount(env, alice), aliceOwners, line); oneCheck("alice minted", mintedCount(env, alice), aliceMinted, line); oneCheck("alice burned", burnedCount(env, alice), aliceBurned, line); @@ -292,7 +300,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // minter still does not have enough XRP for the reserve of an NFT // page. Just for grins (and code coverage), minter mints NFTs that // include a URI. - env(token::mint(minter), token::issuer(alice), token::uri("uri"), ter(tecINSUFFICIENT_RESERVE)); + env(token::mint(minter), + token::issuer(alice), + token::uri("uri"), + ter(tecINSUFFICIENT_RESERVE)); env.close(); checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__); @@ -320,7 +331,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // That NFT page is full. Creating an additional NFT page requires // additional reserve. - env(token::mint(minter), token::issuer(alice), token::uri("uri"), ter(tecINSUFFICIENT_RESERVE)); + env(token::mint(minter), + token::issuer(alice), + token::uri("uri"), + ter(tecINSUFFICIENT_RESERVE)); env.close(); checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__); @@ -463,7 +477,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(token::mint(alice, 0u), token::uri(""), ter(temMALFORMED)); // Invalid URI: too long. - env(token::mint(alice, 0u), token::uri(std::string(maxTokenURILength + 1, 'q')), ter(temMALFORMED)); + env(token::mint(alice, 0u), + token::uri(std::string(maxTokenURILength + 1, 'q')), + ter(temMALFORMED)); //---------------------------------------------------------------------- // preclaim @@ -577,17 +593,23 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // buyer tries to create an NFTokenOffer, but doesn't have the reserve. - env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), token::owner(alice), ter(tecINSUFFICIENT_RESERVE)); + env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), + token::owner(alice), + ter(tecINSUFFICIENT_RESERVE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 0); // Set a negative fee. - env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), fee(STAmount(10ull, true)), ter(temBAD_FEE)); + env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), + fee(STAmount(10ull, true)), + ter(temBAD_FEE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 0); // Set an invalid flag. - env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), txflags(0x00008000), ter(temINVALID_FLAG)); + env(token::createOffer(buyer, nftAlice0ID, XRP(1000)), + txflags(0x00008000), + ter(temINVALID_FLAG)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 0); @@ -599,7 +621,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, buyer) == 0); // Set a bad expiration. - env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](1)), token::expiration(0), ter(temBAD_EXPIRATION)); + env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](1)), + token::expiration(0), + ter(temBAD_EXPIRATION)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 0); @@ -618,7 +642,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // An owner may not offer to buy their own token. - env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)), token::owner(alice), ter(temMALFORMED)); + env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)), + token::owner(alice), + ter(temMALFORMED)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -664,7 +690,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, buyer) == 0); // buyer must have the funds to pay for their offer. - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecNO_LINE)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecNO_LINE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 0); @@ -674,7 +702,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // Issuer (alice) must have a trust line for the offered funds. - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecNO_LINE)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecNO_LINE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -684,7 +714,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Issuer (alice) must have a trust line for the offered funds and // the trust line may not be frozen. - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecFROZEN)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecFROZEN)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -703,7 +735,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(trust(gw, buyer["AUD"](999), tfSetFreeze)); env.close(); - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecFROZEN)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecFROZEN)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -713,7 +747,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(trust(buyer, gwAUD(1000))); env.close(); - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecUNFUNDED_OFFER)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecUNFUNDED_OFFER)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); // the trust line. @@ -726,7 +762,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // However buyer doesn't have enough XRP to cover the reserve for // an NFT offer. - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecINSUFFICIENT_RESERVE)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecINSUFFICIENT_RESERVE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -735,7 +773,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(pay(env.master, buyer, XRP(50) + drops(baseFee * 12 - 1))); env.close(); - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tecINSUFFICIENT_RESERVE)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tecINSUFFICIENT_RESERVE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -745,7 +785,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // We don't care whether the offer is fully funded until the offer is // accepted. Success at last! - env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), token::owner(alice), ter(tesSUCCESS)); + env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)), + token::owner(alice), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 2); } @@ -782,12 +824,16 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // preflight // Set a negative fee. - env(token::cancelOffer(buyer, {buyerOfferIndex}), fee(STAmount(10ull, true)), ter(temBAD_FEE)); + env(token::cancelOffer(buyer, {buyerOfferIndex}), + fee(STAmount(10ull, true)), + ter(temBAD_FEE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); // Set an invalid flag. - env(token::cancelOffer(buyer, {buyerOfferIndex}), txflags(0x00008000), ter(temINVALID_FLAG)); + env(token::cancelOffer(buyer, {buyerOfferIndex}), + txflags(0x00008000), + ter(temINVALID_FLAG)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -941,12 +987,16 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // preflight // Set a negative fee. - env(token::acceptSellOffer(buyer, noXferOfferIndex), fee(STAmount(10ull, true)), ter(temBAD_FEE)); + env(token::acceptSellOffer(buyer, noXferOfferIndex), + fee(STAmount(10ull, true)), + ter(temBAD_FEE)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); // Set an invalid flag. - env(token::acceptSellOffer(buyer, noXferOfferIndex), txflags(0x00008000), ter(temINVALID_FLAG)); + env(token::acceptSellOffer(buyer, noXferOfferIndex), + txflags(0x00008000), + ter(temINVALID_FLAG)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); @@ -1062,18 +1112,21 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); // gw attempts to broker offers that are not for the same token. - env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH)); + env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex), + ter(tecNFTOKEN_BUY_SELL_MISMATCH)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); // gw attempts to broker offers that are not for the same currency. - env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH)); + env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex), + ter(tecNFTOKEN_BUY_SELL_MISMATCH)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); // In a brokered offer, the buyer must offer greater than or // equal to the selling price. - env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex), ter(tecINSUFFICIENT_PAYMENT)); + env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex), + ter(tecINSUFFICIENT_PAYMENT)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); @@ -1136,7 +1189,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == aliceCount); // An account can't accept its own offer. - env(token::acceptBuyOffer(buyer, buyerOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER)); + env(token::acceptBuyOffer(buyer, buyerOfferIndex), + ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); @@ -1182,12 +1236,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); // Don't accept a sell offer without the sell flag set. - env(token::acceptSellOffer(alice, buyerOfferIndex), ter(tecNFTOKEN_OFFER_TYPE_MISMATCH)); + env(token::acceptSellOffer(alice, buyerOfferIndex), + ter(tecNFTOKEN_OFFER_TYPE_MISMATCH)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == aliceCount); // An account can't accept its own offer. - env(token::acceptSellOffer(alice, plainOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER)); + env(token::acceptSellOffer(alice, plainOfferIndex), + ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == buyerCount); @@ -1383,17 +1439,22 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Set flagOnlyXRP and offers using IOUs are rejected. { - uint256 const nftOnlyXrpID{token::getNextID(env, alice, 0u, tfOnlyXRP | tfTransferable)}; + uint256 const nftOnlyXrpID{ + token::getNextID(env, alice, 0u, tfOnlyXRP | tfTransferable)}; env(token::mint(alice, 0u), txflags(tfOnlyXRP | tfTransferable)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); - env(token::createOffer(alice, nftOnlyXrpID, gwAUD(50)), txflags(tfSellNFToken), ter(temBAD_AMOUNT)); + env(token::createOffer(alice, nftOnlyXrpID, gwAUD(50)), + txflags(tfSellNFToken), + ter(temBAD_AMOUNT)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(ownerCount(env, buyer) == 1); - env(token::createOffer(buyer, nftOnlyXrpID, gwAUD(50)), token::owner(alice), ter(temBAD_AMOUNT)); + env(token::createOffer(buyer, nftOnlyXrpID, gwAUD(50)), + token::owner(alice), + ter(temBAD_AMOUNT)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 1); @@ -1455,7 +1516,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // fee will not allow creating offers that use IOUs for payment. for (std::uint32_t xferFee : {0, 1}) { - uint256 const nftNoAutoTrustID{token::getNextID(env, alice, 0u, tfTransferable, xferFee)}; + uint256 const nftNoAutoTrustID{ + token::getNextID(env, alice, 0u, tfTransferable, xferFee)}; env(token::mint(alice, 0u), token::xferFee(xferFee), txflags(tfTransferable)); env.close(); @@ -1476,7 +1538,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // cheri offers to buy the nft for CAD. uint256 const cheriOfferIndex = keylet::nftoffer(cheri, env.seq(cheri)).key; - env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)), token::owner(becky), ter(createOfferTER)); + env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)), + token::owner(becky), + ter(createOfferTER)); env.close(); // To keep things tidy, cancel the offers. @@ -1542,13 +1606,15 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // flagCreateTrustLines will work for preestablished trust lines. { std::uint16_t transferFee = 5000; // 5% - uint256 const nftNoAutoTrustID{token::getNextID(env, alice, 0u, tfTransferable, transferFee)}; + uint256 const nftNoAutoTrustID{ + token::getNextID(env, alice, 0u, tfTransferable, transferFee)}; env(token::mint(alice, 0u), token::xferFee(transferFee), txflags(tfTransferable)); env.close(); // alice sells the nft using AUD. uint256 const aliceSellOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)), + txflags(tfSellNFToken)); env.close(); env(token::acceptSellOffer(cheri, aliceSellOfferIndex)); env.close(); @@ -1559,10 +1625,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210)); // cheri can't sell the NFT for EUR, but can for CAD. - env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)), txflags(tfSellNFToken), ter(tecNO_LINE)); + env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)), + txflags(tfSellNFToken), + ter(tecNO_LINE)); env.close(); uint256 const cheriSellOfferIndex = keylet::nftoffer(cheri, env.seq(cheri)).key; - env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)), txflags(tfSellNFToken)); + env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)), + txflags(tfSellNFToken)); env.close(); env(token::acceptSellOffer(becky, cheriSellOfferIndex)); env.close(); @@ -2106,9 +2175,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite for (auto NumberSwitchOver : {true}) { if (NumberSwitchOver) + { env.enableFeature(fixUniversalNumber); + } else + { env.disableFeature(fixUniversalNumber); + } // An nft with a transfer fee of 1 basis point. uint256 const nftID = token::getNextID(env, alice, 0u, tfTransferable, 1); @@ -2179,7 +2252,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(pay(becky, gw, env.balance(becky, gwXAU))); env.close(); - STAmount const startXAUBalance(gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5); + STAmount const startXAUBalance( + gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5); env(pay(gw, alice, startXAUBalance)); env(pay(gw, minter, startXAUBalance)); env(pay(gw, becky, startXAUBalance)); @@ -2281,22 +2355,34 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto check = [this](std::uint32_t taxon, uint256 const& nftID) { nft::Taxon const gotTaxon = nft::getTaxon(nftID); if (nft::toTaxon(taxon) == gotTaxon) + { pass(); + } else { std::stringstream ss; - ss << "Taxon recovery failed from nftID " << to_string(nftID) << ". Expected: " << taxon - << "; got: " << gotTaxon; + ss << "Taxon recovery failed from nftID " << to_string(nftID) + << ". Expected: " << taxon << "; got: " << gotTaxon; fail(ss.str()); } }; uint256 const nftAliceID = token::getID( - env, alice, taxon, rand_int(), rand_int(), rand_int()); + env, + alice, + taxon, + rand_int(), + rand_int(), + rand_int()); check(taxon, nftAliceID); uint256 const nftBeckyID = token::getID( - env, becky, taxon, rand_int(), rand_int(), rand_int()); + env, + becky, + taxon, + rand_int(), + rand_int(), + rand_int()); check(taxon, nftBeckyID); } } @@ -2388,9 +2474,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite sortedNFTs.reserve(nfts.size()); for (std::size_t i = 0; i < nfts.size(); ++i) sortedNFTs.push_back(nfts[i]); - std::sort(sortedNFTs.begin(), sortedNFTs.end(), [](Json::Value const& lhs, Json::Value const& rhs) { - return lhs[jss::nft_serial] < rhs[jss::nft_serial]; - }); + std::sort( + sortedNFTs.begin(), + sortedNFTs.end(), + [](Json::Value const& lhs, Json::Value const& rhs) { + return lhs[jss::nft_serial] < rhs[jss::nft_serial]; + }); for (std::size_t i = 0; i < entries.size(); ++i) { @@ -2411,7 +2500,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite void testCreateOfferDestination(FeatureBitset features) { - // Explore the CreateOffer Destination field. + // Explore the OfferCreate Destination field. testcase("Create offer destination"); using namespace test::jtx; @@ -2438,16 +2527,24 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // for canceling offers. { uint256 const offerMinterToIssuer = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(issuer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(issuer), + txflags(tfSellNFToken)); uint256 const offerMinterToBuyer = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); uint256 const offerIssuerToMinter = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftokenID, drops(1)), token::owner(minter), token::destination(minter)); + env(token::createOffer(issuer, nftokenID, drops(1)), + token::owner(minter), + token::destination(minter)); uint256 const offerIssuerToBuyer = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftokenID, drops(1)), token::owner(minter), token::destination(buyer)); + env(token::createOffer(issuer, nftokenID, drops(1)), + token::owner(minter), + token::destination(buyer)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 2); @@ -2486,7 +2583,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // accepting that offer. { uint256 const offerMinterSellsToBuyer = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, minter) == 2); @@ -2512,7 +2611,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // accepting that offer. { uint256 const offerMinterBuysFromBuyer = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::owner(buyer), token::destination(buyer)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::owner(buyer), + token::destination(buyer)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, minter) == 1); @@ -2537,7 +2638,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // destination must act as a broker. The NFToken owner may not // simply accept the offer. uint256 const offerBuyerBuysFromMinter = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID, drops(1)), token::owner(minter), token::destination(broker)); + env(token::createOffer(buyer, nftokenID, drops(1)), + token::owner(minter), + token::destination(broker)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, minter) == 1); @@ -2558,7 +2661,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // to another account. { uint256 const offerMinterToBroker = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(broker), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(broker), + txflags(tfSellNFToken)); uint256 const offerBuyerToMinter = keylet::nftoffer(buyer, env.seq(buyer)).key; env(token::createOffer(buyer, nftokenID, drops(1)), token::owner(minter)); @@ -2571,7 +2676,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { // issuer cannot broker the offers, because they are not the // Destination. - env(token::brokerOffers(issuer, offerBuyerToMinter, offerMinterToBroker), ter(tecNO_PERMISSION)); + env(token::brokerOffers(issuer, offerBuyerToMinter, offerMinterToBroker), + ter(tecNO_PERMISSION)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, minter) == 2); @@ -2592,7 +2698,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // does match. { uint256 const offerBuyerToMinter = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID, drops(1)), token::destination(minter), txflags(tfSellNFToken)); + env(token::createOffer(buyer, nftokenID, drops(1)), + token::destination(minter), + txflags(tfSellNFToken)); uint256 const offerMinterToBuyer = keylet::nftoffer(minter, env.seq(minter)).key; env(token::createOffer(minter, nftokenID, drops(1)), token::owner(buyer)); @@ -2608,14 +2716,16 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { // Cannot broker offers when the sell destination is not the // buyer. - env(token::brokerOffers(broker, offerIssuerToBuyer, offerBuyerToMinter), ter(tecNO_PERMISSION)); + env(token::brokerOffers(broker, offerIssuerToBuyer, offerBuyerToMinter), + ter(tecNO_PERMISSION)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); BEAST_EXPECT(ownerCount(env, minter) == 1); BEAST_EXPECT(ownerCount(env, buyer) == 2); - env(token::brokerOffers(broker, offerMinterToBuyer, offerBuyerToMinter), ter(tecNO_PERMISSION)); + env(token::brokerOffers(broker, offerMinterToBuyer, offerBuyerToMinter), + ter(tecNO_PERMISSION)); env.close(); // Buyer is successful with acceptOffer. @@ -2644,15 +2754,20 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // then that destination can broker the offers. { uint256 const offerMinterToBroker = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(broker), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(broker), + txflags(tfSellNFToken)); uint256 const offerBuyerToBroker = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID, drops(1)), token::owner(minter), token::destination(broker)); + env(token::createOffer(buyer, nftokenID, drops(1)), + token::owner(minter), + token::destination(broker)); { // Cannot broker offers when the sell destination is not the // buyer or the broker. - env(token::brokerOffers(issuer, offerBuyerToBroker, offerMinterToBroker), ter(tecNO_PERMISSION)); + env(token::brokerOffers(issuer, offerBuyerToBroker, offerMinterToBroker), + ter(tecNO_PERMISSION)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, minter) == 2); @@ -2715,7 +2830,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { uint256 const offerIndex = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); env.close(); env(token::cancelOffer(minter, {offerIndex})); @@ -2726,7 +2843,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { uint256 const offerIndex = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); env.close(); env(fset(buyer, asfDisallowIncomingNFTokenOffer)); @@ -2743,7 +2862,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { uint256 const offerIndex = keylet::nftoffer(minter, env.seq(minter)).key; - env(token::createOffer(minter, nftokenID, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); env.close(); env(token::acceptSellOffer(buyer, offerIndex)); @@ -2758,13 +2879,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // a random offer to buy the token { - env(token::createOffer(alice, nftokenID, drops(1)), token::owner(buyer), ter(tecNO_PERMISSION)); + env(token::createOffer(alice, nftokenID, drops(1)), + token::owner(buyer), + ter(tecNO_PERMISSION)); env.close(); } // minter offer to buy the token { - env(token::createOffer(minter, nftokenID, drops(1)), token::owner(buyer), ter(tecNO_PERMISSION)); + env(token::createOffer(minter, nftokenID, drops(1)), + token::owner(buyer), + ter(tecNO_PERMISSION)); env.close(); } @@ -2774,7 +2899,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // enable flag env(fset(buyer, asfDisallowIncomingNFTokenOffer)); // a sell offer from the minter to the buyer should be rejected - env(token::mint(minter), token::amount(drops(1)), token::destination(buyer), ter(tecNO_PERMISSION)); + env(token::mint(minter), + token::amount(drops(1)), + token::destination(buyer), + ter(tecNO_PERMISSION)); env.close(); // disable flag @@ -2787,7 +2915,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite void testCreateOfferExpiration(FeatureBitset features) { - // Explore the CreateOffer Expiration field. + // Explore the OfferCreate Expiration field. testcase("Create offer expiration"); using namespace test::jtx; @@ -2812,7 +2940,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite uint256 const nftokenID1 = token::getNextID(env, issuer, 0, tfTransferable); env(token::mint(minter, 0), token::issuer(issuer), txflags(tfTransferable)); env.close(); - uint8_t issuerCount, minterCount, buyerCount; + uint8_t issuerCount = 0, minterCount = 0, buyerCount = 0; // Test how adding an Expiration field to an offer affects permissions // for cancelling offers. @@ -2831,10 +2959,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite txflags(tfSellNFToken)); uint256 const offerIssuerToMinter = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration)); + env(token::createOffer(issuer, nftokenID0, drops(1)), + token::owner(minter), + token::expiration(expiration)); uint256 const offerBuyerToMinter = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration)); + env(token::createOffer(buyer, nftokenID0, drops(1)), + token::owner(minter), + token::expiration(expiration)); env.close(); issuerCount = 1; minterCount = 3; @@ -2963,7 +3095,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Transfer nftokenID0 back to minter so we start the next test in // a simple place. uint256 const offerSellBack = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter)); + env(token::createOffer(buyer, nftokenID0, XRP(0)), + txflags(tfSellNFToken), + token::destination(minter)); env.close(); env(token::acceptSellOffer(minter, offerSellBack)); buyerCount--; @@ -2980,11 +3114,15 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite std::uint32_t const expiration = lastClose(env) + 25; uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration)); + env(token::createOffer(buyer, nftokenID0, drops(1)), + token::owner(minter), + token::expiration(expiration)); buyerCount++; uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID1, drops(1)), token::owner(minter), token::expiration(expiration)); + env(token::createOffer(buyer, nftokenID1, drops(1)), + token::owner(minter), + token::expiration(expiration)); buyerCount++; env.close(); BEAST_EXPECT(lastClose(env) < expiration); @@ -3045,7 +3183,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Transfer nftokenID0 back to minter so we start the next test in // a simple place. uint256 const offerSellBack = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter)); + env(token::createOffer(buyer, nftokenID0, XRP(0)), + txflags(tfSellNFToken), + token::destination(minter)); env.close(); env(token::acceptSellOffer(minter, offerSellBack)); env.close(); @@ -3137,7 +3277,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Transfer nftokenID0 back to minter so we start the next test in // a simple place. uint256 const offerSellBack = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter)); + env(token::createOffer(buyer, nftokenID0, XRP(0)), + txflags(tfSellNFToken), + token::destination(minter)); env.close(); env(token::acceptSellOffer(minter, offerSellBack)); env.close(); @@ -3160,10 +3302,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(token::createOffer(minter, nftokenID1, drops(1)), txflags(tfSellNFToken)); uint256 const buyOffer0 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, drops(1)), token::expiration(expiration), token::owner(minter)); + env(token::createOffer(buyer, nftokenID0, drops(1)), + token::expiration(expiration), + token::owner(minter)); uint256 const buyOffer1 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID1, drops(1)), token::expiration(expiration), token::owner(minter)); + env(token::createOffer(buyer, nftokenID1, drops(1)), + token::expiration(expiration), + token::owner(minter)); env.close(); BEAST_EXPECT(lastClose(env) < expiration); @@ -3212,7 +3358,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Transfer nftokenID0 back to minter so we start the next test in // a simple place. uint256 const offerSellBack = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter)); + env(token::createOffer(buyer, nftokenID0, XRP(0)), + txflags(tfSellNFToken), + token::destination(minter)); env.close(); env(token::acceptSellOffer(minter, offerSellBack)); env.close(); @@ -3239,10 +3387,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite txflags(tfSellNFToken)); uint256 const buyOffer0 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, drops(1)), token::expiration(expiration), token::owner(minter)); + env(token::createOffer(buyer, nftokenID0, drops(1)), + token::expiration(expiration), + token::owner(minter)); uint256 const buyOffer1 = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID1, drops(1)), token::expiration(expiration), token::owner(minter)); + env(token::createOffer(buyer, nftokenID1, drops(1)), + token::expiration(expiration), + token::owner(minter)); env.close(); BEAST_EXPECT(lastClose(env) < expiration); @@ -3282,7 +3434,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Transfer nftokenID0 back to minter so we start the next test in // a simple place. uint256 const offerSellBack = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter)); + env(token::createOffer(buyer, nftokenID0, XRP(0)), + txflags(tfSellNFToken), + token::destination(minter)); env.close(); env(token::acceptSellOffer(minter, offerSellBack)); env.close(); @@ -3341,7 +3495,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // should be cancellable by the creator and the destination. uint256 const dest1OfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftokenID, XRP(1000)), token::destination(becky), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftokenID, XRP(1000)), + token::destination(becky), + txflags(tfSellNFToken)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3357,7 +3513,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // alice can cancel her own offer, even if becky is the destination. uint256 const dest2OfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftokenID, XRP(1000)), token::destination(becky), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftokenID, XRP(1000)), + token::destination(becky), + txflags(tfSellNFToken)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3538,13 +3696,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Lambda to check owner count of all accounts is one. auto checkOwnerCountIsOne = - [this, &env](std::initializer_list> accounts, int line) { + [this, &env]( + std::initializer_list> accounts, + int line) { for (Account const& acct : accounts) { - if (std::uint32_t ownerCount = test::jtx::ownerCount(env, acct); ownerCount != 1) + if (std::uint32_t ownerCount = test::jtx::ownerCount(env, acct); + ownerCount != 1) { std::stringstream ss; - ss << "Account " << acct.human() << " expected ownerCount == 1. Got " << ownerCount; + ss << "Account " << acct.human() << " expected ownerCount == 1. Got " + << ownerCount; fail(ss.str(), __FILE__, line); } } @@ -3553,7 +3715,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Lambda that mints an NFT and returns the nftID. auto mintNFT = [&env, &issuer, &minter](std::uint16_t xferFee = 0) { uint256 const nftID = token::getNextID(env, issuer, 0, tfTransferable, xferFee); - env(token::mint(minter, 0), token::issuer(issuer), token::xferFee(xferFee), txflags(tfTransferable)); + env(token::mint(minter, 0), + token::issuer(issuer), + token::xferFee(xferFee), + txflags(tfTransferable)); env.close(); return nftID; }; @@ -3633,7 +3798,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto const issuerBalance = env.balance(issuer); // Broker charges a 0.5 XRP brokerFee. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), token::brokerFee(XRP(0.5))); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + token::brokerFee(XRP(0.5))); env.close(); // Note that minter's XRP balance goes up even though they @@ -3717,7 +3883,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto const issuerBalance = env.balance(issuer); // Broker charges a 0.75 XRP brokerFee. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), token::brokerFee(XRP(0.75))); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + token::brokerFee(XRP(0.75))); env.close(); // Note that, with a 50% transfer fee, issuer gets 1/2 of what's @@ -3737,7 +3904,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // gwXAU(amount). auto setXAUBalance = [this, &gw, &gwXAU, &env]( - std::initializer_list> accounts, int amount, int line) { + std::initializer_list> accounts, + int amount, + int line) { for (Account const& acct : accounts) { auto const xauAmt = gwXAU(amount); @@ -3755,7 +3924,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite if (env.balance(acct, gwXAU) != xauAmt) { std::stringstream ss; - ss << "Unable to set " << acct.human() << " account balance to gwXAU(" << amount << ")"; + ss << "Unable to set " << acct.human() << " account balance to gwXAU(" + << amount << ")"; this->fail(ss.str(), __FILE__, line); } } @@ -3782,7 +3952,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // broker attempts to broker the offers but cannot. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), ter(tecINSUFFICIENT_FUNDS)); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + ter(tecINSUFFICIENT_FUNDS)); env.close(); // Cancel buyer's bad offer so the next test starts in a @@ -3798,7 +3969,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // broker attempts to broker the offers but cannot. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), ter(tecINSUFFICIENT_PAYMENT)); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + ter(tecINSUFFICIENT_PAYMENT)); env.close(); // Cancel buyer's bad offer so the next test starts in a @@ -3856,7 +4028,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // broker attempts to broker the offers but cannot. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), ter(tecINSUFFICIENT_FUNDS)); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + ter(tecINSUFFICIENT_FUNDS)); env.close(); // Cancel buyer's bad offer so the next test starts in a @@ -3872,7 +4045,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // broker attempts to broker the offers but cannot. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), ter(tecINSUFFICIENT_PAYMENT)); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + ter(tecINSUFFICIENT_PAYMENT)); env.close(); // Cancel buyer's bad offer so the next test starts in a @@ -3894,7 +4068,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // broker charges the full difference between the two offers and // succeeds. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), token::brokerFee(gwXAU(100))); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + token::brokerFee(gwXAU(100))); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); @@ -3932,7 +4107,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // broker charges half difference between the two offers and // succeeds. 25% of the remaining difference goes to issuer. // The rest goes to minter. - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), token::brokerFee(gwXAU(50))); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + token::brokerFee(gwXAU(50))); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); @@ -3965,7 +4141,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(token::createOffer(buyer, nftID, gwXAU(1000)), token::owner(minter)); env.close(); - env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), token::brokerFee(gwXAU(50))); + env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex), + token::brokerFee(gwXAU(50))); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); BEAST_EXPECT(ownerCount(env, minter) == 1); @@ -4101,7 +4278,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // NFTokenCreateOffer BEAST_EXPECT(ownerCount(env, buyer) == 10); uint256 const offerIndex0 = keylet::nftoffer(buyer, buyerTicketSeq).key; - env(token::createOffer(buyer, nftId, XRP(1)), token::owner(issuer), ticket::use(buyerTicketSeq++)); + env(token::createOffer(buyer, nftId, XRP(1)), + token::owner(issuer), + ticket::use(buyerTicketSeq++)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 10); BEAST_EXPECT(ticketCount(env, buyer) == 9); @@ -4114,7 +4293,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // NFTokenCreateOffer. buyer tries again. uint256 const offerIndex1 = keylet::nftoffer(buyer, buyerTicketSeq).key; - env(token::createOffer(buyer, nftId, XRP(2)), token::owner(issuer), ticket::use(buyerTicketSeq++)); + env(token::createOffer(buyer, nftId, XRP(2)), + token::owner(issuer), + ticket::use(buyerTicketSeq++)); env.close(); BEAST_EXPECT(ownerCount(env, buyer) == 8); BEAST_EXPECT(ticketCount(env, buyer) == 7); @@ -4257,7 +4438,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // A lambda that validates nft_XXX_offers query responses. - auto checkOffers = [this, &env, &nftID](char const* request, int expectCount, int expectMarkerCount, int line) { + auto checkOffers = [this, &env, &nftID]( + char const* request, + int expectCount, + int expectMarkerCount, + int line) { int markerCount = 0; Json::Value allOffers(Json::arrayValue); std::string marker; @@ -4277,9 +4462,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // If there are no offers for the NFT we get an error if (expectCount == 0) { - if (expect(nftOffers.isMember(jss::result), "expected \"result\"", __FILE__, line)) + if (expect( + nftOffers.isMember(jss::result), "expected \"result\"", __FILE__, line)) { - if (expect(nftOffers[jss::result].isMember(jss::error), "expected \"error\"", __FILE__, line)) + if (expect( + nftOffers[jss::result].isMember(jss::error), + "expected \"error\"", + __FILE__, + line)) { expect( nftOffers[jss::result][jss::error].asString() == "objectNotFound", @@ -4312,7 +4502,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite } while (!marker.empty()); // Verify the contents of allOffers makes sense. - expect(allOffers.size() == expectCount, "Unexpected returned offer count", __FILE__, line); + expect( + allOffers.size() == expectCount, "Unexpected returned offer count", __FILE__, line); expect(markerCount == expectMarkerCount, "Unexpected marker count", __FILE__, line); std::optional globalFlags; std::set offerIndexes; @@ -4323,7 +4514,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite if (!globalFlags) globalFlags = offer[jss::flags].asInt(); - expect(*globalFlags == offer[jss::flags].asInt(), "Inconsistent flags returned", __FILE__, line); + expect( + *globalFlags == offer[jss::flags].asInt(), + "Inconsistent flags returned", + __FILE__, + line); // The test conditions should produce unique indexes and // amounts for all offers. @@ -4331,7 +4526,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite amounts.insert(offer[jss::amount].asString()); } - expect(offerIndexes.size() == expectCount, "Duplicate indexes returned?", __FILE__, line); + expect( + offerIndexes.size() == expectCount, "Duplicate indexes returned?", __FILE__, line); expect(amounts.size() == expectCount, "Duplicate amounts returned?", __FILE__, line); }; @@ -4452,19 +4648,27 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Make offers with negative amounts for the NFTs uint256 const sellNegXrpOfferIndex = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftID0, XRP(-2)), txflags(tfSellNFToken), ter(offerCreateTER)); + env(token::createOffer(issuer, nftID0, XRP(-2)), + txflags(tfSellNFToken), + ter(offerCreateTER)); env.close(); uint256 const sellNegIouOfferIndex = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftID1, gwXAU(-2)), txflags(tfSellNFToken), ter(offerCreateTER)); + env(token::createOffer(issuer, nftID1, gwXAU(-2)), + txflags(tfSellNFToken), + ter(offerCreateTER)); env.close(); uint256 const buyNegXrpOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftID0, XRP(-1)), token::owner(issuer), ter(offerCreateTER)); + env(token::createOffer(buyer, nftID0, XRP(-1)), + token::owner(issuer), + ter(offerCreateTER)); env.close(); uint256 const buyNegIouOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key; - env(token::createOffer(buyer, nftID1, gwXAU(-1)), token::owner(issuer), ter(offerCreateTER)); + env(token::createOffer(buyer, nftID1, gwXAU(-1)), + token::owner(issuer), + ter(offerCreateTER)); env.close(); { @@ -4487,9 +4691,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite TER const offerAcceptTER = tecOBJECT_NOT_FOUND; // Brokered offers. - env(token::brokerOffers(gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex), ter(offerAcceptTER)); + env(token::brokerOffers(gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex), + ter(offerAcceptTER)); env.close(); - env(token::brokerOffers(gw, buyNegIouOfferIndex, sellNegIouOfferIndex), ter(offerAcceptTER)); + env(token::brokerOffers(gw, buyNegIouOfferIndex, sellNegIouOfferIndex), + ter(offerAcceptTER)); env.close(); } } @@ -4547,57 +4753,68 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(rate(gw, 1.02)); env.close(); - auto expectInitialState = [this, &env, &buyer, &minter, &secondarySeller, &broker, &gw, &gwXAU, &gwXPB]() { - // Buyer should have XAU 1000, XPB 0 - // Minter should have XAU 0, XPB 0 - // Secondary seller should have XAU 0, XPB 0 - // Broker should have XAU 5000, XPB 0 - BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000)); - BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0)); - BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0)); - BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0)); - BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0)); - BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0)); - BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000)); - BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0)); - BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-1000)); - BEAST_EXPECT(env.balance(gw, buyer["XPB"]) == gwXPB(0)); - BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(0)); - BEAST_EXPECT(env.balance(gw, minter["XPB"]) == gwXPB(0)); - BEAST_EXPECT(env.balance(gw, secondarySeller["XAU"]) == gwXAU(0)); - BEAST_EXPECT(env.balance(gw, secondarySeller["XPB"]) == gwXPB(0)); - BEAST_EXPECT(env.balance(gw, broker["XAU"]) == gwXAU(-5000)); - BEAST_EXPECT(env.balance(gw, broker["XPB"]) == gwXPB(0)); - }; - - auto reinitializeTrustLineBalances = - [&expectInitialState, &env, &buyer, &minter, &secondarySeller, &broker, &gw, &gwXAU, &gwXPB]() { - if (auto const difference = gwXAU(1000) - env.balance(buyer, gwXAU); difference > gwXAU(0)) - env(pay(gw, buyer, difference)); - if (env.balance(buyer, gwXPB) > gwXPB(0)) - env(pay(buyer, gw, env.balance(buyer, gwXPB))); - if (env.balance(minter, gwXAU) > gwXAU(0)) - env(pay(minter, gw, env.balance(minter, gwXAU))); - if (env.balance(minter, gwXPB) > gwXPB(0)) - env(pay(minter, gw, env.balance(minter, gwXPB))); - if (env.balance(secondarySeller, gwXAU) > gwXAU(0)) - env(pay(secondarySeller, gw, env.balance(secondarySeller, gwXAU))); - if (env.balance(secondarySeller, gwXPB) > gwXPB(0)) - env(pay(secondarySeller, gw, env.balance(secondarySeller, gwXPB))); - auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU); - if (brokerDiff > gwXAU(0)) - env(pay(gw, broker, brokerDiff)); - else if (brokerDiff < gwXAU(0)) - { - brokerDiff.negate(); - env(pay(broker, gw, brokerDiff)); - } - if (env.balance(broker, gwXPB) > gwXPB(0)) - env(pay(broker, gw, env.balance(broker, gwXPB))); - env.close(); - expectInitialState(); + auto expectInitialState = + [this, &env, &buyer, &minter, &secondarySeller, &broker, &gw, &gwXAU, &gwXPB]() { + // Buyer should have XAU 1000, XPB 0 + // Minter should have XAU 0, XPB 0 + // Secondary seller should have XAU 0, XPB 0 + // Broker should have XAU 5000, XPB 0 + BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000)); + BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0)); + BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0)); + BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0)); + BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0)); + BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0)); + BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000)); + BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0)); + BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-1000)); + BEAST_EXPECT(env.balance(gw, buyer["XPB"]) == gwXPB(0)); + BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(0)); + BEAST_EXPECT(env.balance(gw, minter["XPB"]) == gwXPB(0)); + BEAST_EXPECT(env.balance(gw, secondarySeller["XAU"]) == gwXAU(0)); + BEAST_EXPECT(env.balance(gw, secondarySeller["XPB"]) == gwXPB(0)); + BEAST_EXPECT(env.balance(gw, broker["XAU"]) == gwXAU(-5000)); + BEAST_EXPECT(env.balance(gw, broker["XPB"]) == gwXPB(0)); }; + auto reinitializeTrustLineBalances = [&expectInitialState, + &env, + &buyer, + &minter, + &secondarySeller, + &broker, + &gw, + &gwXAU, + &gwXPB]() { + if (auto const difference = gwXAU(1000) - env.balance(buyer, gwXAU); + difference > gwXAU(0)) + env(pay(gw, buyer, difference)); + if (env.balance(buyer, gwXPB) > gwXPB(0)) + env(pay(buyer, gw, env.balance(buyer, gwXPB))); + if (env.balance(minter, gwXAU) > gwXAU(0)) + env(pay(minter, gw, env.balance(minter, gwXAU))); + if (env.balance(minter, gwXPB) > gwXPB(0)) + env(pay(minter, gw, env.balance(minter, gwXPB))); + if (env.balance(secondarySeller, gwXAU) > gwXAU(0)) + env(pay(secondarySeller, gw, env.balance(secondarySeller, gwXAU))); + if (env.balance(secondarySeller, gwXPB) > gwXPB(0)) + env(pay(secondarySeller, gw, env.balance(secondarySeller, gwXPB))); + auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU); + if (brokerDiff > gwXAU(0)) + { + env(pay(gw, broker, brokerDiff)); + } + else if (brokerDiff < gwXAU(0)) + { + brokerDiff.negate(); + env(pay(broker, gw, brokerDiff)); + } + if (env.balance(broker, gwXPB) > gwXPB(0)) + env(pay(broker, gw, env.balance(broker, gwXPB))); + env.close(); + expectInitialState(); + }; + auto mintNFT = [&env](Account const& minter, int transferFee = 0) { uint256 const nftID = token::getNextID(env, minter, 0, tfTransferable, transferFee); env(token::mint(minter), token::xferFee(transferFee), txflags(tfTransferable)); @@ -4847,7 +5064,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite reinitializeTrustLineBalances(); auto const nftID = mintNFT(gw); auto const offerID = createSellOffer(gw, nftID, gwXAU(2000)); - env(token::acceptSellOffer(buyer, offerID), ter(static_cast(tecINSUFFICIENT_FUNDS))); + env(token::acceptSellOffer(buyer, offerID), + ter(static_cast(tecINSUFFICIENT_FUNDS))); env.close(); expectInitialState(); } @@ -4858,7 +5076,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite reinitializeTrustLineBalances(); auto const nftID = mintNFT(gw); auto const offerID = createBuyOffer(buyer, gw, nftID, gwXAU(2000)); - env(token::acceptBuyOffer(gw, offerID), ter(static_cast(tecINSUFFICIENT_FUNDS))); + env(token::acceptBuyOffer(gw, offerID), + ter(static_cast(tecINSUFFICIENT_FUNDS))); env.close(); expectInitialState(); } @@ -4868,7 +5087,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite reinitializeTrustLineBalances(); auto const nftID = mintNFT(minter); auto const offerID = createSellOffer(minter, nftID, gwXPB(10)); - env(token::acceptSellOffer(buyer, offerID), ter(static_cast(tecINSUFFICIENT_FUNDS))); + env(token::acceptSellOffer(buyer, offerID), + ter(static_cast(tecINSUFFICIENT_FUNDS))); env.close(); expectInitialState(); } @@ -4877,9 +5097,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // have no trust line for and buyer has none of (buyside). reinitializeTrustLineBalances(); auto const nftID = mintNFT(minter); - auto const offerID = - createBuyOffer(buyer, minter, nftID, gwXPB(10), {static_cast(tecUNFUNDED_OFFER)}); - env(token::acceptBuyOffer(minter, offerID), ter(static_cast(tecOBJECT_NOT_FOUND))); + auto const offerID = createBuyOffer( + buyer, minter, nftID, gwXPB(10), {static_cast(tecUNFUNDED_OFFER)}); + env(token::acceptBuyOffer(minter, offerID), + ter(static_cast(tecOBJECT_NOT_FOUND))); env.close(); expectInitialState(); } @@ -5137,7 +5358,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(token::createOffer(bob, nftId, XRP(5)), token::owner(alice)); uint256 const aliceSellOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(0)), token::destination(bob), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftId, XRP(0)), + token::destination(bob), + txflags(tfSellNFToken)); env.close(); // bob accepts alice's offer but forgets to remove the old buy offer. @@ -5197,8 +5420,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // within . auto incLgrSeqForFixNftRemint = [&](Env& env, Account const& acct) { int delta = 0; - auto const deletableLgrSeq = - (*env.le(acct))[~sfFirstNFTokenSequence].value_or(0) + (*env.le(acct))[sfMintedNFTokens] + 255; + auto const deletableLgrSeq = (*env.le(acct))[~sfFirstNFTokenSequence].value_or(0) + + (*env.le(acct))[sfMintedNFTokens] + 255; if (deletableLgrSeq > openLedgerSeq(env)) delta = deletableLgrSeq - openLedgerSeq(env); @@ -5618,32 +5841,47 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite { // The destination may not be the account submitting the // transaction. - env(token::mint(alice), token::amount(XRP(1000)), token::destination(alice), ter(temMALFORMED)); + env(token::mint(alice), + token::amount(XRP(1000)), + token::destination(alice), + ter(temMALFORMED)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); // The destination must be an account already established in the // ledger. - env(token::mint(alice), token::amount(XRP(1000)), token::destination(Account("demon")), ter(tecNO_DST)); + env(token::mint(alice), + token::amount(XRP(1000)), + token::destination(Account("demon")), + ter(tecNO_DST)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); } { // Set a bad expiration. - env(token::mint(alice), token::amount(XRP(1000)), token::expiration(0), ter(temBAD_EXPIRATION)); + env(token::mint(alice), + token::amount(XRP(1000)), + token::expiration(0), + ter(temBAD_EXPIRATION)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); // The new NFTokenOffer may not have passed its expiration time. - env(token::mint(alice), token::amount(XRP(1000)), token::expiration(lastClose(env)), ter(tecEXPIRED)); + env(token::mint(alice), + token::amount(XRP(1000)), + token::expiration(lastClose(env)), + ter(tecEXPIRED)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); } { // Set an invalid amount. - env(token::mint(alice), token::amount(buyer["USD"](1)), txflags(tfOnlyXRP), ter(temBAD_AMOUNT)); + env(token::mint(alice), + token::amount(buyer["USD"](1)), + txflags(tfOnlyXRP), + ter(temBAD_AMOUNT)); env(token::mint(alice), token::amount(buyer["USD"](0)), ter(temBAD_AMOUNT)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -5660,7 +5898,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // If the IOU issuer and the NFToken issuer are the same, // then that issuer does not need a trust line to accept their // fee. - env(token::mint(gw), token::amount(gwAUD(1000)), txflags(tfTransferable), token::xferFee(10)); + env(token::mint(gw), + token::amount(gwAUD(1000)), + txflags(tfTransferable), + token::xferFee(10)); env.close(); // Give alice the needed trust line, but freeze it. @@ -5945,7 +6186,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Alice creates sell offer and set broker as destination uint256 const offerAliceToBroker = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, drops(1)), token::destination(broker), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftId, drops(1)), + token::destination(broker), + txflags(tfSellNFToken)); env.close(); verifyNFTokenOfferID(offerAliceToBroker); @@ -6267,7 +6510,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Alice creates sell offer and set broker as destination uint256 const offerAliceToBroker = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(1)), token::destination(broker), txflags(tfSellNFToken)); + env(token::createOffer(alice, nftId, XRP(1)), + token::destination(broker), + txflags(tfSellNFToken)); env.close(); // Bob creates buy offer @@ -6279,7 +6524,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Returns insufficient funds, because bob burnt tx fee when he // created his buy offer, which makes his spendable balance to be // less than the required amount. - env(token::brokerOffers(broker, offerBobToBroker, offerAliceToBroker), ter(tecINSUFFICIENT_FUNDS)); + env(token::brokerOffers(broker, offerBobToBroker, offerAliceToBroker), + ter(tecINSUFFICIENT_FUNDS)); env.close(); // send Bob `base fee` drops @@ -6334,7 +6580,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Otherwise we can't create NFTokens with tfTrustLine enabled. FeatureBitset const localFeatures = features - fixRemoveNFTokenAutoTrustLine; for (FeatureBitset feats : - {localFeatures - fixEnforceNFTokenTrustline, localFeatures | fixEnforceNFTokenTrustline}) + {localFeatures - fixEnforceNFTokenTrustline, + localFeatures | fixEnforceNFTokenTrustline}) { Env env{*this, feats}; env.fund(XRP(1000), issuer, becky, cheri, gw); @@ -6349,11 +6596,15 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // issuer creates two NFTs: one with and one without AutoTrustLine. std::uint16_t xferFee = 5000; // 5% - uint256 const nftAutoTrustID{token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; - env(token::mint(issuer, 0u), token::xferFee(xferFee), txflags(tfTransferable | tfTrustLine)); + uint256 const nftAutoTrustID{ + token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; + env(token::mint(issuer, 0u), + token::xferFee(xferFee), + txflags(tfTransferable | tfTrustLine)); env.close(); - uint256 const nftNoAutoTrustID{token::getNextID(env, issuer, 0u, tfTransferable, xferFee)}; + uint256 const nftNoAutoTrustID{ + token::getNextID(env, issuer, 0u, tfTransferable, xferFee)}; env(token::mint(issuer, 0u), token::xferFee(xferFee), txflags(tfTransferable)); env.close(); @@ -6378,7 +6629,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Creating an offer for the NFToken without tfTrustLine fails // because issuer does not have a trust line for AUD. - env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)), txflags(tfSellNFToken), ter(tecNO_LINE)); + env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)), + txflags(tfSellNFToken), + ter(tecNO_LINE)); env.close(); // issuer creates a trust line. Now the offer create for the @@ -6499,8 +6752,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // issuer creates two NFTs: one with and one without AutoTrustLine. std::uint16_t xferFee = 5000; // 5% - uint256 const nftAutoTrustID{token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; - env(token::mint(issuer, 0u), token::xferFee(xferFee), txflags(tfTransferable | tfTrustLine)); + uint256 const nftAutoTrustID{ + token::getNextID(env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)}; + env(token::mint(issuer, 0u), + token::xferFee(xferFee), + txflags(tfTransferable | tfTrustLine)); env.close(); uint256 const nftNoAutoTrustID{token::getNextID(env, issuer, 0u, tfTransferable, xferFee)}; @@ -6528,7 +6784,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite // Without featureNFTokenMintOffer becky simply can't // create an offer for a non-tfTrustLine NFToken that would // pay the transfer fee in issuer's own IOU. - env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)), txflags(tfSellNFToken), ter(tecNO_LINE)); + env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)), + txflags(tfSellNFToken), + ter(tecNO_LINE)); env.close(); // And issuer can't create a trust line to themselves. @@ -6656,7 +6914,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env.close(); // Invalid URI length > 256 - env(token::modify(issuer, nftId), token::uri(std::string(maxTokenURILength + 1, 'q')), ter(temMALFORMED)); + env(token::modify(issuer, nftId), + token::uri(std::string(maxTokenURILength + 1, 'q')), + ter(temMALFORMED)); env.close(); } { @@ -6728,7 +6988,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto checkURI = [&accountNFTs, this](Account const& acct, char const* uri, int line) { auto const nfts = accountNFTs(acct); if (nfts.size() == 1) + { pass(); + } else { std::ostringstream text; @@ -6740,7 +7002,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite if (uri == nullptr) { if (!nfts[0u].isMember(sfURI.jsonName)) + { pass(); + } else { std::ostringstream text; @@ -6751,7 +7015,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite } if (nfts[0u][sfURI.jsonName] == strHex(std::string(uri))) + { pass(); + } else { std::ostringstream text; @@ -6888,7 +7154,8 @@ class NFTokenDisallowIncoming_test : public NFTokenBaseUtil_test void run() override { - testWithFeats(allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT); + testWithFeats( + allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT); } }; diff --git a/src/test/app/NetworkID_test.cpp b/src/test/app/NetworkID_test.cpp index 5fddc8f641..a37f8342e1 100644 --- a/src/test/app/NetworkID_test.cpp +++ b/src/test/app/NetworkID_test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { @@ -58,7 +59,7 @@ public: // test mainnet { test::jtx::Env env{*this, makeNetworkConfig(0)}; - BEAST_EXPECT(env.app().config().NETWORK_ID == 0); + BEAST_EXPECT(env.app().getNetworkIDService().getNetworkID() == 0); // try to submit a txn without network id, this should work Json::Value jv; @@ -81,7 +82,7 @@ public: // NetworkID { test::jtx::Env env{*this, makeNetworkConfig(1024)}; - BEAST_EXPECT(env.app().config().NETWORK_ID == 1024); + BEAST_EXPECT(env.app().getNetworkIDService().getNetworkID() == 1024); // try to submit a txn without network id, this should work Json::Value jv; @@ -101,7 +102,7 @@ public: // absent networkid { test::jtx::Env env{*this, makeNetworkConfig(1025)}; - BEAST_EXPECT(env.app().config().NETWORK_ID == 1025); + BEAST_EXPECT(env.app().getNetworkIDService().getNetworkID() == 1025); { env.fund(XRP(200), alice); // try to submit a txn without network id, this should not work @@ -115,7 +116,8 @@ public: Serializer s; jt.stx->add(s); BEAST_EXPECT( - env.rpc("submit", strHex(s.slice()))[jss::result][jss::engine_result] == "telREQUIRES_NETWORK_ID"); + env.rpc("submit", strHex(s.slice()))[jss::result][jss::engine_result] == + "telREQUIRES_NETWORK_ID"); env.close(); } diff --git a/src/test/app/NetworkOPs_test.cpp b/src/test/app/NetworkOPs_test.cpp index ffca1ed79d..a176279444 100644 --- a/src/test/app/NetworkOPs_test.cpp +++ b/src/test/app/NetworkOPs_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace xrpl { namespace test { @@ -28,7 +28,8 @@ public: { using namespace jtx; auto const alice = Account{"alice"}; - Env env{*this, envconfig(), std::make_unique(&logs), beast::severities::kAll}; + Env env{ + *this, envconfig(), std::make_unique(&logs), beast::severities::kAll}; env.memoize(env.master); env.memoize(alice); diff --git a/src/test/app/OfferStream_test.cpp b/src/test/app/OfferStream_test.cpp index 218653a5d4..becb190513 100644 --- a/src/test/app/OfferStream_test.cpp +++ b/src/test/app/OfferStream_test.cpp @@ -1,6 +1,5 @@ -#include - #include +#include namespace xrpl { diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 4847b7edb0..62edc844c7 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -95,10 +95,14 @@ public: PathSet paths(Path(XRP, USD), Path(USD)); - env(pay(alice, bob, USD(100)), json(paths.json()), sendmax(BTC(1000)), txflags(tfPartialPayment)); + env(pay(alice, bob, USD(100)), + json(paths.json()), + sendmax(BTC(1000)), + txflags(tfPartialPayment)); env.require(balance(bob, USD(100))); - BEAST_EXPECT(!isOffer(env, carol, BTC(1), USD(100)) && isOffer(env, carol, BTC(49), XRP(49))); + BEAST_EXPECT( + !isOffer(env, carol, BTC(1), USD(100)) && isOffer(env, carol, BTC(49), XRP(49))); } void @@ -131,18 +135,24 @@ public: // cancel the offer above and replace it with a new offer auto const offer2Seq = env.seq(alice); - env(offer(alice, XRP(300), USD(100)), json(jss::OfferSequence, offer1Seq), require(offers(alice, 1))); + env(offer(alice, XRP(300), USD(100)), + json(jss::OfferSequence, offer1Seq), + require(offers(alice, 1))); env.close(); - BEAST_EXPECT(isOffer(env, alice, XRP(300), USD(100)) && !isOffer(env, alice, XRP(500), USD(100))); + BEAST_EXPECT( + isOffer(env, alice, XRP(300), USD(100)) && !isOffer(env, alice, XRP(500), USD(100))); // Test canceling non-existent offer. // auto const offer3Seq = env.seq (alice); - env(offer(alice, XRP(400), USD(200)), json(jss::OfferSequence, offer1Seq), require(offers(alice, 2))); + env(offer(alice, XRP(400), USD(200)), + json(jss::OfferSequence, offer1Seq), + require(offers(alice, 2))); env.close(); - BEAST_EXPECT(isOffer(env, alice, XRP(300), USD(100)) && isOffer(env, alice, XRP(400), USD(200))); + BEAST_EXPECT( + isOffer(env, alice, XRP(300), USD(100)) && isOffer(env, alice, XRP(400), USD(200))); // Test cancellation now with OfferCancel tx auto const offer4Seq = env.seq(alice); @@ -266,7 +276,10 @@ public: // stAmountCalcSwitchover2 was inactive.) env(offer(erin, drops(2), USD(1))); - env(pay(alice, bob, USD(1)), path(~USD), sendmax(XRP(102)), txflags(tfNoRippleDirect | tfPartialPayment)); + env(pay(alice, bob, USD(1)), + path(~USD), + sendmax(XRP(102)), + txflags(tfNoRippleDirect | tfPartialPayment)); env.require(offers(carol, 0), offers(dan, 1)); @@ -321,15 +334,19 @@ public: env(offer(alice, USD(1), aliceTakerGets)); env.close(); - env.require(offers(carol, 0), balance(carol, - initialCarolUSD)); // offer is removed but not taken + env.require( + offers(carol, 0), + balance( + carol, + initialCarolUSD)); // offer is removed but not taken if (crossBothOffers) { env.require(offers(alice, 0), balance(alice, USD(1))); // alice's offer is crossed } else { - env.require(offers(alice, 1), balance(alice, USD(0))); // alice's offer is not crossed + env.require( + offers(alice, 1), balance(alice, USD(0))); // alice's offer is not crossed } } @@ -353,14 +370,19 @@ public: env.close(); env.require(offers(bob, 1), offers(carol, 1)); - std::uint32_t const flags = partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect; + std::uint32_t const flags = + partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect; TER const expectedTer = partialPayment ? TER{tesSUCCESS} : TER{tecPATH_PARTIAL}; - env(pay(alice, bob, USD(5)), path(~USD), sendmax(XRP(1)), txflags(flags), ter(expectedTer)); + env(pay(alice, bob, USD(5)), + path(~USD), + sendmax(XRP(1)), + txflags(flags), + ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -434,15 +456,19 @@ public: env(offer(alice, USD(1), aliceTakerGets)); env.close(); - env.require(offers(carol, 0), balance(carol, - initialCarolUSD)); // offer is removed but not taken + env.require( + offers(carol, 0), + balance( + carol, + initialCarolUSD)); // offer is removed but not taken if (crossBothOffers) { env.require(offers(alice, 0), balance(alice, USD(1))); // alice's offer is crossed } else { - env.require(offers(alice, 1), balance(alice, USD(0))); // alice's offer is not crossed + env.require( + offers(alice, 1), balance(alice, USD(0))); // alice's offer is not crossed } } @@ -469,14 +495,19 @@ public: env.close(); env.require(offers(bob, 1), offers(carol, 1)); - std::uint32_t const flags = partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect; + std::uint32_t const flags = + partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect; TER const expectedTer = partialPayment ? TER{tesSUCCESS} : TER{tecPATH_PARTIAL}; - env(pay(alice, bob, USD(5)), path(~USD), sendmax(EUR(10)), txflags(flags), ter(expectedTer)); + env(pay(alice, bob, USD(5)), + path(~USD), + sendmax(EUR(10)), + txflags(flags), + ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -556,7 +587,10 @@ public: env(offer(dan, XRP(50), USD1(50))); - env(pay(alice, carol, USD2(50)), path(~USD1, bob), sendmax(XRP(50)), txflags(tfNoRippleDirect)); + env(pay(alice, carol, USD2(50)), + path(~USD1, bob), + sendmax(XRP(50)), + txflags(tfNoRippleDirect)); env.require(balance(alice, xrpMinusFee(env, 10000 - 50))); env.require(balance(bob, USD1(100))); @@ -710,7 +744,9 @@ public: env.close(); // bob creates an offer that expires before the next ledger close. - env(offer(bob, USD(500), XRP(500)), json(sfExpiration.fieldName, lastClose(env) + 1), ter(tesSUCCESS)); + env(offer(bob, USD(500), XRP(500)), + json(sfExpiration.fieldName, lastClose(env) + 1), + ter(tesSUCCESS)); // The offer expires (it's not removed yet). env.close(); @@ -769,11 +805,16 @@ public: // No cross: { TER const expectedCode = tecKILLED; - env(offer(alice, XRP(1000), USD(1000)), txflags(tfImmediateOrCancel), ter(expectedCode)); + env(offer(alice, XRP(1000), USD(1000)), + txflags(tfImmediateOrCancel), + ter(expectedCode)); } env.require( - balance(alice, startBalance - f - f), balance(alice, USD(1000)), owners(alice, 1), offers(alice, 0)); + balance(alice, startBalance - f - f), + balance(alice, USD(1000)), + owners(alice, 1), + offers(alice, 0)); // Partially cross: env(offer(bob, USD(50), XRP(50)), ter(tesSUCCESS)); @@ -913,11 +954,15 @@ public: env.close(); // Order that has invalid flags - env(offer(alice, USD(1000), XRP(1000)), txflags(tfImmediateOrCancel + 1), ter(temINVALID_FLAG)); + env(offer(alice, USD(1000), XRP(1000)), + txflags(tfImmediateOrCancel + 1), + ter(temINVALID_FLAG)); env.require(balance(alice, startBalance), owners(alice, 0), offers(alice, 0)); // Order with incompatible flags - env(offer(alice, USD(1000), XRP(1000)), txflags(tfImmediateOrCancel | tfFillOrKill), ter(temINVALID_FLAG)); + env(offer(alice, USD(1000), XRP(1000)), + txflags(tfImmediateOrCancel | tfFillOrKill), + ter(temINVALID_FLAG)); env.require(balance(alice, startBalance), owners(alice, 0), offers(alice, 0)); // Sell and buy the same asset @@ -952,7 +997,9 @@ public: // Offer with a bad offer sequence { - env(offer(alice, USD(1000), XRP(1000)), json(jss::OfferSequence, std::uint32_t(0)), ter(temBAD_SEQUENCE)); + env(offer(alice, USD(1000), XRP(1000)), + json(jss::OfferSequence, std::uint32_t(0)), + ter(temBAD_SEQUENCE)); env.require(owners(alice, 1), offers(alice, 0)); } @@ -991,22 +1038,40 @@ public: env(trust(alice, usdOffer), ter(tesSUCCESS)); env(pay(gw, alice, usdOffer), ter(tesSUCCESS)); env.close(); - env.require(balance(alice, startBalance - f), balance(alice, usdOffer), offers(alice, 0), owners(alice, 1)); + env.require( + balance(alice, startBalance - f), + balance(alice, usdOffer), + offers(alice, 0), + owners(alice, 1)); - env(offer(alice, xrpOffer, usdOffer), json(sfExpiration.fieldName, lastClose(env)), ter(tecEXPIRED)); + env(offer(alice, xrpOffer, usdOffer), + json(sfExpiration.fieldName, lastClose(env)), + ter(tecEXPIRED)); - env.require(balance(alice, startBalance - f - f), balance(alice, usdOffer), offers(alice, 0), owners(alice, 1)); + env.require( + balance(alice, startBalance - f - f), + balance(alice, usdOffer), + offers(alice, 0), + owners(alice, 1)); env.close(); // Add an offer that expires before the next ledger close - env(offer(alice, xrpOffer, usdOffer), json(sfExpiration.fieldName, lastClose(env) + 1), ter(tesSUCCESS)); + env(offer(alice, xrpOffer, usdOffer), + json(sfExpiration.fieldName, lastClose(env) + 1), + ter(tesSUCCESS)); env.require( - balance(alice, startBalance - f - f - f), balance(alice, usdOffer), offers(alice, 1), owners(alice, 2)); + balance(alice, startBalance - f - f - f), + balance(alice, usdOffer), + offers(alice, 1), + owners(alice, 2)); // The offer expires (it's not removed yet) env.close(); env.require( - balance(alice, startBalance - f - f - f), balance(alice, usdOffer), offers(alice, 1), owners(alice, 2)); + balance(alice, startBalance - f - f - f), + balance(alice, usdOffer), + offers(alice, 1), + owners(alice, 2)); // Add offer - the expired offer is removed env(offer(bob, usdOffer, xrpOffer), ter(tesSUCCESS)); @@ -1143,7 +1208,7 @@ public: { auto acctOffers = offersOnAccount(env, account_to_test); - BEAST_EXPECT(acctOffers.size() == 0); + BEAST_EXPECT(acctOffers.empty()); for (auto const& offerPtr : acctOffers) { auto const& offer = *offerPtr; @@ -1198,7 +1263,8 @@ public: // This is one of the few tests where fixReducedOffersV2 changes the // results. So test both with and without fixReducedOffersV2. - for (FeatureBitset localFeatures : {features - fixReducedOffersV2, features | fixReducedOffersV2}) + for (FeatureBitset localFeatures : + {features - fixReducedOffersV2, features | fixReducedOffersV2}) { Env env{*this, localFeatures}; @@ -1242,7 +1308,8 @@ public: BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "50"); jrr = ledgerEntryState(env, bob, gw, "USD"); - BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-2710505431213761e-33"); + BEAST_EXPECT( + jrr[jss::node][sfBalance.fieldName][jss::value] == "-2710505431213761e-33"); // create crossing offer std::uint32_t const bobOfferSeq = env.seq(bob); @@ -1254,7 +1321,8 @@ public: // offer does not cross alice's offer and goes straight into // the ledger. jrr = ledgerEntryState(env, bob, gw, "USD"); - BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-2710505431213761e-33"); + BEAST_EXPECT( + jrr[jss::node][sfBalance.fieldName][jss::value] == "-2710505431213761e-33"); Json::Value const bobOffer = ledgerEntryOffer(env, bob, bobOfferSeq)[jss::node]; BEAST_EXPECT(bobOffer[sfTakerGets.jsonName][jss::value] == "1"); @@ -1280,14 +1348,17 @@ public: jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "0"); BEAST_EXPECT( - env.balance(bob, xrpIssue()) == bob_initial_balance - env.current()->fees().base * 2 + crossingDelta); + env.balance(bob, xrpIssue()) == + bob_initial_balance - env.current()->fees().base * 2 + crossingDelta); } } void testOfferCrossWithXRP(bool reverse_order, FeatureBitset features) { - testcase(std::string("Offer Crossing with XRP, ") + (reverse_order ? "Reverse" : "Normal") + " order"); + testcase( + std::string("Offer Crossing with XRP, ") + (reverse_order ? "Reverse" : "Normal") + + " order"); using namespace jtx; @@ -1323,14 +1394,18 @@ public: jrr = ledgerEntryRoot(env, bob); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - to_string((XRP(10000) - XRP(reverse_order ? 4000 : 3000) - env.current()->fees().base * 2).xrp())); + to_string( + (XRP(10000) - XRP(reverse_order ? 4000 : 3000) - env.current()->fees().base * 2) + .xrp())); jrr = ledgerEntryState(env, alice, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499"); jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - to_string((XRP(10000) + XRP(reverse_order ? 4000 : 3000) - env.current()->fees().base * 2).xrp())); + to_string( + (XRP(10000) + XRP(reverse_order ? 4000 : 3000) - env.current()->fees().base * 2) + .xrp())); } void @@ -1714,7 +1789,8 @@ public: BEAST_EXPECT(jro[jss::node][jss::TakerPays] == USD(20).value().getJson(JsonOptions::none)); jro = ledgerEntryOffer(env, dan, danOfferSeq); - BEAST_EXPECT(jro[jss::node][jss::TakerGets] == gw2["EUR"](20).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jro[jss::node][jss::TakerGets] == gw2["EUR"](20).value().getJson(JsonOptions::none)); BEAST_EXPECT(jro[jss::node][jss::TakerPays] == XRP(200).value().getText()); } @@ -1809,7 +1885,8 @@ public: // fees: // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 - auto const starting_xrp = XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + auto const starting_xrp = + XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; env.fund(starting_xrp, gw1, gw2, gw3, alice, bob); env.close(); @@ -1831,7 +1908,8 @@ public: BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "100"); jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( - jrr[jss::node][sfBalance.fieldName] == STAmount(env.current()->fees().accountReserve(3)).getText()); + jrr[jss::node][sfBalance.fieldName] == + STAmount(env.current()->fees().accountReserve(3)).getText()); jrr = ledgerEntryState(env, bob, gw1, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -1848,9 +1926,13 @@ public: { Env env{*this, features}; if (NumberSwitchOver) + { env.enableFeature(fixUniversalNumber); + } else + { env.disableFeature(fixUniversalNumber); + } auto const gw = Account{"gateway"}; auto const alice = Account{"alice"}; @@ -1904,7 +1986,8 @@ public: auto const bob = Account{"bob"}; auto const USD = gw["USD"]; - auto const starting_xrp = XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + auto const starting_xrp = + XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); @@ -1924,7 +2007,8 @@ public: BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100"); jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( - jrr[jss::node][sfBalance.fieldName] == STAmount(env.current()->fees().accountReserve(1)).getText()); + jrr[jss::node][sfBalance.fieldName] == + STAmount(env.current()->fees().accountReserve(1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -1944,7 +2028,8 @@ public: auto const bob = Account{"bob"}; auto const USD = gw["USD"]; - auto const starting_xrp = XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + auto const starting_xrp = + XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); @@ -1966,7 +2051,8 @@ public: BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-200"); jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( - jrr[jss::node][sfBalance.fieldName] == STAmount(env.current()->fees().accountReserve(1)).getText()); + jrr[jss::node][sfBalance.fieldName] == + STAmount(env.current()->fees().accountReserve(1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300"); @@ -1987,7 +2073,8 @@ public: auto const XTS = gw["XTS"]; auto const XXX = gw["XXX"]; - auto const starting_xrp = XRP(100.1) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + auto const starting_xrp = + XRP(100.1) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); @@ -2012,7 +2099,8 @@ public: payment[jss::id] = env.seq(bob); payment[jss::build_path] = true; payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1)); - payment[jss::tx_json][jss::Sequence] = env.current()->read(keylet::account(bob.id()))->getFieldU32(sfSequence); + payment[jss::tx_json][jss::Sequence] = + env.current()->read(keylet::account(bob.id()))->getFieldU32(sfSequence); payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base); payment[jss::tx_json][jss::SendMax] = bob["XTS"](1.5).value().getJson(JsonOptions::none); auto jrr = wsc->invoke("submit", payment); @@ -2043,7 +2131,10 @@ public: // a trust line defined). If the trustline is not defaulted then the tests // will not pass. void - verifyDefaultTrustline(jtx::Env& env, jtx::Account const& account, jtx::PrettyAmount const& expectBalance) + verifyDefaultTrustline( + jtx::Env& env, + jtx::Account const& account, + jtx::PrettyAmount const& expectBalance) { auto const sleTrust = env.le(keylet::line(account.id(), expectBalance.value().issue())); BEAST_EXPECT(sleTrust); @@ -2194,7 +2285,7 @@ public: auto acctOffers = offersOnAccount(env, acct); BEAST_EXPECT(acctOffers.size() == t.offers); - if (acctOffers.size() && t.offers) + if (!acctOffers.empty() && t.offers) { auto const& acctOffer = *(acctOffers.front()); @@ -2350,7 +2441,8 @@ public: env(offer(bob, usdOffer, eurOffer)); env.close(); - env.require(balance(alice, eurOffer), balance(bob, usdOffer), offers(alice, 0), offers(bob, 0)); + env.require( + balance(alice, eurOffer), balance(bob, usdOffer), offers(alice, 0), offers(bob, 0)); // Alice's offer crossing created a default EUR trustline and // Bob's offer crossing created a default USD trustline: @@ -2504,7 +2596,7 @@ public: // In pre-flow code alice's offer is left empty in the ledger. auto const aliceOffers = offersOnAccount(env, alice); - if (aliceOffers.size() != 0) + if (!aliceOffers.empty()) { BEAST_EXPECT(aliceOffers.size() == 1); auto const& aliceOffer = *(aliceOffers.front()); @@ -2684,7 +2776,7 @@ public: if (t.offers) { auto const acctOffers = offersOnAccount(env, acct); - if (acctOffers.size() > 0) + if (!acctOffers.empty()) { BEAST_EXPECT(acctOffers.size() == 1); auto const& acctOffer = *(acctOffers.front()); @@ -2896,7 +2988,7 @@ public: env.require(balance(eve, XRP(18000))); auto const evesOffers = offersOnAccount(env, eve); BEAST_EXPECT(evesOffers.size() == 1); - if (evesOffers.size() != 0) + if (!evesOffers.empty()) { auto const& evesOffer = *(evesOffers.front()); BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER); @@ -3072,7 +3164,7 @@ public: // In pre-flow code ova's offer is left empty in the ledger. auto const ovasOffers = offersOnAccount(env, ova); - if (ovasOffers.size() != 0) + if (!ovasOffers.empty()) { BEAST_EXPECT(ovasOffers.size() == 1); auto const& ovasOffer = *(ovasOffers.front()); @@ -3226,7 +3318,7 @@ public: env.close(); std::uint32_t const firstOfferSeq = env.seq(acct) - 1; - int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0; + int offerCount = isTesSuccess(t.firstOfferTec) ? 1 : 0; env.require(owners(acct, 2 + offerCount)); env.require(balance(acct, t.fundUSD)); env.require(balance(acct, t.fundEUR)); @@ -3245,7 +3337,7 @@ public: env.close(); std::uint32_t const secondOfferSeq = env.seq(acct) - 1; - offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount; + offerCount = isTesSuccess(t.secondOfferTec) ? 1 : offerCount; env.require(owners(acct, 2 + offerCount)); env.require(balance(acct, t.fundUSD)); env.require(balance(acct, t.fundEUR)); @@ -3835,9 +3927,12 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::remove_if(actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return (*offer)[sfTakerGets].signum() == 0; - })); + std::remove_if( + actorOffers.begin(), + actorOffers.end(), + [](std::shared_ptr& offer) { + return (*offer)[sfTakerGets].signum() == 0; + })); BEAST_EXPECT(offerCount == actor.offers); env.require(balance(actor.acct, actor.xrp)); @@ -3981,9 +4076,12 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::remove_if(actorOffers.begin(), actorOffers.end(), [](std::shared_ptr& offer) { - return (*offer)[sfTakerGets].signum() == 0; - })); + std::remove_if( + actorOffers.begin(), + actorOffers.end(), + [](std::shared_ptr& offer) { + return (*offer)[sfTakerGets].signum() == 0; + })); BEAST_EXPECT(offerCount == actor.offers); env.require(balance(actor.acct, actor.xrp)); @@ -4248,7 +4346,8 @@ public: jvParams[jss::destination_amount][jss::value] = 10; jvParams[jss::source_account] = hotUS.human(); - Json::Value const jrr{env.rpc("json", "ripple_path_find", to_string(jvParams))[jss::result]}; + Json::Value const jrr{ + env.rpc("json", "ripple_path_find", to_string(jvParams))[jss::result]}; BEAST_EXPECT(jrr[jss::status] == "success"); BEAST_EXPECT(jrr[jss::alternatives].isArray() && jrr[jss::alternatives].size() > 0); @@ -4329,8 +4428,10 @@ public: testcase("Deleted offer issuer"); - auto trustLineExists = - [](jtx::Env const& env, jtx::Account const& src, jtx::Account const& dst, Currency const& cur) -> bool { + auto trustLineExists = [](jtx::Env const& env, + jtx::Account const& src, + jtx::Account const& dst, + Currency const& cur) -> bool { return bool(env.le(keylet::line(src, dst, cur))); }; @@ -4496,13 +4597,18 @@ public: std::map> offers; forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { if (sle->getType() == ltOFFER) - offers.emplace((*sle)[sfSequence], std::make_pair((*sle)[sfTakerPays], (*sle)[sfTakerGets])); + { + offers.emplace( + (*sle)[sfSequence], std::make_pair((*sle)[sfTakerPays], (*sle)[sfTakerGets])); + } }); // first offer auto it = offers.begin(); BEAST_EXPECT(it != offers.end()); - BEAST_EXPECT(it->second.first == XTS(10) && it->second.second < XXX(30) && it->second.second > XXX(29.9994)); + BEAST_EXPECT( + it->second.first == XTS(10) && it->second.second < XXX(30) && + it->second.second > XXX(29.9994)); // second offer ++it; @@ -4649,7 +4755,7 @@ public: // Verify that the third offer alice created was consumed. { auto offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 0); + BEAST_EXPECT(offers.empty()); } env.require(balance(alice, USD(0))); env.require(owners(alice, 1)); @@ -4831,7 +4937,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance -= USD(100); takerUSDBalance += USD(100); @@ -4848,7 +4954,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); @@ -4865,7 +4971,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); @@ -4951,9 +5057,12 @@ public: } BEAST_EXPECT( - env.balance(maker, USD) == makerUSDBalance && env.balance(taker, USD) == takerUSDBalance && - env.balance(maker, EUR) == makerEURBalance && env.balance(taker, EUR) == takerEURBalance && - env.balance(maker, XRP) == makerXRPBalance && env.balance(taker, XRP) == takerXRPBalance); + env.balance(maker, USD) == makerUSDBalance && + env.balance(taker, USD) == takerUSDBalance && + env.balance(maker, EUR) == makerEURBalance && + env.balance(taker, EUR) == takerEURBalance && + env.balance(maker, XRP) == makerXRPBalance && + env.balance(taker, XRP) == takerXRPBalance); } void diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 66a8969164..9da0ee3a31 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -44,20 +44,23 @@ private: // Insufficient reserve if the data series extends to greater than 5 { Env env(*this); - env.fund(env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); - Oracle oracle(env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); + env.fund( + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + Oracle oracle( + env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = - { - {"XRP", "EUR", 740, 1}, - {"XRP", "GBP", 740, 1}, - {"XRP", "CNY", 740, 1}, - {"XRP", "CAD", 740, 1}, - {"XRP", "AUD", 740, 1}, - }, - .fee = static_cast(env.current()->fees().base.drops()), - .err = ter(tecINSUFFICIENT_RESERVE)}); + oracle.set( + UpdateArg{ + .series = + { + {"XRP", "EUR", 740, 1}, + {"XRP", "GBP", 740, 1}, + {"XRP", "CNY", 740, 1}, + {"XRP", "CAD", 740, 1}, + {"XRP", "AUD", 740, 1}, + }, + .fee = static_cast(env.current()->fees().base.drops()), + .err = ter(tecINSUFFICIENT_RESERVE)}); } { @@ -67,45 +70,53 @@ private: Oracle oracle(env, {.owner = owner, .fee = baseFee}, false); // Invalid flag - oracle.set(CreateArg{.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)}); + oracle.set( + CreateArg{.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)}); // Duplicate token pair - oracle.set(CreateArg{ - .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}}, .fee = baseFee, .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); // Price is not included - oracle.set(CreateArg{ - .series = {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .series = {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); // Token pair is in update and delete - oracle.set(CreateArg{ - .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}}, - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); // Token pair is in add and delete - oracle.set(CreateArg{ - .series = {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .series = {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); // Array of token pair is 0 or exceeds 10 - oracle.set(CreateArg{ - .series = - {{"XRP", "US1", 740, 1}, - {"XRP", "US2", 750, 1}, - {"XRP", "US3", 740, 1}, - {"XRP", "US4", 750, 1}, - {"XRP", "US5", 740, 1}, - {"XRP", "US6", 750, 1}, - {"XRP", "US7", 740, 1}, - {"XRP", "US8", 750, 1}, - {"XRP", "US9", 740, 1}, - {"XRP", "U10", 750, 1}, - {"XRP", "U11", 740, 1}}, - .fee = baseFee, - .err = ter(temARRAY_TOO_LARGE)}); + oracle.set( + CreateArg{ + .series = + {{"XRP", "US1", 740, 1}, + {"XRP", "US2", 750, 1}, + {"XRP", "US3", 740, 1}, + {"XRP", "US4", 750, 1}, + {"XRP", "US5", 740, 1}, + {"XRP", "US6", 750, 1}, + {"XRP", "US7", 740, 1}, + {"XRP", "US8", 750, 1}, + {"XRP", "US9", 740, 1}, + {"XRP", "U10", 750, 1}, + {"XRP", "U11", 740, 1}}, + .fee = baseFee, + .err = ter(temARRAY_TOO_LARGE)}); oracle.set(CreateArg{.series = {}, .fee = baseFee, .err = ter(temARRAY_EMPTY)}); } @@ -115,23 +126,26 @@ private: auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); - Oracle oracle(env, CreateArg{.owner = owner, .series = {{{"XRP", "USD", 740, 1}}}, .fee = baseFee}); - oracle.set(UpdateArg{ - .series = - { - {"XRP", "US1", 740, 1}, - {"XRP", "US2", 750, 1}, - {"XRP", "US3", 740, 1}, - {"XRP", "US4", 750, 1}, - {"XRP", "US5", 740, 1}, - {"XRP", "US6", 750, 1}, - {"XRP", "US7", 740, 1}, - {"XRP", "US8", 750, 1}, - {"XRP", "US9", 740, 1}, - {"XRP", "U10", 750, 1}, - }, - .fee = baseFee, - .err = ter(tecARRAY_TOO_LARGE)}); + Oracle oracle( + env, + CreateArg{.owner = owner, .series = {{{"XRP", "USD", 740, 1}}}, .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = + { + {"XRP", "US1", 740, 1}, + {"XRP", "US2", 750, 1}, + {"XRP", "US3", 740, 1}, + {"XRP", "US4", 750, 1}, + {"XRP", "US5", 740, 1}, + {"XRP", "US6", 750, 1}, + {"XRP", "US7", 740, 1}, + {"XRP", "US8", 750, 1}, + {"XRP", "US9", 740, 1}, + {"XRP", "U10", 750, 1}, + }, + .fee = baseFee, + .err = ter(tecARRAY_TOO_LARGE)}); } { @@ -141,26 +155,36 @@ private: Oracle oracle(env, {.owner = owner, .fee = baseFee}, false); // Asset class or provider not included on create - oracle.set(CreateArg{ - .assetClass = std::nullopt, .provider = "provider", .fee = baseFee, .err = ter(temMALFORMED)}); - oracle.set(CreateArg{ - .assetClass = "currency", - .provider = std::nullopt, - .uri = "URI", - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .assetClass = std::nullopt, + .provider = "provider", + .fee = baseFee, + .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{ + .assetClass = "currency", + .provider = std::nullopt, + .uri = "URI", + .fee = baseFee, + .err = ter(temMALFORMED)}); // Asset class or provider are included on update // and don't match the current values oracle.set(CreateArg{.fee = static_cast(env.current()->fees().base.drops())}); BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, .provider = "provider1", .fee = baseFee, .err = ter(temMALFORMED)}); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .assetClass = "currency1", - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .provider = "provider1", + .fee = baseFee, + .err = ter(temMALFORMED)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .assetClass = "currency1", + .fee = baseFee, + .err = ter(temMALFORMED)}); } { @@ -172,7 +196,8 @@ private: // Fields too long // Asset class std::string assetClass(17, '0'); - oracle.set(CreateArg{.assetClass = assetClass, .fee = baseFee, .err = ter(temMALFORMED)}); + oracle.set( + CreateArg{.assetClass = assetClass, .fee = baseFee, .err = ter(temMALFORMED)}); // provider std::string const large(257, '0'); oracle.set(CreateArg{.provider = large, .fee = baseFee, .err = ter(temMALFORMED)}); @@ -198,7 +223,11 @@ private: Oracle oracle(env, {.owner = owner, .fee = baseFee}); BEAST_EXPECT(oracle.exists()); oracle.set( - UpdateArg{.owner = some, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)}); + UpdateArg{ + .owner = some, + .series = {{"XRP", "USD", 740, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); } { @@ -207,38 +236,45 @@ private: Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); auto closeTime = [&]() { - return duration_cast(env.current()->header().closeTime.time_since_epoch() - 10'000s).count(); + return duration_cast( + env.current()->header().closeTime.time_since_epoch() - 10'000s) + .count(); }; env.fund(XRP(1'000), owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); BEAST_EXPECT(oracle.exists()); env.close(seconds(400)); // Less than the last close time - 300s - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = static_cast(closeTime() - 301), - .fee = baseFee, - .err = ter(tecINVALID_UPDATE_TIME)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .lastUpdateTime = static_cast(closeTime() - 301), + .fee = baseFee, + .err = ter(tecINVALID_UPDATE_TIME)}); // Greater than last close time + 300s - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = static_cast(closeTime() + 311), - .fee = baseFee, - .err = ter(tecINVALID_UPDATE_TIME)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .lastUpdateTime = static_cast(closeTime() + 311), + .fee = baseFee, + .err = ter(tecINVALID_UPDATE_TIME)}); oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}, .fee = baseFee}); - BEAST_EXPECT(oracle.expectLastUpdateTime(static_cast(testStartTime.count() + 450))); + BEAST_EXPECT(oracle.expectLastUpdateTime( + static_cast(testStartTime.count() + 450))); // Less than the previous lastUpdateTime - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = static_cast(449), - .fee = baseFee, - .err = ter(tecINVALID_UPDATE_TIME)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .lastUpdateTime = static_cast(449), + .fee = baseFee, + .err = ter(tecINVALID_UPDATE_TIME)}); // Less than the epoch time - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = static_cast(epoch_offset.count() - 1), - .fee = baseFee, - .err = ter(tecINVALID_UPDATE_TIME)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .lastUpdateTime = static_cast(epoch_offset.count() - 1), + .fee = baseFee, + .err = ter(tecINVALID_UPDATE_TIME)}); } { @@ -248,13 +284,17 @@ private: env.fund(XRP(1'000), owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, - .fee = baseFee, - .err = ter(tecTOKEN_PAIR_NOT_FOUND)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, + .fee = baseFee, + .err = ter(tecTOKEN_PAIR_NOT_FOUND)}); // delete all token pairs - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", std::nullopt, std::nullopt}}, .fee = baseFee, .err = ter(tecARRAY_EMPTY)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", std::nullopt, std::nullopt}}, + .fee = baseFee, + .err = ter(tecARRAY_EMPTY)}); } { @@ -263,7 +303,11 @@ private: auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); Oracle oracle( - env, {.owner = owner, .series = {{"USD", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)}); + env, + {.owner = owner, + .series = {{"USD", "USD", 740, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); } { @@ -285,21 +329,24 @@ private: auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund(XRP(1'000), owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); - oracle.set(UpdateArg{ - .series = {{"XRP", "EUR", std::nullopt, std::nullopt}, {"XRP", "EUR", 740, 1}}, - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "EUR", std::nullopt, std::nullopt}, {"XRP", "EUR", 740, 1}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); // Delete token pair that doesn't exist in this oracle - oracle.set(UpdateArg{ - .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, - .fee = baseFee, - .err = ter(tecTOKEN_PAIR_NOT_FOUND)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, + .fee = baseFee, + .err = ter(tecTOKEN_PAIR_NOT_FOUND)}); // Delete token pair in oracle, which is not in the ledger - oracle.set(UpdateArg{ - .documentID = 10, - .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, - .fee = baseFee, - .err = ter(temMALFORMED)}); + oracle.set( + UpdateArg{ + .documentID = 10, + .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, + .fee = baseFee, + .err = ter(temMALFORMED)}); } { @@ -307,7 +354,8 @@ private: Env env(*this); env.fund(XRP(1'000), owner); Oracle oracle(env, {.owner = owner, .fee = -1, .err = ter(temBAD_FEE)}); - Oracle oracle1(env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); + Oracle oracle1( + env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); oracle.set(UpdateArg{.owner = owner, .fee = -1, .err = ter(temBAD_FEE)}); } } @@ -368,7 +416,8 @@ private: env.fund(XRP(1'000), some); Oracle oracle(env, {.owner = owner, .fee = baseFee}); BEAST_EXPECT(oracle.exists()); - oracle.set(CreateArg{.owner = some, .series = {{"912810RR9", "USD", 740, 1}}, .fee = baseFee}); + oracle.set( + CreateArg{.owner = some, .series = {{"912810RR9", "USD", 740, 1}}, .fee = baseFee}); BEAST_EXPECT(Oracle::exists(env, some, oracle.documentID())); } } @@ -457,8 +506,14 @@ private: auto const acctDelFee{drops(env.current()->fees().increment)}; env.fund(XRP(1'000), owner); env.fund(XRP(1'000), alice); - Oracle oracle(env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee}); - Oracle oracle1(env, {.owner = owner, .documentID = 2, .series = {{"XRP", "EUR", 740, 1}}, .fee = baseFee}); + Oracle oracle( + env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee}); + Oracle oracle1( + env, + {.owner = owner, + .documentID = 2, + .series = {{"XRP", "EUR", 740, 1}}, + .fee = baseFee}); BEAST_EXPECT(ownerCount(env, owner) == 2); BEAST_EXPECT(oracle.exists()); BEAST_EXPECT(oracle1.exists()); @@ -515,34 +570,39 @@ private: BEAST_EXPECT(ownerCount(env, owner) == count); // update both pairs - oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}, .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}, .fee = baseFee}); BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}})); // owner count is not changed since the number of pairs is 2 BEAST_EXPECT(ownerCount(env, owner) == count); // owner count is increased by 1 since the number of pairs is 6 - oracle.set(UpdateArg{ - .series = - { - {"BTC", "USD", 741, 2}, - {"ETH", "EUR", 710, 2}, - {"YAN", "EUR", 710, 2}, - {"CAN", "EUR", 710, 2}, - }, - .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = + { + {"BTC", "USD", 741, 2}, + {"ETH", "EUR", 710, 2}, + {"YAN", "EUR", 710, 2}, + {"CAN", "EUR", 710, 2}, + }, + .fee = baseFee}); count += 1; BEAST_EXPECT(ownerCount(env, owner) == count); // update two pairs and delete four - oracle.set(UpdateArg{.series = {{"BTC", "USD", std::nullopt, std::nullopt}}, .fee = baseFee}); - oracle.set(UpdateArg{ - .series = - {{"XRP", "USD", 742, 2}, - {"XRP", "EUR", 711, 2}, - {"ETH", "EUR", std::nullopt, std::nullopt}, - {"YAN", "EUR", std::nullopt, std::nullopt}, - {"CAN", "EUR", std::nullopt, std::nullopt}}, - .fee = baseFee}); + oracle.set( + UpdateArg{.series = {{"BTC", "USD", std::nullopt, std::nullopt}}, .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = + {{"XRP", "USD", 742, 2}, + {"XRP", "EUR", 711, 2}, + {"ETH", "EUR", std::nullopt, std::nullopt}, + {"YAN", "EUR", std::nullopt, std::nullopt}, + {"CAN", "EUR", std::nullopt, std::nullopt}}, + .fee = baseFee}); BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}})); // owner count is decreased by 1 since the number of pairs is 2 count -= 1; @@ -553,7 +613,8 @@ private: { Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); - env.fund(env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + env.fund( + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}, .fee = baseFee}); } @@ -561,7 +622,9 @@ private: for (bool const withFixOrder : {false, true}) { // Should be same order as creation - Env env(*this, withFixOrder ? testable_amendments() : testable_amendments() - fixPriceOracleOrder); + Env env( + *this, + withFixOrder ? testable_amendments() : testable_amendments() - fixPriceOracleOrder); auto const baseFee = static_cast(env.current()->fees().base.drops()); auto test = [&](Env& env, DataSeries const& series) { @@ -571,18 +634,22 @@ private: auto sle = env.le(keylet::oracle(owner, oracle.documentID())); BEAST_EXPECT(sle->getFieldArray(sfPriceDataSeries).size() == series.size()); - auto const beforeQuoteAssetName1 = - sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText(); - auto const beforeQuoteAssetName2 = - sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText(); + auto const beforeQuoteAssetName1 = sle->getFieldArray(sfPriceDataSeries)[0] + .getFieldCurrency(sfQuoteAsset) + .getText(); + auto const beforeQuoteAssetName2 = sle->getFieldArray(sfPriceDataSeries)[1] + .getFieldCurrency(sfQuoteAsset) + .getText(); oracle.set(UpdateArg{.series = series, .fee = baseFee}); sle = env.le(keylet::oracle(owner, oracle.documentID())); - auto const afterQuoteAssetName1 = - sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText(); - auto const afterQuoteAssetName2 = - sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText(); + auto const afterQuoteAssetName1 = sle->getFieldArray(sfPriceDataSeries)[0] + .getFieldCurrency(sfQuoteAsset) + .getText(); + auto const afterQuoteAssetName2 = sle->getFieldArray(sfPriceDataSeries)[1] + .getFieldCurrency(sfQuoteAsset) + .getText(); if (env.current()->rules().enabled(fixPriceOracleOrder)) { @@ -638,11 +705,21 @@ private: BEAST_EXPECT(oracle.exists()); // Update - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, .msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_QUORUM)}); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, .msig = msig(zelda), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)}); - oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie), .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .msig = msig(becky), + .fee = baseFee, + .err = ter(tefBAD_QUORUM)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .msig = msig(zelda), + .fee = baseFee, + .err = ter(tefBAD_SIGNATURE)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie), .fee = baseFee}); BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 1}})); // remove the signer list env(signers(alice, jtx::none), sig(alie)); @@ -652,15 +729,19 @@ private: env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie)); env.close(); // old list fails - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .msig = msig(becky, bogie), - .fee = baseFee, - .err = ter(tefBAD_SIGNATURE)}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 740, 1}}, + .msig = msig(becky, bogie), + .fee = baseFee, + .err = ter(tefBAD_SIGNATURE)}); // updated list succeeds - oracle.set(UpdateArg{.series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob), .fee = baseFee}); + oracle.set( + UpdateArg{ + .series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob), .fee = baseFee}); BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 7412, 2}})); - oracle.set(UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed), .fee = baseFee}); + oracle.set( + UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed), .fee = baseFee}); BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 74245, 3}})); // Remove diff --git a/src/test/app/OversizeMeta_test.cpp b/src/test/app/OversizeMeta_test.cpp index d5e2948f5b..3831e45a78 100644 --- a/src/test/app/OversizeMeta_test.cpp +++ b/src/test/app/OversizeMeta_test.cpp @@ -123,7 +123,9 @@ public: len -= l2 + 1; } else + { len = l2; + } } return lo; } diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index c019c2b8a3..9315370809 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -178,7 +178,8 @@ public: std::optional const& saSrcCurrency = std::nullopt, std::optional const& domain = std::nullopt) { - Json::Value result = find_paths_request(env, src, dst, saDstAmount, saSendMax, saSrcCurrency, domain); + Json::Value result = + find_paths_request(env, src, dst, saDstAmount, saSendMax, saSrcCurrency, domain); BEAST_EXPECT(!result.isMember(jss::error)); STAmount da; @@ -205,6 +206,8 @@ public: Json::Value p; p["Paths"] = path[jss::paths_computed]; STParsedJSONObject po("generic", p); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) paths = po.object->getFieldPathSet(sfPaths); } } @@ -360,8 +363,8 @@ public: STPathSet st; STAmount sa; - std::tie(st, sa, std::ignore) = - find_paths(env, "alice", "bob", Account("bob")["USD"](5), std::nullopt, std::nullopt, domainID); + std::tie(st, sa, std::ignore) = find_paths( + env, "alice", "bob", Account("bob")["USD"](5), std::nullopt, std::nullopt, domainID); BEAST_EXPECT(same(st, stpath("gateway"))); BEAST_EXPECT(equal(sa, Account("alice")["USD"](5))); } @@ -379,14 +382,16 @@ public: if (domainEnabled) domainID = setupDomain(env, {"alice", "bob"}); - auto const result = find_paths(env, "alice", "bob", XRP(5), std::nullopt, std::nullopt, domainID); + auto const result = + find_paths(env, "alice", "bob", XRP(5), std::nullopt, std::nullopt, domainID); BEAST_EXPECT(std::get<0>(result).empty()); } void path_find_consume_all(bool const domainEnabled) { - testcase(std::string("path find consume all") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + testcase( + std::string("path find consume all") + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; { @@ -406,8 +411,14 @@ public: STPathSet st; STAmount sa; STAmount da; - std::tie(st, sa, da) = - find_paths(env, "alice", "edward", Account("edward")["USD"](-1), std::nullopt, std::nullopt, domainID); + std::tie(st, sa, da) = find_paths( + env, + "alice", + "edward", + Account("edward")["USD"](-1), + std::nullopt, + std::nullopt, + domainID); BEAST_EXPECT(same(st, stpath("dan"), stpath("bob", "carol"))); BEAST_EXPECT(equal(sa, Account("alice")["USD"](110))); BEAST_EXPECT(equal(da, Account("edward")["USD"](110))); @@ -479,7 +490,9 @@ public: void alternative_path_consume_both(bool const domainEnabled) { - testcase(std::string("alternative path consume both") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + testcase( + std::string("alternative path consume both") + (domainEnabled ? " w/ " : " w/o ") + + "domain"); using namespace jtx; Env env = pathTestEnv(); auto const gw = Account("gateway"); @@ -499,7 +512,9 @@ public: domainID = setupDomain(env, {"alice", "bob", "gateway", "gateway2"}); env(pay(gw, "alice", USD(70)), domain(*domainID)); env(pay(gw2, "alice", gw2_USD(70)), domain(*domainID)); - env(pay("alice", "bob", Account("bob")["USD"](140)), paths(Account("alice")["USD"]), domain(*domainID)); + env(pay("alice", "bob", Account("bob")["USD"](140)), + paths(Account("alice")["USD"]), + domain(*domainID)); } else { @@ -522,7 +537,8 @@ public: alternative_paths_consume_best_transfer(bool const domainEnabled) { testcase( - std::string("alternative paths consume best transfer") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + std::string("alternative paths consume best transfer") + + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); auto const gw = Account("gateway"); @@ -630,16 +646,18 @@ public: STPathSet st; STAmount sa; - std::tie(st, sa, std::ignore) = - find_paths(env, "alice", "bob", Account("bob")["USD"](5), std::nullopt, std::nullopt, domainID); - BEAST_EXPECT(same(st, stpath("gateway"), stpath("gateway2"), stpath("dan"), stpath("carol"))); + std::tie(st, sa, std::ignore) = find_paths( + env, "alice", "bob", Account("bob")["USD"](5), std::nullopt, std::nullopt, domainID); + BEAST_EXPECT( + same(st, stpath("gateway"), stpath("gateway2"), stpath("dan"), stpath("carol"))); BEAST_EXPECT(equal(sa, Account("alice")["USD"](5))); } void issues_path_negative_issue(bool const domainEnabled) { - testcase(std::string("path negative: Issue #5") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + testcase( + std::string("path negative: Issue #5") + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); env.fund(XRP(10000), "alice", "bob", "carol", "dan"); @@ -658,13 +676,15 @@ public: domainID = setupDomain(env, {"alice", "bob", "carol", "dan"}); } - auto result = find_paths(env, "alice", "bob", Account("bob")["USD"](25), std::nullopt, std::nullopt, domainID); + auto result = find_paths( + env, "alice", "bob", Account("bob")["USD"](25), std::nullopt, std::nullopt, domainID); BEAST_EXPECT(std::get<0>(result).empty()); env(pay("alice", "bob", Account("alice")["USD"](25)), ter(tecPATH_DRY)); env.close(); - result = find_paths(env, "alice", "bob", Account("alice")["USD"](25), std::nullopt, std::nullopt, domainID); + result = find_paths( + env, "alice", "bob", Account("alice")["USD"](25), std::nullopt, std::nullopt, domainID); BEAST_EXPECT(std::get<0>(result).empty()); env.require(balance("alice", Account("bob")["USD"](0))); @@ -765,8 +785,8 @@ public: env.require(balance("bob", AUD(10))); env.require(balance("carol", AUD(39))); - auto const result = - find_paths(env, "alice", "bob", Account("bob")["USD"](25), std::nullopt, std::nullopt, domainID); + auto const result = find_paths( + env, "alice", "bob", Account("bob")["USD"](25), std::nullopt, std::nullopt, domainID); BEAST_EXPECT(std::get<0>(result).empty()); } @@ -783,7 +803,8 @@ public: STPathSet st; STAmount sa; - std::tie(st, sa, std::ignore) = find_paths(env, "alice", "carol", Account("carol")["USD"](5)); + std::tie(st, sa, std::ignore) = + find_paths(env, "alice", "carol", Account("carol")["USD"](5)); BEAST_EXPECT(same(st, stpath("bob"))); BEAST_EXPECT(equal(sa, Account("alice")["USD"](5))); } @@ -827,8 +848,8 @@ public: })", jv); - auto const jv_l = - env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue()))->getJson(JsonOptions::none); + auto const jv_l = env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue())) + ->getJson(JsonOptions::none); for (auto it = jv.begin(); it != jv.end(); ++it) BEAST_EXPECT(*it == jv_l[it.memberName()]); } @@ -869,14 +890,15 @@ public: })", jv); - auto const jv_l = - env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue()))->getJson(JsonOptions::none); + auto const jv_l = env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue())) + ->getJson(JsonOptions::none); for (auto it = jv.begin(); it != jv.end(); ++it) BEAST_EXPECT(*it == jv_l[it.memberName()]); env.trust(Account("bob")["USD"](0), "alice"); env.trust(Account("alice")["USD"](0), "bob"); - BEAST_EXPECT(env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue())) == nullptr); + BEAST_EXPECT( + env.le(keylet::line(Account("bob").id(), Account("alice")["USD"].issue())) == nullptr); } void @@ -919,19 +941,22 @@ public: })", jv); - auto const jv_l = - env.le(keylet::line(Account("alice").id(), Account("bob")["USD"].issue()))->getJson(JsonOptions::none); + auto const jv_l = env.le(keylet::line(Account("alice").id(), Account("bob")["USD"].issue())) + ->getJson(JsonOptions::none); for (auto it = jv.begin(); it != jv.end(); ++it) BEAST_EXPECT(*it == jv_l[it.memberName()]); env(pay("alice", "bob", Account("alice")["USD"](50))); - BEAST_EXPECT(env.le(keylet::line(Account("alice").id(), Account("bob")["USD"].issue())) == nullptr); + BEAST_EXPECT( + env.le(keylet::line(Account("alice").id(), Account("bob")["USD"].issue())) == nullptr); } void path_find_01(bool const domainEnabled) { - testcase(std::string("Path Find: XRP -> XRP and XRP -> IOU") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + testcase( + std::string("Path Find: XRP -> XRP and XRP -> IOU") + + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); Account A1{"A1"}; @@ -983,7 +1008,8 @@ public: { auto const& send_amt = XRP(10); - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency(), domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency(), domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(st.empty()); } @@ -992,14 +1018,16 @@ public: // no path should exist for this since dest account // does not exist. auto const& send_amt = XRP(200); - std::tie(st, sa, da) = find_paths(env, A1, Account{"A0"}, send_amt, std::nullopt, xrpCurrency(), domainID); + std::tie(st, sa, da) = + find_paths(env, A1, Account{"A0"}, send_amt, std::nullopt, xrpCurrency(), domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(st.empty()); } { auto const& send_amt = G3["ABC"](10); - std::tie(st, sa, da) = find_paths(env, A2, G3, send_amt, std::nullopt, xrpCurrency(), domainID); + std::tie(st, sa, da) = + find_paths(env, A2, G3, send_amt, std::nullopt, xrpCurrency(), domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, XRP(100))); BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"])))); @@ -1007,7 +1035,8 @@ public: { auto const& send_amt = A2["ABC"](1); - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency(), domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency(), domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, XRP(10))); BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"]), G3))); @@ -1015,7 +1044,8 @@ public: { auto const& send_amt = A3["ABC"](1); - std::tie(st, sa, da) = find_paths(env, A1, A3, send_amt, std::nullopt, xrpCurrency(), domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A3, send_amt, std::nullopt, xrpCurrency(), domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, XRP(10))); BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"]), G3, A2))); @@ -1025,7 +1055,9 @@ public: void path_find_02(bool const domainEnabled) { - testcase(std::string("Path Find: non-XRP -> XRP") + (domainEnabled ? " w/ " : " w/o ") + "domain"); + testcase( + std::string("Path Find: non-XRP -> XRP") + (domainEnabled ? " w/ " : " w/o ") + + "domain"); using namespace jtx; Env env = pathTestEnv(); Account A1{"A1"}; @@ -1062,7 +1094,8 @@ public: auto const& send_amt = XRP(10); { - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, A2["ABC"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, A2["ABC"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["ABC"](1))); BEAST_EXPECT(same(st, stpath(G3, IPE(xrpIssue())))); @@ -1072,7 +1105,8 @@ public: // paths if (domainEnabled) { - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, A2["ABC"].currency); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, A2["ABC"].currency); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(st.empty()); } @@ -1120,7 +1154,8 @@ public: { auto const& send_amt = A2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, A2["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, A2["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same(st, stpath(G1BS, M1, G2SW))); @@ -1128,7 +1163,8 @@ public: { auto const& send_amt = A1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A2, A1, send_amt, std::nullopt, A1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A2, A1, send_amt, std::nullopt, A1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A2["HKD"](10))); BEAST_EXPECT(same(st, stpath(G2SW, M1, G1BS))); @@ -1136,7 +1172,8 @@ public: { auto const& send_amt = A2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, G1BS, A2, send_amt, std::nullopt, A1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, G1BS, A2, send_amt, std::nullopt, A1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G1BS["HKD"](10))); BEAST_EXPECT(same(st, stpath(M1, G2SW))); @@ -1144,7 +1181,8 @@ public: { auto const& send_amt = M1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, M1, G1BS, send_amt, std::nullopt, A1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, M1, G1BS, send_amt, std::nullopt, A1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, M1["HKD"](10))); BEAST_EXPECT(st.empty()); @@ -1152,7 +1190,8 @@ public: { auto const& send_amt = A1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, G2SW, A1, send_amt, std::nullopt, A1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, G2SW, A1, send_amt, std::nullopt, A1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G2SW["HKD"](10))); BEAST_EXPECT(same(st, stpath(M1, G1BS))); @@ -1163,8 +1202,8 @@ public: path_find_05(bool const domainEnabled) { testcase( - std::string("Path Find: non-XRP -> non-XRP, same currency") + (domainEnabled ? " w/ " : " w/o ") + - "domain"); + std::string("Path Find: non-XRP -> non-XRP, same currency") + + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); Account A1{"A1"}; @@ -1223,7 +1262,8 @@ public: // A) Borrow or repay -- // Source -> Destination (repay source issuer) auto const& send_amt = G1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1233,7 +1273,8 @@ public: // A2) Borrow or repay -- // Source -> Destination (repay destination issuer) auto const& send_amt = A1["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1243,7 +1284,8 @@ public: // B) Common gateway -- // Source -> AC -> Destination auto const& send_amt = A3["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same(st, stpath(G1))); @@ -1253,18 +1295,24 @@ public: // C) Gateway to gateway -- // Source -> OB -> Destination auto const& send_amt = G2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G1["HKD"](10))); - BEAST_EXPECT( - same(st, stpath(IPE(G2["HKD"])), stpath(M1), stpath(M2), stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); + BEAST_EXPECT(same( + st, + stpath(IPE(G2["HKD"])), + stpath(M1), + stpath(M2), + stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); } { // D) User to unlinked gateway via order book -- // Source -> AC -> OB -> Destination auto const& send_amt = G2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1280,7 +1328,8 @@ public: // Source -> AC -> OB to XRP -> OB from XRP -> AC -> // Destination auto const& send_amt = A2["HKD"](10); - std::tie(st, sa, da) = find_paths(env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1296,8 +1345,8 @@ public: path_find_06(bool const domainEnabled) { testcase( - std::string("Path Find: non-XRP -> non-XRP, same currency)") + (domainEnabled ? " w/ " : " w/o ") + - "domain"); + std::string("Path Find: non-XRP -> non-XRP, same currency)") + + (domainEnabled ? " w/ " : " w/o ") + "domain"); using namespace jtx; Env env = pathTestEnv(); Account A1{"A1"}; @@ -1340,7 +1389,8 @@ public: auto const& send_amt = A2["HKD"](10); STPathSet st; STAmount sa, da; - std::tie(st, sa, da) = find_paths(env, G1, A2, send_amt, std::nullopt, G1["HKD"].currency, domainID); + std::tie(st, sa, da) = + find_paths(env, G1, A2, send_amt, std::nullopt, G1["HKD"].currency, domainID); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G1["HKD"](10))); BEAST_EXPECT(same(st, stpath(M1, G2), stpath(IPE(G2["HKD"]), G2))); @@ -1380,14 +1430,16 @@ public: env.close(); } - auto [st, sa, da] = find_paths(env, alice, bob, USD(-1), XRP(100).value(), std::nullopt, domainID); + auto [st, sa, da] = + find_paths(env, alice, bob, USD(-1), XRP(100).value(), std::nullopt, domainID); BEAST_EXPECT(sa == XRP(10)); BEAST_EXPECT(equal(da, USD(10))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) { auto const& pathElem = st[0][0]; BEAST_EXPECT( - pathElem.isOffer() && pathElem.getIssuerID() == gw.id() && pathElem.getCurrency() == USD.currency); + pathElem.isOffer() && pathElem.getIssuerID() == gw.id() && + pathElem.getCurrency() == USD.currency); } } { @@ -1413,7 +1465,8 @@ public: env.close(); } - auto [st, sa, da] = find_paths(env, alice, bob, drops(-1), USD(100).value(), std::nullopt, domainID); + auto [st, sa, da] = + find_paths(env, alice, bob, drops(-1), USD(100).value(), std::nullopt, domainID); BEAST_EXPECT(sa == USD(10)); BEAST_EXPECT(equal(da, XRP(10))); if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1)) @@ -1465,7 +1518,7 @@ public: } else { - BEAST_EXPECT(st.size() == 0); + BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(sa, XRP(0))); } }; @@ -1520,7 +1573,8 @@ public: env(pay(G2, M2, G2["HKD"](5000))); env.close(); - std::optional domainID = setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2}); + std::optional domainID = + setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2}); BEAST_EXPECT(domainID); func(env, M1, M2, G1, G2, *domainID); @@ -1533,7 +1587,13 @@ public: // Source -> Destination (repay source issuer) auto const& send_amt = G1["HKD"](10); std::tie(st, sa, da) = find_paths( - env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + A1, + G1, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1544,7 +1604,13 @@ public: // Source -> Destination (repay destination issuer) auto const& send_amt = A1["HKD"](10); std::tie(st, sa, da) = find_paths( - env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + A1, + G1, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); @@ -1555,7 +1621,13 @@ public: // Source -> AC -> Destination auto const& send_amt = A3["HKD"](10); std::tie(st, sa, da) = find_paths( - env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + A1, + A3, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same(st, stpath(G1))); @@ -1566,11 +1638,21 @@ public: // Source -> OB -> Destination auto const& send_amt = G2["HKD"](10); std::tie(st, sa, da) = find_paths( - env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + G1, + G2, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, G1["HKD"](10))); - BEAST_EXPECT( - same(st, stpath(IPE(G2["HKD"])), stpath(M1), stpath(M2), stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); + BEAST_EXPECT(same( + st, + stpath(IPE(G2["HKD"])), + stpath(M1), + stpath(M2), + stpath(IPE(xrpIssue()), IPE(G2["HKD"])))); } { @@ -1578,7 +1660,13 @@ public: // Source -> AC -> OB -> Destination auto const& send_amt = G2["HKD"](10); std::tie(st, sa, da) = find_paths( - env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + A1, + G2, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1595,7 +1683,13 @@ public: // Destination auto const& send_amt = A2["HKD"](10); std::tie(st, sa, da) = find_paths( - env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency, domainEnabled ? domainID : std::nullopt); + env, + A1, + A2, + send_amt, + std::nullopt, + G1["HKD"].currency, + domainEnabled ? domainID : std::nullopt); BEAST_EXPECT(equal(da, send_amt)); BEAST_EXPECT(equal(sa, A1["HKD"](10))); BEAST_EXPECT(same( @@ -1611,35 +1705,58 @@ public: // offers to make sure that hybrid offers work in pathfinding for open // order book { - testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, XRP(10000), G2["HKD"](1000))); - env(offer(M2, G1["HKD"](1000), XRP(10000))); - }); + testPathfind( + [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, XRP(10000), G2["HKD"](1000))); + env(offer(M2, G1["HKD"](1000), XRP(10000))); + }); - testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, G1["HKD"](1000), XRP(10000))); - }); + testPathfind( + [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, XRP(10000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, G1["HKD"](1000), XRP(10000))); + }); - testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID), txflags(tfHybrid)); - }); + testPathfind( + [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, XRP(10000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, G1["HKD"](1000), XRP(10000)), + domain(domainID), + txflags(tfHybrid)); + }); - testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000))); - env(offer(M2, XRP(10000), G2["HKD"](1000))); - env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID), txflags(tfHybrid)); - }); + testPathfind( + [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000))); + env(offer(M2, XRP(10000), G2["HKD"](1000))); + env(offer(M2, G1["HKD"](1000), XRP(10000)), + domain(domainID), + txflags(tfHybrid)); + }); - testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000))); - env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID), txflags(tfHybrid)); - }); + testPathfind( + [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000))); + env(offer(M2, XRP(10000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, G1["HKD"](1000), XRP(10000)), + domain(domainID), + txflags(tfHybrid)); + }); } // the following tests exercise different combinations of domain/hybrid @@ -1648,7 +1765,9 @@ public: { testPathfind( [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID)); env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID)); }, @@ -1656,8 +1775,12 @@ public: testPathfind( [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { - env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); + env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, XRP(10000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID)); }, true); @@ -1666,15 +1789,21 @@ public: [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID)); env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID)); - env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID), txflags(tfHybrid)); + env(offer(M2, G1["HKD"](1000), XRP(10000)), + domain(domainID), + txflags(tfHybrid)); }, true); testPathfind( [](Env& env, Account M1, Account M2, Account G1, Account G2, uint256 domainID) { env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)), domain(domainID)); - env(offer(M2, XRP(10000), G2["HKD"](1000)), domain(domainID), txflags(tfHybrid)); - env(offer(M2, G1["HKD"](1000), XRP(10000)), domain(domainID), txflags(tfHybrid)); + env(offer(M2, XRP(10000), G2["HKD"](1000)), + domain(domainID), + txflags(tfHybrid)); + env(offer(M2, G1["HKD"](1000), XRP(10000)), + domain(domainID), + txflags(tfHybrid)); }, true); } @@ -1696,7 +1825,8 @@ public: auto const& send_amt = XRP(1); // doing pathfind with domain won't include amm - std::tie(st, sa, da) = find_paths(env, bob, carol, send_amt, std::nullopt, USD.currency, domainID); + std::tie(st, sa, da) = + find_paths(env, bob, carol, send_amt, std::nullopt, USD.currency, domainID); BEAST_EXPECT(st.empty()); // a non-domain pathfind returns amm in the path diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index 019dd42ca1..5322349797 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -25,7 +25,11 @@ struct PayChan_test : public beast::unit_test::suite } static Buffer - signClaimAuth(PublicKey const& pk, SecretKey const& sk, uint256 const& channel, STAmount const& authAmt) + signClaimAuth( + PublicKey const& pk, + SecretKey const& sk, + uint256 const& channel, + STAmount const& authAmt) { Serializer msg; serializePayChanAuthorization(msg, channel, authAmt.xrp()); @@ -96,7 +100,8 @@ struct PayChan_test : public beast::unit_test::suite env(create(alice, alice, XRP(1000), settleDelay, pk), ter(temDST_IS_SRC)); // invalid channel - env(fund(alice, channel(alice, "noAccount", env.seq(alice) - 1), XRP(1000)), ter(tecNO_ENTRY)); + env(fund(alice, channel(alice, "noAccount", env.seq(alice) - 1), XRP(1000)), + ter(tecNO_ENTRY)); // not enough funds env(create(alice, bob, XRP(10000), settleDelay, pk), ter(tecUNFUNDED)); @@ -150,7 +155,8 @@ struct PayChan_test : public beast::unit_test::suite // claim again preBob = env.balance(bob); - env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecUNFUNDED_PAYMENT)); + env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), + ter(tecUNFUNDED_PAYMENT)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); BEAST_EXPECT(env.balance(bob) == preBob - feeDrops); @@ -176,14 +182,16 @@ struct PayChan_test : public beast::unit_test::suite { // Wrong signing key auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500)); - env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), bob.pk()), ter(temBAD_SIGNER)); + env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), bob.pk()), + ter(temBAD_SIGNER)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); } { // Bad signature auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500)); - env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), alice.pk()), ter(temBAD_SIGNATURE)); + env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), alice.pk()), + ter(temBAD_SIGNATURE)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); } @@ -283,7 +291,8 @@ struct PayChan_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); auto const pk = alice.pk(); auto const settleDelay = 100s; - NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime + 3600s; + NetClock::time_point const cancelAfter = + env.current()->header().parentCloseTime + 3600s; auto const channelFunds = XRP(1000); auto const chan = channel(alice, bob, env.seq(alice)); env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter)); @@ -313,7 +322,8 @@ struct PayChan_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, carol); auto const pk = alice.pk(); auto const settleDelay = 100s; - NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime + 3600s; + NetClock::time_point const cancelAfter = + env.current()->header().parentCloseTime + 3600s; auto const channelFunds = XRP(1000); auto const chan = channel(alice, bob, env.seq(alice)); env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter)); @@ -341,7 +351,8 @@ struct PayChan_test : public beast::unit_test::suite auto const pk = alice.pk(); auto const settleDelay = 100s; auto const channelFunds = XRP(1000); - NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime - 1s; + NetClock::time_point const cancelAfter = + env.current()->header().parentCloseTime - 1s; auto const txResult = withFixPayChan ? ter(tecEXPIRED) : ter(tesSUCCESS); env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter), txResult); } @@ -360,7 +371,8 @@ struct PayChan_test : public beast::unit_test::suite auto const settleDelay = 100s; auto const channelFunds = XRP(1000); NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime; - env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter), ter(tesSUCCESS)); + env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter), + ter(tesSUCCESS)); } } } @@ -389,22 +401,29 @@ struct PayChan_test : public beast::unit_test::suite // Owner closes, will close after settleDelay env(claim(alice, chan), txflags(tfClose)); auto counts = [](auto const& t) { return t.time_since_epoch().count(); }; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration)); // increase the expiration time env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 100s})); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 100); // decrease the expiration, but still above minExpiration env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 50s})); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); // decrease the expiration below minExpiration - env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}), ter(temBAD_EXPIRATION)); + env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}), + ter(temBAD_EXPIRATION)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); env(claim(bob, chan), txflags(tfRenew), ter(tecNO_PERMISSION)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50); env(claim(alice, chan), txflags(tfRenew)); BEAST_EXPECT(!channelExpiration(*env.current(), chan)); // decrease the expiration below minExpiration - env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}), ter(temBAD_EXPIRATION)); + env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}), + ter(temBAD_EXPIRATION)); BEAST_EXPECT(!channelExpiration(*env.current(), chan)); env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration})); env.close(minExpiration); @@ -425,7 +444,8 @@ struct PayChan_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); auto const pk = alice.pk(); auto const settleDelay = 3600s; - NetClock::time_point const settleTimepoint = env.current()->header().parentCloseTime + settleDelay; + NetClock::time_point const settleTimepoint = + env.current()->header().parentCloseTime + settleDelay; auto const channelFunds = XRP(1000); auto const chan = channel(alice, bob, env.seq(alice)); env(create(alice, bob, channelFunds, settleDelay, pk)); @@ -776,7 +796,8 @@ struct PayChan_test : public beast::unit_test::suite { // create credentials auto jv = credentials::create(alice, carol, credType); - uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 100; + uint32_t const t = + env.current()->header().parentCloseTime.time_since_epoch().count() + 100; jv[sfExpiration.jsonName] = t; env(jv); env.close(); @@ -790,14 +811,18 @@ struct PayChan_test : public beast::unit_test::suite env.close(); // Fail, credentials not accepted - env(claim(alice, chan, delta, delta), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS)); + env(claim(alice, chan, delta, delta), + credentials::ids({credIdx}), + ter(tecBAD_CREDENTIALS)); env.close(); env(credentials::accept(alice, carol, credType)); env.close(); // Fail, no depositPreauth object - env(claim(alice, chan, delta, delta), credentials::ids({credIdx}), ter(tecNO_PERMISSION)); + env(claim(alice, chan, delta, delta), + credentials::ids({credIdx}), + ter(tecNO_PERMISSION)); env.close(); // Setup deposit authorization @@ -805,13 +830,17 @@ struct PayChan_test : public beast::unit_test::suite env.close(); // Fail, credentials doesn’t belong to root account - env(claim(dillon, chan, delta, delta), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS)); + env(claim(dillon, chan, delta, delta), + credentials::ids({credIdx}), + ter(tecBAD_CREDENTIALS)); // Fails because bob's lsfDepositAuth flag is set. env(claim(alice, chan, delta, delta), ter(tecNO_PERMISSION)); // Fail, bad credentials index. - env(claim(alice, chan, delta, delta), credentials::ids({credBadIdx}), ter(tecBAD_CREDENTIALS)); + env(claim(alice, chan, delta, delta), + credentials::ids({credBadIdx}), + ter(tecBAD_CREDENTIALS)); // Fail, empty credentials env(claim(alice, chan, delta, delta), credentials::ids({}), ter(temMALFORMED)); @@ -896,7 +925,9 @@ struct PayChan_test : public beast::unit_test::suite env(deposit::auth(bob, alice)); env.close(); - env(claim(alice, chan, XRP(500).value(), XRP(500).value()), credentials::ids({credIdx}), ter(temDISABLED)); + env(claim(alice, chan, XRP(500).value(), XRP(500).value()), + credentials::ids({credIdx}), + ter(temDISABLED)); } } @@ -984,9 +1015,11 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(r[jss::result][jss::validated]); BEAST_EXPECT(chan1Str != chan2Str); for (auto const& c : {chan1Str, chan2Str}) + { BEAST_EXPECT( r[jss::result][jss::channels][0u][jss::channel_id] == c || r[jss::result][jss::channels][1u][jss::channel_id] == c); + } } } @@ -1165,7 +1198,8 @@ struct PayChan_test : public beast::unit_test::suite forAllApiVersions([&, this](unsigned apiVersion) { testcase("PayChan Channel_Auth RPC Api " + std::to_string(apiVersion)); args[jss::api_version] = apiVersion; - auto const rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result]; + auto const rs = + env.rpc("json", "channel_authorize", args.toStyledString())[jss::result]; auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType"; BEAST_EXPECT(rs[jss::error] == error); }); @@ -1217,9 +1251,11 @@ struct PayChan_test : public beast::unit_test::suite BEAST_EXPECT(r[jss::result][jss::validated]); BEAST_EXPECT(chan1Str != chan2Str); for (auto const& c : {chan1Str, chan2Str}) + { BEAST_EXPECT( r[jss::result][jss::channels][0u][jss::channel_id] == c || r[jss::result][jss::channels][1u][jss::channel_id] == c); + } } auto sliceToHex = [](Slice const& slice) { @@ -1566,7 +1602,9 @@ struct PayChan_test : public beast::unit_test::suite auto const settleDelay = 100s; auto const pk = alice.pk(); - auto inOwnerDir = [](ReadView const& view, Account const& acc, std::shared_ptr const& chan) -> bool { + auto inOwnerDir = [](ReadView const& view, + Account const& acc, + std::shared_ptr const& chan) -> bool { xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id())); return std::find(ownerDir.begin(), ownerDir.end(), chan) != ownerDir.end(); }; @@ -1625,17 +1663,23 @@ struct PayChan_test : public beast::unit_test::suite testcase("Account Delete"); using namespace test::jtx; using namespace std::literals::chrono_literals; - auto rmAccount = [this](Env& env, Account const& toRm, Account const& dst, TER expectedTer = tesSUCCESS) { - // only allow an account to be deleted if the account's sequence - // number is at least 256 less than the current ledger sequence - for (auto minRmSeq = env.seq(toRm) + 257; env.current()->seq() < minRmSeq; env.close()) - { - } + auto rmAccount = + [this]( + Env& env, Account const& toRm, Account const& dst, TER expectedTer = tesSUCCESS) { + // only allow an account to be deleted if the account's sequence + // number is at least 256 less than the current ledger sequence + for (auto minRmSeq = env.seq(toRm) + 257; env.current()->seq() < minRmSeq; + env.close()) + { + } - env(acctdelete(toRm, dst), fee(drops(env.current()->fees().increment)), ter(expectedTer)); - env.close(); - this->BEAST_EXPECT(isTesSuccess(expectedTer) == !env.closed()->exists(keylet::account(toRm.id()))); - }; + env(acctdelete(toRm, dst), + fee(drops(env.current()->fees().increment)), + ter(expectedTer)); + env.close(); + this->BEAST_EXPECT( + isTesSuccess(expectedTer) == !env.closed()->exists(keylet::account(toRm.id()))); + }; auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -1775,7 +1819,8 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + XRP(100); assert(reqBal <= chanAmt); auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt); - env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ticket::use(bobTicketSeq++)); + env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), + ticket::use(bobTicketSeq++)); env.require(tickets(bob, env.seq(bob) - bobTicketSeq)); BEAST_EXPECT(env.seq(bob) == bobSeq); diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 9b534d9284..70444637bf 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -1,9 +1,6 @@ #include #include -#include -#include -#include #include #include @@ -11,6 +8,9 @@ #include #include #include +#include +#include +#include #include @@ -53,7 +53,12 @@ trustFlag(TrustFlag f, bool useHigh) } bool -getTrustFlag(jtx::Env const& env, jtx::Account const& src, jtx::Account const& dst, Currency const& cur, TrustFlag flag) +getTrustFlag( + jtx::Env const& env, + jtx::Account const& src, + jtx::Account const& dst, + Currency const& cur, + TrustFlag flag) { if (auto sle = env.le(keylet::line(src, dst, cur))) { @@ -127,7 +132,10 @@ STPathElement ipe(Issue const& iss) { return STPathElement( - STPathElement::typeCurrency | STPathElement::typeIssuer, xrpAccount(), iss.currency, iss.account); + STPathElement::typeCurrency | STPathElement::typeIssuer, + xrpAccount(), + iss.currency, + iss.account); }; // Issuer path element @@ -175,8 +183,10 @@ class ElementComboIter hasAny(std::initializer_list sb) const { for (auto const s : sb) + { if (has(s)) return true; + } return false; } @@ -186,8 +196,10 @@ class ElementComboIter size_t result = 0; for (auto const s : sb) + { if (has(s)) result++; + } return result; } @@ -201,8 +213,10 @@ public: { return (allowCompound_ || !(has(SB::acc) && hasAny({SB::cur, SB::iss}))) && (!hasAny({SB::prevAcc, SB::prevCur, SB::prevIss}) || prev_) && - (!hasAny({SB::rootAcc, SB::sameAccIss, SB::existingAcc, SB::prevAcc}) || has(SB::acc)) && - (!hasAny({SB::rootIss, SB::sameAccIss, SB::existingIss, SB::prevIss}) || has(SB::iss)) && + (!hasAny({SB::rootAcc, SB::sameAccIss, SB::existingAcc, SB::prevAcc}) || + has(SB::acc)) && + (!hasAny({SB::rootIss, SB::sameAccIss, SB::existingIss, SB::prevIss}) || + has(SB::iss)) && (!hasAny({SB::xrp, SB::existingCur, SB::prevCur}) || has(SB::cur)) && // These will be duplicates (count({SB::xrp, SB::existingCur, SB::prevCur}) <= 1) && @@ -252,7 +266,7 @@ public: if (has(SB::sameAccIss)) return acc; if (has(SB::existingIss) && existingIss) - return *existingIss; + return existingIss; return issF().id(); }(); auto const cur = [&]() -> std::optional { @@ -261,17 +275,21 @@ public: if (has(SB::xrp)) return xrpCurrency(); if (has(SB::existingCur) && existingCur) - return *existingCur; + return existingCur; return currencyF(); }(); if (!has(SB::boundary)) + { col.emplace_back(acc, cur, iss); + } else + { col.emplace_back( STPathElement::Type::typeBoundary, acc.value_or(AccountID{}), cur.value_or(Currency{}), iss.value_or(AccountID{})); + } } }; @@ -358,11 +376,17 @@ struct ExistingElementPool for (size_t id = 0; id < numCur; ++id) { if (id < 10) + { snprintf(buf, bufSize, "CC%zu", id); + } else if (id < 100) + { snprintf(buf, bufSize, "C%zu", id); + } else + { snprintf(buf, bufSize, "%zu", id); + } currencies.emplace_back(to_currency(buf)); currencyNames.emplace_back(buf); } @@ -397,8 +421,10 @@ struct ExistingElementPool std::vector ious; ious.reserve(numAct * numCur); for (auto const& a : accounts) + { for (auto const& cn : currencyNames) ious.emplace_back(a[cn]); + } // create offers from every currency to every other currency for (auto takerPays = ious.begin(), ie = ious.end(); takerPays != ie; ++takerPays) @@ -535,14 +561,16 @@ struct ExistingElementPool { StateGuard og{*this}; outerResult = prefix; - outer.emplace_into(outerResult, accF, issF, currencyF, existingAcc, existingCur, existingIss); + outer.emplace_into( + outerResult, accF, issF, currencyF, existingAcc, existingCur, existingIss); STPathElement const* prevInner = &outerResult.back(); ElementComboIter inner(prevInner); while (inner.next()) { StateGuard ig{*this}; result = outerResult; - inner.emplace_into(result, accF, issF, currencyF, existingAcc, existingCur, existingIss); + inner.emplace_into( + result, accF, issF, currencyF, existingAcc, existingCur, existingIss); result.insert(result.end(), suffix.begin(), suffix.end()); f(sendMax, deliver, result); } @@ -624,7 +652,7 @@ struct PayStrand_test : public beast::unit_test::suite std::nullopt, env.app().logs().journal("Flow")); (void)_; - BEAST_EXPECT(ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(ter)); } { STPath const path = STPath({ipe(USD), cpe(xrpCurrency())}); @@ -642,7 +670,7 @@ struct PayStrand_test : public beast::unit_test::suite std::nullopt, env.app().logs().journal("Flow")); (void)_; - BEAST_EXPECT(ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(ter)); } } @@ -659,7 +687,8 @@ struct PayStrand_test : public beast::unit_test::suite env(pay(gw, carol, USD(100))); // Insert implied account - test(env, USD, std::nullopt, STPath(), tesSUCCESS, D{alice, gw, usdC}, D{gw, bob, usdC}); + test( + env, USD, std::nullopt, STPath(), tesSUCCESS, D{alice, gw, usdC}, D{gw, bob, usdC}); env.trust(EUR(1000), alice, bob); // Insert implied offer @@ -712,7 +741,8 @@ struct PayStrand_test : public beast::unit_test::suite env, xrpIssue(), USD.issue(), - STPath({STPathElement{STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()}}), + STPath({STPathElement{ + STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()}}), tesSUCCESS, D{alice, gw, usdC}, B{USD, XRP, std::nullopt}, @@ -907,7 +937,7 @@ struct PayStrand_test : public beast::unit_test::suite ammContext, std::nullopt, env.app().logs().journal("Flow")); - BEAST_EXPECT(ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(ter)); BEAST_EXPECT(equal(strand, D{alice, gw, usdC})); } @@ -935,8 +965,9 @@ struct PayStrand_test : public beast::unit_test::suite ammContext, std::nullopt, env.app().logs().journal("Flow")); - BEAST_EXPECT(ter == tesSUCCESS); - BEAST_EXPECT(equal(strand, D{alice, gw, usdC}, B{USD.issue(), xrpIssue(), std::nullopt}, XRPS{bob})); + BEAST_EXPECT(isTesSuccess(ter)); + BEAST_EXPECT(equal( + strand, D{alice, gw, usdC}, B{USD.issue(), xrpIssue(), std::nullopt}, XRPS{bob})); } } @@ -996,7 +1027,10 @@ struct PayStrand_test : public beast::unit_test::suite env(offer(bob, USD(100), XRP(100)), txflags(tfPassive)); // payment path: XRP -> XRP/USD -> USD/XRP - env(pay(alice, carol, XRP(100)), path(~USD, ~XRP), txflags(tfNoRippleDirect), ter(temBAD_SEND_XRP_PATHS)); + env(pay(alice, carol, XRP(100)), + path(~USD, ~XRP), + txflags(tfNoRippleDirect), + ter(temBAD_SEND_XRP_PATHS)); } { @@ -1104,22 +1138,54 @@ struct PayStrand_test : public beast::unit_test::suite PaymentSandbox sb{env.current().get(), tapNONE}; { auto const r = ::xrpl::path::RippleCalc::rippleCalculate( - sb, sendMax, deliver, dstAcc, noAccount(), pathSet, std::nullopt, env.app().logs(), &inputs); + sb, + sendMax, + deliver, + dstAcc, + noAccount(), + pathSet, + std::nullopt, + env.app().logs(), + &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } { auto const r = ::xrpl::path::RippleCalc::rippleCalculate( - sb, sendMax, deliver, noAccount(), srcAcc, pathSet, std::nullopt, env.app().logs(), &inputs); + sb, + sendMax, + deliver, + noAccount(), + srcAcc, + pathSet, + std::nullopt, + env.app().logs(), + &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } { auto const r = ::xrpl::path::RippleCalc::rippleCalculate( - sb, noAccountAmount, deliver, dstAcc, srcAcc, pathSet, std::nullopt, env.app().logs(), &inputs); + sb, + noAccountAmount, + deliver, + dstAcc, + srcAcc, + pathSet, + std::nullopt, + env.app().logs(), + &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } { auto const r = ::xrpl::path::RippleCalc::rippleCalculate( - sb, sendMax, noAccountAmount, dstAcc, srcAcc, pathSet, std::nullopt, env.app().logs(), &inputs); + sb, + sendMax, + noAccountAmount, + dstAcc, + srcAcc, + pathSet, + std::nullopt, + env.app().logs(), + &inputs); BEAST_EXPECT(r.result() == temBAD_PATH); } } diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index 7a55866780..2817bc0502 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -2,8 +2,6 @@ #include #include -#include - #include #include #include @@ -18,6 +16,7 @@ #include #include #include +#include #include #include @@ -84,7 +83,10 @@ class PermissionedDEX_test : public beast::unit_test::suite return false; if (!domainOffer && sle->isFieldPresent(sfDomainID)) return false; - if (!offerInDir(sle->getFieldH256(sfBookDirectory), sle->getFieldU64(sfBookNode), (*sle)[~sfDomainID])) + if (!offerInDir( + sle->getFieldH256(sfBookDirectory), + sle->getFieldU64(sfBookNode), + (*sle)[~sfDomainID])) return false; if (sle->isFlag(lsfHybrid)) @@ -141,7 +143,8 @@ class PermissionedDEX_test : public beast::unit_test::suite do { - auto const page = env.le(keylet::page(directory, *pageIndex)); + auto const page = env.le( + keylet::page(directory, *pageIndex)); // NOLINT(bugprone-unchecked-optional-access) if (!page) break; @@ -161,7 +164,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // test preflight { Env env(*this, features - featurePermissionedDEX); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(offer(bob, XRP(10), USD(10)), domain(domainID), ter(temDISABLED)); env.close(); @@ -175,7 +179,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // preclaim - someone outside of the domain cannot create domain offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // create devin account who is not part of the domain Account devin("devin"); @@ -207,7 +212,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // preclaim - someone with expired cred cannot create domain offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // create devin account who is not part of the domain Account devin("devin"); @@ -241,7 +247,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // preclaim - cannot create an offer in a non existent domain { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); uint256 const badDomain{ "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" "E5"}; @@ -254,7 +261,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // domain { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(credentials::deleteCred(domainOwner, gw, domainOwner, credType)); env.close(); @@ -270,7 +278,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // domain { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(credentials::deleteCred(domainOwner, gw, domainOwner, credType)); env.close(); @@ -285,7 +294,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // apply - two domain offers cross with each other { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const bobOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), domain(domainID)); @@ -312,7 +322,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // apply - create lots of domain offers { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); std::vector offerSeqs; offerSeqs.reserve(100); @@ -344,9 +355,14 @@ class PermissionedDEX_test : public beast::unit_test::suite // test preflight - without enabling featurePermissionedDEX amendment { Env env(*this, features - featurePermissionedDEX); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); - env(pay(bob, alice, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(temDISABLED)); + env(pay(bob, alice, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(temDISABLED)); env.close(); env.enableFeature(featurePermissionedDEX); @@ -362,19 +378,25 @@ class PermissionedDEX_test : public beast::unit_test::suite // preclaim - cannot send payment with non existent domain { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); uint256 const badDomain{ "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" "E5"}; - env(pay(bob, alice, USD(10)), path(~USD), sendmax(XRP(10)), domain(badDomain), ter(tecNO_PERMISSION)); + env(pay(bob, alice, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(badDomain), + ter(tecNO_PERMISSION)); env.close(); } // preclaim - payment with non-domain destination fails { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(offer(bob, XRP(10), USD(10)), domain(domainID)); env.close(); @@ -389,7 +411,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // devin is not part of domain - env(pay(alice, devin, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecNO_PERMISSION)); + env(pay(alice, devin, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecNO_PERMISSION)); env.close(); // domain owner also issues a credential for devin @@ -397,7 +423,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // devin has not yet accepted cred - env(pay(alice, devin, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecNO_PERMISSION)); + env(pay(alice, devin, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecNO_PERMISSION)); env.close(); env(credentials::accept(devin, domainOwner, credType)); @@ -411,7 +441,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // preclaim - non-domain sender cannot send payment { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(offer(bob, XRP(10), USD(10)), domain(domainID)); env.close(); @@ -426,7 +457,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // devin tries to send domain payment - env(pay(devin, alice, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecNO_PERMISSION)); + env(pay(devin, alice, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecNO_PERMISSION)); env.close(); // domain owner also issues a credential for devin @@ -434,7 +469,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // devin has not yet accepted cred - env(pay(devin, alice, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecNO_PERMISSION)); + env(pay(devin, alice, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecNO_PERMISSION)); env.close(); env(credentials::accept(devin, domainOwner, credType)); @@ -448,7 +487,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // apply - domain owner can always send and receive domain payment { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(offer(bob, XRP(10), USD(10)), domain(domainID)); env.close(); @@ -474,7 +514,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // test domain cross currency payment consuming one offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // create a regular offer without domain auto const regularOfferSeq{env.seq(bob)}; @@ -484,10 +525,15 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const regularDirKey = getDefaultOfferDirKey(env, bob, regularOfferSeq); BEAST_EXPECT(regularDirKey); - BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *regularDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // a domain payment cannot consume regular offers - env(pay(alice, carol, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); // create a domain offer @@ -499,7 +545,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const domainDirKey = getDefaultOfferDirKey(env, bob, domainOfferSeq); BEAST_EXPECT(domainDirKey); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // cross-currency permissioned payment consumed // domain offer instead of regular offer @@ -509,14 +556,17 @@ class PermissionedDEX_test : public beast::unit_test::suite BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); // domain directory is empty - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 0)); - BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 0)); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(checkDirectorySize( + env, *regularDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) } // test domain payment consuming two offers in the path { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const EUR = gw["EUR"]; env.trust(EUR(1000), alice); @@ -536,7 +586,11 @@ class PermissionedDEX_test : public beast::unit_test::suite BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); // payment fail because there isn't eur offer - env(pay(alice, carol, EUR(10)), path(~USD, ~EUR), sendmax(XRP(10)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, EUR(10)), + path(~USD, ~EUR), + sendmax(XRP(10)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); @@ -548,7 +602,11 @@ class PermissionedDEX_test : public beast::unit_test::suite // alice tries to pay again, but still fails because the regular // offer cannot be consumed - env(pay(alice, carol, EUR(10)), path(~USD, ~EUR), sendmax(XRP(10)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, EUR(10)), + path(~USD, ~EUR), + sendmax(XRP(10)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); // bob creates a domain USD/EUR offer @@ -579,7 +637,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // domain payment cannot consume offer from another domain { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // Fund devin and create USD trustline Account badDomainOwner("badDomainOwner"); @@ -607,7 +666,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // domain payment can't consume an offer from another domain - env(pay(alice, carol, USD(10)), path(~USD), sendmax(XRP(10)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(10)), + path(~USD), + sendmax(XRP(10)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); // bob creates an offer under the right domain @@ -628,7 +691,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); env(offer(bob, XRP(10), USD(10)), domain(domainID)); env.close(); @@ -653,7 +717,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // offer becomes unfunded when offer owner's cred expires { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // create devin account who is not part of the domain Account devin("devin"); @@ -686,7 +751,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(std::chrono::seconds(20)); // devin's offer is unfunded now due to expired cred - env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(5)), + path(~USD), + sendmax(XRP(5)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); BEAST_EXPECT(checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true)); } @@ -694,7 +763,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // offer becomes unfunded when offer owner's cred is removed { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const offerSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), domain(domainID)); @@ -710,7 +780,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // bob's offer is unfunded now due to expired cred - env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(5)), + path(~USD), + sendmax(XRP(5)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); BEAST_EXPECT(checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true)); } @@ -725,7 +799,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // payment. If the domain wishes to control who is allowed to ripple // through, they should set the rippling individually Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const EURA = alice["EUR"]; auto const EURB = bob["EUR"]; @@ -762,7 +837,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // whether the issuer is in the domain should NOT affect whether an // offer can be consumed in domain payment Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // create an xrp/usd offer with usd as takergets auto const bobOffer1Seq{env.seq(bob)}; @@ -802,7 +878,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // checking that an unfunded offer will be implicitly removed by a // successful payment tx Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const aliceOfferSeq{env.seq(alice)}; env(offer(alice, XRP(100), USD(100)), domain(domainID)); @@ -817,7 +894,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const domainDirKey = getDefaultOfferDirKey(env, bob, bobOfferSeq); BEAST_EXPECT(domainDirKey); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 2)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 2)); // NOLINT(bugprone-unchecked-optional-access) // remove alice from domain and thus alice's offer becomes unfunded env(credentials::deleteCred(domainOwner, alice, domainOwner, credType)); @@ -830,7 +908,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // alice's unfunded offer is removed implicitly BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); - BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1)); + BEAST_EXPECT(checkDirectorySize( + env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) } void @@ -839,11 +918,16 @@ class PermissionedDEX_test : public beast::unit_test::suite testcase("AMM not used"); Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); AMM amm(env, alice, XRP(10), USD(50)); // a domain payment isn't able to consume AMM - env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(bob, carol, USD(5)), + path(~USD), + sendmax(XRP(5)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); // a non domain payment can use AMM @@ -863,9 +947,13 @@ class PermissionedDEX_test : public beast::unit_test::suite // test preflight - invalid hybrid flag { Env env(*this, features - featurePermissionedDEX); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); - env(offer(bob, XRP(10), USD(10)), domain(domainID), txflags(tfHybrid), ter(temDISABLED)); + env(offer(bob, XRP(10), USD(10)), + domain(domainID), + txflags(tfHybrid), + ter(temDISABLED)); env.close(); env(offer(bob, XRP(10), USD(10)), txflags(tfHybrid), ter(temINVALID_FLAG)); @@ -888,7 +976,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // apply - domain offer can cross with hybrid { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const bobOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), txflags(tfHybrid), domain(domainID)); @@ -910,7 +999,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // apply - open offer can cross with hybrid { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const bobOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), txflags(tfHybrid), domain(domainID)); @@ -933,7 +1023,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // domain book { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const bobOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), domain(domainID)); @@ -956,7 +1047,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // because by default, it only tries to cross domain offers { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const bobOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10))); @@ -987,7 +1079,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // in this case, the hybrid offer will be considered as unfunded even in // a regular payment Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const hybridOfferSeq{env.seq(bob)}; env(offer(bob, XRP(50), USD(50)), txflags(tfHybrid), domain(domainID)); @@ -999,7 +1092,11 @@ class PermissionedDEX_test : public beast::unit_test::suite // bob's hybrid offer is unfunded and can not be consumed in a domain // payment - env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, USD(5)), + path(~USD), + sendmax(XRP(5)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true)); @@ -1018,7 +1115,8 @@ class PermissionedDEX_test : public beast::unit_test::suite auto const sleHybridOffer = env.le(keylet::offer(bob.id(), hybridOfferSeq)); BEAST_EXPECT(sleHybridOffer); - auto const openDir = sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); + auto const openDir = + sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); BEAST_EXPECT(checkDirectorySize(env, openDir, 2)); // this normal payment should consume the regular offer and remove the @@ -1039,7 +1137,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // both non domain and domain payments can consume hybrid offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const hybridOfferSeq{env.seq(bob)}; env(offer(bob, XRP(10), USD(10)), txflags(tfHybrid), domain(domainID)); @@ -1060,7 +1159,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // wrong domainID { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); // Fund accounts Account badDomainOwner("badDomainOwner"); @@ -1085,7 +1185,11 @@ class PermissionedDEX_test : public beast::unit_test::suite env.close(); // other domains can't consume the offer - env(pay(devin, badDomainOwner, USD(5)), path(~USD), sendmax(XRP(5)), domain(badDomainID), ter(tecPATH_DRY)); + env(pay(devin, badDomainOwner, USD(5)), + path(~USD), + sendmax(XRP(5)), + domain(badDomainID), + ter(tecPATH_DRY)); env.close(); BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true)); @@ -1103,7 +1207,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // test domain payment consuming two offers w/ hybrid offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const EUR = gw["EUR"]; env.trust(EUR(1000), alice); @@ -1122,7 +1227,11 @@ class PermissionedDEX_test : public beast::unit_test::suite BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); // payment fail because there isn't eur offer - env(pay(alice, carol, EUR(5)), path(~USD, ~EUR), sendmax(XRP(5)), domain(domainID), ter(tecPATH_PARTIAL)); + env(pay(alice, carol, EUR(5)), + path(~USD, ~EUR), + sendmax(XRP(5)), + domain(domainID), + ter(tecPATH_PARTIAL)); env.close(); BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); @@ -1143,7 +1252,8 @@ class PermissionedDEX_test : public beast::unit_test::suite // test regular payment using a regular offer and a hybrid offer { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const EUR = gw["EUR"]; env.trust(EUR(1000), alice); @@ -1181,7 +1291,8 @@ class PermissionedDEX_test : public beast::unit_test::suite testHybridOfferDirectories(FeatureBitset features) { Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); std::vector offerSeqs; offerSeqs.reserve(100); @@ -1205,7 +1316,9 @@ class PermissionedDEX_test : public beast::unit_test::suite BEAST_EXPECT(sleOffer); BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir); BEAST_EXPECT(sleOffer->getFieldArray(sfAdditionalBooks).size() == 1); - BEAST_EXPECT(sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory) == openDir); + BEAST_EXPECT( + sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory) == + openDir); BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); BEAST_EXPECT(checkDirectorySize(env, domainDir, i)); @@ -1229,7 +1342,8 @@ class PermissionedDEX_test : public beast::unit_test::suite testcase("Auto bridge"); Env env(*this, features); - auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); auto const EUR = gw["EUR"]; for (auto const& account : {alice, bob, carol}) diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 8485202144..c4bbd80e74 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -1,9 +1,8 @@ #include -#include - #include #include +#include #include #include @@ -164,7 +163,8 @@ class PermissionedDomains_test : public beast::unit_test::suite "Cred0123456789012345678901234567890123456789012345678901234567890"; static_assert(longCredentialType.size() == maxCredentialTypeLength + 1); txJsonMutable["AcceptedCredentials"][2u] = credentialOrig; - txJsonMutable["AcceptedCredentials"][2u][jss::Credential]["CredentialType"] = std::string(longCredentialType); + txJsonMutable["AcceptedCredentials"][2u][jss::Credential]["CredentialType"] = + std::string(longCredentialType); BEAST_EXPECT(exceptionExpected(env, txJsonMutable).starts_with("invalidParams")); // Remove Credentialtype from a credential and apply. @@ -199,9 +199,13 @@ class PermissionedDomains_test : public beast::unit_test::suite uint256 d; if (domain) + { d = *domain; + } else + { d = pdomain::getNewDomain(env.meta()); + } env.close(); auto objects = pdomain::getObjects(account, env); auto const fromObject = pdomain::credentialsFromJson(objects[d], human2Acc); @@ -228,9 +232,13 @@ class PermissionedDomains_test : public beast::unit_test::suite uint256 d; if (domain) + { d = *domain; + } else + { d = pdomain::getNewDomain(env.meta()); + } env.close(); auto objects = pdomain::getObjects(account, env); auto const fromObject = pdomain::credentialsFromJson(objects[d], human2Acc); @@ -312,7 +320,8 @@ class PermissionedDomains_test : public beast::unit_test::suite BEAST_EXPECT(domain.isNonZero()); BEAST_EXPECT(object["LedgerEntryType"] == "PermissionedDomain"); BEAST_EXPECT(object["Owner"] == alice[0].human()); - BEAST_EXPECT(pdomain::credentialsFromJson(object, human2Acc) == longCredentials); + BEAST_EXPECT( + pdomain::credentialsFromJson(object, human2Acc) == longCredentials); break; } } @@ -342,13 +351,16 @@ class PermissionedDomains_test : public beast::unit_test::suite domain2 = pdomain::getNewDomain(env.meta()); auto objects = pdomain::getObjects(alice[0], env); auto object = objects[domain2]; - BEAST_EXPECT(pdomain::credentialsFromJson(object, human2Acc) == pdomain::sortCredentials(credentials10)); + BEAST_EXPECT( + pdomain::credentialsFromJson(object, human2Acc) == + pdomain::sortCredentials(credentials10)); } // Update with 1 credential. env(pdomain::setTx(alice[0], credentials1, domain2)); BEAST_EXPECT( - pdomain::credentialsFromJson(pdomain::getObjects(alice[0], env)[domain2], human2Acc) == credentials1); + pdomain::credentialsFromJson(pdomain::getObjects(alice[0], env)[domain2], human2Acc) == + credentials1); // Update with 10 credentials. env(pdomain::setTx(alice[0], credentials10, domain2)); @@ -475,7 +487,7 @@ class PermissionedDomains_test : public beast::unit_test::suite pdomain::Credentials credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), ter(tecINSUFFICIENT_RESERVE)); BEAST_EXPECT(env.ownerCount(alice) == 0); - BEAST_EXPECT(pdomain::getObjects(alice, env).size() == 0); + BEAST_EXPECT(pdomain::getObjects(alice, env).empty()); env.close(); auto const baseFee = env.current()->fees().base.drops(); diff --git a/src/test/app/PseudoTx_test.cpp b/src/test/app/PseudoTx_test.cpp index 9c37ef6967..e61430903b 100644 --- a/src/test/app/PseudoTx_test.cpp +++ b/src/test/app/PseudoTx_test.cpp @@ -1,8 +1,7 @@ #include -#include - #include +#include #include #include diff --git a/src/test/app/RCLValidations_test.cpp b/src/test/app/RCLValidations_test.cpp index 495176104b..a7941ea64f 100644 --- a/src/test/app/RCLValidations_test.cpp +++ b/src/test/app/RCLValidations_test.cpp @@ -17,9 +17,11 @@ class RCLValidations_test : public beast::unit_test::suite testcase("Change validation trusted status"); auto keys = randomKeyPair(KeyType::secp256k1); auto v = std::make_shared( - xrpl::NetClock::time_point{}, keys.first, keys.second, calcNodeID(keys.first), [&](STValidation& v) { - v.setFieldU32(sfLedgerSequence, 123456); - }); + xrpl::NetClock::time_point{}, + keys.first, + keys.second, + calcNodeID(keys.first), + [&](STValidation& v) { v.setFieldU32(sfLedgerSequence, 123456); }); BEAST_EXPECT(v->isTrusted()); v->setUntrusted(); @@ -54,8 +56,8 @@ class RCLValidations_test : public beast::unit_test::suite jtx::Env env(*this); Config config; - auto prev = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + auto prev = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); history.push_back(prev); for (auto i = 0; i < (2 * maxAncestors + 1); ++i) { @@ -67,7 +69,8 @@ class RCLValidations_test : public beast::unit_test::suite // altHistory agrees with first half of regular history Seq const diverge = history.size() / 2; - std::vector> altHistory(history.begin(), history.begin() + diverge); + std::vector> altHistory( + history.begin(), history.begin() + diverge); // advance clock to get new ledgers using namespace std::chrono_literals; env.timeKeeper().set(env.timeKeeper().now() + 1200s); @@ -109,9 +112,13 @@ class RCLValidations_test : public beast::unit_test::suite for (Seq s = a.seq(); s > 0; s--) { if (s >= a.minSeq()) + { BEAST_EXPECT(a[s] == history[s - 1]->header().hash); + } else + { BEAST_EXPECT(a[s] == ID{0}); + } } } @@ -211,8 +218,8 @@ class RCLValidations_test : public beast::unit_test::suite jtx::Env env(*this); auto& j = env.journal; Config config; - auto prev = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + auto prev = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); history.push_back(prev); for (auto i = 0; i < (maxAncestors + 10); ++i) { diff --git a/src/test/app/ReducedOffer_test.cpp b/src/test/app/ReducedOffer_test.cpp index b967096eb8..d0cf1b5324 100644 --- a/src/test/app/ReducedOffer_test.cpp +++ b/src/test/app/ReducedOffer_test.cpp @@ -24,12 +24,16 @@ class ReducedOffer_test : public beast::unit_test::suite offerInLedger(jtx::Env& env, jtx::Account const& acct, std::uint32_t offerSeq) { Json::Value ledgerOffer = ledgerEntryOffer(env, acct, offerSeq); - return !(ledgerOffer.isMember(jss::error) && ledgerOffer[jss::error].asString() == "entryNotFound"); + return !( + ledgerOffer.isMember(jss::error) && + ledgerOffer[jss::error].asString() == "entryNotFound"); } // Common code to clean up unneeded offers. static void - cleanupOldOffers(jtx::Env& env, std::initializer_list> list) + cleanupOldOffers( + jtx::Env& env, + std::initializer_list> list) { for (auto [acct, offerSeq] : list) env(offer_cancel(acct, offerSeq)); @@ -69,7 +73,8 @@ public: // 3. Cleans up for the next offer pair. // Returns 1 if the crossed offer has a bad rate for the book. auto exerciseOfferPair = [this, &env, &alice, &bob]( - Amounts const& inLedger, Amounts const& newOffer) -> unsigned int { + Amounts const& inLedger, + Amounts const& newOffer) -> unsigned int { // Put inLedger offer in the ledger so newOffer can cross it. std::uint32_t const aliceOfferSeq = env.seq(alice); env(offer(alice, inLedger.in, inLedger.out)); @@ -87,9 +92,11 @@ public: // alice's offer should be fully crossed and so gone from // the ledger. if (!BEAST_EXPECT(!offerInLedger(env, alice, aliceOfferSeq))) + { // If the in-ledger offer was not consumed then further // results are meaningless. return 1; + } // bob's offer should be in the ledger, but reduced in size. unsigned int badRate = 1; @@ -103,7 +110,8 @@ public: STAmount const bobGot = env.balance(bob) + bobFee - bobInitialBalance; BEAST_EXPECT(reducedTakerPays < newOffer.in); BEAST_EXPECT(reducedTakerGets < newOffer.out); - STAmount const inLedgerRate = Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate(); + STAmount const inLedgerRate = + Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate(); badRate = inLedgerRate > initialRate ? 1 : 0; @@ -115,7 +123,8 @@ public: if (badRate == 0) { STAmount const tweakedTakerPays = reducedTakerPays + drops(1); - STAmount const tweakedRate = Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate(); + STAmount const tweakedRate = + Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate(); BEAST_EXPECT(tweakedRate > initialRate); } #if 0 @@ -147,11 +156,14 @@ public: // iteration. This should mean that the size of the offer bob // places in the ledger should increase with each iteration. unsigned int blockedCount = 0; - for (std::uint64_t mantissaReduce = 1'000'000'000ull; mantissaReduce <= 5'000'000'000ull; + for (std::uint64_t mantissaReduce = 1'000'000'000ull; + mantissaReduce <= 5'000'000'000ull; mantissaReduce += 20'000'000ull) { STAmount aliceUSD{ - bobOffer.out.issue(), bobOffer.out.mantissa() - mantissaReduce, bobOffer.out.exponent()}; + bobOffer.out.issue(), + bobOffer.out.mantissa() - mantissaReduce, + bobOffer.out.exponent()}; STAmount aliceXRP{bobOffer.in.issue(), bobOffer.in.mantissa() - 1}; Amounts aliceOffer{aliceUSD, aliceXRP}; blockedCount += exerciseOfferPair(aliceOffer, bobOffer); @@ -193,7 +205,8 @@ public: // 2. Collects the results, and // 3. Cleans up for the next offer pair. auto exerciseOfferPair = [this, &env, &alice, &bob]( - Amounts const& inLedger, Amounts const& newOffer) -> unsigned int { + Amounts const& inLedger, + Amounts const& newOffer) -> unsigned int { // Get the inLedger offer into the ledger so newOffer can cross // it. STAmount const initialRate = Quality(inLedger).rate(); @@ -229,7 +242,8 @@ public: STAmount const aliceGot = env.balance(alice) - aliceInitialBalance; BEAST_EXPECT(reducedTakerPays < inLedger.in); BEAST_EXPECT(reducedTakerGets < inLedger.out); - STAmount const inLedgerRate = Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate(); + STAmount const inLedgerRate = + Quality(Amounts{reducedTakerPays, reducedTakerGets}).rate(); badRate = inLedgerRate > initialRate ? 1 : 0; // If the inLedgerRate is less than initial rate, then @@ -240,7 +254,8 @@ public: if (badRate == 0) { STAmount const tweakedTakerPays = reducedTakerPays + drops(1); - STAmount const tweakedRate = Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate(); + STAmount const tweakedRate = + Quality(Amounts{tweakedTakerPays, reducedTakerGets}).rate(); BEAST_EXPECT(tweakedRate > initialRate); } #if 0 @@ -273,11 +288,14 @@ public: // This should mean that the size of the offer alice leaves in the // ledger should increase with each iteration. unsigned int blockedCount = 0; - for (std::uint64_t mantissaReduce = 1'000'000'000ull; mantissaReduce <= 4'000'000'000ull; + for (std::uint64_t mantissaReduce = 1'000'000'000ull; + mantissaReduce <= 4'000'000'000ull; mantissaReduce += 20'000'000ull) { STAmount bobUSD{ - aliceOffer.out.issue(), aliceOffer.out.mantissa() - mantissaReduce, aliceOffer.out.exponent()}; + aliceOffer.out.issue(), + aliceOffer.out.mantissa() - mantissaReduce, + aliceOffer.out.exponent()}; STAmount bobXRP{aliceOffer.in.issue(), aliceOffer.in.mantissa() - 1}; Amounts bobOffer{bobUSD, bobXRP}; @@ -311,7 +329,8 @@ public: env.trust(USD(1000), alice, bob); int blockedOrderBookCount = 0; - for (STAmount initialBobUSD = USD(0.45); initialBobUSD <= USD(1); initialBobUSD += USD(0.025)) + for (STAmount initialBobUSD = USD(0.45); initialBobUSD <= USD(1); + initialBobUSD += USD(0.025)) { // underfund bob's offer env(pay(gw, bob, initialBobUSD)); @@ -355,7 +374,8 @@ public: cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}}); // Zero out alice's and bob's USD balances. - if (STAmount const aliceBalance = env.balance(alice, USD); aliceBalance.signum() > 0) + if (STAmount const aliceBalance = env.balance(alice, USD); + aliceBalance.signum() > 0) env(pay(alice, gw, aliceBalance)); if (STAmount const bobBalance = env.balance(bob, USD); bobBalance.signum() > 0) @@ -403,7 +423,8 @@ public: STAmount const endLoop(USD.issue(), /*mantissa*/ 50, /*exponent*/ -81); int blockedOrderBookCount = 0; - for (STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop; initialBobUSD += tinyUSD) + for (STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop; + initialBobUSD += tinyUSD) { // underfund bob's offer env(pay(gw, bob, initialBobUSD)); @@ -497,7 +518,8 @@ public: // Make one test run without fixReducedOffersV2 and one with. for (FeatureBitset features : - {testable_amendments() - fixReducedOffersV2, testable_amendments() | fixReducedOffersV2}) + {testable_amendments() - fixReducedOffersV2, + testable_amendments() | fixReducedOffersV2}) { // Make sure none of the offers we generate are under funded. Env env{*this, features}; @@ -518,15 +540,16 @@ public: // 1. Exercises one offer trio, // 2. Collects the results, and // 3. Cleans up for the next offer trio. - auto exerciseOfferTrio = - [this, &env, &alice, &bob, &carol, &USD](Amounts const& carolOffer) -> unsigned int { + auto exerciseOfferTrio = [this, &env, &alice, &bob, &carol, &USD]( + Amounts const& carolOffer) -> unsigned int { // alice submits an offer that may become a blocker. std::uint32_t const aliceOfferSeq = env.seq(alice); static Amounts const aliceInitialOffer(USD(2), drops(3382562)); env(offer(alice, aliceInitialOffer.in, aliceInitialOffer.out)); env.close(); - STAmount const initialRate = - Quality(jsonOfferToAmounts(ledgerEntryOffer(env, alice, aliceOfferSeq)[jss::node])).rate(); + STAmount const initialRate = Quality(jsonOfferToAmounts(ledgerEntryOffer( + env, alice, aliceOfferSeq)[jss::node])) + .rate(); // bob submits an offer that is more desirable than alice's std::uint32_t const bobOfferSeq = env.seq(bob); @@ -541,11 +564,14 @@ public: // carol's offer should not have made it into the ledger and // bob's offer should be fully consumed. - if (!BEAST_EXPECT(!offerInLedger(env, carol, carolOfferSeq) && !offerInLedger(env, bob, bobOfferSeq))) + if (!BEAST_EXPECT( + !offerInLedger(env, carol, carolOfferSeq) && + !offerInLedger(env, bob, bobOfferSeq))) { // If carol's or bob's offers are still in the ledger then // further results are meaningless. - cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}}); + cleanupOldOffers( + env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}}); return 1; } // alice's offer should still be in the ledger, but reduced in @@ -573,7 +599,8 @@ public: aliceReducedOffer.in.mantissa() + 1, aliceReducedOffer.in.exponent(), aliceReducedOffer.in.negative()); - STAmount const tweakedRate = Quality(Amounts{aliceReducedOffer.in, tweakedTakerGets}).rate(); + STAmount const tweakedRate = + Quality(Amounts{aliceReducedOffer.in, tweakedTakerGets}).rate(); BEAST_EXPECT(tweakedRate > initialRate); } #if 0 @@ -593,7 +620,8 @@ public: // In preparation for the next iteration make sure all three // offers are gone from the ledger. - cleanupOldOffers(env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}}); + cleanupOldOffers( + env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}}); return badRate; }; @@ -604,7 +632,8 @@ public: STAmount const step(increaseGets.issue(), 1, -8); for (unsigned int i = 0; i < loopCount; ++i) { - blockedCount += exerciseOfferTrio(Amounts(drops(1642020), USD(1) + increaseGets)); + blockedCount += + exerciseOfferTrio(Amounts(drops(1642020), USD(1) + increaseGets)); increaseGets += step; } } diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index 052b334ef5..d7535d5578 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -3,13 +3,13 @@ #include #include -#include #include #include #include #include #include +#include namespace xrpl { namespace test { @@ -59,7 +59,7 @@ struct Regression_test : public beast::unit_test::suite OpenView accum(&*next); auto const result = xrpl::apply(env.app(), accum, *jt.stx, tapNONE, env.journal); - BEAST_EXPECT(result.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(result.ter)); BEAST_EXPECT(result.applied); accum.apply(*next); @@ -270,7 +270,7 @@ struct Regression_test : public beast::unit_test::suite if (BEAST_EXPECT(bob_index.isNonZero()) && BEAST_EXPECT(digest.has_value())) { auto& cache = env.app().cachedSLEs(); - cache.del(*digest, false); + cache.del(*digest, false); // NOLINT(bugprone-unchecked-optional-access) auto const beforeCounts = mapCounts(CountedObjects::getInstance().getCounts(0)); env(check::cash(alice, bob_index, check::DeliverMin(XRP(100))), ter(tecNO_ENTRY)); @@ -278,10 +278,13 @@ struct Regression_test : public beast::unit_test::suite auto const afterCounts = mapCounts(CountedObjects::getInstance().getCounts(0)); using namespace std::string_literals; - BEAST_EXPECT(beforeCounts.at("CachedView::hit"s) == afterCounts.at("CachedView::hit"s)); BEAST_EXPECT( - beforeCounts.at("CachedView::hitExpired"s) + 1 == afterCounts.at("CachedView::hitExpired"s)); - BEAST_EXPECT(beforeCounts.at("CachedView::miss"s) == afterCounts.at("CachedView::miss"s)); + beforeCounts.at("CachedView::hit"s) == afterCounts.at("CachedView::hit"s)); + BEAST_EXPECT( + beforeCounts.at("CachedView::hitExpired"s) + 1 == + afterCounts.at("CachedView::hitExpired"s)); + BEAST_EXPECT( + beforeCounts.at("CachedView::miss"s) == afterCounts.at("CachedView::miss"s)); } } } diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index c671d6fc27..d7d3e281f4 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -44,7 +44,8 @@ class SHAMapStore_test : public beast::unit_test::suite auto const seq = json[jss::result][jss::ledger_index].asUInt(); - std::optional outInfo = env.app().getRelationalDatabase().getLedgerInfoByIndex(seq); + std::optional outInfo = + env.app().getRelationalDatabase().getLedgerInfoByIndex(seq); if (!outInfo) return false; LedgerHeader const& info = outInfo.value(); @@ -62,7 +63,8 @@ class SHAMapStore_test : public beast::unit_test::suite auto const& ledger = json[jss::result][jss::ledger]; return outHash == ledger[jss::ledger_hash].asString() && outSeq == seq && - outParentHash == ledger[jss::parent_hash].asString() && outDrops == ledger[jss::total_coins].asString() && + outParentHash == ledger[jss::parent_hash].asString() && + outDrops == ledger[jss::total_coins].asString() && outCloseTime == ledger[jss::close_time].asUInt() && outParentCloseTime == ledger[jss::parent_close_time].asUInt() && outCloseTimeResolution == ledger[jss::close_time_resolution].asUInt() && @@ -92,7 +94,7 @@ class SHAMapStore_test : public beast::unit_test::suite ledgerCheck(jtx::Env& env, int const rows, int const first) { auto const [actualRows, actualFirst, actualLast] = - dynamic_cast(&env.app().getRelationalDatabase())->getLedgerCountMinMax(); + env.app().getRelationalDatabase().getLedgerCountMinMax(); BEAST_EXPECT(actualRows == rows); BEAST_EXPECT(actualFirst == first); @@ -102,14 +104,13 @@ class SHAMapStore_test : public beast::unit_test::suite void transactionCheck(jtx::Env& env, int const rows) { - BEAST_EXPECT(dynamic_cast(&env.app().getRelationalDatabase())->getTransactionCount() == rows); + BEAST_EXPECT(env.app().getRelationalDatabase().getTransactionCount() == rows); } void accountTransactionCheck(jtx::Env& env, int const rows) { - BEAST_EXPECT( - dynamic_cast(&env.app().getRelationalDatabase())->getAccountTransactionCount() == rows); + BEAST_EXPECT(env.app().getRelationalDatabase().getAccountTransactionCount() == rows); } int @@ -187,7 +188,9 @@ public: for (auto i = 3; i < deleteInterval + lastRotated; ++i) { ledgers.emplace(std::make_pair(i, env.rpc("ledger", std::to_string(i)))); - BEAST_EXPECT(goodLedger(env, ledgers[i], std::to_string(i), true) && getHash(ledgers[i]).length()); + BEAST_EXPECT( + goodLedger(env, ledgers[i], std::to_string(i), true) && + !getHash(ledgers[i]).empty()); } ledgerCheck(env, deleteInterval + 1, 2); @@ -222,8 +225,11 @@ public: BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i + 3))); ledgers.emplace(std::make_pair(i, env.rpc("ledger", std::to_string(i)))); - BEAST_EXPECT(store.getLastRotated() == lastRotated || i == lastRotated + deleteInterval - 2); - BEAST_EXPECT(goodLedger(env, ledgers[i], std::to_string(i), true) && getHash(ledgers[i]).length()); + BEAST_EXPECT( + store.getLastRotated() == lastRotated || i == lastRotated + deleteInterval - 2); + BEAST_EXPECT( + goodLedger(env, ledgers[i], std::to_string(i), true) && + !getHash(ledgers[i]).empty()); } store.rendezvous(); @@ -395,7 +401,8 @@ public: // This does not kick off a cleanup canDelete = env.rpc("can_delete", "always"); BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result])); - BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == std::numeric_limits::max()); + BEAST_EXPECT( + canDelete[jss::result][jss::can_delete] == std::numeric_limits::max()); for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq) { @@ -540,7 +547,8 @@ public: // callback BEAST_EXPECT(dbr->getName() == "3"); }; - auto const cbReentrant = [&](std::string const& writableName, std::string const& archiveName) { + auto const cbReentrant = [&](std::string const& writableName, + std::string const& archiveName) { BEAST_EXPECT(writableName == "2"); BEAST_EXPECT(archiveName == "1"); auto newBackend = makeBackendRotating(env, scheduler, std::to_string(++threadNum)); diff --git a/src/test/app/SetAuth_test.cpp b/src/test/app/SetAuth_test.cpp index 400e2b0e29..a38cf7175a 100644 --- a/src/test/app/SetAuth_test.cpp +++ b/src/test/app/SetAuth_test.cpp @@ -17,7 +17,8 @@ struct SetAuth_test : public beast::unit_test::suite using namespace jtx; Json::Value jv; jv[jss::Account] = account.human(); - jv[jss::LimitAmount] = STAmount(Issue{to_currency(currency), dest}).getJson(JsonOptions::none); + jv[jss::LimitAmount] = + STAmount(Issue{to_currency(currency), dest}).getJson(JsonOptions::none); jv[jss::TransactionType] = jss::TrustSet; jv[jss::Flags] = tfSetfAuth; return jv; diff --git a/src/test/app/SetRegularKey_test.cpp b/src/test/app/SetRegularKey_test.cpp index aec8dddc7a..3cce01a112 100644 --- a/src/test/app/SetRegularKey_test.cpp +++ b/src/test/app/SetRegularKey_test.cpp @@ -65,13 +65,15 @@ public: env.fund(XRP(10000), alice, bob); auto ar = env.le(alice); - BEAST_EXPECT(ar->isFieldPresent(sfFlags) && ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == 0)); + BEAST_EXPECT( + ar->isFieldPresent(sfFlags) && ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == 0)); env(regkey(alice, bob), sig(alice), fee(0)); ar = env.le(alice); BEAST_EXPECT( - ar->isFieldPresent(sfFlags) && ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == lsfPasswordSpent)); + ar->isFieldPresent(sfFlags) && + ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == lsfPasswordSpent)); // The second SetRegularKey transaction with Fee=0 should fail. env(regkey(alice, bob), sig(alice), fee(0), ter(telINSUF_FEE_P)); @@ -79,7 +81,8 @@ public: env.trust(bob["USD"](1), alice); env(pay(bob, alice, bob["USD"](1))); ar = env.le(alice); - BEAST_EXPECT(ar->isFieldPresent(sfFlags) && ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == 0)); + BEAST_EXPECT( + ar->isFieldPresent(sfFlags) && ((ar->getFieldU32(sfFlags) & lsfPasswordSpent) == 0)); } void diff --git a/src/test/app/TestHostFunctions.h b/src/test/app/TestHostFunctions.h index 68cf42b362..ec3d77803c 100644 --- a/src/test/app/TestHostFunctions.h +++ b/src/test/app/TestHostFunctions.h @@ -1,13 +1,12 @@ #include #include -#include -#include -#include -#include - +#include #include #include +#include +#include +#include namespace xrpl { @@ -36,7 +35,7 @@ public: } Expected - getLedgerSqn() override + getLedgerSqn() const override { return env_.current()->seq(); } @@ -51,7 +50,8 @@ struct TestHostFunctions : public HostFunctions void const* rt_ = nullptr; public: - TestHostFunctions(test::jtx::Env& env, int cd = 0) : HostFunctions(env.journal), env_(env), clock_drift_(cd) + TestHostFunctions(test::jtx::Env& env, int cd = 0) + : HostFunctions(env.journal), env_(env), clock_drift_(cd) { accountID_ = env_.master.id(); std::string t = "10000"; @@ -71,37 +71,37 @@ public: } Expected - getLedgerSqn() override + getLedgerSqn() const override { return 12345; } Expected - getParentLedgerTime() override + getParentLedgerTime() const override { return 67890; } Expected - getParentLedgerHash() override + getParentLedgerHash() const override { return env_.current()->header().parentHash; } Expected - getBaseFee() override + getBaseFee() const override { return 10; } Expected - isAmendmentEnabled(uint256 const& amendmentId) override + isAmendmentEnabled(uint256 const& amendmentId) const override { return 1; } Expected - isAmendmentEnabled(std::string_view const& amendmentName) override + isAmendmentEnabled(std::string_view const& amendmentName) const override { return 1; } @@ -113,7 +113,7 @@ public: } Expected - getTxField(SField const& fname) override + getTxField(SField const& fname) const override { if (fname == sfAccount) return Bytes(accountID_.begin(), accountID_.end()); @@ -137,7 +137,7 @@ public: } Expected - getCurrentLedgerObjField(SField const& fname) override + getCurrentLedgerObjField(SField const& fname) const override { auto const& sn = fname.getName(); if (sn == "Destination" || sn == "Account") @@ -155,7 +155,7 @@ public: } Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) override + getLedgerObjField(int32_t cacheIdx, SField const& fname) const override { if (fname == sfBalance) { @@ -171,7 +171,7 @@ public: } Expected - getTxNestedField(Slice const& locator) override + getTxNestedField(Slice const& locator) const override { if (locator.size() == 4) { @@ -189,7 +189,7 @@ public: } Expected - getCurrentLedgerObjNestedField(Slice const& locator) override + getCurrentLedgerObjNestedField(Slice const& locator) const override { if (locator.size() == 4) { @@ -207,7 +207,7 @@ public: } Expected - getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override + getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const override { if (locator.size() == 4) { @@ -225,37 +225,37 @@ public: } Expected - getTxArrayLen(SField const& fname) override + getTxArrayLen(SField const& fname) const override { return 32; } Expected - getCurrentLedgerObjArrayLen(SField const& fname) override + getCurrentLedgerObjArrayLen(SField const& fname) const override { return 32; } Expected - getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override + getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const override { return 32; } Expected - getTxNestedArrayLen(Slice const& locator) override + getTxNestedArrayLen(Slice const& locator) const override { return 32; } Expected - getCurrentLedgerObjNestedArrayLen(Slice const& locator) override + getCurrentLedgerObjNestedArrayLen(Slice const& locator) const override { return 32; } Expected - getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override + getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const override { return 32; } @@ -267,19 +267,19 @@ public: } Expected - checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) override + checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) const override { return 1; } Expected - computeSha512HalfHash(Slice const& data) override + computeSha512HalfHash(Slice const& data) const override { return env_.current()->header().parentHash; } Expected - accountKeylet(AccountID const& account) override + accountKeylet(AccountID const& account) const override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -288,7 +288,7 @@ public: } Expected - ammKeylet(Asset const& issue1, Asset const& issue2) override + ammKeylet(Asset const& issue1, Asset const& issue2) const override { if (issue1 == issue2) return Unexpected(HostFunctionError::INVALID_PARAMS); @@ -299,7 +299,7 @@ public: } Expected - checkKeylet(AccountID const& account, std::uint32_t seq) override + checkKeylet(AccountID const& account, std::uint32_t seq) const override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -308,16 +308,18 @@ public: } Expected - credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) override + credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) + const override { - if (!subject || !issuer || credentialType.empty() || credentialType.size() > maxCredentialTypeLength) + if (!subject || !issuer || credentialType.empty() || + credentialType.size() > maxCredentialTypeLength) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::credential(subject, issuer, credentialType); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected - escrowKeylet(AccountID const& account, std::uint32_t seq) override + escrowKeylet(AccountID const& account, std::uint32_t seq) const override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -326,7 +328,7 @@ public: } Expected - oracleKeylet(AccountID const& account, std::uint32_t documentId) override + oracleKeylet(AccountID const& account, std::uint32_t documentId) const override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); @@ -335,7 +337,7 @@ public: } Expected - getNFT(AccountID const& account, uint256 const& nftId) override + getNFT(AccountID const& account, uint256 const& nftId) const override { if (!account || !nftId) { @@ -347,38 +349,38 @@ public: } Expected - getNFTIssuer(uint256 const& nftId) override + getNFTIssuer(uint256 const& nftId) const override { return Bytes(accountID_.begin(), accountID_.end()); } Expected - getNFTTaxon(uint256 const& nftId) override + getNFTTaxon(uint256 const& nftId) const override { return 4; } Expected - getNFTFlags(uint256 const& nftId) override + getNFTFlags(uint256 const& nftId) const override { return 8; } Expected - getNFTTransferFee(uint256 const& nftId) override + getNFTTransferFee(uint256 const& nftId) const override { return 10; } Expected - getNFTSerial(uint256 const& nftId) override + getNFTSerial(uint256 const& nftId) const override { return 4; } template void - log(std::string_view const& msg, F&& dataFn) + log(std::string_view const& msg, F&& dataFn) const { #ifdef DEBUG_OUTPUT auto& j = std::cerr; @@ -395,11 +397,13 @@ public: } Expected - trace(std::string_view const& msg, Slice const& data, bool asHex) override + trace(std::string_view const& msg, Slice const& data, bool asHex) const override { if (!asHex) { - log(msg, [&data] { return std::string_view(reinterpret_cast(data.data()), data.size()); }); + log(msg, [&data] { + return std::string_view(reinterpret_cast(data.data()), data.size()); + }); } else { @@ -415,95 +419,95 @@ public: } Expected - traceNum(std::string_view const& msg, int64_t data) override + traceNum(std::string_view const& msg, int64_t data) const override { log(msg, [data] { return data; }); return 0; } Expected - traceAccount(std::string_view const& msg, AccountID const& account) override + traceAccount(std::string_view const& msg, AccountID const& account) const override { log(msg, [&account] { return toBase58(account); }); return 0; } Expected - traceFloat(std::string_view const& msg, Slice const& data) override + traceFloat(std::string_view const& msg, Slice const& data) const override { log(msg, [&data] { return wasm_float::floatToString(data); }); return 0; } Expected - traceAmount(std::string_view const& msg, STAmount const& amount) override + traceAmount(std::string_view const& msg, STAmount const& amount) const override { log(msg, [&amount] { return amount.getFullText(); }); return 0; } Expected - floatFromInt(int64_t x, int32_t mode) override + floatFromInt(int64_t x, int32_t mode) const override { return wasm_float::floatFromIntImpl(x, mode); } Expected - floatFromUint(uint64_t x, int32_t mode) override + floatFromUint(uint64_t x, int32_t mode) const override { return wasm_float::floatFromUintImpl(x, mode); } Expected - floatSet(int64_t mantissa, int32_t exponent, int32_t mode) override + floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override { return wasm_float::floatSetImpl(mantissa, exponent, mode); } Expected - floatCompare(Slice const& x, Slice const& y) override + floatCompare(Slice const& x, Slice const& y) const override { return wasm_float::floatCompareImpl(x, y); } Expected - floatAdd(Slice const& x, Slice const& y, int32_t mode) override + floatAdd(Slice const& x, Slice const& y, int32_t mode) const override { return wasm_float::floatAddImpl(x, y, mode); } Expected - floatSubtract(Slice const& x, Slice const& y, int32_t mode) override + floatSubtract(Slice const& x, Slice const& y, int32_t mode) const override { return wasm_float::floatSubtractImpl(x, y, mode); } Expected - floatMultiply(Slice const& x, Slice const& y, int32_t mode) override + floatMultiply(Slice const& x, Slice const& y, int32_t mode) const override { return wasm_float::floatMultiplyImpl(x, y, mode); } Expected - floatDivide(Slice const& x, Slice const& y, int32_t mode) override + floatDivide(Slice const& x, Slice const& y, int32_t mode) const override { return wasm_float::floatDivideImpl(x, y, mode); } Expected - floatRoot(Slice const& x, int32_t n, int32_t mode) override + floatRoot(Slice const& x, int32_t n, int32_t mode) const override { return wasm_float::floatRootImpl(x, n, mode); } Expected - floatPower(Slice const& x, int32_t n, int32_t mode) override + floatPower(Slice const& x, int32_t n, int32_t mode) const override { return wasm_float::floatPowerImpl(x, n, mode); } Expected - floatLog(Slice const& x, int32_t mode) override + floatLog(Slice const& x, int32_t mode) const override { return wasm_float::floatLogImpl(x, mode); } @@ -528,737 +532,5 @@ public: } }; -struct PerfHostFunctions : public TestHostFunctions -{ - Keylet leKey; - std::shared_ptr currentLedgerObj = nullptr; - bool isLedgerObjCached = false; - - static int constexpr MAX_CACHE = 256; - std::array, MAX_CACHE> cache; - // std::optional data_; // deferred data update, not used in - // performance - std::shared_ptr tx_; - - void const* rt_ = nullptr; - - PerfHostFunctions(test::jtx::Env& env, Keylet const& k, std::shared_ptr&& tx) - : TestHostFunctions(env), leKey(k), tx_(std::move(tx)) - { - } - - Expected - getLedgerSqn() override - { - return env_.current()->seq(); - } - - Expected - getParentLedgerTime() override - { - return env_.current()->parentCloseTime().time_since_epoch().count(); - } - - Expected - getParentLedgerHash() override - { - return env_.current()->header().parentHash; - } - - Expected - getBaseFee() override - { - return env_.current()->fees().base.drops(); - } - - Expected - isAmendmentEnabled(uint256 const& amendmentId) override - { - return env_.current()->rules().enabled(amendmentId); - } - - Expected - isAmendmentEnabled(std::string_view const& amendmentName) override - { - auto const& table = env_.app().getAmendmentTable(); - auto const amendment = table.find(std::string(amendmentName)); - return env_.current()->rules().enabled(amendment); - } - - Expected, HostFunctionError> - getCurrentLedgerObj() - { - if (!isLedgerObjCached) - { - isLedgerObjCached = true; - currentLedgerObj = env_.le(leKey); - } - if (currentLedgerObj) - return currentLedgerObj; - return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); - } - - Expected, HostFunctionError> - peekCurrentLedgerObj(int32_t cacheIdx) - { - --cacheIdx; - if (cacheIdx < 0 || cacheIdx >= MAX_CACHE) - return Unexpected(HostFunctionError::SLOT_OUT_RANGE); - - if (!cache[cacheIdx]) - { // return Unexpected(HostFunctionError::INVALID_SLOT); - auto const r = getCurrentLedgerObj(); - if (!r) - return Unexpected(r.error()); - cache[cacheIdx] = *r; - } - - return cache[cacheIdx]; - } - - Expected - normalizeCacheIndex(int32_t cacheIdx) - { - --cacheIdx; - if (cacheIdx < 0 || cacheIdx >= MAX_CACHE) - return Unexpected(HostFunctionError::SLOT_OUT_RANGE); - if (!cache[cacheIdx]) - return Unexpected(HostFunctionError::EMPTY_SLOT); - return cacheIdx; - } - - virtual Expected - cacheLedgerObj(uint256 const&, int32_t cacheIdx) override - { - // auto const& keylet = keylet::unchecked(objId); - - static int32_t intIdx = 0; - - if (cacheIdx < 0 || cacheIdx > MAX_CACHE) - return Unexpected(HostFunctionError::SLOT_OUT_RANGE); - - if (!cacheIdx) - { - for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx) - if (!cache[cacheIdx]) - break; - if (cacheIdx >= MAX_CACHE) - cacheIdx = intIdx++ % MAX_CACHE; - } - else - --cacheIdx; - - if (cacheIdx >= MAX_CACHE) - return Unexpected(HostFunctionError::SLOTS_FULL); - - cache[cacheIdx] = env_.le(leKey); - if (!cache[cacheIdx]) - return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); - return cacheIdx + 1; - } - - static Expected - getAnyFieldData(STBase const* obj) - { - // auto const& fname = obj.getFName(); - if (!obj) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - - auto const stype = obj->getSType(); - switch (stype) - { - // LCOV_EXCL_START - case STI_UNKNOWN: - case STI_NOTPRESENT: - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - break; - // LCOV_EXCL_STOP - case STI_OBJECT: - case STI_ARRAY: - return Unexpected(HostFunctionError::NOT_LEAF_FIELD); - break; - case STI_ACCOUNT: { - auto const* account(static_cast(obj)); - auto const& data = account->value(); - return Bytes{data.begin(), data.end()}; - } - break; - case STI_AMOUNT: - // will be processed by serializer - break; - case STI_ISSUE: { - auto const* issue(static_cast(obj)); - Asset const& asset(issue->value()); - // XRP and IOU will be processed by serializer - if (asset.holds()) - { - // MPT - auto const& mptIssue = asset.get(); - auto const& mptID = mptIssue.getMptID(); - return Bytes{mptID.cbegin(), mptID.cend()}; - } - } - break; - case STI_VL: { - auto const* vl(static_cast(obj)); - auto const& data = vl->value(); - return Bytes{data.begin(), data.end()}; - } - break; - case STI_UINT16: { - auto const& num(static_cast const*>(obj)); - std::uint16_t const data = num->value(); - auto const* b = reinterpret_cast(&data); - auto const* e = reinterpret_cast(&data + 1); - return Bytes{b, e}; - } - case STI_UINT32: { - auto const* num(static_cast const*>(obj)); - std::uint32_t const data = num->value(); - auto const* b = reinterpret_cast(&data); - auto const* e = reinterpret_cast(&data + 1); - return Bytes{b, e}; - } - break; - default: - break; // default to serializer - } - - Serializer msg; - obj->add(msg); - auto const data = msg.getData(); - - return data; - } - - Expected - getTxField(SField const& fname) override - { - return getAnyFieldData(tx_->peekAtPField(fname)); - } - - Expected - getCurrentLedgerObjField(SField const& fname) override - { - auto const sle = getCurrentLedgerObj(); - if (!sle) - return Unexpected(sle.error()); - return getAnyFieldData((*sle)->peekAtPField(fname)); - } - - Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) override - { - auto const sle = peekCurrentLedgerObj(cacheIdx); - if (!sle) - return Unexpected(sle.error()); - return getAnyFieldData((*sle)->peekAtPField(fname)); - } - - static inline bool - noField(STBase const* field) - { - return !field || (STI_NOTPRESENT == field->getSType()) || (STI_UNKNOWN == field->getSType()); - } - - static Expected - locateField(STObject const& obj, Slice const& locator) - { - if (locator.empty() || (locator.size() & 3)) // must be multiple of 4 - return Unexpected(HostFunctionError::LOCATOR_MALFORMED); - - int32_t const* locPtr = reinterpret_cast(locator.data()); - int32_t const locSize = locator.size() / 4; - STBase const* field = nullptr; - auto const& knownSFields = SField::getKnownCodeToField(); - - { - int32_t const sfieldCode = locPtr[0]; - auto const it = knownSFields.find(sfieldCode); - if (it == knownSFields.end()) - return Unexpected(HostFunctionError::INVALID_FIELD); - - auto const& fname(*it->second); - field = obj.peekAtPField(fname); - if (noField(field)) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - } - - for (int i = 1; i < locSize; ++i) - { - int32_t const sfieldCode = locPtr[i]; - - if (STI_ARRAY == field->getSType()) - { - auto const* arr = static_cast(field); - if (sfieldCode >= arr->size()) - return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS); - field = &(arr->operator[](sfieldCode)); - } - else if (STI_OBJECT == field->getSType()) - { - auto const* o = static_cast(field); - - auto const it = knownSFields.find(sfieldCode); - if (it == knownSFields.end()) - return Unexpected(HostFunctionError::INVALID_FIELD); - - auto const& fname(*it->second); - field = o->peekAtPField(fname); - } - else // simple field must be the last one - { - return Unexpected(HostFunctionError::LOCATOR_MALFORMED); - } - - if (noField(field)) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - } - - return field; - } - - Expected - getTxNestedField(Slice const& locator) override - { - // std::cout << tx_->getJson(JsonOptions::none).toStyledString() << - // std::endl; - auto const r = locateField(*tx_, locator); - if (!r) - return Unexpected(r.error()); - return getAnyFieldData(*r); - } - - Expected - getCurrentLedgerObjNestedField(Slice const& locator) override - { - auto const sle = getCurrentLedgerObj(); - if (!sle) - return Unexpected(sle.error()); - - auto const r = locateField(**sle, locator); - if (!r) - return Unexpected(r.error()); - - return getAnyFieldData(*r); - } - - Expected - getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override - { - auto const sle = peekCurrentLedgerObj(cacheIdx); - if (!sle) - return Unexpected(sle.error()); - - auto const r = locateField(**sle, locator); - if (!r) - return Unexpected(r.error()); - - return getAnyFieldData(*r); - } - - Expected - getTxArrayLen(SField const& fname) override - { - if (fname.fieldType != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - - auto const* field = tx_->peekAtPField(fname); - if (noField(field)) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - getCurrentLedgerObjArrayLen(SField const& fname) override - { - if (fname.fieldType != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - - auto const sle = getCurrentLedgerObj(); - if (!sle) - return Unexpected(sle.error()); - - auto const* field = (*sle)->peekAtPField(fname); - if (noField(field)) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override - { - if (fname.fieldType != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - - auto const sle = peekCurrentLedgerObj(cacheIdx); - if (!sle) - return Unexpected(sle.error()); - - auto const* field = (*sle)->peekAtPField(fname); - if (noField(field)) - return Unexpected(HostFunctionError::FIELD_NOT_FOUND); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - getTxNestedArrayLen(Slice const& locator) override - { - auto const r = locateField(*tx_, locator); - if (!r) - return Unexpected(r.error()); - auto const* field = r.value(); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - getCurrentLedgerObjNestedArrayLen(Slice const& locator) override - { - auto const sle = getCurrentLedgerObj(); - if (!sle) - return Unexpected(sle.error()); - - auto const r = locateField(**sle, locator); - if (!r) - return Unexpected(r.error()); - auto const* field = r.value(); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override - { - auto const sle = peekCurrentLedgerObj(cacheIdx); - if (!sle) - return Unexpected(sle.error()); - - auto const r = locateField(**sle, locator); - if (!r) - return Unexpected(r.error()); - - auto const* field = r.value(); - - if (field->getSType() != STI_ARRAY) - return Unexpected(HostFunctionError::NO_ARRAY); - int32_t const sz = static_cast(field)->size(); - - return sz; - } - - Expected - updateData(Slice const& data) override - { - if (data.size() > maxWasmDataLength) - return Unexpected(HostFunctionError::DATA_FIELD_TOO_LARGE); - - xrpl::detail::ApplyViewBase v(env_.app().openLedger().current().get(), tapNONE); - - auto sle = v.peek(leKey); - if (!sle) - return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); - - sle->setFieldVL(sfData, data); - v.update(sle); - - return data.size(); - } - - Expected - checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) override - { - if (!publicKeyType(pubkey)) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - PublicKey const pk(pubkey); - return verify(pk, message, signature); - } - - Expected - computeSha512HalfHash(Slice const& data) override - { - auto const hash = sha512Half(data); - return hash; - } - - Expected - accountKeylet(AccountID const& account) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::account(account); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - ammKeylet(Asset const& issue1, Asset const& issue2) override - { - if (issue1 == issue2) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - // note: this should be removed with the MPT DEX amendment - if (issue1.holds() || issue2.holds()) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - auto const keylet = keylet::amm(issue1, issue2); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - checkKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::check(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) override - { - if (!subject || !issuer) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - - if (credentialType.empty() || credentialType.size() > maxCredentialTypeLength) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - auto const keylet = keylet::credential(subject, issuer, credentialType); - - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - didKeylet(AccountID const& account) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::did(account); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - delegateKeylet(AccountID const& account, AccountID const& authorize) override - { - if (!account || !authorize) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - if (account == authorize) - return Unexpected(HostFunctionError::INVALID_PARAMS); - auto const keylet = keylet::delegate(account, authorize); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - depositPreauthKeylet(AccountID const& account, AccountID const& authorize) override - { - if (!account || !authorize) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - if (account == authorize) - return Unexpected(HostFunctionError::INVALID_PARAMS); - auto const keylet = keylet::depositPreauth(account, authorize); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - escrowKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::escrow(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) override - { - if (!account1 || !account2) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - if (account1 == account2) - return Unexpected(HostFunctionError::INVALID_PARAMS); - if (currency.isZero()) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - auto const keylet = keylet::line(account1, account2, currency); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) override - { - if (!issuer) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - - auto const keylet = keylet::mptIssuance(seq, issuer); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - mptokenKeylet(MPTID const& mptid, AccountID const& holder) override - { - if (!mptid) - return Unexpected(HostFunctionError::INVALID_PARAMS); - if (!holder) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - - auto const keylet = keylet::mptoken(mptid, holder); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - nftOfferKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::nftoffer(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - offerKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::offer(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - oracleKeylet(AccountID const& account, std::uint32_t documentId) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::oracle(account, documentId); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) override - { - if (!account || !destination) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - if (account == destination) - return Unexpected(HostFunctionError::INVALID_PARAMS); - auto const keylet = keylet::payChan(account, destination, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::permissionedDomain(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - signersKeylet(AccountID const& account) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::signers(account); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - ticketKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::ticket(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - vaultKeylet(AccountID const& account, std::uint32_t seq) override - { - if (!account) - return Unexpected(HostFunctionError::INVALID_ACCOUNT); - auto const keylet = keylet::vault(account, seq); - return Bytes{keylet.key.begin(), keylet.key.end()}; - } - - Expected - getNFT(AccountID const& account, uint256 const& nftId) override - { - if (!account || !nftId) - { - getJournal().trace() << "WASM getNFT: Invalid account or NFT ID"; - return Unexpected(HostFunctionError::INVALID_PARAMS); - } - - auto obj = nft::findToken(*env_.current(), account, nftId); - if (!obj) - { - getJournal().trace() << "WASM getNFT: NFT not found"; - return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); - } - - auto ouri = obj->at(~sfURI); - if (!ouri) - return Bytes(); - - Slice const s = ouri->value(); - return Bytes(s.begin(), s.end()); - } - - Expected - getNFTIssuer(uint256 const& nftId) override - { - auto const issuer = nft::getIssuer(nftId); - if (!issuer) - return Unexpected(HostFunctionError::INVALID_PARAMS); - - return Bytes{issuer.begin(), issuer.end()}; - } - - Expected - getNFTTaxon(uint256 const& nftId) override - { - return nft::toUInt32(nft::getTaxon(nftId)); - } - - Expected - getNFTFlags(uint256 const& nftId) override - { - return nft::getFlags(nftId); - } - - Expected - getNFTTransferFee(uint256 const& nftId) override - { - return nft::getTransferFee(nftId); - } - - Expected - getNFTSerial(uint256 const& nftId) override - { - return nft::getSerial(nftId); - } -}; - } // namespace test } // namespace xrpl diff --git a/src/test/app/TheoreticalQuality_test.cpp b/src/test/app/TheoreticalQuality_test.cpp index 1f626ad477..b4f6f3697d 100644 --- a/src/test/app/TheoreticalQuality_test.cpp +++ b/src/test/app/TheoreticalQuality_test.cpp @@ -1,16 +1,15 @@ #include #include -#include -#include -#include -#include - #include #include #include #include #include +#include +#include +#include +#include namespace xrpl { namespace test { @@ -26,7 +25,9 @@ struct RippleCalcTestParams STPathSet paths; explicit RippleCalcTestParams(Json::Value const& jv) + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) : srcAccount{*parseBase58(jv[jss::Account].asString())} + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) , dstAccount{*parseBase58(jv[jss::Destination].asString())} , dstAmt{amountFromJson(sfAmount, jv[jss::Amount])} { @@ -46,16 +47,25 @@ struct RippleCalcTestParams { assert(!pe.isMember(jss::currency) && !pe.isMember(jss::issuer)); p.emplace_back( - *parseBase58(pe[jss::account].asString()), std::nullopt, std::nullopt); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *parseBase58(pe[jss::account].asString()), + std::nullopt, + std::nullopt); } else if (pe.isMember(jss::currency) && pe.isMember(jss::issuer)) { auto const currency = to_currency(pe[jss::currency].asString()); std::optional issuer; if (!isXRP(currency)) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) issuer = *parseBase58(pe[jss::issuer].asString()); + } else + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) assert(isXRP(*parseBase58(pe[jss::issuer].asString()))); + } p.emplace_back(std::nullopt, currency, issuer); } else @@ -110,7 +120,11 @@ class RandomAccountParams // Setup the trust amounts and in/out qualities (but not the balances) void - setupTrustLine(jtx::Env& env, jtx::Account const& acc, jtx::Account const& peer, Currency const& currency) + setupTrustLine( + jtx::Env& env, + jtx::Account const& acc, + jtx::Account const& peer, + Currency const& currency) { using namespace jtx; IOU const iou{peer, currency}; @@ -135,19 +149,29 @@ public: // Set the initial balance, taking into account the qualities void - setInitialBalance(jtx::Env& env, jtx::Account const& acc, jtx::Account const& peer, Currency const& currency) + setInitialBalance( + jtx::Env& env, + jtx::Account const& acc, + jtx::Account const& peer, + Currency const& currency) const { using namespace jtx; IOU const iou{acc, currency}; // This payment sets the acc's balance to `initialBalance`. // Since input qualities complicate this payment, use `sendMax` with // `initialBalance` to make sure the balance is set correctly. - env(pay(peer, acc, iou(trustAmount_)), sendmax(iou(initialBalance_)), txflags(tfPartialPayment)); + env(pay(peer, acc, iou(trustAmount_)), + sendmax(iou(initialBalance_)), + txflags(tfPartialPayment)); env.close(); } void - maybeSetInitialBalance(jtx::Env& env, jtx::Account const& acc, jtx::Account const& peer, Currency const& currency) + maybeSetInitialBalance( + jtx::Env& env, + jtx::Account const& acc, + jtx::Account const& peer, + Currency const& currency) { using namespace jtx; if (zeroOneDist_(engine_) > probRedeem_) @@ -158,7 +182,11 @@ public: // Setup the trust amounts and in/out qualities (but not the balances) on // both sides of the trust line void - setupTrustLines(jtx::Env& env, jtx::Account const& acc1, jtx::Account const& acc2, Currency const& currency) + setupTrustLines( + jtx::Env& env, + jtx::Account const& acc1, + jtx::Account const& acc2, + Currency const& currency) { setupTrustLine(env, acc1, acc2, currency); setupTrustLine(env, acc2, acc1, currency); @@ -218,9 +246,9 @@ class TheoreticalQuality_test : public beast::unit_test::suite std::nullopt, dummyJ); - BEAST_EXPECT(sr.first == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(sr.first)); - if (sr.first != tesSUCCESS) + if (!isTesSuccess(sr.first)) return; // Due to the floating point calculations, theoretical and actual @@ -236,8 +264,10 @@ class TheoreticalQuality_test : public beast::unit_test::suite for (auto const& strand : sr.second) { - Quality const theoreticalQ = *qualityUpperBound(sb, strand); - auto const f = flow(sb, strand, IOUAmount(10, 0), IOUAmount(5, 0), dummyJ); + Quality const theoreticalQ = + *qualityUpperBound(sb, strand); // NOLINT(bugprone-unchecked-optional-access) + auto const f = + flow(sb, strand, IOUAmount(10, 0), IOUAmount(5, 0), dummyJ); BEAST_EXPECT(f.success); Quality const actualQ(f.out, f.in); if (actualQ != theoreticalQ && !compareClose(actualQ, theoreticalQ)) @@ -341,7 +371,9 @@ public: // Accounts are set up, make the payment IOU const iou{accounts.back(), currency}; RippleCalcTestParams rcp{env.json( - pay(accounts.front(), accounts.back(), iou(paymentAmount)), accountsPath, txflags(tfNoRippleDirect))}; + pay(accounts.front(), accounts.back(), iou(paymentAmount)), + accountsPath, + txflags(tfNoRippleDirect))}; testCase(rcp, env.closed()); } @@ -466,7 +498,7 @@ public: return std::nullopt; try { - std::size_t pos; + std::size_t pos = 0; auto const r = stoi(s, &pos); if (pos != s.size()) return std::nullopt; diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 0835c02289..7d300f61ca 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -9,7 +9,7 @@ namespace xrpl { class Ticket_test : public beast::unit_test::suite { - /// @brief Validate metadata for a successful CreateTicket transaction. + /// @brief Validate metadata for a successful TicketCreate transaction. /// /// @param env current jtx env (tx and meta are extracted using it) void @@ -21,7 +21,8 @@ class Ticket_test : public beast::unit_test::suite { std::string const txType = tx[sfTransactionType.jsonName].asString(); - if (!BEAST_EXPECTS(txType == jss::TicketCreate, "Unexpected TransactionType: "s + txType)) + if (!BEAST_EXPECTS( + txType == jss::TicketCreate, "Unexpected TransactionType: "s + txType)) return; } @@ -96,9 +97,11 @@ class Ticket_test : public beast::unit_test::suite else { // Verify the OwnerCount did the right thing. - std::uint32_t const prevCount = {previousFields[sfOwnerCount.jsonName].asUInt()}; + std::uint32_t const prevCount = { + previousFields[sfOwnerCount.jsonName].asUInt()}; - std::uint32_t const finalCount = {finalFields[sfOwnerCount.jsonName].asUInt()}; + std::uint32_t const finalCount = { + finalFields[sfOwnerCount.jsonName].asUInt()}; BEAST_EXPECT(prevCount + count - consumedTickets == finalCount); } @@ -121,9 +124,12 @@ class Ticket_test : public beast::unit_test::suite ? previousFields[sfTicketCount.jsonName].asUInt() : 0u}; - BEAST_EXPECT((startCount == 0u) ^ previousFields.isMember(sfTicketCount.jsonName)); + BEAST_EXPECT( + (startCount == 0u) ^ previousFields.isMember(sfTicketCount.jsonName)); - BEAST_EXPECT(startCount + count - consumedTickets == finalFields[sfTicketCount.jsonName]); + BEAST_EXPECT( + startCount + count - consumedTickets == + finalFields[sfTicketCount.jsonName]); } } else if (entryType == jss::DirectoryNode) @@ -171,7 +177,8 @@ class Ticket_test : public beast::unit_test::suite // Verify the deleted ticket has the right TicketSequence. BEAST_EXPECT( - finalFields[sfTicketSequence.jsonName].asUInt() == tx[sfTicketSequence.jsonName].asUInt()); + finalFields[sfTicketSequence.jsonName].asUInt() == + tx[sfTicketSequence.jsonName].asUInt()); } } else @@ -222,19 +229,24 @@ class Ticket_test : public beast::unit_test::suite // was deleted. BEAST_EXPECT(tx[sfSequence.jsonName].asUInt() == 0); std::string const account{tx[sfAccount.jsonName].asString()}; - if (!BEAST_EXPECTS(tx.isMember(sfTicketSequence.jsonName), "Not metadata for a ticket consuming transaction.")) + if (!BEAST_EXPECTS( + tx.isMember(sfTicketSequence.jsonName), + "Not metadata for a ticket consuming transaction.")) return; std::uint32_t const ticketSeq{tx[sfTicketSequence.jsonName].asUInt()}; Json::Value const& metadata{env.meta()->getJson(JsonOptions::none)}; - if (!BEAST_EXPECTS(metadata.isMember(sfTransactionResult.jsonName), "Metadata is missing TransactionResult.")) + if (!BEAST_EXPECTS( + metadata.isMember(sfTransactionResult.jsonName), + "Metadata is missing TransactionResult.")) return; { std::string const transactionResult{metadata[sfTransactionResult.jsonName].asString()}; if (!BEAST_EXPECTS( - transactionResult == "tesSUCCESS" || transactionResult.compare(0, 3, "tec") == 0, + transactionResult == "tesSUCCESS" || + transactionResult.compare(0, 3, "tec") == 0, transactionResult + " neither tesSUCCESS nor tec")) return; } @@ -269,15 +281,20 @@ class Ticket_test : public beast::unit_test::suite "AccountRoot previous is missing TicketCount")) return; - std::uint32_t const prevTicketCount = previousFields[sfTicketCount.jsonName].asUInt(); + std::uint32_t const prevTicketCount = + previousFields[sfTicketCount.jsonName].asUInt(); BEAST_EXPECT(prevTicketCount > 0); if (prevTicketCount == 1) + { BEAST_EXPECT(!finalFields.isMember(sfTicketCount.jsonName)); + } else + { BEAST_EXPECT( finalFields.isMember(sfTicketCount.jsonName) && finalFields[sfTicketCount.jsonName].asUInt() == prevTicketCount - 1); + } } } else if (node.isMember(sfDeletedNode.jsonName)) @@ -288,10 +305,13 @@ class Ticket_test : public beast::unit_test::suite if (entryType == jss::Ticket) { // Verify the account of the deleted ticket. - BEAST_EXPECT(deleted[sfFinalFields.jsonName][sfAccount.jsonName].asString() == account); + BEAST_EXPECT( + deleted[sfFinalFields.jsonName][sfAccount.jsonName].asString() == account); // Verify the deleted ticket has the right TicketSequence. - BEAST_EXPECT(deleted[sfFinalFields.jsonName][sfTicketSequence.jsonName].asUInt() == ticketSeq); + BEAST_EXPECT( + deleted[sfFinalFields.jsonName][sfTicketSequence.jsonName].asUInt() == + ticketSeq); ++ticketsRemoved; } @@ -463,7 +483,10 @@ class Ticket_test : public beast::unit_test::suite // Give alice not quite enough to make the reserve for a total of // 250 Tickets. - env(pay(env.master, alice, env.current()->fees().accountReserve(250) - drops(1) - env.balance(alice))); + env( + pay(env.master, + alice, + env.current()->fees().accountReserve(250) - drops(1) - env.balance(alice))); env.close(); // alice doesn't quite have the reserve for a total of 250 @@ -563,7 +586,10 @@ class Ticket_test : public beast::unit_test::suite checkTicketCreateMeta(env); env.close(); - env(noop(alice), ticket::use(ticketSeq_G), json(R"({"AccountTxnID": "0"})"), ter(temINVALID)); + env(noop(alice), + ticket::use(ticketSeq_G), + json(R"({"AccountTxnID": "0"})"), + ter(temINVALID)); env.close(); env.require(owners(alice, 2), tickets(alice, 1)); } @@ -657,7 +683,8 @@ class Ticket_test : public beast::unit_test::suite error_code_i txErrCode{rpcSUCCESS}; using TxPair = std::pair, std::shared_ptr>; - std::variant maybeTx = Transaction::load(txID, env.app(), txErrCode); + std::variant maybeTx = + Transaction::load(txID, env.app(), txErrCode); BEAST_EXPECT(txErrCode == rpcSUCCESS); if (auto txPtr = std::get_if(&maybeTx)) diff --git a/src/test/app/TrustAndBalance_test.cpp b/src/test/app/TrustAndBalance_test.cpp index aa06ccbdf1..3a2aa8e7c9 100644 --- a/src/test/app/TrustAndBalance_test.cpp +++ b/src/test/app/TrustAndBalance_test.cpp @@ -215,7 +215,8 @@ class TrustAndBalance_test : public beast::unit_test::suite auto const& t = jval[jss::transaction]; return t[jss::TransactionType] == jss::Payment; })); - BEAST_EXPECT(wsc->findMsg(5s, [](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); + BEAST_EXPECT(wsc->findMsg( + 5s, [](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); BEAST_EXPECT(wsc->invoke("unsubscribe", jv)[jss::status] == "success"); } @@ -312,7 +313,9 @@ class TrustAndBalance_test : public beast::unit_test::suite void testIndirectMultiPath(bool with_rate, FeatureBitset features) { - testcase(std::string("Indirect Payment, Multi Path, ") + (with_rate ? "With " : "Without ") + " Xfer Fee, "); + testcase( + std::string("Indirect Payment, Multi Path, ") + (with_rate ? "With " : "Without ") + + " Xfer Fee, "); using namespace test::jtx; Env env{*this, features}; @@ -340,17 +343,23 @@ class TrustAndBalance_test : public beast::unit_test::suite // alice pays amazon via multiple paths if (with_rate) + { env(pay(alice, amazon, gw["USD"](150)), sendmax(alice["USD"](200)), test::jtx::path(bob), test::jtx::path(carol)); + } else + { env(pay(alice, amazon, gw["USD"](150)), test::jtx::path(bob), test::jtx::path(carol)); + } if (with_rate) { - env.require( - balance(alice, STAmount(carol["USD"].issue(), 6500000000000000ull, -14, true, STAmount::unchecked{}))); + env.require(balance( + alice, + STAmount( + carol["USD"].issue(), 6500000000000000ull, -14, true, STAmount::unchecked{}))); env.require(balance(carol, gw["USD"](35))); } else @@ -386,7 +395,8 @@ class TrustAndBalance_test : public beast::unit_test::suite char const* invoiceId = "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"; Json::Value jv; - auto tx = env.jt(pay(env.master, alice, XRP(10000)), json(sfInvoiceID.fieldName, invoiceId)); + auto tx = + env.jt(pay(env.master, alice, XRP(10000)), json(sfInvoiceID.fieldName, invoiceId)); jv[jss::tx_blob] = strHex(tx.stx->getSerializer().slice()); auto jrr = wsc->invoke("submit", jv)[jss::result]; BEAST_EXPECT(jrr[jss::status] == "success"); diff --git a/src/test/app/SetTrust_test.cpp b/src/test/app/TrustSet_test.cpp similarity index 95% rename from src/test/app/SetTrust_test.cpp rename to src/test/app/TrustSet_test.cpp index 4704365550..51828fa544 100644 --- a/src/test/app/SetTrust_test.cpp +++ b/src/test/app/TrustSet_test.cpp @@ -7,7 +7,7 @@ namespace xrpl { namespace test { -class SetTrust_test : public beast::unit_test::suite +class TrustSet_test : public beast::unit_test::suite { public: void @@ -151,9 +151,13 @@ public: testFreeTrustlines(FeatureBitset features, bool thirdLineCreatesLE, bool createOnHighAcct) { if (thirdLineCreatesLE) + { testcase("Allow two free trustlines"); + } else + { testcase("Dynamic reserve for trustline"); + } using namespace jtx; Env env(*this, features); @@ -184,7 +188,9 @@ public: if (thirdLineCreatesLE) { // creator does not have enough for the third trust line - env(trust(creator, assistor["USD"](100)), ter(tecNO_LINE_INSUF_RESERVE), require(lines(creator, 2))); + env(trust(creator, assistor["USD"](100)), + ter(tecNO_LINE_INSUF_RESERVE), + require(lines(creator, 2))); } else { @@ -221,9 +227,9 @@ public: } void - testTicketSetTrust(FeatureBitset features) + testTicketTrustSet(FeatureBitset features) { - testcase("SetTrust using a ticket"); + testcase("TrustSet using a ticket"); using namespace jtx; @@ -268,7 +274,7 @@ public: void testMalformedTransaction(FeatureBitset features) { - testcase("SetTrust checks for malformed transactions"); + testcase("TrustSet checks for malformed transactions"); using namespace jtx; Env env{*this, features}; @@ -278,10 +284,14 @@ public: env.fund(XRP(10000), gw, alice); // Require valid tf flags - for (std::uint64_t badFlag = 1u; badFlag <= std::numeric_limits::max(); badFlag *= 2) + for (std::uint64_t badFlag = 1u; badFlag <= std::numeric_limits::max(); + badFlag *= 2) { if (badFlag & tfTrustSetMask) - env(trust(alice, gw["USD"](100), static_cast(badFlag)), ter(temINVALID_FLAG)); + { + env(trust(alice, gw["USD"](100), static_cast(badFlag)), + ter(temINVALID_FLAG)); + } } // trust amount can't be XRP @@ -294,7 +304,8 @@ public: env(trust(alice, gw["USD"](-1000)), ter(temBAD_LIMIT)); // trust amount can't be from invalid issuer - env(trust_explicit_amt(alice, STAmount{Issue{to_currency("USD"), noAccount()}, 100}), ter(temDST_NEEDED)); + env(trust_explicit_amt(alice, STAmount{Issue{to_currency("USD"), noAccount()}, 100}), + ter(temDST_NEEDED)); // trust cannot be to self env(trust(alice, alice["USD"](100)), ter(temDST_IS_SRC)); @@ -400,8 +411,9 @@ public: void testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct) { - testcase << "SetTrust " << (createQuality ? "creates" : "removes") << " quality of trustline for " - << (createOnHighAcct ? "high" : "low") << " account"; + testcase << "TrustSet " << (createQuality ? "creates" : "removes") + << " quality of trustline for " << (createOnHighAcct ? "high" : "low") + << " account"; using namespace jtx; Env env{*this, features}; @@ -564,7 +576,7 @@ public: // true, true case doesn't matter since creating a trustline ledger // entry requires reserve from the creator // independent of hi/low account ids for endpoints - testTicketSetTrust(features); + testTicketTrustSet(features); testMalformedTransaction(features); testModifyQualityOfTrustline(features, false, false); testModifyQualityOfTrustline(features, false, true); @@ -587,6 +599,6 @@ public: testWithFeats(sa); } }; -BEAST_DEFINE_TESTSUITE(SetTrust, app, xrpl); +BEAST_DEFINE_TESTSUITE(TrustSet, app, xrpl); } // namespace test } // namespace xrpl diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 13feceb74e..59db7e0d5d 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -6,13 +6,13 @@ #include #include -#include #include -#include #include #include #include +#include +#include namespace xrpl { @@ -330,7 +330,13 @@ public: env(noop(env.master), fee(baseFee * 2), queued); ++metrics.txCount; - checkMetrics(*this, env, metrics.txCount, metrics.txQMaxSize, metrics.txPerLedger + 1, metrics.txPerLedger); + checkMetrics( + *this, + env, + metrics.txCount, + metrics.txQMaxSize, + metrics.txPerLedger + 1, + metrics.txPerLedger); } void @@ -543,11 +549,17 @@ public: // let's try replacing them. // The lowest fee ticket is baseFee * 2.1, trying to replace it - env(noop(alice), ticket::use(tkt1 + 18), fee(baseFee * 2.1 * 1.25 - 1), ter(telCAN_NOT_QUEUE_FEE)); + env(noop(alice), + ticket::use(tkt1 + 18), + fee(baseFee * 2.1 * 1.25 - 1), + ter(telCAN_NOT_QUEUE_FEE)); env(noop(alice), ticket::use(tkt1 + 18), fee(baseFee * 2.1 * 1.25 + 1), queued); // New lowest fee ticket is baseFee * 2.2 - env(noop(alice), ticket::use(tkt250 - 4), fee(baseFee * 2.2 * 1.25 - 1), ter(telCAN_NOT_QUEUE_FEE)); + env(noop(alice), + ticket::use(tkt250 - 4), + fee(baseFee * 2.2 * 1.25 - 1), + ter(telCAN_NOT_QUEUE_FEE)); env(noop(alice), ticket::use(tkt250 - 4), fee(baseFee * 2.2 * 1.25 + 1), queued); env.close(); @@ -710,12 +722,14 @@ public: auto aliceStat = txQ.getAccountTxs(alice.id()); BEAST_EXPECT(aliceStat.size() == 1); BEAST_EXPECT(aliceStat.begin()->feeLevel == baseFeeLevel); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(aliceStat.begin()->lastValid && *aliceStat.begin()->lastValid == 8); BEAST_EXPECT(!aliceStat.begin()->consequences.isBlocker()); auto bobStat = txQ.getAccountTxs(bob.id()); BEAST_EXPECT(bobStat.size() == 1); - BEAST_EXPECT(bobStat.begin()->feeLevel == FeeLevel64{baseFeeLevel.fee() * largeFeeMultiplier}); + BEAST_EXPECT( + bobStat.begin()->feeLevel == FeeLevel64{baseFeeLevel.fee() * largeFeeMultiplier}); BEAST_EXPECT(!bobStat.begin()->lastValid); BEAST_EXPECT(!bobStat.begin()->consequences.isBlocker()); @@ -960,7 +974,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{"minimum_txn_in_ledger_standalone", "3"}}, + {{"account_reserve", "200"}, {"owner_reserve", "50"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -1034,7 +1049,11 @@ public: auto lastLedgerSeq = env.current()->header().seq + 2; for (auto i = 0; i < 7; i++) { - env(noop(alice), seq(aliceSeq), json(jss::LastLedgerSequence, lastLedgerSeq + i), fee(--aliceFee), queued); + env(noop(alice), + seq(aliceSeq), + json(jss::LastLedgerSequence, lastLedgerSeq + i), + fee(--aliceFee), + queued); ++aliceSeq; } checkMetrics(*this, env, 8, 8, 5, 4, 513); @@ -1051,7 +1070,8 @@ public: BEAST_EXPECT(tx.feeLevel == toFeeLevel(XRPAmount(--aliceFee), baseFee)); BEAST_EXPECT(tx.lastValid); BEAST_EXPECT( - (tx.consequences.fee() == drops(aliceFee) && tx.consequences.potentialSpend() == drops(0) && + (tx.consequences.fee() == drops(aliceFee) && + tx.consequences.potentialSpend() == drops(0) && !tx.consequences.isBlocker()) || tx.seqProxy.value() == env.seq(alice) + 6); ++seq; @@ -1291,14 +1311,16 @@ public: // transaction ordering BEAST_EXPECT( aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq + gwenSeq + hankSeq + 6 == - env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) + env.seq(elmo) + env.seq(fred) + - env.seq(gwen) + env.seq(hank)); + env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) + env.seq(elmo) + + env.seq(fred) + env.seq(gwen) + env.seq(hank)); // These tests may change if TxQ ordering is changed using namespace std::string_literals; BEAST_EXPECTS( - aliceSeq == env.seq(alice), "alice: "s + std::to_string(aliceSeq) + ", " + std::to_string(env.seq(alice))); + aliceSeq == env.seq(alice), + "alice: "s + std::to_string(aliceSeq) + ", " + std::to_string(env.seq(alice))); BEAST_EXPECTS( - bobSeq + 1 == env.seq(bob), "bob: "s + std::to_string(bobSeq) + ", " + std::to_string(env.seq(bob))); + bobSeq + 1 == env.seq(bob), + "bob: "s + std::to_string(bobSeq) + ", " + std::to_string(env.seq(bob))); BEAST_EXPECTS( charlieSeq + 2 == env.seq(charlie), "charlie: "s + std::to_string(charlieSeq) + ", " + std::to_string(env.seq(charlie))); @@ -1306,13 +1328,17 @@ public: dariaSeq + 1 == env.seq(daria), "daria: "s + std::to_string(dariaSeq) + ", " + std::to_string(env.seq(daria))); BEAST_EXPECTS( - elmoSeq + 1 == env.seq(elmo), "elmo: "s + std::to_string(elmoSeq) + ", " + std::to_string(env.seq(elmo))); + elmoSeq + 1 == env.seq(elmo), + "elmo: "s + std::to_string(elmoSeq) + ", " + std::to_string(env.seq(elmo))); BEAST_EXPECTS( - fredSeq == env.seq(fred), "fred: "s + std::to_string(fredSeq) + ", " + std::to_string(env.seq(fred))); + fredSeq == env.seq(fred), + "fred: "s + std::to_string(fredSeq) + ", " + std::to_string(env.seq(fred))); BEAST_EXPECTS( - gwenSeq == env.seq(gwen), "gwen: "s + std::to_string(gwenSeq) + ", " + std::to_string(env.seq(gwen))); + gwenSeq == env.seq(gwen), + "gwen: "s + std::to_string(gwenSeq) + ", " + std::to_string(env.seq(gwen))); BEAST_EXPECTS( - hankSeq + 1 == env.seq(hank), "hank: "s + std::to_string(hankSeq) + ", " + std::to_string(env.seq(hank))); + hankSeq + 1 == env.seq(hank), + "hank: "s + std::to_string(hankSeq) + ", " + std::to_string(env.seq(hank))); // Which sequences get incremented may change if TxQ ordering is // changed @@ -1370,8 +1396,8 @@ public: // transaction ordering BEAST_EXPECT( aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq + gwenSeq + hankSeq + 7 == - env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) + env.seq(elmo) + env.seq(fred) + - env.seq(gwen) + env.seq(hank)); + env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) + env.seq(elmo) + + env.seq(fred) + env.seq(gwen) + env.seq(hank)); // These tests may change if TxQ ordering is changed BEAST_EXPECTS( aliceSeq + qTxCount1[alice.id()] - qTxCount2[alice.id()] == env.seq(alice), @@ -1468,7 +1494,8 @@ public: if (i == 4) { double const feeMultiplier = static_cast(cost.drops()) / baseFee; - medFeeLevel = FeeLevel64{static_cast(feeMultiplier * baseFeeLevel.fee())}; + medFeeLevel = + FeeLevel64{static_cast(feeMultiplier * baseFeeLevel.fee())}; } env(noop(alice), fee(cost)); @@ -1557,7 +1584,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{"minimum_txn_in_ledger_standalone", "3"}}, + {{"account_reserve", "200"}, {"owner_reserve", "50"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -1672,9 +1700,15 @@ public: env(noop(alice), seq(aliceSeq + 1), queued); // Can't replace either queued transaction with a blocker - env(fset(alice, asfAccountTxnID), seq(aliceSeq + 0), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(fset(alice, asfAccountTxnID), + seq(aliceSeq + 0), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); - env(regkey(alice, bob), seq(aliceSeq + 1), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(regkey(alice, bob), + seq(aliceSeq + 1), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); // Can't append a blocker to the queue. env(signers(alice, 2, {{bob}, {charlie}, {daria}}), @@ -1711,7 +1745,10 @@ public: env(noop(bob), queued); // We can replace the blocker with a different blocker. - env(signers(alice, 2, {{bob}, {charlie}, {daria}}), seq(aliceSeq + 0), fee(baseFee * 2.6), queued); + env(signers(alice, 2, {{bob}, {charlie}, {daria}}), + seq(aliceSeq + 0), + fee(baseFee * 2.6), + queued); // Prove that the queue is still blocked. env(noop(alice), seq(aliceSeq + 1), ter(telCAN_NOT_QUEUE_BLOCKED)); @@ -1795,12 +1832,20 @@ public: env(noop(alice), ticket::use(tkt + 1), queued); // Can't replace either queued transaction with a blocker - env(fset(alice, asfAccountTxnID), ticket::use(tkt + 1), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(fset(alice, asfAccountTxnID), + ticket::use(tkt + 1), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); - env(regkey(alice, bob), ticket::use(tkt + 2), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(regkey(alice, bob), + ticket::use(tkt + 2), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); // Can't append a blocker to the queue. - env(signers(alice, 2, {{bob}, {charlie}, {daria}}), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(signers(alice, 2, {{bob}, {charlie}, {daria}}), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); env(signers(alice, 2, {{bob}, {charlie}, {daria}}), ticket::use(tkt + 0), @@ -1832,7 +1877,10 @@ public: // Since there's an entry in the queue we cannot append a // blocker to the account's queue. env(regkey(alice, bob), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); - env(regkey(alice, bob), ticket::use(tkt + 1), fee(baseFee * 2), ter(telCAN_NOT_QUEUE_BLOCKS)); + env(regkey(alice, bob), + ticket::use(tkt + 1), + fee(baseFee * 2), + ter(telCAN_NOT_QUEUE_BLOCKS)); // However we can _replace_ that lone entry with a blocker. env(regkey(alice, bob), ticket::use(tkt + 0), fee(baseFee * 2), queued); @@ -1846,7 +1894,10 @@ public: env(noop(bob), queued); // We can replace the blocker with a different blocker. - env(signers(alice, 2, {{bob}, {charlie}, {daria}}), ticket::use(tkt + 0), fee(baseFee * 2.6), queued); + env(signers(alice, 2, {{bob}, {charlie}, {daria}}), + ticket::use(tkt + 0), + fee(baseFee * 2.6), + queued); // Prove that the queue is still blocked. env(noop(alice), ter(telCAN_NOT_QUEUE_BLOCKED)); @@ -1905,7 +1956,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{"minimum_txn_in_ledger_standalone", "3"}}, + {{"account_reserve", "200"}, {"owner_reserve", "50"}})); auto alice = Account("alice"); auto charlie = Account("charlie"); @@ -2256,8 +2308,9 @@ public: env.memoize("carol"); { auto const jtx = env.jt(offer_cancel(alice, 3), seq(5), fee(10)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2267,8 +2320,9 @@ public: auto USD = alice["USD"]; auto const jtx = env.jt(trust("carol", USD(50000000)), seq(1), fee(10)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2276,8 +2330,9 @@ public: { auto const jtx = env.jt(ticket::create(alice, 1), seq(1), fee(10)); - auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + auto const pf = + preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2399,10 +2454,13 @@ public: auto fee = env.rpc("fee"); - if (BEAST_EXPECT(fee.isMember(jss::result)) && BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) + if (BEAST_EXPECT(fee.isMember(jss::result)) && + BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) { auto const& result = fee[jss::result]; - BEAST_EXPECT(result.isMember(jss::ledger_current_index) && result[jss::ledger_current_index] == 3); + BEAST_EXPECT( + result.isMember(jss::ledger_current_index) && + result[jss::ledger_current_index] == 3); BEAST_EXPECT(result.isMember(jss::current_ledger_size)); BEAST_EXPECT(result.isMember(jss::current_queue_size)); BEAST_EXPECT(result.isMember(jss::expected_ledger_size)); @@ -2425,10 +2483,13 @@ public: fee = env.rpc("fee"); - if (BEAST_EXPECT(fee.isMember(jss::result)) && BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) + if (BEAST_EXPECT(fee.isMember(jss::result)) && + BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) { auto const& result = fee[jss::result]; - BEAST_EXPECT(result.isMember(jss::ledger_current_index) && result[jss::ledger_current_index] == 4); + BEAST_EXPECT( + result.isMember(jss::ledger_current_index) && + result[jss::ledger_current_index] == 4); BEAST_EXPECT(result.isMember(jss::current_ledger_size)); BEAST_EXPECT(result.isMember(jss::current_queue_size)); BEAST_EXPECT(result.isMember(jss::expected_ledger_size)); @@ -2549,7 +2610,9 @@ public: testcase("full queue gap handling"); auto cfg = makeConfig( - {{"minimum_txn_in_ledger_standalone", "1"}, {"ledgers_in_queue", "10"}, {"maximum_txn_per_account", "11"}}); + {{"minimum_txn_in_ledger_standalone", "1"}, + {"ledgers_in_queue", "10"}, + {"maximum_txn_per_account", "11"}}); cfg->FEES.reference_fee = 10; Env env(*this, std::move(cfg)); @@ -2694,14 +2757,18 @@ public: for (int i = 0; i < 5; ++i) { if (i == 2) + { envs( noop(alice), fee(baseFee * 100), seq(none), json(jss::LastLedgerSequence, lastLedgerSeq), ter(terQUEUED))(submitParams); + } else + { envs(noop(alice), fee(baseFee * 100), seq(none), ter(terQUEUED))(submitParams); + } } checkMetrics(*this, env, 5, std::nullopt, 7, 6); { @@ -2828,13 +2895,15 @@ public: { // account_info without the "queue" argument. auto const info = env.rpc("json", "account_info", to_string(withoutQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[jss::result].isMember(jss::queue_data)); } { // account_info with the "queue" argument. auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; BEAST_EXPECT(result.isMember(jss::queue_data)); auto const& queue_data = result[jss::queue_data]; @@ -2854,7 +2923,8 @@ public: { auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; BEAST_EXPECT(result.isMember(jss::queue_data)); auto const& queue_data = result[jss::queue_data]; @@ -2877,7 +2947,8 @@ public: { auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; auto const& data = result[jss::account_data]; BEAST_EXPECT(result.isMember(jss::queue_data)); @@ -2931,7 +3002,8 @@ public: { auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; auto const& data = result[jss::account_data]; BEAST_EXPECT(result.isMember(jss::queue_data)); @@ -2977,12 +3049,14 @@ public: } } - envs(noop(alice), fee(baseFee * 10), seq(none), ter(telCAN_NOT_QUEUE_BLOCKED))(submitParams); + envs( + noop(alice), fee(baseFee * 10), seq(none), ter(telCAN_NOT_QUEUE_BLOCKED))(submitParams); checkMetrics(*this, env, 1, 8, 5, 4); { auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; auto const& data = result[jss::account_data]; BEAST_EXPECT(result.isMember(jss::queue_data)); @@ -3045,7 +3119,8 @@ public: { auto const info = env.rpc("json", "account_info", to_string(withQueue)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& result = info[jss::result]; BEAST_EXPECT(result.isMember(jss::queue_data)); auto const& queue_data = result[jss::queue_data]; @@ -3076,7 +3151,8 @@ public: { auto const server_info = env.rpc("server_info"); - BEAST_EXPECT(server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); + BEAST_EXPECT( + server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); auto const& info = server_info[jss::result][jss::info]; BEAST_EXPECT(info.isMember(jss::load_factor) && info[jss::load_factor] == 1); BEAST_EXPECT(!info.isMember(jss::load_factor_server)); @@ -3089,12 +3165,17 @@ public: auto const& state = server_state[jss::result][jss::state]; BEAST_EXPECT(state.isMember(jss::load_factor) && state[jss::load_factor] == 256); BEAST_EXPECT(state.isMember(jss::load_base) && state[jss::load_base] == 256); - BEAST_EXPECT(state.isMember(jss::load_factor_server) && state[jss::load_factor_server] == 256); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_escalation) && state[jss::load_factor_fee_escalation] == 256); - BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) && state[jss::load_factor_fee_queue] == 256); + state.isMember(jss::load_factor_server) && state[jss::load_factor_server] == 256); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_reference) && state[jss::load_factor_fee_reference] == 256); + state.isMember(jss::load_factor_fee_escalation) && + state[jss::load_factor_fee_escalation] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_queue) && + state[jss::load_factor_fee_queue] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_reference) && + state[jss::load_factor_fee_reference] == 256); } checkMetrics(*this, env, 0, 6, 0, 3); @@ -3110,16 +3191,20 @@ public: { auto const server_info = env.rpc("server_info"); - BEAST_EXPECT(server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); + BEAST_EXPECT( + server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); auto const& info = server_info[jss::result][jss::info]; // Avoid double rounding issues by comparing to a range. BEAST_EXPECT( - info.isMember(jss::load_factor) && info[jss::load_factor] > 888.88 && info[jss::load_factor] < 888.89); - BEAST_EXPECT(info.isMember(jss::load_factor_server) && info[jss::load_factor_server] == 1); + info.isMember(jss::load_factor) && info[jss::load_factor] > 888.88 && + info[jss::load_factor] < 888.89); + BEAST_EXPECT( + info.isMember(jss::load_factor_server) && info[jss::load_factor_server] == 1); BEAST_EXPECT(!info.isMember(jss::load_factor_local)); BEAST_EXPECT(!info.isMember(jss::load_factor_net)); BEAST_EXPECT( - info.isMember(jss::load_factor_fee_escalation) && info[jss::load_factor_fee_escalation] > 888.88 && + info.isMember(jss::load_factor_fee_escalation) && + info[jss::load_factor_fee_escalation] > 888.88 && info[jss::load_factor_fee_escalation] < 888.89); } { @@ -3127,19 +3212,25 @@ public: auto const& state = server_state[jss::result][jss::state]; BEAST_EXPECT(state.isMember(jss::load_factor) && state[jss::load_factor] == 227555); BEAST_EXPECT(state.isMember(jss::load_base) && state[jss::load_base] == 256); - BEAST_EXPECT(state.isMember(jss::load_factor_server) && state[jss::load_factor_server] == 256); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_escalation) && state[jss::load_factor_fee_escalation] == 227555); - BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) && state[jss::load_factor_fee_queue] == 256); + state.isMember(jss::load_factor_server) && state[jss::load_factor_server] == 256); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_reference) && state[jss::load_factor_fee_reference] == 256); + state.isMember(jss::load_factor_fee_escalation) && + state[jss::load_factor_fee_escalation] == 227555); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_queue) && + state[jss::load_factor_fee_queue] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_reference) && + state[jss::load_factor_fee_reference] == 256); } env.app().getFeeTrack().setRemoteFee(256000); { auto const server_info = env.rpc("server_info"); - BEAST_EXPECT(server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); + BEAST_EXPECT( + server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); auto const& info = server_info[jss::result][jss::info]; // Avoid double rounding issues by comparing to a range. BEAST_EXPECT(info.isMember(jss::load_factor) && info[jss::load_factor] == 1000); @@ -3147,7 +3238,8 @@ public: BEAST_EXPECT(!info.isMember(jss::load_factor_local)); BEAST_EXPECT(info.isMember(jss::load_factor_net) && info[jss::load_factor_net] == 1000); BEAST_EXPECT( - info.isMember(jss::load_factor_fee_escalation) && info[jss::load_factor_fee_escalation] > 888.88 && + info.isMember(jss::load_factor_fee_escalation) && + info[jss::load_factor_fee_escalation] > 888.88 && info[jss::load_factor_fee_escalation] < 888.89); } { @@ -3155,12 +3247,18 @@ public: auto const& state = server_state[jss::result][jss::state]; BEAST_EXPECT(state.isMember(jss::load_factor) && state[jss::load_factor] == 256000); BEAST_EXPECT(state.isMember(jss::load_base) && state[jss::load_base] == 256); - BEAST_EXPECT(state.isMember(jss::load_factor_server) && state[jss::load_factor_server] == 256000); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_escalation) && state[jss::load_factor_fee_escalation] == 227555); - BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) && state[jss::load_factor_fee_queue] == 256); + state.isMember(jss::load_factor_server) && + state[jss::load_factor_server] == 256000); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_reference) && state[jss::load_factor_fee_reference] == 256); + state.isMember(jss::load_factor_fee_escalation) && + state[jss::load_factor_fee_escalation] == 227555); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_queue) && + state[jss::load_factor_fee_queue] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_reference) && + state[jss::load_factor_fee_reference] == 256); } env.app().getFeeTrack().setRemoteFee(256); @@ -3172,11 +3270,13 @@ public: { auto const server_info = env.rpc("server_info"); - BEAST_EXPECT(server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); + BEAST_EXPECT( + server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); auto const& info = server_info[jss::result][jss::info]; // Avoid double rounding issues by comparing to a range. BEAST_EXPECT( - info.isMember(jss::load_factor) && info[jss::load_factor] > 888.88 && info[jss::load_factor] < 888.89); + info.isMember(jss::load_factor) && info[jss::load_factor] > 888.88 && + info[jss::load_factor] < 888.89); // There can be a race between LoadManager lowering the fee, // and the call to server_info, so check a wide range. // The important thing is that it's not 1. @@ -3188,7 +3288,8 @@ public: info[jss::load_factor_local] < 2.4415); BEAST_EXPECT(!info.isMember(jss::load_factor_net)); BEAST_EXPECT( - info.isMember(jss::load_factor_fee_escalation) && info[jss::load_factor_fee_escalation] > 888.88 && + info.isMember(jss::load_factor_fee_escalation) && + info[jss::load_factor_fee_escalation] > 888.88 && info[jss::load_factor_fee_escalation] < 888.89); } { @@ -3203,17 +3304,22 @@ public: state.isMember(jss::load_factor_server) && state[jss::load_factor_server] >= 320 && state[jss::load_factor_server] <= 625); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_escalation) && state[jss::load_factor_fee_escalation] == 227555); - BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) && state[jss::load_factor_fee_queue] == 256); + state.isMember(jss::load_factor_fee_escalation) && + state[jss::load_factor_fee_escalation] == 227555); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_reference) && state[jss::load_factor_fee_reference] == 256); + state.isMember(jss::load_factor_fee_queue) && + state[jss::load_factor_fee_queue] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_reference) && + state[jss::load_factor_fee_reference] == 256); } env.close(); { auto const server_info = env.rpc("server_info"); - BEAST_EXPECT(server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); + BEAST_EXPECT( + server_info.isMember(jss::result) && server_info[jss::result].isMember(jss::info)); auto const& info = server_info[jss::result][jss::info]; // Avoid double rounding issues by comparing to a range. @@ -3221,7 +3327,8 @@ public: // and the call to server_info, so check a wide range. // The important thing is that it's not 1. BEAST_EXPECT( - info.isMember(jss::load_factor) && info[jss::load_factor] > 1.245 && info[jss::load_factor] < 2.4415); + info.isMember(jss::load_factor) && info[jss::load_factor] > 1.245 && + info[jss::load_factor] < 2.4415); BEAST_EXPECT(!info.isMember(jss::load_factor_server)); BEAST_EXPECT( info.isMember(jss::load_factor_local) && info[jss::load_factor_local] > 1.245 && @@ -3233,7 +3340,8 @@ public: auto const server_state = env.rpc("server_state"); auto const& state = server_state[jss::result][jss::state]; BEAST_EXPECT( - state.isMember(jss::load_factor) && state[jss::load_factor] >= 320 && state[jss::load_factor] <= 625); + state.isMember(jss::load_factor) && state[jss::load_factor] >= 320 && + state[jss::load_factor] <= 625); BEAST_EXPECT(state.isMember(jss::load_base) && state[jss::load_base] == 256); // There can be a race between LoadManager lowering the fee, // and the call to server_info, so check a wide range. @@ -3242,10 +3350,14 @@ public: state.isMember(jss::load_factor_server) && state[jss::load_factor_server] >= 320 && state[jss::load_factor_server] <= 625); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_escalation) && state[jss::load_factor_fee_escalation] == 256); - BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) && state[jss::load_factor_fee_queue] == 256); + state.isMember(jss::load_factor_fee_escalation) && + state[jss::load_factor_fee_escalation] == 256); BEAST_EXPECT( - state.isMember(jss::load_factor_fee_reference) && state[jss::load_factor_fee_reference] == 256); + state.isMember(jss::load_factor_fee_queue) && + state[jss::load_factor_fee_queue] == 256); + BEAST_EXPECT( + state.isMember(jss::load_factor_fee_reference) && + state[jss::load_factor_fee_reference] == 256); } } @@ -3276,20 +3388,26 @@ public: // First transaction establishes the messaging using namespace std::chrono_literals; BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 256 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 256 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 256 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); // Last transaction escalates the fee BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 227555 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 227555 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 227555 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 227555 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); @@ -3297,11 +3415,14 @@ public: // Closing ledger should publish a status update BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 256 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 256 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 256 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); @@ -3323,37 +3444,47 @@ public: // Last transaction escalates the fee BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 200000 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 200000 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 200000 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 200000 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); env.close(); // Ledger close publishes with escalated fees for queued transactions BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 184320 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 184320 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 184320 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 184320 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); env.close(); // ledger close clears queue so fee is back to normal BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 && - jv.isMember(jss::load_base) && jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && - jv[jss::load_factor_server] == 256 && jv.isMember(jss::load_factor_fee_escalation) && - jv[jss::load_factor_fee_escalation] == 256 && jv.isMember(jss::load_factor_fee_queue) && - jv[jss::load_factor_fee_queue] == 256 && jv.isMember(jss::load_factor_fee_reference) && + return jv[jss::type] == "serverStatus" && jv.isMember(jss::load_factor) && + jv[jss::load_factor] == 256 && jv.isMember(jss::load_base) && + jv[jss::load_base] == 256 && jv.isMember(jss::load_factor_server) && + jv[jss::load_factor_server] == 256 && + jv.isMember(jss::load_factor_fee_escalation) && + jv[jss::load_factor_fee_escalation] == 256 && + jv.isMember(jss::load_factor_fee_queue) && jv[jss::load_factor_fee_queue] == 256 && + jv.isMember(jss::load_factor_fee_reference) && jv[jss::load_factor_fee_reference] == 256; })); - BEAST_EXPECT(!wsc->findMsg(1s, [&](auto const& jv) { return jv[jss::type] == "serverStatus"; })); + BEAST_EXPECT( + !wsc->findMsg(1s, [&](auto const& jv) { return jv[jss::type] == "serverStatus"; })); auto jv = wsc->invoke("unsubscribe", stream); BEAST_EXPECT(jv[jss::status] == "success"); @@ -3376,7 +3507,8 @@ public: fillQueue(env, alice); auto calcTotalFee = [&](std::int64_t alreadyPaid, - std::optional numToClear = std::nullopt) -> std::uint64_t { + std::optional numToClear = + std::nullopt) -> std::uint64_t { auto totalFactor = 0; auto const metrics = env.app().getTxQ().getMetrics(*env.current()); if (!numToClear) @@ -3744,7 +3876,7 @@ public: env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { auto const tx = env.jt(noop(alice), seq(aliceSeq), fee(openLedgerCost(env))); auto const result = xrpl::apply(env.app(), view, *tx.stx, tapUNLIMITED, j); - BEAST_EXPECT(result.ter == tesSUCCESS && result.applied); + BEAST_EXPECT(isTesSuccess(result.ter) && result.applied); return result.applied; }); // the queued transaction is still there @@ -3812,7 +3944,7 @@ public: env.app().openLedger().modify([&](OpenView& view, beast::Journal j) { auto const tx = env.jt(noop(alice), ticket::use(tktSeq0 + 1), fee(openLedgerCost(env))); auto const result = xrpl::apply(env.app(), view, *tx.stx, tapUNLIMITED, j); - BEAST_EXPECT(result.ter == tesSUCCESS && result.applied); + BEAST_EXPECT(isTesSuccess(result.ter) && result.applied); return result.applied; }); // the queued transaction is still there @@ -3932,7 +4064,13 @@ public: auto const baseFee = env.current()->fees().base.drops(); // We're very close to the flag ledger. Fill the ledger. fillQueue(env, alice); - checkMetrics(*this, env, 0, ledgersInQueue * expectedPerLedger, expectedPerLedger + 1, expectedPerLedger); + checkMetrics( + *this, + env, + 0, + ledgersInQueue * expectedPerLedger, + expectedPerLedger + 1, + expectedPerLedger); // Fill everyone's queues. auto seqAlice = env.seq(alice); @@ -3944,7 +4082,9 @@ public: // Use fees to guarantee order int txFee{static_cast(baseFee * 9)}; - auto prepareFee = [&](uint64_t multiplier) { return fee(txFee - multiplier * baseFee / 10); }; + auto prepareFee = [&](uint64_t multiplier) { + return fee(txFee - multiplier * baseFee / 10); + }; uint64_t multiplier = 0; for (int i = 0; i < 10; ++i) @@ -3958,7 +4098,12 @@ public: } std::size_t expectedInQueue = multiplier; checkMetrics( - *this, env, expectedInQueue, ledgersInQueue * expectedPerLedger, expectedPerLedger + 1, expectedPerLedger); + *this, + env, + expectedInQueue, + ledgersInQueue * expectedPerLedger, + expectedPerLedger + 1, + expectedPerLedger); // The next close should cause the in-ledger amendments to change. // Alice's queued transactions have a cached PreflightResult @@ -3973,20 +4118,36 @@ public: { env.close(closeDuration); auto expectedInLedger = expectedInQueue; - expectedInQueue = (expectedInQueue > expectedPerLedger + 2 ? expectedInQueue - (expectedPerLedger + 2) : 0); + expectedInQueue = + (expectedInQueue > expectedPerLedger + 2 ? expectedInQueue - (expectedPerLedger + 2) + : 0); expectedInLedger -= expectedInQueue; ++expectedPerLedger; checkMetrics( - *this, env, expectedInQueue, ledgersInQueue * expectedPerLedger, expectedInLedger, expectedPerLedger); + *this, + env, + expectedInQueue, + ledgersInQueue * expectedPerLedger, + expectedInLedger, + expectedPerLedger); { auto const expectedPerAccount = expectedInQueue / 6; auto const expectedRemainder = expectedInQueue % 6; BEAST_EXPECT(env.seq(alice) == seqAlice - expectedPerAccount); - BEAST_EXPECT(env.seq(bob) == seqBob - expectedPerAccount - (expectedRemainder > 4 ? 1 : 0)); - BEAST_EXPECT(env.seq(carol) == seqCarol - expectedPerAccount - (expectedRemainder > 3 ? 1 : 0)); - BEAST_EXPECT(env.seq(daria) == seqDaria - expectedPerAccount - (expectedRemainder > 2 ? 1 : 0)); - BEAST_EXPECT(env.seq(ellie) == seqEllie - expectedPerAccount - (expectedRemainder > 1 ? 1 : 0)); - BEAST_EXPECT(env.seq(fiona) == seqFiona - expectedPerAccount - (expectedRemainder > 0 ? 1 : 0)); + BEAST_EXPECT( + env.seq(bob) == seqBob - expectedPerAccount - (expectedRemainder > 4 ? 1 : 0)); + BEAST_EXPECT( + env.seq(carol) == + seqCarol - expectedPerAccount - (expectedRemainder > 3 ? 1 : 0)); + BEAST_EXPECT( + env.seq(daria) == + seqDaria - expectedPerAccount - (expectedRemainder > 2 ? 1 : 0)); + BEAST_EXPECT( + env.seq(ellie) == + seqEllie - expectedPerAccount - (expectedRemainder > 1 ? 1 : 0)); + BEAST_EXPECT( + env.seq(fiona) == + seqFiona - expectedPerAccount - (expectedRemainder > 0 ? 1 : 0)); } } while (expectedInQueue > 0); } @@ -4338,22 +4499,29 @@ public: { auto const fee = env.rpc("fee"); - if (BEAST_EXPECT(fee.isMember(jss::result)) && BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) + if (BEAST_EXPECT(fee.isMember(jss::result)) && + BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) { auto const& result = fee[jss::result]; BEAST_EXPECT(result.isMember(jss::levels)); auto const& levels = result[jss::levels]; - BEAST_EXPECT(levels.isMember(jss::median_level) && levels[jss::median_level] == "128000"); - BEAST_EXPECT(levels.isMember(jss::minimum_level) && levels[jss::minimum_level] == "256"); - BEAST_EXPECT(levels.isMember(jss::open_ledger_level) && levels[jss::open_ledger_level] == "256"); - BEAST_EXPECT(levels.isMember(jss::reference_level) && levels[jss::reference_level] == "256"); + BEAST_EXPECT( + levels.isMember(jss::median_level) && levels[jss::median_level] == "128000"); + BEAST_EXPECT( + levels.isMember(jss::minimum_level) && levels[jss::minimum_level] == "256"); + BEAST_EXPECT( + levels.isMember(jss::open_ledger_level) && + levels[jss::open_ledger_level] == "256"); + BEAST_EXPECT( + levels.isMember(jss::reference_level) && levels[jss::reference_level] == "256"); auto const& drops = result[jss::drops]; BEAST_EXPECT(drops.isMember(jss::base_fee) && drops[jss::base_fee] == "0"); BEAST_EXPECT(drops.isMember(jss::median_fee) && drops[jss::median_fee] == "0"); BEAST_EXPECT(drops.isMember(jss::minimum_fee) && drops[jss::minimum_fee] == "0"); - BEAST_EXPECT(drops.isMember(jss::open_ledger_fee) && drops[jss::open_ledger_fee] == "0"); + BEAST_EXPECT( + drops.isMember(jss::open_ledger_fee) && drops[jss::open_ledger_fee] == "0"); } } @@ -4389,22 +4557,29 @@ public: { auto const fee = env.rpc("fee"); - if (BEAST_EXPECT(fee.isMember(jss::result)) && BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) + if (BEAST_EXPECT(fee.isMember(jss::result)) && + BEAST_EXPECT(!RPC::contains_error(fee[jss::result]))) { auto const& result = fee[jss::result]; BEAST_EXPECT(result.isMember(jss::levels)); auto const& levels = result[jss::levels]; - BEAST_EXPECT(levels.isMember(jss::median_level) && levels[jss::median_level] == "128000"); - BEAST_EXPECT(levels.isMember(jss::minimum_level) && levels[jss::minimum_level] == "256"); - BEAST_EXPECT(levels.isMember(jss::open_ledger_level) && levels[jss::open_ledger_level] == "355555"); - BEAST_EXPECT(levels.isMember(jss::reference_level) && levels[jss::reference_level] == "256"); + BEAST_EXPECT( + levels.isMember(jss::median_level) && levels[jss::median_level] == "128000"); + BEAST_EXPECT( + levels.isMember(jss::minimum_level) && levels[jss::minimum_level] == "256"); + BEAST_EXPECT( + levels.isMember(jss::open_ledger_level) && + levels[jss::open_ledger_level] == "355555"); + BEAST_EXPECT( + levels.isMember(jss::reference_level) && levels[jss::reference_level] == "256"); auto const& drops = result[jss::drops]; BEAST_EXPECT(drops.isMember(jss::base_fee) && drops[jss::base_fee] == "0"); BEAST_EXPECT(drops.isMember(jss::median_fee) && drops[jss::median_fee] == "0"); BEAST_EXPECT(drops.isMember(jss::minimum_fee) && drops[jss::minimum_fee] == "0"); - BEAST_EXPECT(drops.isMember(jss::open_ledger_fee) && drops[jss::open_ledger_fee] == "1389"); + BEAST_EXPECT( + drops.isMember(jss::open_ledger_fee) && drops[jss::open_ledger_fee] == "1389"); } } diff --git a/src/test/app/ValidatorKeys_test.cpp b/src/test/app/ValidatorKeys_test.cpp index fac8a4bb7e..9efe318662 100644 --- a/src/test/app/ValidatorKeys_test.cpp +++ b/src/test/app/ValidatorKeys_test.cpp @@ -1,12 +1,13 @@ #include +#include -#include #include #include #include #include #include +#include #include @@ -22,19 +23,15 @@ class ValidatorKeys_test : public beast::unit_test::suite std::string const tokenSecretStr = "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi"; std::vector const tokenBlob = { - " " - "eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n", - " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl " - " \n", - "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE" - "\n", - "\t " - "hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t " - "\t\n", + " eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n", + " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n", + "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n", + "\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n", "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n", "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n", "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n", - "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n"}; + "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n", + }; std::string const tokenManifest = "JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/vCxHXXLplc2GnMhAkE1agqXxBwD" @@ -52,7 +49,8 @@ class ValidatorKeys_test : public beast::unit_test::suite "NWF6dTJMVHlqL1pjQkpBbitmNGhtQTQ0U0tYbGtTTUFqak1rSWRyR1Rxa21SNjBzVG\n", "JaTjZOOUYwdk9UV3VYcUZ6eDFoSGIyL0RqWElVZXhDVGlITEcxTG9UdUp1eXdXbk55\n", "RFE9PSIsInZhbGlkYXRpb25fc2VjcmV0X2tleSI6IjkyRDhCNDBGMzYwMTc5MTkwMU\n", - "MzQTUzMzI3NzBDMkUwMTA4MDI0NTZFOEM2QkI0NEQ0N0FFREQ0NzJGMDQ2RkYifQ==\n"}; + "MzQTUzMzI3NzBDMkUwMTA4MDI0NTZFOEM2QkI0NEQ0N0FFREQ0NzJGMDQ2RkYifQ==\n", + }; public: void @@ -64,17 +62,22 @@ public: beast::Journal journal{env.app().journal("ValidatorKeys_test")}; // Keys/ID when using [validation_seed] - SecretKey const seedSecretKey = generateSecretKey(KeyType::secp256k1, *parseBase58(seed)); + SecretKey const seedSecretKey = + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + generateSecretKey(KeyType::secp256k1, *parseBase58(seed)); PublicKey const seedPublicKey = derivePublicKey(KeyType::secp256k1, seedSecretKey); NodeID const seedNodeID = calcNodeID(seedPublicKey); // Keys when using [validation_token] + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const tokenSecretKey = *parseBase58(TokenType::NodePrivate, tokenSecretStr); auto const tokenPublicKey = derivePublicKey(KeyType::secp256k1, tokenSecretKey); auto const m = deserializeManifest(base64_decode(tokenManifest)); BEAST_EXPECT(m); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) NodeID const tokenNodeID = calcNodeID(m->masterKey); { @@ -91,10 +94,10 @@ public: c.section(SECTION_VALIDATION_SEED).append(seed); ValidatorKeys k{c, journal}; - if (BEAST_EXPECT(k.keys)) + if (BEAST_EXPECT(k.keys); k.keys.has_value()) { BEAST_EXPECT(k.keys->publicKey == seedPublicKey); - BEAST_EXPECT(k.keys->secretKey == seedSecretKey); + BEAST_EXPECT(test::equal(k.keys->secretKey, seedSecretKey)); } BEAST_EXPECT(k.nodeID == seedNodeID); BEAST_EXPECT(k.manifest.empty()); @@ -118,10 +121,10 @@ public: c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob); ValidatorKeys k{c, journal}; - if (BEAST_EXPECT(k.keys)) + if (BEAST_EXPECT(k.keys); k.keys.has_value()) { BEAST_EXPECT(k.keys->publicKey == tokenPublicKey); - BEAST_EXPECT(k.keys->secretKey == tokenSecretKey); + BEAST_EXPECT(test::equal(k.keys->secretKey, tokenSecretKey)); } BEAST_EXPECT(k.nodeID == tokenNodeID); BEAST_EXPECT(k.manifest == tokenManifest); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 4955686966..32e899ad95 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -42,7 +42,12 @@ private: } static std::string - makeManifestString(PublicKey const& pk, SecretKey const& sk, PublicKey const& spk, SecretKey const& ssk, int seq) + makeManifestString( + PublicKey const& pk, + SecretKey const& sk, + PublicKey const& spk, + SecretKey const& ssk, + int seq) { STObject st(sfGeneric); st[sfSequence] = seq; @@ -51,9 +56,11 @@ private: if (seq != std::numeric_limits::max()) { st[sfSigningPubKey] = spk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk); } + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -69,6 +76,7 @@ private: st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature); Serializer s; @@ -86,7 +94,8 @@ private: return { masterPublic, signingKeys.first, - base64_encode(makeManifestString(masterPublic, secret, signingKeys.first, signingKeys.second, 1))}; + base64_encode(makeManifestString( + masterPublic, secret, signingKeys.first, signingKeys.second, 1))}; } std::string @@ -96,16 +105,16 @@ private: std::size_t validUntil, std::optional validFrom = {}) { - std::string data = - "{\"sequence\":" + std::to_string(sequence) + ",\"expiration\":" + std::to_string(validUntil); + std::string data = "{\"sequence\":" + std::to_string(sequence) + + ",\"expiration\":" + std::to_string(validUntil); if (validFrom) data += ",\"effective\":" + std::to_string(*validFrom); data += ",\"validators\":["; for (auto const& val : validators) { - data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + "\",\"manifest\":\"" + val.manifest + - "\"},"; + data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + + "\",\"manifest\":\"" + val.manifest + "\"},"; } data.pop_back(); @@ -154,13 +163,22 @@ private: auto& app = env.app(); { auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); BEAST_EXPECT(trustedKeys->quorum() == 1); } { std::size_t minQuorum = 0; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal, minQuorum); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal, + minQuorum); BEAST_EXPECT(trustedKeys->quorum() == minQuorum); } } @@ -181,8 +199,8 @@ private: auto const localMasterSecret = randomSecretKey(); auto const localMasterPublic = derivePublicKey(KeyType::ed25519, localMasterSecret); - std::string const cfgManifest( - makeManifestString(localMasterPublic, localMasterSecret, localSigningPublicOuter, localSigningSecret, 1)); + std::string const cfgManifest(makeManifestString( + localMasterPublic, localMasterSecret, localSigningPublicOuter, localSigningSecret, 1)); auto format = [](PublicKey const& publicKey, char const* comment = nullptr) { auto ret = toBase58(TokenType::NodePublic, publicKey); @@ -213,17 +231,24 @@ private: { ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); // Correct (empty) configuration BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, emptyCfgPublishers)); // load local validator key with or without manifest - BEAST_EXPECT(trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); + BEAST_EXPECT( + trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifests.applyManifest(*deserializeManifest(cfgManifest)); - BEAST_EXPECT(trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); + BEAST_EXPECT( + trustedKeys->load(localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->listed(localMasterPublic)); BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter)); @@ -232,7 +257,11 @@ private: // load should add validator keys from config ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); BEAST_EXPECT(trustedKeys->load({}, cfgKeys, emptyCfgPublishers)); @@ -243,7 +272,8 @@ private: auto const masterNode1 = randomMasterKey(); auto const masterNode2 = randomMasterKey(); - std::vector cfgMasterKeys({format(masterNode1), format(masterNode2, " Comment")}); + std::vector cfgMasterKeys( + {format(masterNode1), format(masterNode2, " Comment")}); BEAST_EXPECT(trustedKeys->load({}, cfgMasterKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->listed(masterNode1)); BEAST_EXPECT(trustedKeys->listed(masterNode2)); @@ -254,20 +284,26 @@ private: // load terminates when encountering an invalid entry auto const goodKey = randomNode(); - BEAST_EXPECT(!trustedKeys->load({}, {format(randomNode(), "!"), format(goodKey)}, emptyCfgPublishers)); + BEAST_EXPECT(!trustedKeys->load( + {}, {format(randomNode(), "!"), format(goodKey)}, emptyCfgPublishers)); BEAST_EXPECT(!trustedKeys->listed(goodKey)); } { // local validator key on config list ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); - auto const localSigningPublic = parseBase58(TokenType::NodePublic, cfgKeys.front()); - - BEAST_EXPECT(trustedKeys->load(*localSigningPublic, cfgKeys, emptyCfgPublishers)); + auto const localSigningPublic = + parseBase58(TokenType::NodePublic, cfgKeys.front()); + BEAST_EXPECT(trustedKeys->load(localSigningPublic, cfgKeys, emptyCfgPublishers)); BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(trustedKeys->listed(*localSigningPublic)); for (auto const& n : configList) BEAST_EXPECT(trustedKeys->listed(n)); @@ -276,7 +312,11 @@ private: // local validator key not on config list ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const localSigningPublic = randomNode(); BEAST_EXPECT(trustedKeys->load(localSigningPublic, cfgKeys, emptyCfgPublishers)); @@ -290,8 +330,13 @@ private: // local validator key (with manifest) not on config list ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifests.applyManifest(*deserializeManifest(cfgManifest)); BEAST_EXPECT(trustedKeys->load(localSigningPublicOuter, cfgKeys, emptyCfgPublishers)); @@ -305,7 +350,11 @@ private: { ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); // load should reject invalid validator list signing keys std::vector badPublishers({"NotASigningKey"}); @@ -335,9 +384,14 @@ private: { ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); - std::vector keys({randomMasterKey(), randomMasterKey(), randomMasterKey(), randomMasterKey()}); + std::vector keys( + {randomMasterKey(), randomMasterKey(), randomMasterKey(), randomMasterKey()}); std::vector cfgPublishers; for (auto const& key : keys) cfgPublishers.push_back(strHex(key)); @@ -354,13 +408,18 @@ private: ManifestCache valManifests; ManifestCache pubManifests; auto trustedKeys = std::make_unique( - valManifests, pubManifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + valManifests, + pubManifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const pubRevokedSecret = randomSecretKey(); auto const pubRevokedPublic = derivePublicKey(KeyType::ed25519, pubRevokedSecret); auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1); // make this manifest revoked (seq num = max) // -- thus should not be loaded + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) pubManifests.applyManifest(*deserializeManifest(makeManifestString( pubRevokedPublic, pubRevokedSecret, @@ -372,7 +431,8 @@ private: auto legitKey1 = randomMasterKey(); auto legitKey2 = randomMasterKey(); - std::vector cfgPublishers = {strHex(pubRevokedPublic), strHex(legitKey1), strHex(legitKey2)}; + std::vector cfgPublishers = { + strHex(pubRevokedPublic), strHex(legitKey1), strHex(legitKey2)}; BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); BEAST_EXPECT(!trustedKeys->trustedPublisher(pubRevokedPublic)); @@ -387,13 +447,18 @@ private: ManifestCache valManifests; ManifestCache pubManifests; auto trustedKeys = std::make_unique( - valManifests, pubManifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + valManifests, + pubManifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const pubRevokedSecret = randomSecretKey(); auto const pubRevokedPublic = derivePublicKey(KeyType::ed25519, pubRevokedSecret); auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1); // make this manifest revoked (seq num = max) // -- thus should not be loaded + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) pubManifests.applyManifest(*deserializeManifest(makeManifestString( pubRevokedPublic, pubRevokedSecret, @@ -422,51 +487,56 @@ private: std::string const siteUri = "testApplyList.test"; - auto checkAvailable = [this]( - auto const& trustedKeys, - auto const& hexPublic, - auto const& manifest, - auto const version, - std::vector> const& expected) { - auto const available = trustedKeys->getAvailable(hexPublic); + auto checkAvailable = + [this]( + auto const& trustedKeys, + auto const& hexPublic, + auto const& manifest, + auto const version, + std::vector> const& expected) { + auto const available = trustedKeys->getAvailable(hexPublic); - BEAST_EXPECT(!version || available); - if (available) - { - auto const& a = *available; - BEAST_EXPECT(a[jss::public_key] == hexPublic); - BEAST_EXPECT(a[jss::manifest] == manifest); - // Because multiple lists were processed, the version was - // overridden - BEAST_EXPECT(a[jss::version] == version); - if (version == 1) + BEAST_EXPECT(!version || available); + if (available) { - BEAST_EXPECT(expected.size() == 1); - BEAST_EXPECT(a[jss::blob] == expected[0].first); - BEAST_EXPECT(a[jss::signature] == expected[0].second); - BEAST_EXPECT(!a.isMember(jss::blobs_v2)); - } - else if (BEAST_EXPECT(a.isMember(jss::blobs_v2))) - { - BEAST_EXPECT(!a.isMember(jss::blob)); - BEAST_EXPECT(!a.isMember(jss::signature)); - auto const& blobs_v2 = a[jss::blobs_v2]; - BEAST_EXPECT(blobs_v2.isArray() && blobs_v2.size() == expected.size()); - - for (unsigned int i = 0; i < expected.size(); ++i) + auto const& a = *available; + BEAST_EXPECT(a[jss::public_key] == hexPublic); + BEAST_EXPECT(a[jss::manifest] == manifest); + // Because multiple lists were processed, the version was + // overridden + BEAST_EXPECT(a[jss::version] == version); + if (version == 1) { - BEAST_EXPECT(blobs_v2[i][jss::blob] == expected[i].first); - BEAST_EXPECT(blobs_v2[i][jss::signature] == expected[i].second); + BEAST_EXPECT(expected.size() == 1); + BEAST_EXPECT(a[jss::blob] == expected[0].first); + BEAST_EXPECT(a[jss::signature] == expected[0].second); + BEAST_EXPECT(!a.isMember(jss::blobs_v2)); + } + else if (BEAST_EXPECT(a.isMember(jss::blobs_v2))) + { + BEAST_EXPECT(!a.isMember(jss::blob)); + BEAST_EXPECT(!a.isMember(jss::signature)); + auto const& blobs_v2 = a[jss::blobs_v2]; + BEAST_EXPECT(blobs_v2.isArray() && blobs_v2.size() == expected.size()); + + for (unsigned int i = 0; i < expected.size(); ++i) + { + BEAST_EXPECT(blobs_v2[i][jss::blob] == expected[i].first); + BEAST_EXPECT(blobs_v2[i][jss::signature] == expected[i].second); + } } } - } - }; + }; ManifestCache manifests; jtx::Env env(*this); auto& app = env.app(); auto trustedKeys = std::make_unique( - manifests, manifests, env.app().timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.app().timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto expectTrusted = [this, &trustedKeys](std::vector const& list) { for (auto const& val : list) @@ -488,8 +558,8 @@ private: auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); auto const hexPublic = strHex(publisherPublic.begin(), publisherPublic.end()); auto const pubSigningKeys1 = randomKeyPair(KeyType::secp256k1); - auto const manifest1 = base64_encode( - makeManifestString(publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); + auto const manifest1 = base64_encode(makeManifestString( + publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); std::vector cfgKeys1({strHex(publisherPublic)}); std::vector emptyCfgKeys; @@ -515,7 +585,8 @@ private: env.timeKeeper().set(env.timeKeeper().now() + 1s); auto const version = 1; auto const sequence1 = 1; - auto const expiredblob = makeList(lists.at(1), sequence1, env.timeKeeper().now().time_since_epoch().count()); + auto const expiredblob = + makeList(lists.at(1), sequence1, env.timeKeeper().now().time_since_epoch().count()); auto const expiredSig = signList(expiredblob, pubSigningKeys1); NetClock::time_point const validUntil = env.timeKeeper().now() + 3600s; @@ -524,7 +595,8 @@ private: auto const sig2 = signList(blob2, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{expiredblob, expiredSig, {}}, {blob2, sig2, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{expiredblob, expiredSig, {}}, {blob2, sig2, {}}}, siteUri), publisherPublic, ListDisposition::expired, ListDisposition::accepted); @@ -539,18 +611,25 @@ private: auto const effective7 = validUntil - 60s; auto const expiration7 = effective7 + 3600s; auto const blob7 = makeList( - lists.at(7), sequence7, expiration7.time_since_epoch().count(), effective7.time_since_epoch().count()); + lists.at(7), + sequence7, + expiration7.time_since_epoch().count(), + effective7.time_since_epoch().count()); auto const sig7 = signList(blob7, pubSigningKeys1); auto const sequence8 = 8; auto const effective8 = expiration7 - 60s; auto const expiration8 = effective8 + 3600s; auto const blob8 = makeList( - lists.at(8), sequence8, expiration8.time_since_epoch().count(), effective8.time_since_epoch().count()); + lists.at(8), + sequence8, + expiration8.time_since_epoch().count(), + effective8.time_since_epoch().count()); auto const sig8 = signList(blob8, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version2, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version2, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), publisherPublic, ListDisposition::pending, ListDisposition::pending); @@ -563,7 +642,10 @@ private: auto const effective6 = effective7 - 60s; auto const expiration6 = effective6 + 3600s; auto const blob6 = makeList( - lists.at(6), sequence6, expiration6.time_since_epoch().count(), effective6.time_since_epoch().count()); + lists.at(6), + sequence6, + expiration6.time_since_epoch().count(), + effective6.time_since_epoch().count()); auto const sig6 = signList(blob6, pubSigningKeys1); // Process future list that is overridden by a later list @@ -571,11 +653,15 @@ private: auto const effective6a = effective6 + 60s; auto const expiration6a = effective6a + 3600s; auto const blob6a = makeList( - lists.at(5), sequence6a, expiration6a.time_since_epoch().count(), effective6a.time_since_epoch().count()); + lists.at(5), + sequence6a, + expiration6a.time_since_epoch().count(), + effective6a.time_since_epoch().count()); auto const sig6a = signList(blob6a, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{blob6a, sig6a, {}}, {blob6, sig6, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{blob6a, sig6a, {}}, {blob6, sig6, {}}}, siteUri), publisherPublic, ListDisposition::pending, ListDisposition::pending); @@ -586,7 +672,8 @@ private: // Do not apply re-process lists known future sequence numbers checkResult( - trustedKeys->applyLists(manifest1, version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), publisherPublic, ListDisposition::known_sequence, ListDisposition::known_sequence); @@ -604,14 +691,17 @@ private: checkResult( trustedKeys->applyLists( - base64_encode("not a manifest"), version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + base64_encode("not a manifest"), + version, + {{blob7, sig7, {}}, {blob6, sig6, {}}}, + siteUri), publisherPublic, ListDisposition::invalid, ListDisposition::invalid); // do not use list from untrusted publisher - auto const untrustedManifest = base64_encode( - makeManifestString(randomMasterKey(), publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); + auto const untrustedManifest = base64_encode(makeManifestString( + randomMasterKey(), publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); checkResult( trustedKeys->applyLists(untrustedManifest, version, {{blob2, sig2, {}}}, siteUri), @@ -645,11 +735,16 @@ private: // Note that blob6a is not present, because it was dropped during // processing checkAvailable( - trustedKeys, hexPublic, manifest1, 2, {{blob3, sig3}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); + trustedKeys, + hexPublic, + manifest1, + 2, + {{blob3, sig3}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); // do not re-apply lists with past or current sequence numbers checkResult( - trustedKeys->applyLists(manifest1, version, {{blob2, sig2, {}}, {blob3, sig3, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{blob2, sig2, {}}, {blob3, sig3, {}}}, siteUri), publisherPublic, ListDisposition::stale, ListDisposition::same_sequence); @@ -657,8 +752,8 @@ private: // apply list with new publisher key updated by manifest. Also send some // old lists along with the old manifest auto const pubSigningKeys2 = randomKeyPair(KeyType::secp256k1); - auto manifest2 = base64_encode( - makeManifestString(publisherPublic, publisherSecret, pubSigningKeys2.first, pubSigningKeys2.second, 2)); + auto manifest2 = base64_encode(makeManifestString( + publisherPublic, publisherSecret, pubSigningKeys2.first, pubSigningKeys2.second, 2)); auto const sequence4 = 4; auto const blob4 = makeList(lists.at(4), sequence4, validUntil.time_since_epoch().count()); @@ -666,7 +761,10 @@ private: checkResult( trustedKeys->applyLists( - manifest2, version, {{blob2, sig2, manifest1}, {blob3, sig3, manifest1}, {blob4, sig4, {}}}, siteUri), + manifest2, + version, + {{blob2, sig2, manifest1}, {blob3, sig3, manifest1}, {blob4, sig4, {}}}, + siteUri), publisherPublic, ListDisposition::stale, ListDisposition::accepted); @@ -676,7 +774,11 @@ private: expectTrusted(lists.at(4)); checkAvailable( - trustedKeys, hexPublic, manifest2, 2, {{blob4, sig4}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); + trustedKeys, + hexPublic, + manifest2, + 2, + {{blob4, sig4}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); auto const sequence5 = 5; auto const blob5 = makeList(lists.at(5), sequence5, validUntil.time_since_epoch().count()); @@ -694,7 +796,8 @@ private: // Reprocess the pending list, but the signature is no longer valid checkResult( - trustedKeys->applyLists(manifest1, version, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), publisherPublic, ListDisposition::invalid, ListDisposition::invalid); @@ -707,12 +810,17 @@ private: // updateTrusted. Note that the timekeeper is NOT moved, so the close // time will be ahead of the test's wall clock trustedKeys->updateTrusted( - {}, effective6 + 1s, env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); + {}, + effective6 + 1s, + env.app().getOPs(), + env.app().overlay(), + env.app().getHashRouter()); expectUntrusted(lists.at(3)); expectTrusted(lists.at(6)); - checkAvailable(trustedKeys, hexPublic, manifest2, 2, {{blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); + checkAvailable( + trustedKeys, hexPublic, manifest2, 2, {{blob6, sig6}, {blob7, sig7}, {blob8, sig8}}); // Automatically rotate the LAST pending list using updateTrusted, // bypassing blob7. Note that the timekeeper IS moved, so the provided @@ -720,7 +828,11 @@ private: // clock is used. env.timeKeeper().set(effective8); trustedKeys->updateTrusted( - {}, effective8 + 1s, env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); + {}, + effective8 + 1s, + env.app().getOPs(), + env.app().overlay(), + env.app().getHashRouter()); expectUntrusted(lists.at(6)); expectUntrusted(lists.at(7)); @@ -735,7 +847,8 @@ private: auto const sig8_2 = signList(blob8, pubSigningKeys2); checkResult( - trustedKeys->applyLists(manifest2, version, {{blob8, sig8, manifest1}, {blob8, sig8_2, {}}}, siteUri), + trustedKeys->applyLists( + manifest2, version, {{blob8, sig8, manifest1}, {blob8, sig8_2, {}}}, siteUri), publisherPublic, ListDisposition::invalid, ListDisposition::same_sequence); @@ -781,14 +894,18 @@ private: jtx::Env env(*this); auto& app = env.app(); auto trustedKeys = std::make_unique( - manifests, manifests, env.app().timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.app().timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const publisherSecret = randomSecretKey(); auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); auto const hexPublic = strHex(publisherPublic.begin(), publisherPublic.end()); auto const pubSigningKeys1 = randomKeyPair(KeyType::secp256k1); - auto const manifest = base64_encode( - makeManifestString(publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); + auto const manifest = base64_encode(makeManifestString( + publisherPublic, publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); std::vector cfgKeys1({strHex(publisherPublic)}); std::vector emptyCfgKeys; @@ -840,6 +957,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 0); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(!a); } @@ -849,6 +967,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 3); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(!a); } @@ -858,6 +977,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 1); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(a[jss::public_key] == hexPublic); BEAST_EXPECT(a[jss::manifest] == manifest); @@ -874,6 +994,7 @@ private: auto const available = trustedKeys->getAvailable(hexPublic, 2); if (BEAST_EXPECT(available)) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& a = *available; BEAST_EXPECT(a[jss::public_key] == hexPublic); BEAST_EXPECT(a[jss::manifest] == manifest); @@ -904,7 +1025,11 @@ private: jtx::Env env(*this); auto& app = env.app(); auto trustedKeysOuter = std::make_unique( - manifestsOuter, manifestsOuter, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifestsOuter, + manifestsOuter, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); std::vector cfgPublishersOuter; hash_set activeValidatorsOuter; @@ -920,9 +1045,13 @@ private: auto const valKey = randomNode(); cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey)); if (cfgKeys.size() <= maxKeys - 5) + { activeValidatorsOuter.emplace(calcNodeID(valKey)); + } else + { unseenValidators.emplace(calcNodeID(valKey)); + } } BEAST_EXPECT(trustedKeysOuter->load({}, cfgKeys, cfgPublishersOuter)); @@ -950,7 +1079,9 @@ private: BEAST_EXPECT(trustedKeysOuter->trusted(*valKey)); } else + { fail(); + } } changes = trustedKeysOuter->updateTrusted( @@ -992,10 +1123,12 @@ private: BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic1)); // Should trust the ephemeral signing key from the applied manifest - auto m1 = deserializeManifest( - makeManifestString(masterPublic, masterPrivate, signingPublic1, signingKeys1.second, 1)); + auto m1 = deserializeManifest(makeManifestString( + masterPublic, masterPrivate, signingPublic1, signingKeys1.second, 1)); - BEAST_EXPECT(manifestsOuter.applyManifest(std::move(*m1)) == ManifestDisposition::accepted); + BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + manifestsOuter.applyManifest(std::move(*m1)) == ManifestDisposition::accepted); BEAST_EXPECT(trustedKeysOuter->listed(masterPublic)); BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic)); BEAST_EXPECT(trustedKeysOuter->listed(signingPublic1)); @@ -1005,9 +1138,11 @@ private: // from the newest applied manifest auto const signingKeys2 = randomKeyPair(KeyType::secp256k1); auto const signingPublic2 = signingKeys2.first; - auto m2 = deserializeManifest( - makeManifestString(masterPublic, masterPrivate, signingPublic2, signingKeys2.second, 2)); - BEAST_EXPECT(manifestsOuter.applyManifest(std::move(*m2)) == ManifestDisposition::accepted); + auto m2 = deserializeManifest(makeManifestString( + masterPublic, masterPrivate, signingPublic2, signingKeys2.second, 2)); + BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + manifestsOuter.applyManifest(std::move(*m2)) == ManifestDisposition::accepted); BEAST_EXPECT(trustedKeysOuter->listed(masterPublic)); BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic)); BEAST_EXPECT(trustedKeysOuter->listed(signingPublic2)); @@ -1021,8 +1156,12 @@ private: activeValidatorsOuter.emplace(calcNodeID(signingPublicMax)); auto mMax = deserializeManifest(makeRevocationString(masterPublic, masterPrivate)); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(mMax->revoked()); - BEAST_EXPECT(manifestsOuter.applyManifest(std::move(*mMax)) == ManifestDisposition::accepted); + BEAST_EXPECT( + manifestsOuter.applyManifest(std::move(*mMax)) == ManifestDisposition::accepted); + // NOLINTEND(bugprone-unchecked-optional-access) + BEAST_EXPECT(manifestsOuter.getSigningKey(masterPublic) == masterPublic); BEAST_EXPECT(manifestsOuter.revoked(masterPublic)); @@ -1052,7 +1191,11 @@ private: // Make quorum unattainable if lists from any publishers are // unavailable auto trustedKeys = std::make_unique( - manifestsOuter, manifestsOuter, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifestsOuter, + manifestsOuter, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const publisherSecret = randomSecretKey(); auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); @@ -1075,7 +1218,11 @@ private: // Trust explicitly listed validators also when list threshold is // higher than 1 auto trustedKeys = std::make_unique( - manifestsOuter, manifestsOuter, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifestsOuter, + manifestsOuter, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const masterPrivate = randomSecretKey(); auto const masterPublic = derivePublicKey(KeyType::ed25519, masterPrivate); std::vector cfgKeys({toBase58(TokenType::NodePublic, masterPublic)}); @@ -1084,7 +1231,8 @@ private: auto const publisher1Public = derivePublicKey(KeyType::ed25519, publisher1Secret); auto const publisher2Secret = randomSecretKey(); auto const publisher2Public = derivePublicKey(KeyType::ed25519, publisher2Secret); - std::vector cfgPublishers({strHex(publisher1Public), strHex(publisher2Public)}); + std::vector cfgPublishers( + {strHex(publisher1Public), strHex(publisher2Public)}); BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishers, std::size_t(2))); @@ -1104,7 +1252,12 @@ private: std::size_t const minQuorum = 1; ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal, minQuorum); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal, + minQuorum); std::size_t n = 10; std::vector cfgKeys; @@ -1119,9 +1272,13 @@ private: cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey)); expectedTrusted.emplace(calcNodeID(valKey)); if (cfgKeys.size() < std::ceil(n * 0.8f)) + { activeValidators.emplace(calcNodeID(valKey)); + } else if (cfgKeys.size() < std::ceil(n * 0.8f)) + { toBeSeen = calcNodeID(valKey); + } } BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishersOuter)); @@ -1161,14 +1318,19 @@ private: auto const publisherKeys = randomKeyPair(KeyType::secp256k1); auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); auto const manifest = base64_encode(makeManifestString( - publisherKeys.first, publisherKeys.second, pubSigningKeys.first, pubSigningKeys.second, 1)); + publisherKeys.first, + publisherKeys.second, + pubSigningKeys.first, + pubSigningKeys.second, + 1)); std::vector cfgKeys({strHex(publisherKeys.first)}); BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys)); std::vector list({randomValidator(), randomValidator()}); - hash_set activeValidators(asNodeIDs({list[0].masterPublic, list[1].masterPublic})); + hash_set activeValidators( + asNodeIDs({list[0].masterPublic, list[1].masterPublic})); // do not apply expired list auto const version = 1; @@ -1180,7 +1342,8 @@ private: BEAST_EXPECT( ListDisposition::accepted == - trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri).bestDisposition()); + trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri) + .bestDisposition()); TrustChanges changes = trustedKeys->updateTrusted( activeValidators, @@ -1220,7 +1383,8 @@ private: BEAST_EXPECT( ListDisposition::accepted == - trustedKeys->applyLists(manifest, version, {{blob2, sig2, {}}}, siteUri).bestDisposition()); + trustedKeys->applyLists(manifest, version, {{blob2, sig2, {}}}, siteUri) + .bestDisposition()); changes = trustedKeys->updateTrusted( activeValidators, @@ -1229,7 +1393,8 @@ private: env.app().overlay(), env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); - BEAST_EXPECT(changes.added == asNodeIDs({list2[0].masterPublic, list2[1].masterPublic})); + BEAST_EXPECT( + changes.added == asNodeIDs({list2[0].masterPublic, list2[1].masterPublic})); for (Validator const& val : list2) { BEAST_EXPECT(trustedKeys->trusted(val.masterPublic)); @@ -1242,7 +1407,11 @@ private: { // Test 1-9 configured validators auto trustedKeys = std::make_unique( - manifestsOuter, manifestsOuter, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifestsOuter, + manifestsOuter, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); std::vector cfgPublishers; hash_set activeValidators; @@ -1274,7 +1443,11 @@ private: { // Test 2-9 configured validators as validator auto trustedKeys = std::make_unique( - manifestsOuter, manifestsOuter, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifestsOuter, + manifestsOuter, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); auto const localKey = randomNode(); std::vector cfgPublishers; @@ -1299,9 +1472,13 @@ private: env.app().getHashRouter()); BEAST_EXPECT(changes.removed.empty()); if (cfgKeys.size() > 2) + { BEAST_EXPECT(changes.added == asNodeIDs({valKey})); + } else + { BEAST_EXPECT(changes.added == asNodeIDs({localKey, valKey})); + } BEAST_EXPECT(trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f)); @@ -1313,7 +1490,11 @@ private: // Trusted set should include all validators from multiple lists ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); hash_set activeValidators; std::vector valKeys; @@ -1329,7 +1510,9 @@ private: // locals[1]: from 1 to maxKeys - 2 // locals[2]: from 2 to maxKeys constexpr static int publishers = 3; - std::array, publishers> + std::array< + std::pair, + publishers> locals = { std::make_pair(valKeys.cbegin(), valKeys.cend() - 4), std::make_pair(valKeys.cbegin() + 1, valKeys.cend() - 2), @@ -1341,7 +1524,11 @@ private: auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); auto const manifest = base64_encode(makeManifestString( - publisherPublic, publisherSecret, pubSigningKeys.first, pubSigningKeys.second, 1)); + publisherPublic, + publisherSecret, + pubSigningKeys.first, + pubSigningKeys.second, + 1)); std::vector cfgPublishers({strHex(publisherPublic)}); std::vector emptyCfgKeys; @@ -1354,12 +1541,14 @@ private: using namespace std::chrono_literals; NetClock::time_point const validUntil = env.timeKeeper().now() + 3600s; std::vector localKeys{locals[i].first, locals[i].second}; - auto const blob = makeList(localKeys, sequence, validUntil.time_since_epoch().count()); + auto const blob = + makeList(localKeys, sequence, validUntil.time_since_epoch().count()); auto const sig = signList(blob, pubSigningKeys); BEAST_EXPECT( ListDisposition::accepted == - trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri).bestDisposition()); + trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri) + .bestDisposition()); }; // Apply multiple published lists @@ -1389,7 +1578,11 @@ private: // Trusted set should include validators from intersection of lists ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); hash_set activeValidators; std::vector valKeys; @@ -1407,43 +1600,69 @@ private: // intersection of at least 2: same as locals[1] // intersection when 1 is dropped: from 2 to maxKeys - 4 constexpr static int publishers = 3; - std::array, publishers> + std::array< + std::pair, + publishers> locals = { std::make_pair(valKeys.cbegin(), valKeys.cend() - 4), std::make_pair(valKeys.cbegin() + 1, valKeys.cend() - 2), std::make_pair(valKeys.cbegin() + 2, valKeys.cend()), }; - auto addPublishedList = [&, this]( - int i, NetClock::time_point& validUntil1, NetClock::time_point& validUntil2) { - auto const publisherSecret = randomSecretKey(); - auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); - auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); - auto const manifest = base64_encode(makeManifestString( - publisherPublic, publisherSecret, pubSigningKeys.first, pubSigningKeys.second, 1)); + auto addPublishedList = + [&, this]( + int i, NetClock::time_point& validUntil1, NetClock::time_point& validUntil2) { + auto const publisherSecret = randomSecretKey(); + auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); + auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); + auto const manifest = base64_encode(makeManifestString( + publisherPublic, + publisherSecret, + pubSigningKeys.first, + pubSigningKeys.second, + 1)); - std::vector cfgPublishers({strHex(publisherPublic)}); - std::vector emptyCfgKeys; + std::vector cfgPublishers({strHex(publisherPublic)}); + std::vector emptyCfgKeys; - BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); + BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers)); - auto const version = 1; - auto const sequence = 1; - using namespace std::chrono_literals; - // Want to drop 1 sooner - NetClock::time_point const validUntil = env.timeKeeper().now() + (i == 2 ? 120s : i == 1 ? 60s : 3600s); - if (i == 1) - validUntil1 = validUntil; - else if (i == 2) - validUntil2 = validUntil; - std::vector localKeys{locals[i].first, locals[i].second}; - auto const blob = makeList(localKeys, sequence, validUntil.time_since_epoch().count()); - auto const sig = signList(blob, pubSigningKeys); + auto const version = 1; + auto const sequence = 1; + using namespace std::chrono_literals; + // Want to drop 1 sooner + std::chrono::seconds duration; + if (i == 2) + { + duration = 120s; + } + else if (i == 1) + { + duration = 60s; + } + else + { + duration = 3600s; + } + NetClock::time_point const validUntil = env.timeKeeper().now() + duration; + if (i == 1) + { + validUntil1 = validUntil; + } + else if (i == 2) + { + validUntil2 = validUntil; + } + std::vector localKeys{locals[i].first, locals[i].second}; + auto const blob = + makeList(localKeys, sequence, validUntil.time_since_epoch().count()); + auto const sig = signList(blob, pubSigningKeys); - BEAST_EXPECT( - ListDisposition::accepted == - trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri).bestDisposition()); - }; + BEAST_EXPECT( + ListDisposition::accepted == + trustedKeys->applyLists(manifest, version, {{blob, sig, {}}}, siteUri) + .bestDisposition()); + }; // Apply multiple published lists // validUntil1 is expiration time for locals[1] @@ -1474,7 +1693,9 @@ private: added.insert(calcNodeID(val.masterPublic)); } else + { BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); + } } BEAST_EXPECT(changes.added == added); BEAST_EXPECT(changes.removed.empty()); @@ -1498,7 +1719,9 @@ private: { auto const& val = valKeys[i]; if (i >= 2 && i < maxKeys - 4) + { BEAST_EXPECT(trustedKeys->trusted(val.masterPublic)); + } else { BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); @@ -1526,9 +1749,13 @@ private: { auto const& val = valKeys[i]; if (i < maxKeys - 4) + { BEAST_EXPECT(trustedKeys->listed(val.masterPublic)); + } else + { BEAST_EXPECT(!trustedKeys->listed(val.masterPublic)); + } BEAST_EXPECT(!trustedKeys->trusted(val.masterPublic)); if (i >= 2 && i < maxKeys - 4) @@ -1550,13 +1777,19 @@ private: jtx::Env env(*this); auto& app = env.app(); - auto toStr = [](PublicKey const& publicKey) { return toBase58(TokenType::NodePublic, publicKey); }; + auto toStr = [](PublicKey const& publicKey) { + return toBase58(TokenType::NodePublic, publicKey); + }; // Config listed keys { ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); // Empty list has no expiration BEAST_EXPECT(trustedKeys->expires() == std::nullopt); @@ -1564,7 +1797,9 @@ private: // Config listed keys have maximum expiry PublicKey localCfgListed = randomNode(); trustedKeys->load({}, {toStr(localCfgListed)}, {}); - BEAST_EXPECT(trustedKeys->expires() && trustedKeys->expires().value() == NetClock::time_point::max()); + BEAST_EXPECT( + trustedKeys->expires() && + trustedKeys->expires().value() == NetClock::time_point::max()); BEAST_EXPECT(trustedKeys->listed(localCfgListed)); } @@ -1572,7 +1807,11 @@ private: { ManifestCache manifests; auto trustedKeys = std::make_unique( - manifests, manifests, env.app().timeKeeper(), app.config().legacy("database_path"), env.journal); + manifests, + manifests, + env.app().timeKeeper(), + app.config().legacy("database_path"), + env.journal); std::vector validators = {randomValidator()}; hash_set activeValidators; @@ -1594,7 +1833,11 @@ private: auto const publisherPublic = derivePublicKey(KeyType::ed25519, publisherSecret); auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1); auto const manifest = base64_encode(makeManifestString( - publisherPublic, publisherSecret, pubSigningKeys.first, pubSigningKeys.second, 1)); + publisherPublic, + publisherSecret, + pubSigningKeys.first, + pubSigningKeys.second, + 1)); std::vector cfgPublishers({strHex(publisherPublic)}); std::vector emptyCfgKeys; @@ -1604,7 +1847,8 @@ private: auto const version = 2; auto const sequence1 = 1; NetClock::time_point const expiration1 = env.timeKeeper().now() + 1800s; - auto const blob1 = makeList(validators, sequence1, expiration1.time_since_epoch().count()); + auto const blob1 = + makeList(validators, sequence1, expiration1.time_since_epoch().count()); auto const sig1 = signList(blob1, pubSigningKeys); NetClock::time_point const effective2 = expiration1 - 300s; @@ -1651,7 +1895,9 @@ private: ListDisposition::pending, ListDisposition::accepted); // We now have loaded both lists, so expiration is known - BEAST_EXPECT(trustedKeys->expires() && trustedKeys->expires().value() == prep1.expirations.back()); + BEAST_EXPECT( + trustedKeys->expires() && + trustedKeys->expires().value() == prep1.expirations.back()); // Advance past the first list's LAST validFrom date. It remains // the earliest validUntil, while rotating in the second list @@ -1663,7 +1909,9 @@ private: env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); - BEAST_EXPECT(trustedKeys->expires() && trustedKeys->expires().value() == prep1.expirations.back()); + BEAST_EXPECT( + trustedKeys->expires() && + trustedKeys->expires().value() == prep1.expirations.back()); BEAST_EXPECT(!changes.added.empty()); BEAST_EXPECT(changes.removed.empty()); } @@ -1678,7 +1926,9 @@ private: env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); - BEAST_EXPECT(trustedKeys->expires() && trustedKeys->expires().value() == prep1.expirations.back()); + BEAST_EXPECT( + trustedKeys->expires() && + trustedKeys->expires().value() == prep1.expirations.back()); BEAST_EXPECT(changes.added.empty()); BEAST_EXPECT(changes.removed.empty()); } @@ -1693,7 +1943,8 @@ private: ManifestCache manifests; auto createValidatorList = - [&](std::uint32_t vlSize, std::optional minimumQuorum = {}) -> std::shared_ptr { + [&](std::uint32_t vlSize, + std::optional minimumQuorum = {}) -> std::shared_ptr { auto trustedKeys = std::make_shared( manifests, manifests, @@ -1720,7 +1971,8 @@ private: env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); - if (minimumQuorum == trustedKeys->quorum() || trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f)) + if (minimumQuorum == trustedKeys->quorum() || + trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f)) return trustedKeys; } return nullptr; @@ -1771,7 +2023,8 @@ private: env.app().getHashRouter()); BEAST_EXPECT( validators->quorum() == - static_cast(std::ceil(std::max((us - nUnlSize) * 0.8f, us * 0.6f)))); + static_cast( + std::ceil(std::max((us - nUnlSize) * 0.8f, us * 0.6f)))); } } } @@ -1803,7 +2056,7 @@ private: { for (auto& n : nUnl_temp) { - if (nUnl.find(n) == nUnl.end()) + if (!nUnl.contains(n)) return false; } validators->updateTrusted( @@ -1956,7 +2209,9 @@ private: auto start = buffer.begin(); auto end = buffer.end(); std::vector slice(start, end); - buffers.commit(boost::asio::buffer_copy(buffers.prepare(slice.size()), boost::asio::buffer(slice))); + buffers.commit( + boost::asio::buffer_copy( + buffers.prepare(slice.size()), boost::asio::buffer(slice))); boost::system::error_code ec; auto header = detail::parseMessageHeader(ec, buffers.data(), buffers.size()); @@ -1965,9 +2220,11 @@ private: }; auto extractProtocolMessage1 = [this, &extractHeader](Message& message) { auto [header, buffers] = extractHeader(message); - if (BEAST_EXPECT(header) && BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST)) + if (BEAST_EXPECT(header) && + BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST)) { - auto const msg = detail::parseMessageContent(*header, buffers.data()); + auto const msg = + detail::parseMessageContent(*header, buffers.data()); BEAST_EXPECT(msg); return msg; } @@ -1975,91 +2232,102 @@ private: }; auto extractProtocolMessage2 = [this, &extractHeader](Message& message) { auto [header, buffers] = extractHeader(message); - if (BEAST_EXPECT(header) && BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST_COLLECTION)) + if (BEAST_EXPECT(header) && + BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST_COLLECTION)) { - auto const msg = - detail::parseMessageContent(*header, buffers.data()); + auto const msg = detail::parseMessageContent( + *header, buffers.data()); BEAST_EXPECT(msg); return msg; } return std::shared_ptr(); }; - auto verifyMessage = [this, manifestCutoff, &extractProtocolMessage1, &extractProtocolMessage2]( - auto const version, - auto const& manifest, - auto const& blobInfos, - auto const& messages, - std::vector>> expectedInfo) { - BEAST_EXPECT(messages.size() == expectedInfo.size()); - auto msgIter = expectedInfo.begin(); - for (auto const& messageWithHash : messages) - { - if (!BEAST_EXPECT(msgIter != expectedInfo.end())) - break; - if (!BEAST_EXPECT(messageWithHash.message)) - continue; - auto const& expectedSeqs = msgIter->second; - auto seqIter = expectedSeqs.begin(); - auto const size = messageWithHash.message->getBuffer(compression::Compressed::Off).size(); - // This size is arbitrary, but shouldn't change - BEAST_EXPECT(size == msgIter->first); - if (expectedSeqs.size() == 1) + auto verifyMessage = + [this, manifestCutoff, &extractProtocolMessage1, &extractProtocolMessage2]( + auto const version, + auto const& manifest, + auto const& blobInfos, + auto const& messages, + std::vector>> expectedInfo) { + BEAST_EXPECT(messages.size() == expectedInfo.size()); + auto msgIter = expectedInfo.begin(); + for (auto const& messageWithHash : messages) { - auto const msg = extractProtocolMessage1(*messageWithHash.message); - auto const expectedVersion = 1; - if (BEAST_EXPECT(msg)) + if (!BEAST_EXPECT(msgIter != expectedInfo.end())) + break; + if (!BEAST_EXPECT(messageWithHash.message)) + continue; + auto const& expectedSeqs = msgIter->second; + auto seqIter = expectedSeqs.begin(); + auto const size = + messageWithHash.message->getBuffer(compression::Compressed::Off).size(); + // This size is arbitrary, but shouldn't change + BEAST_EXPECT(size == msgIter->first); + if (expectedSeqs.size() == 1) { - BEAST_EXPECT(msg->version() == expectedVersion); - if (!BEAST_EXPECT(seqIter != expectedSeqs.end())) - continue; - auto const& expectedBlob = blobInfos.at(*seqIter); - BEAST_EXPECT((*seqIter < manifestCutoff) == !!expectedBlob.manifest); - auto const expectedManifest = - *seqIter < manifestCutoff && expectedBlob.manifest ? *expectedBlob.manifest : manifest; - BEAST_EXPECT(msg->manifest() == expectedManifest); - BEAST_EXPECT(msg->blob() == expectedBlob.blob); - BEAST_EXPECT(msg->signature() == expectedBlob.signature); - ++seqIter; - BEAST_EXPECT(seqIter == expectedSeqs.end()); - - BEAST_EXPECT( - messageWithHash.hash == - sha512Half(expectedManifest, expectedBlob.blob, expectedBlob.signature, expectedVersion)); - } - } - else - { - std::vector hashingBlobs; - hashingBlobs.reserve(msgIter->second.size()); - - auto const msg = extractProtocolMessage2(*messageWithHash.message); - if (BEAST_EXPECT(msg)) - { - BEAST_EXPECT(msg->version() == version); - BEAST_EXPECT(msg->manifest() == manifest); - for (auto const& blobInfo : msg->blobs()) + auto const msg = extractProtocolMessage1(*messageWithHash.message); + auto const expectedVersion = 1; + if (BEAST_EXPECT(msg)) { + BEAST_EXPECT(msg->version() == expectedVersion); if (!BEAST_EXPECT(seqIter != expectedSeqs.end())) - break; + continue; auto const& expectedBlob = blobInfos.at(*seqIter); - hashingBlobs.push_back(expectedBlob); - BEAST_EXPECT(blobInfo.has_manifest() == !!expectedBlob.manifest); - BEAST_EXPECT(blobInfo.has_manifest() == (*seqIter < manifestCutoff)); - - if (*seqIter < manifestCutoff) - BEAST_EXPECT(blobInfo.manifest() == *expectedBlob.manifest); - BEAST_EXPECT(blobInfo.blob() == expectedBlob.blob); - BEAST_EXPECT(blobInfo.signature() == expectedBlob.signature); + BEAST_EXPECT((*seqIter < manifestCutoff) == !!expectedBlob.manifest); + auto const expectedManifest = + *seqIter < manifestCutoff && expectedBlob.manifest + ? *expectedBlob.manifest + : manifest; + BEAST_EXPECT(msg->manifest() == expectedManifest); + BEAST_EXPECT(msg->blob() == expectedBlob.blob); + BEAST_EXPECT(msg->signature() == expectedBlob.signature); ++seqIter; + BEAST_EXPECT(seqIter == expectedSeqs.end()); + + BEAST_EXPECT( + messageWithHash.hash == + sha512Half( + expectedManifest, + expectedBlob.blob, + expectedBlob.signature, + expectedVersion)); } - BEAST_EXPECT(seqIter == expectedSeqs.end()); } - BEAST_EXPECT(messageWithHash.hash == sha512Half(manifest, hashingBlobs, version)); + else + { + std::vector hashingBlobs; + hashingBlobs.reserve(msgIter->second.size()); + + auto const msg = extractProtocolMessage2(*messageWithHash.message); + if (BEAST_EXPECT(msg)) + { + BEAST_EXPECT(msg->version() == version); + BEAST_EXPECT(msg->manifest() == manifest); + for (auto const& blobInfo : msg->blobs()) + { + if (!BEAST_EXPECT(seqIter != expectedSeqs.end())) + break; + auto const& expectedBlob = blobInfos.at(*seqIter); + hashingBlobs.push_back(expectedBlob); + BEAST_EXPECT(blobInfo.has_manifest() == !!expectedBlob.manifest); + BEAST_EXPECT( + blobInfo.has_manifest() == (*seqIter < manifestCutoff)); + + if (*seqIter < manifestCutoff) + BEAST_EXPECT(blobInfo.manifest() == *expectedBlob.manifest); + BEAST_EXPECT(blobInfo.blob() == expectedBlob.blob); + BEAST_EXPECT(blobInfo.signature() == expectedBlob.signature); + ++seqIter; + } + BEAST_EXPECT(seqIter == expectedSeqs.end()); + } + BEAST_EXPECT( + messageWithHash.hash == sha512Half(manifest, hashingBlobs, version)); + } + ++msgIter; } - ++msgIter; - } - BEAST_EXPECT(msgIter == expectedInfo.end()); - }; + BEAST_EXPECT(msgIter == expectedInfo.end()); + }; auto verifyBuildMessages = [this]( std::pair const& result, std::size_t expectedSequence, @@ -2102,8 +2370,11 @@ private: // This peer has a VL ahead of our "current" verifyBuildMessages( - ValidatorList::buildValidatorListMessages(1, 8, maxSequence, version, manifest, blobInfos, messages), 0, 0); - BEAST_EXPECT(messages.size() == 0); + ValidatorList::buildValidatorListMessages( + 1, 8, maxSequence, version, manifest, blobInfos, messages), + 0, + 0); + BEAST_EXPECT(messages.empty()); // Don't repeat the work if messages is populated, even though the // peerSequence provided indicates it should. Note that this @@ -2111,29 +2382,40 @@ private: // real code. messages.emplace_back(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(1, 3, maxSequence, version, manifest, blobInfos, messages), 5, 0); + ValidatorList::buildValidatorListMessages( + 1, 3, maxSequence, version, manifest, blobInfos, messages), + 5, + 0); BEAST_EXPECT(messages.size() == 1 && !messages.front().message); // Generate a version 1 message messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(1, 3, maxSequence, version, manifest, blobInfos, messages), 5, 1); + ValidatorList::buildValidatorListMessages( + 1, 3, maxSequence, version, manifest, blobInfos, messages), + 5, + 1); if (BEAST_EXPECT(messages.size() == 1) && BEAST_EXPECT(messages.front().message)) { auto const& messageWithHash = messages.front(); auto const msg = extractProtocolMessage1(*messageWithHash.message); - auto const size = messageWithHash.message->getBuffer(compression::Compressed::Off).size(); + auto const size = + messageWithHash.message->getBuffer(compression::Compressed::Off).size(); // This size is arbitrary, but shouldn't change BEAST_EXPECT(size == 108); auto const& expected = blobInfos.at(5); if (BEAST_EXPECT(msg)) { BEAST_EXPECT(msg->version() == 1); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(msg->manifest() == *expected.manifest); BEAST_EXPECT(msg->blob() == expected.blob); BEAST_EXPECT(msg->signature() == expected.signature); } - BEAST_EXPECT(messageWithHash.hash == sha512Half(*expected.manifest, expected.blob, expected.signature, 1)); + BEAST_EXPECT( + messageWithHash.hash == + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + sha512Half(*expected.manifest, expected.blob, expected.signature, 1)); } // Version 2 @@ -2146,7 +2428,7 @@ private: 2, maxSequence * 2, maxSequence, version, manifest, blobInfos, messages), 0, 0); - BEAST_EXPECT(messages.size() == 0); + BEAST_EXPECT(messages.empty()); // Don't repeat the work if messages is populated, even though the // peerSequence provided indicates it should. Note that this @@ -2154,7 +2436,8 @@ private: // real code. messages.emplace_back(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 3, maxSequence, version, manifest, blobInfos, messages), + ValidatorList::buildValidatorListMessages( + 2, 3, maxSequence, version, manifest, blobInfos, messages), maxSequence, 0); BEAST_EXPECT(messages.size() == 1 && !messages.front().message); @@ -2162,7 +2445,8 @@ private: // Generate a version 2 message. Don't send the current messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 5, maxSequence, version, manifest, blobInfos, messages), + ValidatorList::buildValidatorListMessages( + 2, 5, maxSequence, version, manifest, blobInfos, messages), maxSequence, 4); verifyMessage(version, manifest, blobInfos, messages, {{372, {6, 7, 10, 12}}}); @@ -2172,7 +2456,8 @@ private: // Set a limit that should give two messages messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 5, maxSequence, version, manifest, blobInfos, messages, 300), + ValidatorList::buildValidatorListMessages( + 2, 5, maxSequence, version, manifest, blobInfos, messages, 300), maxSequence, 4); verifyMessage(version, manifest, blobInfos, messages, {{212, {6, 7}}, {192, {10, 12}}}); @@ -2181,27 +2466,41 @@ private: // will split and the other won't messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 5, maxSequence, version, manifest, blobInfos, messages, 200), + ValidatorList::buildValidatorListMessages( + 2, 5, maxSequence, version, manifest, blobInfos, messages, 200), maxSequence, 4); - verifyMessage(version, manifest, blobInfos, messages, {{108, {6}}, {108, {7}}, {192, {10, 12}}}); + verifyMessage( + version, manifest, blobInfos, messages, {{108, {6}}, {108, {7}}, {192, {10, 12}}}); // Set a limit so that all the VLs are sent individually messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 5, maxSequence, version, manifest, blobInfos, messages, 150), + ValidatorList::buildValidatorListMessages( + 2, 5, maxSequence, version, manifest, blobInfos, messages, 150), maxSequence, 4); - verifyMessage(version, manifest, blobInfos, messages, {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}}); + verifyMessage( + version, + manifest, + blobInfos, + messages, + {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}}); // Set a limit smaller than some of the messages. Because single // messages send regardless, they will all still be sent messages.clear(); verifyBuildMessages( - ValidatorList::buildValidatorListMessages(2, 5, maxSequence, version, manifest, blobInfos, messages, 108), + ValidatorList::buildValidatorListMessages( + 2, 5, maxSequence, version, manifest, blobInfos, messages, 108), maxSequence, 4); - verifyMessage(version, manifest, blobInfos, messages, {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}}); + verifyMessage( + version, + manifest, + blobInfos, + messages, + {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}}); } void @@ -2228,7 +2527,7 @@ private: PublicKey pubKey; std::pair signingKeys; std::string manifest; - NetClock::time_point expiry = {}; + NetClock::time_point expiry = {}; // NOLINT(readability-redundant-member-init) }; // Create ValidatorList with a set of countTotal publishers, of which @@ -2243,7 +2542,11 @@ private: std::vector& publishers // out ) -> std::unique_ptr { auto result = std::make_unique( - valManifests, pubManifests, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + valManifests, + pubManifests, + env.timeKeeper(), + app.config().legacy("database_path"), + env.journal); std::vector cfgPublishers; for (std::size_t i = 0; i < countTotal; ++i) @@ -2260,7 +2563,13 @@ private: pubSigningKeys.first, pubSigningKeys.second, i < countRevoked ? revoked : 1)); - publishers.push_back(Publisher{i < countRevoked, publisherPublic, pubSigningKeys, manifest}); + publishers.push_back( + Publisher{ + .revoked = i < countRevoked, + .pubKey = publisherPublic, + .signingKeys = pubSigningKeys, + .manifest = manifest, + }); } std::vector const emptyCfgKeys; @@ -2268,7 +2577,8 @@ private: if (self) { valManifests.applyManifest(*deserializeManifest(base64_decode(self->manifest))); - BEAST_EXPECT(result->load(self->signingPublic, emptyCfgKeys, cfgPublishers, threshold)); + BEAST_EXPECT( + result->load(self->signingPublic, emptyCfgKeys, cfgPublishers, threshold)); } else { @@ -2279,12 +2589,15 @@ private: { using namespace std::chrono_literals; publishers[i].expiry = env.timeKeeper().now() + (i == countTotal - 1 ? 60s : 3600s); - auto const blob = makeList(valKeys, 1, publishers[i].expiry.time_since_epoch().count()); + auto const blob = + makeList(valKeys, 1, publishers[i].expiry.time_since_epoch().count()); auto const sig = signList(blob, publishers[i].signingKeys); BEAST_EXPECT( - result->applyLists(publishers[i].manifest, 1, {{blob, sig, {}}}, siteUri).bestDisposition() == - (publishers[i].revoked ? ListDisposition::untrusted : ListDisposition::accepted)); + result->applyLists(publishers[i].manifest, 1, {{blob, sig, {}}}, siteUri) + .bestDisposition() == + (publishers[i].revoked ? ListDisposition::untrusted + : ListDisposition::accepted)); } return result; @@ -2398,7 +2711,7 @@ private: env.app().overlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -2666,7 +2979,7 @@ private: env.app().overlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -3104,7 +3417,7 @@ private: env.app().overlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) @@ -3286,7 +3599,7 @@ private: env.app().overlay(), env.app().getHashRouter()); BEAST_EXPECT(trustedKeys->quorum() == quorumDisabled); - BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().size() == 0); + BEAST_EXPECT(trustedKeys->getTrustedMasterKeys().empty()); hash_set removed; for (auto const& val : valKeys) diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index 3b616ad5c9..c47c93c426 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -155,7 +155,7 @@ private: std::vector list; std::string uri; FetchListConfig const& cfg; - bool isRetry; + bool isRetry{}; }; std::vector servers; @@ -175,7 +175,12 @@ private: NetClock::time_point const effective2 = expires - cfg.effectiveOverlap; NetClock::time_point const expires2 = effective2 + cfg.expiresFromNow; item.server = make_TrustedPublisherServer( - env.app().getIOContext(), item.list, expires, {{effective2, expires2}}, cfg.ssl, cfg.serverVersion); + env.app().getIOContext(), + item.list, + expires, + {{effective2, expires2}}, + cfg.ssl, + cfg.serverVersion); std::string pubHex = strHex(item.server->publisherPublic()); cfgPublishers.push_back(pubHex); @@ -220,21 +225,26 @@ private: Json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) + { if (vs[jss::uri].asString().find(u.uri) != std::string::npos) myStatus = vs; + } BEAST_EXPECTS( myStatus[jss::last_refresh_message].asString().empty() != u.cfg.failFetch, to_string(myStatus) + "\n" + sink.messages().str()); if (!u.cfg.msg.empty()) { - BEAST_EXPECTS(sink.messages().str().find(u.cfg.msg) != std::string::npos, sink.messages().str()); + BEAST_EXPECTS( + sink.messages().str().find(u.cfg.msg) != std::string::npos, + sink.messages().str()); } if (u.cfg.expectedRefreshMin) { BEAST_EXPECTS( - myStatus[jss::refresh_interval_min].asInt() == u.cfg.expectedRefreshMin, to_string(myStatus)); + myStatus[jss::refresh_interval_min].asInt() == u.cfg.expectedRefreshMin, + to_string(myStatus)); } if (u.cfg.failFetch) @@ -255,7 +265,8 @@ private: void testFileList(std::vector> const& paths) { - testcase << "File list - " << paths[0].first << (paths.size() > 1 ? ", " + paths[1].first : ""); + testcase << "File list - " << paths[0].first + << (paths.size() > 1 ? ", " + paths[1].first : ""); using namespace jtx; @@ -298,12 +309,18 @@ private: auto const jv = sites->getJson(); Json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) + { if (vs[jss::uri].asString().find(u.uri) != std::string::npos) myStatus = vs; - BEAST_EXPECTS(myStatus[jss::last_refresh_message].asString().empty() != u.shouldFail, to_string(myStatus)); + } + BEAST_EXPECTS( + myStatus[jss::last_refresh_message].asString().empty() != u.shouldFail, + to_string(myStatus)); if (u.shouldFail) { - BEAST_EXPECTS(sink.messages().str().find(u.expectMsg) != std::string::npos, sink.messages().str()); + BEAST_EXPECTS( + sink.messages().str().find(u.expectMsg) != std::string::npos, + sink.messages().str()); } } } @@ -323,7 +340,8 @@ private: // Create a file with arbitrary content detail::FileDirGuard hello(*this, "test_val", "helloworld.txt", "Hello, world!"); // Create a file with malformed Json - detail::FileDirGuard json(*this, "test_val", "json.txt", R"json({ "version": 2, "extra" : "value" })json"); + detail::FileDirGuard json( + *this, "test_val", "json.txt", R"json({ "version": 2, "extra" : "value" })json"); auto const goodPath = fullPath(good); auto const helloPath = fullPath(hello); auto const jsonPath = fullPath(json); @@ -365,9 +383,18 @@ public: // UNLs with a "gap" between validUntil of one and validFrom of the // next testFetchList( - good, {{"/validators2", "", ssl, false, false, 1, detail::default_expires, std::chrono::seconds{-90}}}); + good, + {{"/validators2", + "", + ssl, + false, + false, + 1, + detail::default_expires, + std::chrono::seconds{-90}}}); // fetch single site with unending redirect (fails to load) - testFetchList(good, {{"/redirect_forever/301", "Exceeded max redirects", ssl, true, true}}); + testFetchList( + good, {{"/redirect_forever/301", "Exceeded max redirects", ssl, true, true}}); // two that redirect forever testFetchList( good, @@ -375,25 +402,47 @@ public: {"/redirect_forever/308", "Exceeded max redirects", ssl, true, true}}); // one unending redirect, one not testFetchList( - good, {{"/validators", "", ssl}, {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + good, + {{"/validators", "", ssl}, + {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); // one unending redirect, one not testFetchList( good, - {{"/validators2", "", ssl}, {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + {{"/validators2", "", ssl}, + {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); // invalid redir Location - testFetchList(good, {{"/redirect_to/ftp://invalid-url/302", "Invalid redirect location", ssl, true, true}}); testFetchList( - good, {{"/redirect_to/file://invalid-url/302", "Invalid redirect location", ssl, true, true}}); + good, + {{"/redirect_to/ftp://invalid-url/302", + "Invalid redirect location", + ssl, + true, + true}}); + testFetchList( + good, + {{"/redirect_to/file://invalid-url/302", + "Invalid redirect location", + ssl, + true, + true}}); // invalid json - testFetchList(good, {{"/validators/bad", "Unable to parse JSON response", ssl, true, true}}); - testFetchList(good, {{"/validators2/bad", "Unable to parse JSON response", ssl, true, true}}); + testFetchList( + good, {{"/validators/bad", "Unable to parse JSON response", ssl, true, true}}); + testFetchList( + good, {{"/validators2/bad", "Unable to parse JSON response", ssl, true, true}}); // error status returned testFetchList(good, {{"/bad-resource", "returned bad status", ssl, true, true}}); // location field missing - testFetchList(good, {{"/redirect_nolo/308", "returned a redirect with no Location", ssl, true, true}}); + testFetchList( + good, + {{"/redirect_nolo/308", "returned a redirect with no Location", ssl, true, true}}); // json fields missing - testFetchList(good, {{"/validators/missing", "Missing fields in JSON response", ssl, true, true}}); - testFetchList(good, {{"/validators2/missing", "Missing fields in JSON response", ssl, true, true}}); + testFetchList( + good, + {{"/validators/missing", "Missing fields in JSON response", ssl, true, true}}); + testFetchList( + good, + {{"/validators2/missing", "Missing fields in JSON response", ssl, true, true}}); // timeout testFetchList(good, {{"/sleep/13", "took too long", ssl, true, true}}); // bad manifest format using known versions @@ -409,9 +458,19 @@ public: testFetchList(good, {{"/validators2", "1 unsupported version", ssl, false, true, 4}}); using namespace std::chrono_literals; // get expired validator list - testFetchList(good, {{"/validators", "Applied 1 expired validator list(s)", ssl, false, false, 1, 0s}}); testFetchList( - good, {{"/validators2", "Applied 1 expired validator list(s)", ssl, false, false, 1, 0s, -1s}}); + good, + {{"/validators", "Applied 1 expired validator list(s)", ssl, false, false, 1, 0s}}); + testFetchList( + good, + {{"/validators2", + "Applied 1 expired validator list(s)", + ssl, + false, + false, + 1, + 0s, + -1s}}); // force an out-of-range validUntil value testFetchList( good, @@ -427,13 +486,27 @@ public: // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", "", ssl, false, false, 1, std::chrono::seconds{Json::Value::maxInt - 300}, 299s}}); + {{"/validators2", + "", + ssl, + false, + false, + 1, + std::chrono::seconds{Json::Value::maxInt - 300}, + 299s}}); // force an out-of-range validFrom value // The first list is accepted. The second fails. The parser // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", "", ssl, false, false, 1, std::chrono::seconds{Json::Value::maxInt - 300}, 301s}}); + {{"/validators2", + "", + ssl, + false, + false, + 1, + std::chrono::seconds{Json::Value::maxInt - 300}, + 301s}}); // force an out-of-range validUntil value on _both_ lists testFetchList( good, diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 5d31c674c0..27a8d3b2e0 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -3,15 +3,15 @@ #include #include #include -#include #include #include #include #include #include +#include #include -#include +#include #include #include #include @@ -50,7 +50,10 @@ class Vault_test : public beast::unit_test::suite Account dave{"dave"}; auto const testSequence = [&, this]( - std::string const& prefix, Env& env, Vault& vault, PrettyAsset const& asset) { + std::string const& prefix, + Env& env, + Vault& vault, + PrettyAsset const& asset) { auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); tx[sfData] = "AFEED00E"; tx[sfAssetsMaximum] = asset(100).number(); @@ -64,15 +67,23 @@ class Vault_test : public beast::unit_test::suite auto const vault = env.le(keylet); BEAST_EXPECT(vault != nullptr); if (!asset.integral()) + { BEAST_EXPECT(vault->at(sfScale) == 6); + } else + { BEAST_EXPECT(vault->at(sfScale) == 0); + } auto const shares = env.le(keylet::mptIssuance(vault->at(sfShareMPTID))); BEAST_EXPECT(shares != nullptr); if (!asset.integral()) + { BEAST_EXPECT(shares->at(sfAssetScale) == 6); + } else + { BEAST_EXPECT(shares->at(sfAssetScale) == 0); + } return {MPTIssue(vault->at(sfShareMPTID)), Account("vault", vault->at(sfAccount))}; }(); auto const shares = share.raw().get(); @@ -87,14 +98,16 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to deposit more than assets held"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(10000)}); + auto tx = vault.deposit( + {.depositor = depositor, .id = keylet.key, .amount = asset(10000)}); env(tx, ter(tecINSUFFICIENT_FUNDS)); env.close(); } { testcase(prefix + " deposit non-zero amount"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx); env.close(); BEAST_EXPECT(env.balance(depositor, shares) == share(50 * scale)); @@ -102,7 +115,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " deposit non-zero amount again"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx); env.close(); BEAST_EXPECT(env.balance(depositor, shares) == share(100 * scale)); @@ -165,7 +179,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to deposit more than maximum"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx, ter(tecLIMIT_EXCEEDED)); env.close(); } @@ -180,14 +195,16 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to withdraw more than assets held"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); env(tx, ter(tecINSUFFICIENT_FUNDS)); env.close(); } { testcase(prefix + " deposit some more"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx); env.close(); BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale)); @@ -196,8 +213,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " clawback some"); auto code = asset.raw().native() ? ter(temMALFORMED) : ter(tesSUCCESS); - auto tx = - vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(10)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(10)}); env(tx, code); env.close(); if (!asset.raw().native()) @@ -218,13 +235,17 @@ class Vault_test : public beast::unit_test::suite { auto tx = vault.clawback( - {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(10)}); + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(10)}); env(tx, ter{tecPRECISION_LOSS}); env.close(); } { - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(10)}); env(tx, ter{tecPRECISION_LOSS}); env.close(); } @@ -234,7 +255,8 @@ class Vault_test : public beast::unit_test::suite if (!asset.raw().native()) { testcase(prefix + " deposit again"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(200)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(200)}); env(tx); env.close(); BEAST_EXPECT(env.balance(depositor, shares) == share(200 * scale)); @@ -244,7 +266,8 @@ class Vault_test : public beast::unit_test::suite testcase(prefix + " deposit/withdrawal same or less than fee"); auto const amount = env.current()->fees().base; - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = amount}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = amount}); env(tx); env.close(); @@ -262,18 +285,21 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); - tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = amount - 1}); + tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = amount - 1}); env(tx); env.close(); - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = amount - 1}); + tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = amount - 1}); env(tx); env.close(); } { testcase(prefix + " fail to withdraw to 3rd party lsfDepositAuth"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); tx[sfDestination] = alice.human(); env(tx, ter{tecNO_PERMISSION}); env.close(); @@ -281,7 +307,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to withdraw to zero destination"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); tx[sfDestination] = "0"; env(tx, ter(temMALFORMED)); env.close(); @@ -290,7 +317,8 @@ class Vault_test : public beast::unit_test::suite if (!asset.raw().native()) { testcase(prefix + " fail to withdraw to 3rd party no authorization"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); tx[sfDestination] = erin.human(); env(tx, ter{asset.raw().holds() ? tecNO_LINE : tecNO_AUTH}); env.close(); @@ -298,7 +326,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to withdraw to 3rd party lsfRequireDestTag"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); tx[sfDestination] = dave.human(); env(tx, ter{tecDST_TAG_NEEDED}); env.close(); @@ -306,7 +335,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " withdraw to 3rd party lsfRequireDestTag"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); tx[sfDestination] = dave.human(); tx[sfDestinationTag] = "0"; env(tx); @@ -322,14 +352,16 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " fail to withdraw lsfRequireDestTag"); - auto tx = vault.withdraw({.depositor = dave, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = dave, .id = keylet.key, .amount = asset(50)}); env(tx, ter{tecDST_TAG_NEEDED}); env.close(); } { testcase(prefix + " withdraw with tag"); - auto tx = vault.withdraw({.depositor = dave, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = dave, .id = keylet.key, .amount = asset(50)}); tx[sfDestinationTag] = "0"; env(tx); env.close(); @@ -337,7 +369,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " withdraw to authorized 3rd party"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); tx[sfDestination] = charlie.human(); env(tx); env.close(); @@ -346,7 +379,8 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " withdraw to issuer"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); tx[sfDestination] = issuer.human(); env(tx); env.close(); @@ -356,13 +390,15 @@ class Vault_test : public beast::unit_test::suite if (!asset.raw().native()) { testcase(prefix + " issuer deposits"); - auto tx = vault.deposit({.depositor = issuer, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = issuer, .id = keylet.key, .amount = asset(10)}); env(tx); env.close(); BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale)); testcase(prefix + " issuer withdraws"); - tx = vault.withdraw({.depositor = issuer, .id = keylet.key, .amount = share(10 * scale)}); + tx = vault.withdraw( + {.depositor = issuer, .id = keylet.key, .amount = share(10 * scale)}); env(tx); env.close(); BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale)); @@ -370,21 +406,26 @@ class Vault_test : public beast::unit_test::suite { testcase(prefix + " withdraw remaining assets"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx); env.close(); BEAST_EXPECT(env.balance(depositor, shares) == share(0)); if (!asset.raw().native()) { - auto tx = - vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(0)}); env(tx, ter{tecPRECISION_LOSS}); env.close(); } { - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = share(10)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = share(10)}); env(tx, ter{tecINSUFFICIENT_FUNDS}); env.close(); } @@ -409,7 +450,8 @@ class Vault_test : public beast::unit_test::suite env.close(); // depositor will gain MPToken for shares again - env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)})); + env(vault.deposit( + {.depositor = depositor, .id = keylet.key, .amount = asset(1)})); env.close(); env(tx); @@ -418,7 +460,8 @@ class Vault_test : public beast::unit_test::suite testcase(prefix + " withdraw to authorized 3rd party"); // Depositor withdraws assets, destined to Erin - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); + tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); tx[sfDestination] = erin.human(); env(tx); env.close(); @@ -457,7 +500,8 @@ class Vault_test : public beast::unit_test::suite } }; - auto testCases = [&, this](std::string prefix, std::function setup) { + auto testCases = [&, this]( + std::string prefix, std::function setup) { Env env{*this, testable_amendments() | featureSingleAssetVault}; Vault vault{env}; @@ -514,34 +558,41 @@ class Vault_test : public beast::unit_test::suite FeatureBitset features = testable_amendments() | featureSingleAssetVault; }; - auto testCase = - [&, this]( - std::function test, - CaseArgs args = {}) { - Env env{*this, args.features}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Vault vault{env}; - env.fund(XRP(1000), issuer, owner); - env.close(); + auto testCase = [&, this]( + std::function test, + CaseArgs args = {}) { + Env env{*this, args.features}; + Account issuer{"issuer"}; + Account owner{"owner"}; + Vault vault{env}; + env.fund(XRP(1000), issuer, owner); + env.close(); - env(fset(issuer, asfAllowTrustLineClawback)); - env(fset(issuer, asfRequireAuth)); - env.close(); + env(fset(issuer, asfAllowTrustLineClawback)); + env(fset(issuer, asfRequireAuth)); + env.close(); - PrettyAsset asset = issuer["IOU"]; - env(trust(owner, asset(1000))); - env(trust(issuer, asset(0), owner, tfSetfAuth)); - env(pay(issuer, owner, asset(1000))); - env.close(); + PrettyAsset asset = issuer["IOU"]; + env(trust(owner, asset(1000))); + env(trust(issuer, asset(0), owner, tfSetfAuth)); + env(pay(issuer, owner, asset(1000))); + env.close(); - test(env, issuer, owner, asset, vault); - }; + test(env, issuer, owner, asset, vault); + }; auto testDisabled = [&](TER resultAfterCreate = temDISABLED) { return [&, resultAfterCreate]( - Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("disabled single asset vault"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -553,18 +604,20 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); - env(tx, ter{resultAfterCreate}); - } - - { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); env(tx, ter{resultAfterCreate}); } { auto tx = - vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + env(tx, ter{resultAfterCreate}); + } + + { + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); env(tx, ter{resultAfterCreate}); } @@ -577,10 +630,15 @@ class Vault_test : public beast::unit_test::suite testCase(testDisabled(), {.features = testable_amendments() - featureSingleAssetVault}); - testCase(testDisabled(tecNO_ENTRY), {.features = testable_amendments() - featureMPTokensV1}); + testCase( + testDisabled(tecNO_ENTRY), {.features = testable_amendments() - featureMPTokensV1}); testCase( - [&](Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + [&](Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("disabled permissioned domains"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -600,7 +658,11 @@ class Vault_test : public beast::unit_test::suite }, {.features = testable_amendments() - featurePermissionedDomains}); - testCase([&](Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + testCase([&](Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("invalid flags"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -614,19 +676,22 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[sfFlags] = tfClearDeepFreeze; env(tx, ter{temINVALID_FLAG}); } { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[sfFlags] = tfClearDeepFreeze; env(tx, ter{temINVALID_FLAG}); } { - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); tx[sfFlags] = tfClearDeepFreeze; env(tx, ter{temINVALID_FLAG}); } @@ -638,7 +703,11 @@ class Vault_test : public beast::unit_test::suite } }); - testCase([&](Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + testCase([&](Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("invalid fee"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -652,19 +721,22 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[jss::Fee] = "-1"; env(tx, ter{temBAD_FEE}); } { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[jss::Fee] = "-1"; env(tx, ter{temBAD_FEE}); } { - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(10)}); tx[jss::Fee] = "-1"; env(tx, ter{temBAD_FEE}); } @@ -696,9 +768,14 @@ class Vault_test : public beast::unit_test::suite env(tx, ter{temDISABLED}); } }, - {.features = (testable_amendments() | featureSingleAssetVault) - featurePermissionedDomains}); + {.features = + (testable_amendments() | featureSingleAssetVault) - featurePermissionedDomains}); - testCase([&](Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + testCase([&](Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("use zero vault"); auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpIssue()}); @@ -712,17 +789,20 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.deposit({.depositor = owner, .id = beast::zero, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = owner, .id = beast::zero, .amount = asset(10)}); env(tx, ter(temMALFORMED)); } { - auto tx = vault.withdraw({.depositor = owner, .id = beast::zero, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = beast::zero, .amount = asset(10)}); env(tx, ter{temMALFORMED}); } { - auto tx = vault.clawback({.issuer = issuer, .id = beast::zero, .holder = owner, .amount = asset(10)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = beast::zero, .holder = owner, .amount = asset(10)}); env(tx, ter{temMALFORMED}); } @@ -735,184 +815,202 @@ class Vault_test : public beast::unit_test::suite } }); - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("withdraw to bad destination"); + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("withdraw to bad destination"); - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); - tx[jss::Destination] = "0"; - env(tx, ter{temMALFORMED}); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("create with Scale"); - - { auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - tx[sfScale] = 255; - env(tx, ter(temMALFORMED)); - } - { + { + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + tx[jss::Destination] = "0"; + env(tx, ter{temMALFORMED}); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("create with Scale"); + + { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 255; + env(tx, ter(temMALFORMED)); + } + + { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 19; + env(tx, ter(temMALFORMED)); + } + + // accepted range from 0 to 18 + { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 18; + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 18); + } + + { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 0; + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 0); + } + + { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 6); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("create or set invalid data"); + + auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); + + { + auto tx = tx1; + tx[sfData] = ""; + env(tx, ter(temMALFORMED)); + } + + { + auto tx = tx1; + // A hexadecimal string of 257 bytes. + tx[sfData] = std::string(514, 'A'); + env(tx, ter(temMALFORMED)); + } + + { + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfData] = ""; + env(tx, ter{temMALFORMED}); + } + + { + auto tx = vault.set({.owner = owner, .id = keylet.key}); + // A hexadecimal string of 257 bytes. + tx[sfData] = std::string(514, 'A'); + env(tx, ter{temMALFORMED}); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("set nothing updated"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - tx[sfScale] = 19; - env(tx, ter(temMALFORMED)); - } - // accepted range from 0 to 18 - { + { + auto tx = vault.set({.owner = owner, .id = keylet.key}); + env(tx, ter{temMALFORMED}); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("create with invalid metadata"); + + auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); + + { + auto tx = tx1; + tx[sfMPTokenMetadata] = ""; + env(tx, ter(temMALFORMED)); + } + + { + auto tx = tx1; + // This metadata is for the share token. + // A hexadecimal string of 1025 bytes. + tx[sfMPTokenMetadata] = std::string(2050, 'B'); + env(tx, ter(temMALFORMED)); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("set negative maximum"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - tx[sfScale] = 18; - env(tx); - env.close(); - auto const sleVault = env.le(keylet); - BEAST_EXPECT(sleVault); - BEAST_EXPECT((*sleVault)[sfScale] == 18); - } - { + { + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfAssetsMaximum] = negativeAmount(asset).number(); + env(tx, ter{temMALFORMED}); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("invalid deposit amount"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - tx[sfScale] = 0; - env(tx); - env.close(); - auto const sleVault = env.le(keylet); - BEAST_EXPECT(sleVault); - BEAST_EXPECT((*sleVault)[sfScale] == 0); - } - { + { + auto tx = vault.deposit( + {.depositor = owner, .id = keylet.key, .amount = negativeAmount(asset)}); + env(tx, ter(temBAD_AMOUNT)); + } + + { + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(0)}); + env(tx, ter(temBAD_AMOUNT)); + } + }); + + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("invalid set immutable flag"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - env(tx); - env.close(); - auto const sleVault = env.le(keylet); - BEAST_EXPECT(sleVault); - BEAST_EXPECT((*sleVault)[sfScale] == 6); - } - }); - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("create or set invalid data"); + { + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfFlags] = tfVaultPrivate; + env(tx, ter(temINVALID_FLAG)); + } + }); - auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("invalid withdraw amount"); - { - auto tx = tx1; - tx[sfData] = ""; - env(tx, ter(temMALFORMED)); - } + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - { - auto tx = tx1; - // A hexadecimal string of 257 bytes. - tx[sfData] = std::string(514, 'A'); - env(tx, ter(temMALFORMED)); - } + { + auto tx = vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = negativeAmount(asset)}); + env(tx, ter(temBAD_AMOUNT)); + } - { - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfData] = ""; - env(tx, ter{temMALFORMED}); - } + { + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(0)}); + env(tx, ter(temBAD_AMOUNT)); + } + }); - { - auto tx = vault.set({.owner = owner, .id = keylet.key}); - // A hexadecimal string of 257 bytes. - tx[sfData] = std::string(514, 'A'); - env(tx, ter{temMALFORMED}); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("set nothing updated"); - - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.set({.owner = owner, .id = keylet.key}); - env(tx, ter{temMALFORMED}); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("create with invalid metadata"); - - auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = tx1; - tx[sfMPTokenMetadata] = ""; - env(tx, ter(temMALFORMED)); - } - - { - auto tx = tx1; - // This metadata is for the share token. - // A hexadecimal string of 1025 bytes. - tx[sfMPTokenMetadata] = std::string(2050, 'B'); - env(tx, ter(temMALFORMED)); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("set negative maximum"); - - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfAssetsMaximum] = negativeAmount(asset).number(); - env(tx, ter{temMALFORMED}); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("invalid deposit amount"); - - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = negativeAmount(asset)}); - env(tx, ter(temBAD_AMOUNT)); - } - - { - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(0)}); - env(tx, ter(temBAD_AMOUNT)); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("invalid set immutable flag"); - - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfFlags] = tfVaultPrivate; - env(tx, ter(temINVALID_FLAG)); - } - }); - - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("invalid withdraw amount"); - - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - - { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = negativeAmount(asset)}); - env(tx, ter(temBAD_AMOUNT)); - } - - { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(0)}); - env(tx, ter(temBAD_AMOUNT)); - } - }); - - testCase([&](Env& env, Account const& issuer, Account const& owner, Asset const& asset, Vault& vault) { + testCase([&](Env& env, + Account const& issuer, + Account const& owner, + Asset const& asset, + Vault& vault) { testcase("invalid clawback"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -920,47 +1018,52 @@ class Vault_test : public beast::unit_test::suite // Preclaim only checks for native assets. if (asset.native()) { - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); env(tx, ter(temMALFORMED)); } { auto tx = vault.clawback( - {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = negativeAmount(asset)}); + {.issuer = issuer, + .id = keylet.key, + .holder = owner, + .amount = negativeAmount(asset)}); env(tx, ter(temBAD_AMOUNT)); } }); - testCase([&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { - testcase("invalid create"); + testCase( + [&](Env& env, Account const&, Account const& owner, Asset const& asset, Vault& vault) { + testcase("invalid create"); - auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); + auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset}); - { - auto tx = tx1; - tx[sfWithdrawalPolicy] = 0; - env(tx, ter(temMALFORMED)); - } + { + auto tx = tx1; + tx[sfWithdrawalPolicy] = 0; + env(tx, ter(temMALFORMED)); + } - { - auto tx = tx1; - tx[sfDomainID] = to_string(base_uint<256>(42ul)); - env(tx, ter{temMALFORMED}); - } + { + auto tx = tx1; + tx[sfDomainID] = to_string(base_uint<256>(42ul)); + env(tx, ter{temMALFORMED}); + } - { - auto tx = tx1; - tx[sfAssetsMaximum] = negativeAmount(asset).number(); - env(tx, ter{temMALFORMED}); - } + { + auto tx = tx1; + tx[sfAssetsMaximum] = negativeAmount(asset).number(); + env(tx, ter{temMALFORMED}); + } - { - auto tx = tx1; - tx[sfFlags] = tfVaultPrivate; - tx[sfDomainID] = "0"; - env(tx, ter{temMALFORMED}); - } - }); + { + auto tx = tx1; + tx[sfFlags] = tfVaultPrivate; + tx[sfDomainID] = "0"; + env(tx, ter{temMALFORMED}); + } + }); } // Test for non-asset specific behaviors. @@ -969,13 +1072,14 @@ class Vault_test : public beast::unit_test::suite { using namespace test::jtx; - auto testCase = [this](std::function test) { + auto testCase = [this]( + std::function test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; Account issuer{"issuer"}; Account owner{"owner"}; @@ -1009,7 +1113,8 @@ class Vault_test : public beast::unit_test::suite PrettyAsset const& asset, Vault& vault) { testcase("nothing to deposit to"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)}); + auto tx = vault.deposit( + {.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)}); env(tx, ter(tecNO_ENTRY)); }); @@ -1021,7 +1126,8 @@ class Vault_test : public beast::unit_test::suite PrettyAsset const& asset, Vault& vault) { testcase("nothing to withdraw from"); - auto tx = vault.withdraw({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)}); env(tx, ter(tecNO_ENTRY)); }); @@ -1138,7 +1244,8 @@ class Vault_test : public beast::unit_test::suite { { testcase("IOU fail because MPT is disabled"); - Env env{*this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault}; + Env env{ + *this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault}; Account issuer{"issuer"}; Account owner{"owner"}; env.fund(XRP(1000), issuer, owner); @@ -1232,11 +1339,17 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2); if (!asset1.native() && !asset2.native()) + { fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All); + } else if (asset1.native()) + { fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All); + } else if (asset2.native()) + { fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All); + } AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0}); @@ -1255,13 +1368,14 @@ class Vault_test : public beast::unit_test::suite { using namespace test::jtx; - auto testCase = [this](std::function test) { + auto testCase = [this]( + std::function test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; Account issuer{"issuer"}; Account owner{"owner"}; @@ -1343,7 +1457,8 @@ class Vault_test : public beast::unit_test::suite { testcase("nontransferable deposits"); - auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(40)}); + auto tx1 = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(40)}); env(tx1); auto tx2 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(60)}); @@ -1356,10 +1471,12 @@ class Vault_test : public beast::unit_test::suite auto jvVault = env.rpc("vault_info", strHex(key)); BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "100"); - BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "100000000"); + BEAST_EXPECT( + jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "100000000"); // Vault pseudo-account - return parseBase58(jvVault[jss::result][jss::vault][jss::Account].asString()).value(); + return parseBase58(jvVault[jss::result][jss::vault][jss::Account].asString()) + .value(); }(); auto const MptID = makeMptID(1, vaultAccount); @@ -1373,7 +1490,8 @@ class Vault_test : public beast::unit_test::suite { testcase("nontransferable shares can be used to withdraw"); - auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)}); + auto tx1 = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)}); env(tx1); auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)}); @@ -1385,12 +1503,14 @@ class Vault_test : public beast::unit_test::suite testcase("nontransferable shares balance check"); auto jvVault = env.rpc("vault_info", strHex(keylet.key)); BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "50"); - BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "50000000"); + BEAST_EXPECT( + jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "50000000"); } { testcase("nontransferable shares withdraw rest"); - auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)}); + auto tx1 = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)}); env(tx1); auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)}); @@ -1439,7 +1559,8 @@ class Vault_test : public beast::unit_test::suite MPTTester mptt{env, issuer, mptInitNoFund}; auto const none = LedgerSpecificFlags(0); mptt.create( - {.flags = tfMPTCanTransfer | tfMPTCanLock | (args.enableClawback ? tfMPTCanClawback : none) | + {.flags = tfMPTCanTransfer | tfMPTCanLock | + (args.enableClawback ? tfMPTCanClawback : none) | (args.requireAuth ? tfMPTRequireAuth : none), .mutableFlags = tmfMPTCanMutateCanTransfer}); PrettyAsset asset = mptt.issuanceID(); @@ -1466,8 +1587,11 @@ class Vault_test : public beast::unit_test::suite Vault& vault, MPTTester& mptt) { testcase("MPT nothing to clawback from"); - auto tx = - vault.clawback({.issuer = issuer, .id = keylet::skip().key, .holder = depositor, .amount = asset(10)}); + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet::skip().key, + .holder = depositor, + .amount = asset(10)}); env(tx, ter(tecNO_ENTRY)); }); @@ -1546,7 +1670,8 @@ class Vault_test : public beast::unit_test::suite env(tx, ter(tecLOCKED)); // Clawback is still permitted, even with global lock - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); env(tx); env.close(); @@ -1611,7 +1736,8 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); - tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); + tx = vault.deposit( + {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); env(tx); env.close(); @@ -1624,7 +1750,8 @@ class Vault_test : public beast::unit_test::suite auto const sleMPT1 = env.le(mptoken); BEAST_EXPECT(sleMPT1 == nullptr); - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx, ter{tecNO_AUTH}); env.close(); @@ -1638,7 +1765,8 @@ class Vault_test : public beast::unit_test::suite env.fund(XRP(1000), charlie); env.close(); - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); tx[sfDestination] = charlie.human(); env(tx, ter(tecNO_AUTH)); } @@ -1663,7 +1791,9 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(v); tx = vault.deposit( - {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); // all assets held by depositor + {.depositor = depositor, + .id = keylet.key, + .amount = asset(1000)}); // all assets held by depositor env(tx); env.close(); @@ -1676,7 +1806,8 @@ class Vault_test : public beast::unit_test::suite auto const sleMPT1 = env.le(mptoken); BEAST_EXPECT(sleMPT1 == nullptr); - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx); env.close(); @@ -1694,7 +1825,8 @@ class Vault_test : public beast::unit_test::suite auto const sleMPT1 = env.le(mptoken); BEAST_EXPECT(sleMPT1 == nullptr); - tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); tx[sfDestination] = owner.human(); env(tx, ter(tecNO_AUTH)); env.close(); @@ -1733,7 +1865,9 @@ class Vault_test : public beast::unit_test::suite env.close(); tx = vault.deposit( - {.depositor = owner, .id = keylet.key, .amount = asset(1000)}); // all assets held by owner + {.depositor = owner, + .id = keylet.key, + .amount = asset(1000)}); // all assets held by owner env(tx); env.close(); @@ -1751,7 +1885,8 @@ class Vault_test : public beast::unit_test::suite env.close(); // No reserve to create MPToken for asset in VaultWithdraw - tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)}); + tx = vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = asset(100)}); env(tx, ter{tecINSUFFICIENT_RESERVE}); env.close(); @@ -1784,7 +1919,8 @@ class Vault_test : public beast::unit_test::suite env.close(); { - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); env(tx); } @@ -1797,17 +1933,20 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); env(tx, ter{tecOBJECT_NOT_FOUND}); } { - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)}); env(tx, ter{tecOBJECT_NOT_FOUND}); } { - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); env(tx, ter{tecOBJECT_NOT_FOUND}); } @@ -1851,7 +1990,8 @@ class Vault_test : public beast::unit_test::suite env(pay(depositor, owner, shares(1))); env.close(); - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); env(tx); env.close(); @@ -1880,7 +2020,8 @@ class Vault_test : public beast::unit_test::suite env.close(); // destroy all remaining shares, so we can delete vault - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); env(tx); env.close(); @@ -1905,13 +2046,17 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); - tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); + tx = vault.deposit( + {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); env(tx); env.close(); { - auto tx = - vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(0)}); env(tx, ter{tecNO_PERMISSION}); } }, @@ -1937,7 +2082,8 @@ class Vault_test : public beast::unit_test::suite env.close(); { - auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = vault.withdraw( + {.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx, ter(tecNO_AUTH)); // Withdrawal to other (authorized) accounts works @@ -1952,17 +2098,20 @@ class Vault_test : public beast::unit_test::suite { // Cannot deposit some more - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); env(tx, ter(tecNO_AUTH)); } { // Cannot clawback if issuer is the holder - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = issuer, .amount = asset(800)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = issuer, .amount = asset(800)}); env(tx, ter(tecNO_PERMISSION)); } // Clawback works - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(800)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(800)}); env(tx); env.close(); @@ -2011,7 +2160,8 @@ class Vault_test : public beast::unit_test::suite env(tx, ter(tecLOCKED)); // Clawback works, even when locked - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(100)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(100)}); env(tx); // Can delete an empty vault even when asset is locked. @@ -2030,7 +2180,8 @@ class Vault_test : public beast::unit_test::suite Vault vault{env}; MPTTester mptt{env, issuer, mptInitNoFund}; - mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth}); + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth}); mptt.authorize({.account = owner}); mptt.authorize({.account = issuer, .holder = owner}); PrettyAsset asset = mptt.issuanceID(); @@ -2140,7 +2291,9 @@ class Vault_test : public beast::unit_test::suite env(fset(issuer, asfDefaultRipple)); } else + { env.trust(asset(1000), charlie); + } env.close(); env(rate(issuer, args.transferRate)); env.close(); @@ -2148,7 +2301,9 @@ class Vault_test : public beast::unit_test::suite auto const vaultAccount = [&env](xrpl::Keylet keylet) -> Account { return Account("vault", env.le(keylet)->at(sfAccount)); }; - auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID { return env.le(keylet)->at(sfShareMPTID); }; + auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID { + return env.le(keylet)->at(sfShareMPTID); + }; test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId); }; @@ -2193,7 +2348,8 @@ class Vault_test : public beast::unit_test::suite } { - auto tx = vault.withdraw({.depositor = issuer, .id = keylet.key, .amount = foo(20)}); + auto tx = + vault.withdraw({.depositor = issuer, .id = keylet.key, .amount = foo(20)}); env(tx, ter{tecWRONG_ASSET}); env.close(); } @@ -2242,12 +2398,14 @@ class Vault_test : public beast::unit_test::suite // is reported as "locked" state of the vault shares, because // this state is attached to shares by means of the transitive // isFrozen. - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(80)}); + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(80)}); env(tx, ter{tecLOCKED}); } { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)}); env(tx, ter{tecLOCKED}); // also when trying to withdraw to a 3rd party @@ -2258,7 +2416,8 @@ class Vault_test : public beast::unit_test::suite { // Clawback works, even when locked - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); env(tx); env.close(); } @@ -2268,7 +2427,8 @@ class Vault_test : public beast::unit_test::suite env(trustSet); env.close(); - env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(50'000'000)})); + env(vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = share(50'000'000)})); env(vault.del({.owner = owner, .id = keylet.key})); env.close(); @@ -2301,8 +2461,8 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(100)); { - auto tx = - vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)}); env(tx); env.close(); } @@ -2311,14 +2471,16 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(owner, issue) == asset(100)); BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50)); - env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(20'000'000)})); + env(vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = share(20'000'000)})); // transfer fees ignored on withdraw BEAST_EXPECT(env.balance(owner, issue) == asset(120)); BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30)); { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(30'000'000)}); + auto tx = vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = share(30'000'000)}); tx[sfDestination] = charlie.human(); env(tx); } @@ -2353,7 +2515,8 @@ class Vault_test : public beast::unit_test::suite // Withdraw to 3rd party works auto const withdrawToCharlie = [&](xrpl::Keylet keylet) { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[sfDestination] = charlie.human(); return tx; }(keylet); @@ -2364,7 +2527,8 @@ class Vault_test : public beast::unit_test::suite env.close(); // Cannot withdraw - auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto const withdraw = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); env(withdraw, ter{tecFROZEN}); // Cannot withdraw to 3rd party @@ -2373,13 +2537,15 @@ class Vault_test : public beast::unit_test::suite { // Cannot deposit some more - auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)}); env(tx, ter{tecFROZEN}); } { // Clawback still works - auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); + auto tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); env(tx); env.close(); } @@ -2412,7 +2578,8 @@ class Vault_test : public beast::unit_test::suite // Withdraw to 3rd party without trust line auto const tx1 = [&](xrpl::Keylet keylet) { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[sfDestination] = erin.human(); return tx; }(keylet); @@ -2446,7 +2613,8 @@ class Vault_test : public beast::unit_test::suite // Withdraw without trust line, will succeed auto const tx1 = [&](xrpl::Keylet keylet) { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); return tx; }(keylet); env(tx1); @@ -2471,54 +2639,59 @@ class Vault_test : public beast::unit_test::suite // Turn on noripple on the pseudo account's trust line. // Charlie's is already set. - env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple), THISLINE); + env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple)); { // Charlie cannot deposit - auto tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(100)}); - env(tx, ter{terNO_RIPPLE}, THISLINE); + auto tx = vault.deposit( + {.depositor = charlie, .id = keylet.key, .amount = asset(100)}); + env(tx, ter{terNO_RIPPLE}); env.close(); } { PrettyAsset shares = issuanceId(keylet); - auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}); - env(tx1, THISLINE); + auto tx1 = + vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}); + env(tx1); env.close(); // Charlie cannot receive funds - auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = shares(100)}); + auto tx2 = vault.withdraw( + {.depositor = owner, .id = keylet.key, .amount = shares(100)}); tx2[sfDestination] = charlie.human(); - env(tx2, ter{terNO_RIPPLE}, THISLINE); + env(tx2, ter{terNO_RIPPLE}); env.close(); { // Create MPToken for shares held by Charlie Json::Value tx{Json::objectValue}; tx[sfAccount] = charlie.human(); - tx[sfMPTokenIssuanceID] = to_string(shares.raw().get().getMptID()); + tx[sfMPTokenIssuanceID] = + to_string(shares.raw().get().getMptID()); tx[sfTransactionType] = jss::MPTokenAuthorize; env(tx); env.close(); } - env(pay(owner, charlie, shares(100)), THISLINE); + env(pay(owner, charlie, shares(100))); env.close(); // Charlie cannot withdraw - auto tx3 = vault.withdraw({.depositor = charlie, .id = keylet.key, .amount = shares(100)}); + auto tx3 = vault.withdraw( + {.depositor = charlie, .id = keylet.key, .amount = shares(100)}); env(tx3, ter{terNO_RIPPLE}); env.close(); - env(pay(charlie, owner, shares(100)), THISLINE); + env(pay(charlie, owner, shares(100))); env.close(); } tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)}); - env(tx, THISLINE); + env(tx); env.close(); // Delete vault with zero balance - env(vault.del({.owner = owner, .id = keylet.key}), THISLINE); + env(vault.del({.owner = owner, .id = keylet.key})); }, {.charlieRipple = false}); @@ -2548,8 +2721,8 @@ class Vault_test : public beast::unit_test::suite // blocked by Vault invariants. env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)})); - auto const tx1 = - vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(Number(375, -2))}); + auto const tx1 = vault.deposit( + {.depositor = owner, .id = keylet.key, .amount = asset(Number(375, -2))}); for (auto i = 0; i < 5; ++i) { env(tx1); @@ -2569,7 +2742,10 @@ class Vault_test : public beast::unit_test::suite // Total vault balance should be 118.5 IOU. Withdraw and delete // the vault to verify this exact amount was deposited and the // owner has matching shares - env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(Number(1000 + 37 * 5, -1))})); + env(vault.withdraw( + {.depositor = owner, + .id = keylet.key, + .amount = asset(Number(1000 + 37 * 5, -1))})); { BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value()); @@ -2689,7 +2865,8 @@ class Vault_test : public beast::unit_test::suite // Withdraw to 3rd party works auto const withdrawToCharlie = [&](xrpl::Keylet keylet) { - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); tx[sfDestination] = charlie.human(); return tx; }(keylet); @@ -2700,7 +2877,8 @@ class Vault_test : public beast::unit_test::suite env.close(); // Can withdraw - auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto const withdraw = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); env(withdraw); env.close(); @@ -2708,7 +2886,8 @@ class Vault_test : public beast::unit_test::suite env(withdrawToCharlie, ter{tecFROZEN}); env.close(); - env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)})); + env(vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)})); env.close(); env(vault.del({.owner = owner, .id = keylet.key})); @@ -2738,7 +2917,8 @@ class Vault_test : public beast::unit_test::suite { // Cannot withdraw - auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); + auto tx = + vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)}); env(tx, ter{tecFROZEN}); // Cannot withdraw to 3rd party @@ -2753,7 +2933,8 @@ class Vault_test : public beast::unit_test::suite } // Clawback is permitted - env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)})); + env(vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)})); env.close(); env(vault.del({.owner = owner, .id = keylet.key})); @@ -2806,7 +2987,8 @@ class Vault_test : public beast::unit_test::suite { testcase("private vault depositor not authorized yet"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx, ter{tecNO_AUTH}); } @@ -2821,7 +3003,8 @@ class Vault_test : public beast::unit_test::suite testcase("private vault set domainId"); { - pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}}; + pdomain::Credentials const credentials1{ + {.issuer = credIssuer1, .credType = credType}}; env(pdomain::setTx(pdOwner, credentials1)); auto const domainId1 = [&]() { @@ -2841,7 +3024,8 @@ class Vault_test : public beast::unit_test::suite { pdomain::Credentials const credentials{ - {.issuer = credIssuer1, .credType = credType}, {.issuer = credIssuer2, .credType = credType}}; + {.issuer = credIssuer1, .credType = credType}, + {.issuer = credIssuer2, .credType = credType}}; env(pdomain::setTx(pdOwner, credentials)); auto const domainId = [&]() { @@ -2864,7 +3048,8 @@ class Vault_test : public beast::unit_test::suite { testcase("private vault depositor still not authorized"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx, ter{tecNO_AUTH}); env.close(); } @@ -2880,7 +3065,8 @@ class Vault_test : public beast::unit_test::suite auto credSle = env.le(credKeylet); BEAST_EXPECT(credSle != nullptr); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx); env.close(); @@ -2897,7 +3083,8 @@ class Vault_test : public beast::unit_test::suite auto credSle = env.le(credKeylet); BEAST_EXPECT(credSle == nullptr); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx, ter{tecNO_AUTH}); env.close(); } @@ -2910,7 +3097,8 @@ class Vault_test : public beast::unit_test::suite { testcase("private vault expired authorization"); - uint32_t const closeTime = env.current()->header().parentCloseTime.time_since_epoch().count(); + uint32_t const closeTime = + env.current()->header().parentCloseTime.time_since_epoch().count(); { auto tx0 = credentials::create(depositor, credIssuer2, credType); tx0[sfExpiration] = closeTime + 20; @@ -2926,11 +3114,13 @@ class Vault_test : public beast::unit_test::suite } { - auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx1 = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx1); env.close(); - auto const tokenKeylet = keylet::mptoken(shares.get().getMptID(), depositor.id()); + auto const tokenKeylet = + keylet::mptoken(shares.get().getMptID(), depositor.id()); BEAST_EXPECT(env.le(tokenKeylet) != nullptr); } @@ -2943,7 +3133,8 @@ class Vault_test : public beast::unit_test::suite auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType); BEAST_EXPECT(env.le(credsKeylet) != nullptr); - auto tx2 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)}); + auto tx2 = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)}); env(tx2, ter{tecEXPIRED}); env.close(); @@ -2953,10 +3144,12 @@ class Vault_test : public beast::unit_test::suite { auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType); BEAST_EXPECT(env.le(credsKeylet) != nullptr); - auto const tokenKeylet = keylet::mptoken(shares.get().getMptID(), charlie.id()); + auto const tokenKeylet = + keylet::mptoken(shares.get().getMptID(), charlie.id()); BEAST_EXPECT(env.le(tokenKeylet) == nullptr); - auto tx3 = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(2)}); + auto tx3 = + vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(2)}); env(tx3, ter{tecEXPIRED}); env.close(); @@ -2980,10 +3173,12 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)}); env(tx); - tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); + tx = vault.clawback( + {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}); env(tx); env.close(); @@ -3016,7 +3211,8 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); - auto const [vaultAccount, issuanceId] = [&env, keylet = keylet, this]() -> std::tuple { + auto const [vaultAccount, issuanceId] = + [&env, keylet = keylet, this]() -> std::tuple { auto const vault = env.le(keylet); BEAST_EXPECT(vault != nullptr); return {vault->at(sfAccount), vault->at(sfShareMPTID)}; @@ -3039,7 +3235,8 @@ class Vault_test : public beast::unit_test::suite { testcase("private XRP vault depositor not authorized yet"); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx, ter{tecNO_AUTH}); } @@ -3067,7 +3264,8 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.le(credKeylet)); - auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); + auto tx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)}); env(tx); env.close(); } @@ -3106,7 +3304,10 @@ class Vault_test : public beast::unit_test::suite { AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key); - env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill)); + env(pay(env.master.id(), accountId, XRP(1000)), + seq(autofill), + fee(autofill), + sig(autofill)); } auto [tx, keylet1] = vault.create({.owner = owner, .asset = xrpIssue()}); @@ -3134,7 +3335,8 @@ class Vault_test : public beast::unit_test::suite std::function)> peek; }; - auto testCase = [&, this](std::uint8_t scale, std::function test) { + auto testCase = [&, this]( + std::uint8_t scale, std::function test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; Account const owner{"owner"}; Account const issuer{"issuer"}; @@ -3155,14 +3357,15 @@ class Vault_test : public beast::unit_test::suite tx[sfScale] = scale; env(tx); - auto const [vaultAccount, issuanceId] = [&env](xrpl::Keylet keylet) -> std::tuple { + auto const [vaultAccount, issuanceId] = + [&env](xrpl::Keylet keylet) -> std::tuple { auto const vault = env.le(keylet); return {Account("vault", vault->at(sfAccount)), vault->at(sfShareMPTID)}; }(keylet); MPTIssue shares(issuanceId); env.memoize(vaultAccount); - auto const peek = [=, &env, this](std::function fn) -> bool { + auto const peek = [keylet, &env, this](std::function fn) -> bool { return env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool { Sandbox sb(&view, tapNONE); auto vault = sb.peek(keylet::vault(keylet.key)); @@ -3199,7 +3402,8 @@ class Vault_test : public beast::unit_test::suite testCase(18, [&, this](Env& env, Data d) { testcase("Scale deposit overflow on first deposit"); - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)}); env(tx, ter{tecPATH_DRY}); env.close(); }); @@ -3208,13 +3412,15 @@ class Vault_test : public beast::unit_test::suite testcase("Scale deposit overflow on second deposit"); { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); env(tx); env.close(); } { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)}); env(tx, ter{tecPATH_DRY}); env.close(); } @@ -3224,13 +3430,15 @@ class Vault_test : public beast::unit_test::suite testcase("Scale deposit overflow on total shares"); { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); env(tx); env.close(); } { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); env(tx, ter{tecPATH_DRY}); env.close(); } @@ -3240,7 +3448,8 @@ class Vault_test : public beast::unit_test::suite testcase("Scale deposit exact"); auto const start = env.balance(d.depositor, d.assets).number(); - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10)); @@ -3251,7 +3460,9 @@ class Vault_test : public beast::unit_test::suite testcase("Scale deposit insignificant amount"); auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(9, -2))}); env(tx, ter{tecPRECISION_LOSS}); }); @@ -3260,11 +3471,14 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(15, -1))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(15, -1))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(15, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(15, -1))); }); testCase(1, [&, this](Env& env, Data d) { @@ -3275,29 +3489,41 @@ class Vault_test : public beast::unit_test::suite // vault and receive 12 shares in exchange { auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(125, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(125, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(12, -1))); } { auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1201, -3))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(24, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(24, -1))); } { auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1299, -3))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(36, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(36, -1))); } }); @@ -3307,20 +3533,27 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); // round to 12 auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1201, -3))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1))); { // round to 6 auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(69, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(69, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(18, -1))); } }); @@ -3330,20 +3563,27 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); // round to 12 auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1299, -3))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1))); { // round to 6 auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(62, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(62, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(18, -1))); } }); @@ -3351,13 +3591,18 @@ class Vault_test : public beast::unit_test::suite // initial setup: deposit 100 IOU, receive 1000 shares auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0))); { testcase("Scale redeem exact"); @@ -3367,13 +3612,18 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(100, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(100, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0))); } { @@ -3393,13 +3643,21 @@ class Vault_test : public beast::unit_test::suite // closed (because a modification like above is not persistent), // which is why the checks below are expected to pass. auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(25, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(25, 0))}); env(tx, ter{tecINSUFFICIENT_FUNDS}); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(900 - 25, 0))); } { @@ -3411,21 +3669,31 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(21, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(21, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(21, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 21, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 21, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(21, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 21, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(875 - 21, 0))); } { testcase("Scale redeem rest"); auto const rest = env.balance(d.depositor, d.shares).number(); - tx = - d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, rest)}); + tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, rest)}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); @@ -3438,14 +3706,17 @@ class Vault_test : public beast::unit_test::suite testcase("Scale withdraw overflow"); { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); env(tx); env.close(); } { auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(10, 0))}); env(tx, ter{tecPATH_DRY}); env.close(); } @@ -3455,13 +3726,18 @@ class Vault_test : public beast::unit_test::suite // initial setup: deposit 100 IOU, receive 1000 shares auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0))); { testcase("Scale withdraw exact"); @@ -3474,19 +3750,26 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(10, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0))); } { testcase("Scale withdraw insignificant amount"); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(4, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(4, -2))}); env(tx, ter{tecPRECISION_LOSS}); } @@ -3510,13 +3793,21 @@ class Vault_test : public beast::unit_test::suite // closed (because a modification like above is not persistent), // which is why the checks below are expected to pass. auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(25, -1))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(25, -1))}); env(tx, ter{tecINSUFFICIENT_FUNDS}); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(900 - 25, 0))); } { @@ -3530,13 +3821,21 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(375, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(375, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(38, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(875 - 38, 0))); } { @@ -3550,13 +3849,21 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(372, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(372, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(37, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(837 - 37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(837 - 37, 0))); } { @@ -3564,21 +3871,30 @@ class Vault_test : public beast::unit_test::suite auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.withdraw( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(9, -2))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(1, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(800 - 1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(800 - 1, 0))); } { testcase("Scale withdraw rest"); auto const rest = env.balance(d.vaultAccount, d.assets).number(); - tx = - d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, rest)}); + tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, rest)}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); @@ -3591,7 +3907,8 @@ class Vault_test : public beast::unit_test::suite testcase("Scale clawback overflow"); { - auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); + auto tx = d.vault.deposit( + {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)}); env(tx); env.close(); } @@ -3611,13 +3928,18 @@ class Vault_test : public beast::unit_test::suite // initial setup: deposit 100 IOU, receive 1000 shares auto const start = env.balance(d.depositor, d.assets).number(); auto tx = d.vault.deposit( - {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))}); + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); - BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(1000, 0))); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(1000, 0))); { testcase("Scale clawback exact"); // assetsToSharesWithdraw: @@ -3637,8 +3959,10 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900)); BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start)); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900, 0))); } { @@ -3670,8 +3994,12 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25)); BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start)); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(900 - 25, 0))); } { @@ -3693,8 +4021,12 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38)); BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start)); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(875 - 38, 0))); } { @@ -3716,8 +4048,12 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37)); BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start)); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(837 - 37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(837 - 37, 0))); } { @@ -3733,8 +4069,12 @@ class Vault_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1)); BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start)); - BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1))); - BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(800 - 1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, -Number(800 - 1, 0))); } { @@ -3750,7 +4090,10 @@ class Vault_test : public beast::unit_test::suite // * when the ledger is closed with unmodified AssetsAvailable // because a modification like above is not persistent. tx = d.vault.clawback( - {.issuer = d.issuer, .id = d.keylet.key, .holder = d.depositor, .amount = STAmount(d.asset, rest)}); + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, rest)}); env(tx); env.close(); BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); @@ -3801,14 +4144,19 @@ class Vault_test : public beast::unit_test::suite }(); auto const check = [&, keylet = keylet, sle = sleVault, this]( - Json::Value const& vault, Json::Value const& issuance = Json::nullValue) { + Json::Value const& vault, + Json::Value const& issuance = Json::nullValue) { BEAST_EXPECT(vault.isObject()); - constexpr auto checkString = [](auto& node, SField const& field, std::string v) -> bool { - return node.isMember(field.fieldName) && node[field.fieldName].isString() && node[field.fieldName] == v; + constexpr auto checkString = + [](auto& node, SField const& field, std::string v) -> bool { + return node.isMember(field.fieldName) && node[field.fieldName].isString() && + node[field.fieldName] == v; }; - constexpr auto checkObject = [](auto& node, SField const& field, Json::Value v) -> bool { - return node.isMember(field.fieldName) && node[field.fieldName].isObject() && node[field.fieldName] == v; + constexpr auto checkObject = + [](auto& node, SField const& field, Json::Value v) -> bool { + return node.isMember(field.fieldName) && node[field.fieldName].isObject() && + node[field.fieldName] == v; }; constexpr auto checkInt = [](auto& node, SField const& field, int v) -> bool { return node.isMember(field.fieldName) && @@ -3839,7 +4187,8 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(issuance["LedgerEntryType"].asString() == "MPTokenIssuance"); BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID); BEAST_EXPECT(checkInt(issuance, sfSequence, 1)); - BEAST_EXPECT(checkInt(issuance, sfFlags, int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer))); + BEAST_EXPECT(checkInt( + issuance, sfFlags, int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer))); BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50000000")); } }; @@ -4167,128 +4516,6 @@ class Vault_test : public beast::unit_test::suite } } - void - testDelegate() - { - using namespace test::jtx; - - Env env(*this, testable_amendments()); - Account alice{"alice"}; - Account bob{"bob"}; - Account carol{"carol"}; - - struct CaseArgs - { - PrettyAsset asset = xrpIssue(); - }; - - auto const xrpBalance = [this](Env const& env, Account const& account) -> std::optional { - auto sle = env.le(keylet::account(account.id())); - if (BEAST_EXPECT(sle != nullptr)) - return sle->getFieldAmount(sfBalance).xrp().drops(); - return std::nullopt; - }; - - auto testCase = [&, this](auto test, CaseArgs args = {}) { - Env env{*this, testable_amendments() | featureSingleAssetVault}; - - Vault vault{env}; - - // use different initial amount to distinguish the source balance - env.fund(XRP(10000), alice); - env.fund(XRP(20000), bob); - env.fund(XRP(30000), carol); - env.close(); - - env(delegate::set( - carol, - alice, - {"Payment", - "VaultCreate", - "VaultSet", - "VaultDelete", - "VaultDeposit", - "VaultWithdraw", - "VaultClawback"})); - - test(env, vault, args.asset); - }; - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated vault creation"); - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated deposit and withdrawal"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 1513; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - }); - - testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) { - testcase("delegated withdrawal same as base fee and deletion"); - auto [tx, keylet] = vault.create({.owner = carol, .asset = asset}); - env(tx); - env.close(); - - auto const amount = 25537; - auto const baseFee = env.current()->fees().base; - - auto startBalance = xrpBalance(env, carol); - if (!BEAST_EXPECT(startBalance.has_value())) - return; - - tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)}); - env(tx); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount); - - tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)}); - env(tx, delegate::as(alice)); - env.close(); - BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee); - - tx = vault.del({.owner = carol, .id = keylet.key}); - env(tx, delegate::as(alice)); - env.close(); - }); - } - void testVaultClawbackBurnShares() { @@ -4314,12 +4541,13 @@ class Vault_test : public beast::unit_test::suite return sleIssuance->at(sfOutstandingAmount); }; - auto const setupVault = - [&](PrettyAsset const& asset, Account const& owner, Account const& depositor) -> std::pair { + auto const setupVault = [&](PrettyAsset const& asset, + Account const& owner, + Account const& depositor) -> std::pair { Vault vault{env}; auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tesSUCCESS), THISLINE); + env(tx, ter(tesSUCCESS)); env.close(); auto const& vaultSle = env.le(vaultKeylet); @@ -4327,9 +4555,9 @@ class Vault_test : public beast::unit_test::suite Asset share = vaultSle->at(sfShareMPTID); - env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), - ter(tesSUCCESS), - THISLINE); + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tesSUCCESS)); env.close(); auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet); @@ -4338,15 +4566,17 @@ class Vault_test : public beast::unit_test::suite // attempt to clawback shares while there are assets fails env(vault.clawback( - {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + {.issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + .amount = share(0).value()}), + ter(tecNO_PERMISSION)); env.close(); auto const& sharesAvailable = vaultShareBalance(vaultKeylet); auto const& brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner)); - env(set(owner, vaultKeylet.key), THISLINE); + env(set(owner, vaultKeylet.key)); env.close(); auto const& loanKeylet = keylet::loan(brokerKeylet.key, 1); @@ -4359,21 +4589,22 @@ class Vault_test : public beast::unit_test::suite paymentTotal(10), sig(sfCounterpartySignature, owner), fee(env.current()->fees().base * 2), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); // attempt to clawback shares while there assetsAvailable == 0 and // assetsTotal > 0 fails env(vault.clawback( - {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + {.issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + .amount = share(0).value()}), + ter(tecNO_PERMISSION)); env.close(); env.close(std::chrono::seconds{120 + 60}); - env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS), THISLINE); + env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS)); auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet); @@ -4384,124 +4615,130 @@ class Vault_test : public beast::unit_test::suite return std::make_pair(vault, vaultKeylet); }; - auto const testCase = - [&](PrettyAsset const& asset, std::string const& prefix, Account const& owner, Account const& depositor) { - { - testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails"); - auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = depositor, - .amount = asset(100).value(), - }), - // when asset is XRP or owner is not issuer clawback fail - // when owner is issuer precision loss occurs as vault is - // empty - asset.native() ? ter(temMALFORMED) - : asset.raw().getIssuer() != owner.id() ? ter(tecNO_PERMISSION) - : ter(tecPRECISION_LOSS), - THISLINE); - env.close(); - } + auto const testCase = [&](PrettyAsset const& asset, + std::string const& prefix, + Account const& owner, + Account const& depositor) { + { + testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); + // when asset is XRP or owner is not issuer clawback fail + // when owner is issuer precision loss occurs as vault is + // empty + auto const expectedTer = [&]() { + if (asset.native()) + return ter(temMALFORMED); + if (asset.raw().getIssuer() != owner.id()) + return ter(tecNO_PERMISSION); + return ter(tecPRECISION_LOSS); + }(); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + .amount = asset(100).value(), + }), + expectedTer); + env.close(); + } - { - testcase("VaultClawback (share) - " + prefix + " owner incomplete share clawback fails"); - auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); - auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) - return; - Asset share = vaultSle->at(sfShareMPTID); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = depositor, - .amount = share(1).value(), - }), - ter(tecLIMIT_EXCEEDED), - THISLINE); - env.close(); - } + { + testcase( + "VaultClawback (share) - " + prefix + " owner incomplete share clawback fails"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); + auto const& vaultSle = env.le(vaultKeylet); + BEAST_EXPECT(vaultSle != nullptr); + if (!vaultSle) + return; + Asset share = vaultSle->at(sfShareMPTID); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + .amount = share(1).value(), + }), + ter(tecLIMIT_EXCEEDED)); + env.close(); + } - { - testcase("VaultClawback (share) - " + prefix + " owner implicit complete share clawback"); - auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = depositor, - }), - // when owner is issuer implicit clawback fails - asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) : ter(tecWRONG_ASSET), - THISLINE); - env.close(); - } + { + testcase( + "VaultClawback (share) - " + prefix + + " owner implicit complete share clawback"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + }), + // when owner is issuer implicit clawback fails + asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) + : ter(tecWRONG_ASSET)); + env.close(); + } - { - testcase("VaultClawback (share) - " + prefix + " owner explicit complete share clawback succeeds"); - auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); - auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) - return; - Asset share = vaultSle->at(sfShareMPTID); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = depositor, - .amount = share(vaultShareBalance(vaultKeylet)).value(), - }), - ter(tesSUCCESS), - THISLINE); - env.close(); - } - { - testcase("VaultClawback (share) - " + prefix + " owner can clawback own shares"); - auto [vault, vaultKeylet] = setupVault(asset, owner, owner); - auto const& vaultSle = env.le(vaultKeylet); - BEAST_EXPECT(vaultSle != nullptr); - if (!vaultSle) - return; - Asset share = vaultSle->at(sfShareMPTID); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = owner, - .amount = share(vaultShareBalance(vaultKeylet)).value(), - }), - ter(tesSUCCESS), - THISLINE); - env.close(); - } + { + testcase( + "VaultClawback (share) - " + prefix + + " owner explicit complete share clawback succeeds"); + auto [vault, vaultKeylet] = setupVault(asset, owner, depositor); + auto const& vaultSle = env.le(vaultKeylet); + BEAST_EXPECT(vaultSle != nullptr); + if (!vaultSle) + return; + Asset share = vaultSle->at(sfShareMPTID); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = depositor, + .amount = share(vaultShareBalance(vaultKeylet)).value(), + }), + ter(tesSUCCESS)); + env.close(); + } + { + testcase("VaultClawback (share) - " + prefix + " owner can clawback own shares"); + auto [vault, vaultKeylet] = setupVault(asset, owner, owner); + auto const& vaultSle = env.le(vaultKeylet); + BEAST_EXPECT(vaultSle != nullptr); + if (!vaultSle) + return; + Asset share = vaultSle->at(sfShareMPTID); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = owner, + .amount = share(vaultShareBalance(vaultKeylet)).value(), + }), + ter(tesSUCCESS)); + env.close(); + } - { - testcase("VaultClawback (share) - " + prefix + " empty vault share clawback fails"); - auto [vault, vaultKeylet] = setupVault(asset, owner, owner); - auto const& vaultSle = env.le(vaultKeylet); - if (BEAST_EXPECT(vaultSle != nullptr)) - return; - Asset share = vaultSle->at(sfShareMPTID); - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = owner, - .amount = share(vaultShareBalance(vaultKeylet)).value(), - }), - ter(tesSUCCESS), - THISLINE); + { + testcase("VaultClawback (share) - " + prefix + " empty vault share clawback fails"); + auto [vault, vaultKeylet] = setupVault(asset, owner, owner); + auto const& vaultSle = env.le(vaultKeylet); + if (BEAST_EXPECT(vaultSle != nullptr)) + return; + Asset share = vaultSle->at(sfShareMPTID); + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = owner, + .amount = share(vaultShareBalance(vaultKeylet)).value(), + }), + ter(tesSUCCESS)); - // Now the vault is empty, clawback again fails - env(vault.clawback({ - .issuer = owner, - .id = vaultKeylet.key, - .holder = owner, - }), - ter(tecNO_PERMISSION), - THISLINE); - env.close(); - } - }; + // Now the vault is empty, clawback again fails + env(vault.clawback({ + .issuer = owner, + .id = vaultKeylet.key, + .holder = owner, + }), + ter(tecNO_PERMISSION)); + env.close(); + } + }; Account owner{"alice"}; Account depositor{"bob"}; @@ -4556,14 +4793,14 @@ class Vault_test : public beast::unit_test::suite Vault vault{env}; auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tesSUCCESS), THISLINE); + env(tx, ter(tesSUCCESS)); env.close(); auto const& vaultSle = env.le(vaultKeylet); BEAST_EXPECT(vaultSle != nullptr); - env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), - ter(tesSUCCESS), - THISLINE); + env(vault.deposit( + {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), + ter(tesSUCCESS)); env.close(); return std::make_pair(vault, vaultKeylet); @@ -4586,21 +4823,20 @@ class Vault_test : public beast::unit_test::suite .holder = issuer, .amount = asset(1).value(), }), - ter(temMALFORMED), - THISLINE); + ter(temMALFORMED)); // When asset is implicit, clawback fails as no permission. env(vault.clawback({ .issuer = issuer, .id = vaultKeylet.key, .holder = issuer, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); return; } { - testcase("VaultClawback (asset) - " + prefix + " clawback for different asset fails"); + testcase( + "VaultClawback (asset) - " + prefix + " clawback for different asset fails"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); Account issuer2{"issuer2"}; @@ -4611,20 +4847,20 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset2(1).value(), }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { - testcase("VaultClawback (asset) - " + prefix + " ambiguous owner/issuer asset clawback fails"); + testcase( + "VaultClawback (asset) - " + prefix + + " ambiguous owner/issuer asset clawback fails"); auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer); env(vault.clawback({ .issuer = issuer, .id = vaultKeylet.key, .holder = issuer, }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { @@ -4636,8 +4872,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env(vault.clawback({ .issuer = owner, @@ -4645,8 +4880,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -4657,8 +4891,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = issuer, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -4676,12 +4909,13 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = share(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { - testcase("VaultClawback (asset) - " + prefix + " partial issuer asset clawback succeeds"); + testcase( + "VaultClawback (asset) - " + prefix + + " partial issuer asset clawback succeeds"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); env(vault.clawback({ @@ -4690,12 +4924,12 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { - testcase("VaultClawback (asset) - " + prefix + " full issuer asset clawback succeeds"); + testcase( + "VaultClawback (asset) - " + prefix + " full issuer asset clawback succeeds"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); env(vault.clawback({ @@ -4704,12 +4938,13 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(100).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { - testcase("VaultClawback (asset) - " + prefix + " implicit full issuer asset clawback succeeds"); + testcase( + "VaultClawback (asset) - " + prefix + + " implicit full issuer asset clawback succeeds"); auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer); env(vault.clawback({ @@ -4717,8 +4952,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } }; @@ -4774,8 +5008,8 @@ class Vault_test : public beast::unit_test::suite BEAST_EXPECT(maxInt64 == "9223372036854775807"); // Naming things is hard - auto const maxInt64Plus1 = - std::to_string(static_cast(std::numeric_limits::max()) + 1); + auto const maxInt64Plus1 = std::to_string( + static_cast(std::numeric_limits::max()) + 1); BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808"); auto const initialXRP = to_string(INITIAL_XRP); @@ -4793,29 +5027,29 @@ class Vault_test : public beast::unit_test::suite tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // This value will be rounded auto const insertAt = maxInt64Plus1.size() - 3; - auto const decimalTest = - maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000 + auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." + + maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000 BEAST_EXPECT(decimalTest == "9223372036854775.808"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -4838,36 +5072,36 @@ class Vault_test : public beast::unit_test::suite return mptAsset; }(); - env(pay(issuer, owner, mptAsset(100'000)), THISLINE); + env(pay(issuer, owner, mptAsset(100'000))); env.close(); auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset}); tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // This value will be rounded auto const insertAt = maxInt64Plus1.size() - 1; - auto const decimalTest = - maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10 + auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." + + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10 BEAST_EXPECT(decimalTest == "922337203685477580.8"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); @@ -4890,19 +5124,19 @@ class Vault_test : public beast::unit_test::suite tx[sfData] = "4D65746144617461"; tx[sfAssetsMaximum] = maxInt64; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRPPlus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = initialXRP; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = maxInt64Plus1; - env(tx, THISLINE); + env(tx); env.close(); tx[sfAssetsMaximum] = "1000000000000000e80"; @@ -4914,48 +5148,54 @@ class Vault_test : public beast::unit_test::suite // These values will be rounded to 15 significant digits { auto const insertAt = maxInt64Plus1.size() - 1; - auto const decimalTest = - maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10 + auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." + + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10 BEAST_EXPECT(decimalTest == "922337203685477580.8"); tx[sfAssetsMaximum] = decimalTest; auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); if (!BEAST_EXPECT(vaultSle)) return; - BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 2, Number::normalized{}})); + BEAST_EXPECT( + (vaultSle->at(sfAssetsMaximum) == + Number{9223372036854776, 2, Number::normalized{}})); } { tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); if (!BEAST_EXPECT(vaultSle)) return; - BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 43, Number::normalized{}})); + BEAST_EXPECT( + (vaultSle->at(sfAssetsMaximum) == + Number{9223372036854776, 43, Number::normalized{}})); } { tx[sfAssetsMaximum] = "9223372036854775807e-40"; // max int64 * 10^-40 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); auto const vaultSle = env.le(newKeylet); if (!BEAST_EXPECT(vaultSle)) return; - BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, -37, Number::normalized{}})); + BEAST_EXPECT( + (vaultSle->at(sfAssetsMaximum) == + Number{9223372036854776, -37, Number::normalized{}})); } { tx[sfAssetsMaximum] = "9223372036854775807e-100"; // max int64 * 10^-100 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner)); - env(tx, THISLINE); + env(tx); env.close(); // Field 'AssetsMaximum' may not be explicitly set to default. @@ -4969,24 +5209,24 @@ class Vault_test : public beast::unit_test::suite // What _can't_ IOUs do? // 1. Exceed maximum exponent / offset tx[sfAssetsMaximum] = "1000000000000000e81"; - env(tx, ter(tefEXCEPTION), THISLINE); + env(tx, ter(tefEXCEPTION)); env.close(); // 2. Mantissa larger than uint64 max + env.set_parse_failure_expected(true); try { tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1 - env(tx, THISLINE); - BEAST_EXPECT(false); + env(tx); + BEAST_EXPECTS(false, "Expected parse_error for mantissa larger than uint64 max"); } catch (parse_error const& e) { using namespace std::string_literals; BEAST_EXPECT( - e.what() == - "invalidParamsField 'tx_json.AssetsMaximum' has invalid " - "data."s); + e.what() == "invalidParamsField 'tx_json.AssetsMaximum' has invalid data."s); } + env.set_parse_failure_expected(false); } } @@ -5007,7 +5247,6 @@ public: testFailedPseudoAccount(); testScaleIOU(); testRPC(); - testDelegate(); testVaultClawbackBurnShares(); testVaultClawbackAssets(); testAssetsMaximum(); diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index f6e861296d..d49f12f487 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include @@ -32,12 +32,93 @@ hexToBytes(std::string const& hex) return Bytes(ws.begin(), ws.end()); } +template +unsigned +uleb128(IT& it, T val) +{ + unsigned count = 0; + do + { + std::uint8_t byte = val & 0x7f; + val >>= 7; + if (val) + byte |= 0x80; + *it++ = byte; + ++count; + } while (val != 0); + + return count; +} + +template +std::pair +uleb128(IT&& it) +{ + static_assert(sizeof(*it) == 1, "invalid iterator type"); + std::uint64_t val = 0; + std::uint64_t byte = 0; + unsigned shift = 0; + unsigned count = 0; + + do + { + if (shift > sizeof(std::uint64_t) * 8 - 7) + return {0, 0}; + byte = *it++; + val |= (byte & 0x7F) << shift; + shift += 7; + ++count; + } while (byte >= 0x80); + + return {val, count}; +} + +std::pair +getSection(Bytes const& module, std::uint8_t n) +{ + static std::uint8_t const hdr[] = {0x00, 0x61, 0x73, 0x6D}; + static std::uint8_t const ver[] = {0x01, 0x00, 0x00, 0x00}; + static std::uint8_t const lastSec = 12; + + // sections: + // 0: "Custom", 1: "Type", 2: "Import", 3: "Function", 4: "Table", 5: "Memory", 6: "Global", + // 7: "Export", 8: "Start", 9: "Element", 10: "Code", 11: "Data", 12: "DataCount" + + if (module.size() < sizeof(hdr) + sizeof(ver) + 2) + return {0, 0}; + if (memcmp(module.data(), hdr, sizeof(hdr)) != 0) + return {0, 0}; + if (memcmp(module.data() + sizeof(hdr), ver, sizeof(ver)) != 0) + return {0, 0}; + + unsigned pos = sizeof(hdr) + sizeof(ver); // sections start + for (; pos < module.size();) + { + auto const start = pos; + std::uint8_t const byte = module[pos++]; + if (byte > lastSec) + return {0, 0}; + + auto [sz, cnt] = uleb128(module.cbegin() + pos); + if (!cnt) + return {0, 0}; + if (pos + cnt + sz > module.size()) + return {0, 0}; + pos += cnt + sz; + + if (byte == n) + return {start, pos}; + } + return {0, 0}; +} + std::optional runFinishFunction(std::string const& code) { auto& engine = WasmEngine::instance(); auto const wasm = hexToBytes(code); - auto const re = engine.run(wasm, "finish"); + HostFunctions hfs; + auto const re = engine.run(wasm, hfs, 10'000'000, "finish"); if (re.has_value()) { return std::optional(re->result); @@ -107,10 +188,11 @@ struct Wasm_test : public beast::unit_test::suite // clang-format on auto& vm = WasmEngine::instance(); - std::shared_ptr imports(std::make_shared()); - WasmImpFunc(*imports, "func-add", reinterpret_cast(&Add)); + HostFunctions hfs; + ImportVec imports; + WasmImpFunc(imports, "func-add", reinterpret_cast(&Add), &hfs); - auto re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports); + auto re = vm.run(wasm, hfs, 10'000'000, "addTwo", wasmParams(1234, 5678), imports); // if (res) printf("invokeAdd get the result: %d\n", res.value()); @@ -125,13 +207,13 @@ struct Wasm_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - std::shared_ptr hfs(new HostFunctions(env.journal)); + HostFunctions hfs(env.journal); { auto wasm = hexToBytes("00000000"); std::string funcName("mock_escrow"); - auto re = runEscrowWasm(wasm, hfs, funcName, {}, 15); + auto re = runEscrowWasm(wasm, hfs, 15, funcName, {}); BEAST_EXPECT(!re); } @@ -175,22 +257,88 @@ struct Wasm_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - auto imports = std::make_shared(); - WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get(), 33); + TestLedgerDataProvider hfs(env); + ImportVec imports; + WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs, 33); auto& engine = WasmEngine::instance(); - auto re = engine.run(ledgerSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run( + ledgerSqnWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imports, env.journal); checkResult(re, 0, 440); env.close(); env.close(); - // empty module - run the same instance - re = engine.run({}, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal); + // empty module, throwing exception + re = engine.run({}, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imports, env.journal); + BEAST_EXPECT(!re); + env.close(); + } - checkResult(re, 5, 488); + void + testImpExp() + { + testcase("Wasm import/export functions"); + + auto impExpWasm = hexToBytes(impExpHex); + + using namespace test::jtx; + + Env env{*this}; + TestLedgerDataProvider hfs(env); + ImportVec imports; + WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs, 33); + WASM_IMPORT_FUNC2(imports, getParentLedgerHash, "get_parent_ledger_hash", &hfs, 60); + auto& engine = WasmEngine::instance(); + + // Test exp_func1() - should return 1 + auto re = engine.run(impExpWasm, hfs, 1'000'000, "exp_func1", {}, imports, env.journal); + checkResult(re, 1, 30); + + // Test exp_func2(5) - should return 2 * 5 = 10 + re = engine.run( + impExpWasm, hfs, 1'000'000, "exp_func2", wasmParams(5), imports, env.journal); + checkResult(re, 10, 52); + + // Test test_imports() - should call get_ledger_sqn and get_parent_ledger_hash + re = engine.run(impExpWasm, hfs, 1'000'000, "test_imports", {}, imports, env.journal); + // Should return the ledger sequence number (3 by default in test env) + checkResult(re, 3, 294); + + // Test corrupted import/export sections - invert each byte and expect failure + testcase("Wasm import/export section corruption"); + { + // Import section(#2): bytes [26, 79) - 53 bytes + // Export section(#7): bytes [90, 141) - 51 bytes + auto [importStart, importEnd] = getSection(impExpWasm, 2); + auto [exportStart, exportEnd] = getSection(impExpWasm, 7); + + BEAST_EXPECTS(importStart == 26, std::to_string(importStart)); + BEAST_EXPECTS(importEnd == 79, std::to_string(importEnd)); + BEAST_EXPECTS(exportStart == 90, std::to_string(exportStart)); + BEAST_EXPECTS(exportEnd == 141, std::to_string(exportEnd)); + + auto testInv = [&](unsigned i) { + auto corruptedWasm = impExpWasm; + corruptedWasm[i] = ~corruptedWasm[i]; // Invert byte + + // Try to run any function - should fail due to corruption + auto result = engine.run( + corruptedWasm, hfs, 1'000'000, "exp_func1", {}, imports, env.journal); + BEAST_EXPECT(!result); + }; + + // Test each byte in import section + for (unsigned i = importStart; i < importEnd; ++i) + testInv(i); + + // Test each byte in export section + for (unsigned i = exportStart; i < exportEnd; ++i) + testInv(i); + } + + env.close(); } void @@ -200,43 +348,13 @@ struct Wasm_test : public beast::unit_test::suite auto const fibWasm = hexToBytes(fibWasmHex); auto& engine = WasmEngine::instance(); + HostFunctions hfs; - auto const re = engine.run(fibWasm, "fib", wasmParams(10)); + auto const re = engine.run(fibWasm, hfs, 10'000'000, "fib", wasmParams(10)); checkResult(re, 55, 1'137); } - void - testWasmSha() - { - testcase("Wasm sha"); - - auto const sha512Wasm = hexToBytes(sha512PureWasmHex); - auto& engine = WasmEngine::instance(); - - auto const re = engine.run(sha512Wasm, "sha512_process", wasmParams(sha512PureWasmHex)); - - checkResult(re, 34'432, 151'155); - } - - void - testWasmB58() - { - testcase("Wasm base58"); - auto const b58Wasm = hexToBytes(b58WasmHex); - auto& engine = WasmEngine::instance(); - - Bytes outb; - outb.resize(1024); - - auto const minSize = std::min(static_cast(512), static_cast(b58WasmHex.size())); - auto const s = std::string_view(b58WasmHex.c_str(), minSize); - - auto const re = engine.run(b58Wasm, "b58enco", wasmParams(outb, s)); - - checkResult(re, 700, 2'886'069); - } - void testHFCost() { @@ -250,12 +368,13 @@ struct Wasm_test : public beast::unit_test::suite auto& engine = WasmEngine::instance(); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto imp = createWasmImport(*hfs); - for (auto& i : *imp) + TestHostFunctions hfs(env, 0); + auto imp = createWasmImport(hfs); + for (auto& i : imp) i.second.gas = 0; - auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 1'000'000, env.journal); + auto re = engine.run( + allHostFuncWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imp, env.journal); checkResult(re, 1, 27'080); @@ -273,10 +392,11 @@ struct Wasm_test : public beast::unit_test::suite auto& engine = WasmEngine::instance(); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto const imp = createWasmImport(*hfs); + TestHostFunctions hfs(env, 0); + auto const imp = createWasmImport(hfs); - auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 1'000'000, env.journal); + auto re = engine.run( + allHostFuncWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imp, env.journal); checkResult(re, 1, 65'840); @@ -289,14 +409,16 @@ struct Wasm_test : public beast::unit_test::suite auto& engine = WasmEngine::instance(); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto const imp = createWasmImport(*hfs); + TestHostFunctions hfs(env, 0); + auto const imp = createWasmImport(hfs); - auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 200, env.journal); + auto re = + engine.run(allHostFuncWasm, hfs, 200, ESCROW_FUNCTION_NAME, {}, imp, env.journal); if (BEAST_EXPECT(!re)) { - BEAST_EXPECTS(re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error()))); + BEAST_EXPECTS( + re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error()))); } env.close(); @@ -313,51 +435,68 @@ struct Wasm_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; { - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm(allHFWasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); checkResult(re, 1, 65'840); } + { + // Invalid gas limit (0) should be rejected (boundary condition) + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm(allHFWasm, hfs, -1, ESCROW_FUNCTION_NAME, {}); + BEAST_EXPECT(!re.has_value()); + BEAST_EXPECT(re.error() == temBAD_AMOUNT); + } + + { + // Invalid gas limit (-1) should be rejected + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm(allHFWasm, hfs, 0, ESCROW_FUNCTION_NAME, {}); + BEAST_EXPECT(!re.has_value()); + BEAST_EXPECT(re.error() == temBAD_AMOUNT); + } + { // max() gas - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, -1); + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm( + allHFWasm, hfs, std::numeric_limits::max(), ESCROW_FUNCTION_NAME, {}); checkResult(re, 1, 65'840); } { // fail because trying to access nonexistent field - struct BadTestHostFunctions : public TestHostFunctions + struct FieldNotFoundHostFunctions : public TestHostFunctions { - explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env) + explicit FieldNotFoundHostFunctions(Env& env) : TestHostFunctions(env) { } Expected - getTxField(SField const& fname) override + getTxField(SField const& fname) const override { return Unexpected(HostFunctionError::FIELD_NOT_FOUND); } }; - std::shared_ptr hfs(new BadTestHostFunctions(env)); - auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + FieldNotFoundHostFunctions hfs(env); + auto re = runEscrowWasm(allHFWasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); checkResult(re, -201, 28'965); } { // fail because trying to allocate more than MAX_PAGES memory - struct BadTestHostFunctions : public TestHostFunctions + struct OversizedFieldHostFunctions : public TestHostFunctions { - explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env) + explicit OversizedFieldHostFunctions(Env& env) : TestHostFunctions(env) { } Expected - getTxField(SField const& fname) override + getTxField(SField const& fname) const override { return Bytes((128 + 1) * 64 * 1024, 1); } }; - std::shared_ptr hfs(new BadTestHostFunctions(env)); - auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + OversizedFieldHostFunctions hfs(env); + auto re = runEscrowWasm(allHFWasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); checkResult(re, -201, 28'965); } @@ -365,14 +504,14 @@ struct Wasm_test : public beast::unit_test::suite auto const deepWasm = hexToBytes(deepRecursionHex); - std::shared_ptr hfs(new TestHostFunctionsSink(env)); + TestHostFunctionsSink hfs(env); std::string funcName("finish"); - auto re = runEscrowWasm(deepWasm, hfs, funcName, {}, 1'000'000'000); + auto re = runEscrowWasm(deepWasm, hfs, 1'000'000'000, funcName, {}); BEAST_EXPECT(!re && re.error()); // std::cout << "bad case (deep recursion) result " << re.error() // << std::endl; - auto const& sink = hfs->getSink(); + auto const& sink = hfs.getSink(); auto countSubstr = [](std::string const& str, std::string const& substr) { std::size_t pos = 0; int occurrences = 0; @@ -392,10 +531,10 @@ struct Wasm_test : public beast::unit_test::suite { // infinite loop auto const infiniteLoopWasm = hexToBytes(infiniteLoopWasmHex); std::string const funcName("loop"); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); + TestHostFunctions hfs(env, 0); // infinite loop should be caught and fail - auto const re = runEscrowWasm(infiniteLoopWasm, hfs, funcName, {}, 1'000'000); + auto const re = runEscrowWasm(infiniteLoopWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); @@ -405,13 +544,14 @@ struct Wasm_test : public beast::unit_test::suite { // expected import not provided auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex); - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - std::shared_ptr imports(std::make_shared()); - WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn2", hfs.get()); + TestLedgerDataProvider hfs(env); + ImportVec imports; + WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn2", &hfs); auto& engine = WasmEngine::instance(); - auto re = engine.run(lgrSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run( + lgrSqnWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imports, env.journal); BEAST_EXPECT(!re); } @@ -419,14 +559,15 @@ struct Wasm_test : public beast::unit_test::suite { // bad import format auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex); - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - std::shared_ptr imports(std::make_shared()); - WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get()); - (*imports)[0].first = nullptr; + TestLedgerDataProvider hfs(env); + ImportVec imports; + WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs); + imports[0].first = nullptr; auto& engine = WasmEngine::instance(); - auto re = engine.run(lgrSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run( + lgrSqnWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imports, env.journal); BEAST_EXPECT(!re); } @@ -434,12 +575,12 @@ struct Wasm_test : public beast::unit_test::suite { // bad function name auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex); - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - std::shared_ptr imports(std::make_shared()); - WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get()); + TestLedgerDataProvider hfs(env); + ImportVec imports; + WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs); auto& engine = WasmEngine::instance(); - auto re = engine.run(lgrSqnWasm, "func1", {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run(lgrSqnWasm, hfs, 1'000'000, "func1", {}, imports, env.journal); BEAST_EXPECT(!re); } @@ -458,8 +599,8 @@ struct Wasm_test : public beast::unit_test::suite { auto const floatTestWasm = hexToBytes(floatTestsWasmHex); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto re = runEscrowWasm(floatTestWasm, hfs, funcName, {}, 200'000); + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm(floatTestWasm, hfs, 200'000, funcName, {}); checkResult(re, 1, 110'699); env.close(); } @@ -467,101 +608,13 @@ struct Wasm_test : public beast::unit_test::suite { auto const float0Wasm = hexToBytes(float0Hex); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto re = runEscrowWasm(float0Wasm, hfs, funcName, {}, 100'000); + TestHostFunctions hfs(env, 0); + auto re = runEscrowWasm(float0Wasm, hfs, 100'000, funcName, {}); checkResult(re, 1, 4'259); env.close(); } } - void - perfTest() - { - testcase("Perf test host functions"); - - using namespace jtx; - using namespace std::chrono; - - // std::string const funcName("test"); - auto const perfWasm = hexToBytes(hfPerfTest); - - // std::string const credType = "abcde"; - // std::string const credType2 = "fghijk"; - // std::string const credType3 = "0123456"; - // char const uri[] = "uri"; - - Account const alan{"alan"}; - Account const bob{"bob"}; - Account const issuer{"issuer"}; - - { - Env env(*this); - // Env env(*this, envconfig(), {}, nullptr, - // beast::severities::kTrace); - env.fund(XRP(5000), alan, bob, issuer); - env.close(); - - // // create escrow - // auto const seq = env.seq(alan); - // auto const k = keylet::escrow(alan, seq); - // // auto const allowance = 3'600; - // auto escrowCreate = escrow::create(alan, bob, XRP(1000)); - // XRPAmount txnFees = env.current()->fees().base + 1000; - // env(escrowCreate, - // escrow::finish_function(wasmHex), - // escrow::finish_time(env.now() + 11s), - // escrow::cancel_time(env.now() + 100s), - // escrow::data("1000000000"), // 1000 XRP in drops - // memodata("memo1234567"), - // memodata("2memo1234567"), - // fee(txnFees)); - - // // create depositPreauth - // auto const k = keylet::depositPreauth( - // bob, - // {{issuer.id(), makeSlice(credType)}, - // {issuer.id(), makeSlice(credType2)}, - // {issuer.id(), makeSlice(credType3)}}); - // env(deposit::authCredentials( - // bob, - // {{issuer, credType}, - // {issuer, credType2}, - // {issuer, credType3}})); - - // create nft - [[maybe_unused]] uint256 const nft0{token::getNextID(env, alan, 0u)}; - env(token::mint(alan, 0u)); - auto const k = keylet::nftoffer(alan, 0); - [[maybe_unused]] uint256 const nft1{token::getNextID(env, alan, 0u)}; - - env(token::mint(alan, 0u), - token::uri("https://github.com/XRPLF/XRPL-Standards/discussions/" - "279?id=github.com/XRPLF/XRPL-Standards/discussions/" - "279&ut=github.com/XRPLF/XRPL-Standards/discussions/" - "279&sid=github.com/XRPLF/XRPL-Standards/discussions/" - "279&aot=github.com/XRPLF/XRPL-Standards/disc")); - [[maybe_unused]] uint256 const nft2{token::getNextID(env, alan, 0u)}; - env(token::mint(alan, 0u)); - env.close(); - - std::shared_ptr hfs(new PerfHostFunctions(env, k, env.tx())); - - auto re = runEscrowWasm(perfWasm, hfs, ESCROW_FUNCTION_NAME); - if (BEAST_EXPECT(re.has_value())) - { - BEAST_EXPECT(re->result); - std::cout << "Res: " << re->result << " cost: " << re->cost << std::endl; - } - - // env(escrow::finish(alan, alan, seq), - // escrow::comp_allowance(allowance), - // fee(txnFees), - // ter(tesSUCCESS)); - - env.close(); - } - } - void testCodecovWasm() { @@ -572,10 +625,10 @@ struct Wasm_test : public beast::unit_test::suite Env env{*this}; auto const codecovWasm = hexToBytes(codecovTestsWasmHex); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); + TestHostFunctions hfs(env, 0); - auto const allowance = 339'303; - auto re = runEscrowWasm(codecovWasm, hfs, ESCROW_FUNCTION_NAME, {}, allowance); + auto const allowance = 340'524; + auto re = runEscrowWasm(codecovWasm, hfs, allowance, ESCROW_FUNCTION_NAME, {}); checkResult(re, 1, allowance); } @@ -590,11 +643,11 @@ struct Wasm_test : public beast::unit_test::suite auto disabledFloatWasm = hexToBytes(disabledFloatHex); std::string const funcName("finish"); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); + TestHostFunctions hfs(env, 0); { // f32 set constant, opcode disabled exception - auto const re = runEscrowWasm(disabledFloatWasm, hfs, funcName, {}, 1'000'000); + auto const re = runEscrowWasm(disabledFloatWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); @@ -604,7 +657,7 @@ struct Wasm_test : public beast::unit_test::suite { // f32 add, can't create module exception disabledFloatWasm[0x117] = 0x92; - auto const re = runEscrowWasm(disabledFloatWasm, hfs, funcName, {}, 1'000'000); + auto const re = runEscrowWasm(disabledFloatWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); @@ -703,91 +756,18 @@ struct Wasm_test : public beast::unit_test::suite Env env(*this); auto const startLoopWasm = hexToBytes(startLoopHex); - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - std::shared_ptr imports(std::make_shared()); + TestLedgerDataProvider hfs(env); + ImportVec imports; auto& engine = WasmEngine::instance(); - auto checkRes = engine.check(startLoopWasm, "finish", {}, imports, hfs, env.journal); + auto checkRes = engine.check(startLoopWasm, hfs, "finish", {}, imports, env.journal); BEAST_EXPECTS(checkRes == tesSUCCESS, std::to_string(TERtoInt(checkRes))); - auto re = engine.run(startLoopWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run( + startLoopWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imports, env.journal); BEAST_EXPECTS(re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error()))); } - void - testBadAlloc() - { - testcase("Wasm Bad Alloc"); - - // bad_alloc.c - auto const badAllocWasm = hexToBytes(badAllocHex); - - using namespace test::jtx; - - Env env{*this}; - std::shared_ptr hfs(new TestLedgerDataProvider(env)); - - // std::shared_ptr imports(std::make_shared()); - uint8_t buf1[8] = {7, 8, 9, 10, 11, 12, 13, 14}; - { // forged "allocate" return valid address - std::vector params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = sizeof(buf1)}}}}; - auto& engine = WasmEngine::instance(); - - auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal); - if (BEAST_EXPECT(re)) - { - BEAST_EXPECTS(re->result == 7, std::to_string(re->result)); - BEAST_EXPECTS(re->cost == 430, std::to_string(re->cost)); - } - } - - { // return 0 whithout calling wasm - std::vector params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 0}}}}; - auto& engine = WasmEngine::instance(); - auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal); - BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); - } - - { // forged "allocate" return 8Mb (which is more than memory limit) - std::vector params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 1}}}}; - auto& engine = WasmEngine::instance(); - auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal); - BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); - } - - { // forged "allocate" return 0 - std::vector params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 2}}}}; - auto& engine = WasmEngine::instance(); - auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal); - BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); - } - - { // forged "allocate" return -1 - std::vector params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 3}}}}; - auto& engine = WasmEngine::instance(); - auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal); - - BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); - } - - { - std::string what; - try - { - char const test[] = "test"; - std::size_t sz = std::numeric_limits::max() + 1ull; - auto p = wasmParams(std::string_view(test, sz)); - } - catch (std::exception const& e) - { - what = e.what(); - } - BEAST_EXPECT(what.find("can't allocate memory, size: 2147483648") != std::string::npos); - } - - env.close(); - } - void testBadAlign() { @@ -799,14 +779,14 @@ struct Wasm_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - std::shared_ptr hfs(new TestHostFunctions(env, 0)); - auto imports = createWasmImport(*hfs); + TestHostFunctions hfs(env, 0); + auto imports = createWasmImport(hfs); { // Calls float_from_uint with bad alignment. // Can be checked through codecov auto& engine = WasmEngine::instance(); - auto re = engine.run(badAlignWasm, "test", {}, imports, hfs, 1'000'000, env.journal); + auto re = engine.run(badAlignWasm, hfs, 1'000'000, "test", {}, imports, env.journal); if (BEAST_EXPECTS(re, transToken(re.error()))) { BEAST_EXPECTS(re->result == 0x684f7941, std::to_string(re->result)); @@ -821,7 +801,9 @@ struct Wasm_test : public beast::unit_test::suite { using namespace test::jtx; Env env(*this); - std::shared_ptr hfs(new TestHostFunctions(env, 0)); + TestHostFunctions hfs(env, 0); + + testcase("Wasm invalid return type"); // return int64. { // (module @@ -833,7 +815,7 @@ struct Wasm_test : public beast::unit_test::suite "071302066d656d6f727902000666696e69736800000a0a01" "08004280808080100b"; auto const wasm = hexToBytes(wasmHex); - auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + auto const re = runEscrowWasm(wasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); BEAST_EXPECT(!re); } @@ -849,7 +831,7 @@ struct Wasm_test : public beast::unit_test::suite "0061736d01000000010401600000030201000503010001071302066d656d6f" "727902000666696e69736800000a050103000f0b"; auto const wasm = hexToBytes(wasmHex); - auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + auto const re = runEscrowWasm(wasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); BEAST_EXPECT(!re); } @@ -864,7 +846,59 @@ struct Wasm_test : public beast::unit_test::suite "6d6f727902000666696e69736800000a10010e0041808080800141ff818080" "010b"; auto const wasm = hexToBytes(wasmHex); - auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000); + auto const re = runEscrowWasm(wasm, hfs, 100'000, ESCROW_FUNCTION_NAME, {}); + BEAST_EXPECT(!re); + } + } + + void + testParameterType() + { + using namespace test::jtx; + Env env(*this); + TestHostFunctions hfs(env, 0); + + testcase("Wasm invalid params"); + + // (module + // (memory (export "memory") 1) + // (func $test1 (export "test1") (param i32) (result i32) + // i32.const 1000) + // (func $test2 (export "test2") (param i32 i32) (result i32) + // i32.const 1001)) + auto const wasmHex = + "0061736d01000000010c0260017f017f60027f7f017f03030200010503010001071a03066d656d6f727902" + "00057465737431000005746573743200010a0d02050041e8070b050041e9070b"; + auto const wasm = hexToBytes(wasmHex); + + // good params, module is working properly + { + auto const re = runEscrowWasm(wasm, hfs, 100'000, "test2", wasmParams(2, 10)); + BEAST_EXPECT(re && re->result == 1001 && re->cost == 37); + } + + // no params + { + auto const re = runEscrowWasm(wasm, hfs, 100'000, "test1", {}); + BEAST_EXPECT(!re); + } + + // more params + { + auto const re = runEscrowWasm(wasm, hfs, 100'000, "test1", wasmParams(0, 1)); + BEAST_EXPECT(!re); + } + + // less params + { + auto const re = runEscrowWasm(wasm, hfs, 100'000, "test2", wasmParams(1)); + BEAST_EXPECT(!re); + } + + // invalid type + { + auto const re = + runEscrowWasm(wasm, hfs, 100'000, "test1", wasmParams(std::int64_t(15))); BEAST_EXPECT(!re); } } @@ -923,6 +957,561 @@ struct Wasm_test : public beast::unit_test::suite BEAST_EXPECT(b6 == SWAP_DATAI16); } + void + testManyParams() + { + testcase("Wasm Many params"); + + auto const params1k = hexToBytes(thousandParamsHex); + auto const params1k1 = hexToBytes(thousand1ParamsHex); + + using namespace test::jtx; + + Env env{*this}; + TestHostFunctions hfs(env, 0); + auto imports = createWasmImport(hfs); + + // add 1k parameter (max that wasmi support) + std::vector params; + for (int i = 0; i < 1000; ++i) + params.push_back({.type = WT_I32, .of = {.i32 = 2 * i}}); + + auto& engine = WasmEngine::instance(); + { + auto re = engine.run(params1k, hfs, 1'000'000, "test", params, imports, env.journal); + BEAST_EXPECT(re && re->result == 999000); + } + + // add 1 more parameter, module can't be created now + params.push_back({.type = WT_I32, .of = {.i32 = 2 * 1000}}); + { + auto re = engine.run(params1k1, hfs, 1'000'000, "test", params, imports, env.journal); + BEAST_EXPECT(!re); + } + + // function that create 10k local variables + auto const locals10k = hexToBytes(locals10kHex); + { + auto re = engine.run( + locals10k, hfs, 1'000'000, "test", wasmParams(0, 1), imports, env.journal); + BEAST_EXPECT(re && re->result == 890'489'442); + } + + // module has 5k functions + auto const functions5k = hexToBytes(functions5kHex); + { + auto re = engine.run( + functions5k, hfs, 1'000'000, "test0001", wasmParams(2, 3), imports, env.journal); + BEAST_EXPECT(re && re->result == 5); + } + + env.close(); + } + + void + testOpcodes() + { + using namespace test::jtx; + + unsigned const RESERVED = 64; + std::uint8_t nop = 0x01; + std::array const codeMarker = { + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop}; + auto const opcReserved = hexToBytes(opcReservedHex); + + Env env{*this}; + auto& engine = WasmEngine::instance(); + + TestHostFunctions hfs(env, 0); + auto imports = createWasmImport(hfs); + env.close(); + + { + auto run = [&](std::vector const& code, + bool good = false, + int64_t cost = -1, + std::source_location const location = std::source_location::current()) { + auto const lineStr = " (" + std::to_string(location.line()) + ")"; + auto re = + engine.run(code, hfs, 1'000'000, "all_instructions", {}, imports, env.journal); + if (BEAST_EXPECTS(re.has_value() == good, transToken(re.error()) + lineStr) && good) + BEAST_EXPECTS(re->cost == cost, std::to_string(re->cost) + lineStr); + }; + + // 1 byte instruction + auto test = [&](std::uint8_t start, + std::uint8_t finish, + bool good = false, + int64_t cost = -1, + std::source_location const location = std::source_location::current()) { + auto const lineStr = " (" + std::to_string(location.line()) + ")"; + auto code = opcReserved; + auto codeRange = std::ranges::search(code, codeMarker); + if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) + return; + + auto it = codeRange.begin(); + for (std::uint16_t i = start; i <= finish; ++i) + { + *it = i; + run(code, good, cost, location); + } + }; + + // 2 bytes instruction + auto test2 = [&](std::uint8_t major, + std::uint16_t start, + std::uint16_t finish, + bool good = false, + int64_t cost = -1, + std::source_location const location = + std::source_location::current()) { + auto const lineStr = " (" + std::to_string(location.line()) + ")"; + auto code = opcReserved; + auto codeRange = std::ranges::search(code, codeMarker); + if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) + return; + + auto it = codeRange.begin(); + *it++ = major; + for (std::uint16_t i = start; i <= finish; ++i) + { + auto it2 = it; + uleb128(it2, i); + run(code, good, cost, location); + } + }; + + // multibytes instructions + auto testMB = [&](std::vector const& codeSnap, + bool good = false, + int64_t cost = -1, + std::source_location const location = + std::source_location::current()) { + auto const lineStr = " (" + std::to_string(location.line()) + ")"; + auto code = opcReserved; + auto codeRange = std::ranges::search(code, codeMarker); + if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) + return; + + if (!BEAST_EXPECTS(codeSnap.size() < RESERVED, lineStr)) + return; + auto it = codeRange.begin(); + for (auto x : codeSnap) + *it++ = x; + run(code, good, cost, location); + }; + + // normal run + testcase("Wasm reserved opcodes main"); + test(nop, nop, true, 534); + + // reserved main + test(0x06, 0x0A); + test(0x12, 0x19); + test(0x25, 0x27); + test(0xC0, 0xFA); + test(0xFF, 0xFF); + + // reserved gc, string + testcase("Wasm reserved opcodes gc"); + test2(0xFB, 0x00, 0xBF); // not supported by compiler + + // reserved FC + testcase("Wasm reserved opcodes FC"); + test2(0xFC, 0x00, 0x07); // floats, disabled + test2(0xFC, 0x12, 0x1F); + + // reserved SIMD + testcase("Wasm reserved opcodes SIMD"); + test2(0xFD, 0x9A, 0x9A); + test2(0xFD, 0xA2, 0xA2); + test2(0xFD, 0xA5, 0xA6); + test2(0xFD, 0xAF, 0xB0); + test2(0xFD, 0xB2, 0xB4); + test2(0xFD, 0xB8, 0xB8); + test2(0xFD, 0xC2, 0xC2); + test2(0xFD, 0xC5, 0xC6); + test2(0xFD, 0xCF, 0xD0); + test2(0xFD, 0xD2, 0xD4); + test2(0xFD, 0xE2, 0xE2); + test2(0xFD, 0xEE, 0xEE); + test2(0xFD, 0x115, 0x12F); + + testcase("Wasm opcodes THREADS"); + test2(0xFE, 0x00, 0x4F); // not supported by compiler + + // FC mem instructions + testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x04, 0xFC, 0x08, 0x00, 0x00}); // memory.init + testMB({0xFC, 0x09, 0x00}); // data.drop + testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0A, 0x00, 0x00}); // memory.copy + testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0B, 0x00}); // memory.fill + testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0C, 0x00, 0x00}); // table.init + testMB({0xFC, 0x0D, 0x00}); // elem.drop + testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0E, 0x00, 0x00}); // table.copy + testMB({0xD2, 0x00, 0x41, 0x00, 0xFC, 0x0F, 0x00, 0x1A}); // table.grow + testMB({0x1A, 0xFC, 0x10, 0x00, 0x1A}); // table.size + testMB({0x41, 0x00, 0xD2, 0x00, 0x41, 0x00, 0xFC, 0x11, 0x00}); // table.fill + + testcase("Wasm opcodes SIMD"); + // clang-format off + + // generated by auggie + // SIMD instructions + testMB({0x41, 0x00, 0xFD, 0x00, 0x04, 0x00, 0x1A}); // v128.load + testMB({0x41, 0x00, 0xFD, 0x01, 0x03, 0x00, 0x1A}); // v128.load8x8_s + testMB({0x41, 0x00, 0xFD, 0x02, 0x03, 0x00, 0x1A}); // v128.load8x8_u + testMB({0x41, 0x00, 0xFD, 0x03, 0x03, 0x00, 0x1A}); // v128.load16x4_s + testMB({0x41, 0x00, 0xFD, 0x04, 0x03, 0x00, 0x1A}); // v128.load16x4_u + testMB({0x41, 0x00, 0xFD, 0x05, 0x03, 0x00, 0x1A}); // v128.load32x2_s + testMB({0x41, 0x00, 0xFD, 0x06, 0x03, 0x00, 0x1A}); // v128.load32x2_u + testMB({0x41, 0x00, 0xFD, 0x07, 0x00, 0x00, 0x1A}); // v128.load8_splat + testMB({0x41, 0x00, 0xFD, 0x08, 0x01, 0x00, 0x1A}); // v128.load16_splat + testMB({0x41, 0x00, 0xFD, 0x09, 0x02, 0x00, 0x1A}); // v128.load32_splat + testMB({0x41, 0x00, 0xFD, 0x0A, 0x03, 0x00, 0x1A}); // v128.load64_splat + testMB({0x41, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0B, 0x04, 0x00}); // v128.store + testMB({0xFD, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A}); // v128.const + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0D, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x1A}); // i8x16.shuffle + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0E, 0x1A}); // i8x16.swizzle + testMB({0x41, 0x2A, 0xFD, 0x0F, 0x1A}); // i8x16.splat + testMB({0x41, 0x2A, 0xFD, 0x10, 0x1A}); // i16x8.splat + testMB({0x41, 0x2A, 0xFD, 0x11, 0x1A}); // i32x4.splat + testMB({0x42, 0x2A, 0xFD, 0x12, 0x1A}); // i64x2.splat + + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x15, 0x00, 0x1A}); // i8x16.extract_lane_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x16, 0x00, 0x1A}); // i8x16.extract_lane_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x17, 0x00, 0x1A}); // i8x16.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x18, 0x00, 0x1A}); // i16x8.extract_lane_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x19, 0x00, 0x1A}); // i16x8.extract_lane_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x1A, 0x00, 0x1A}); // i16x8.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1B, 0x00, 0x1A}); // i32x4.extract_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x1C, 0x00, 0x1A}); // i32x4.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1D, 0x00, 0x1A}); // i64x2.extract_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x2A, 0xFD, 0x1E, 0x00, 0x1A}); // i64x2.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1F, 0x00, 0x1A}); // f32x4.extract_lane + testMB( + {0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x80, 0x3F, 0xFD, 0x20, 0x00, 0x1A}); // f32x4.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x21, 0x00, 0x1A}); // f64x2.extract_lane + testMB( + {0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0xFD, 0x22, 0x00, 0x1A}); // f64x2.replace_lane + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x23, 0x1A}); // i8x16.eq + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x24, 0x1A}); // i8x16.ne + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x25, 0x1A}); // i8x16.lt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x26, 0x1A}); // i8x16.lt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x27, 0x1A}); // i8x16.gt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x28, 0x1A}); // i8x16.gt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x29, 0x1A}); // i8x16.le_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2A, 0x1A}); // i8x16.le_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2B, 0x1A}); // i8x16.ge_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2C, 0x1A}); // i8x16.ge_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2D, 0x1A}); // i16x8.eq + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2E, 0x1A}); // i16x8.ne + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2F, 0x1A}); // i16x8.lt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x30, 0x1A}); // i16x8.lt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x31, 0x1A}); // i16x8.gt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x32, 0x1A}); // i16x8.gt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x33, 0x1A}); // i16x8.le_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x34, 0x1A}); // i16x8.le_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x35, 0x1A}); // i16x8.ge_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x36, 0x1A}); // i16x8.ge_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x37, 0x1A}); // i32x4.eq + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x38, 0x1A}); // i32x4.ne + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x39, 0x1A}); // i32x4.lt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3A, 0x1A}); // i32x4.lt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3B, 0x1A}); // i32x4.gt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3C, 0x1A}); // i32x4.gt_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3D, 0x1A}); // i32x4.le_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3E, 0x1A}); // i32x4.le_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3F, 0x1A}); // i32x4.ge_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x40, 0x1A}); // i32x4.ge_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4D, 0x1A}); // v128.not + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4E, 0x1A}); // v128.and + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4F, 0x1A}); // v128.andnot + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x50, 0x1A}); // v128.or + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x51, 0x1A}); // v128.xor + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x52, 0x1A}); // v128.bitselect + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x53, 0x1A}); // v128.any_true + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x60, 0x1A}); // i8x16.abs + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x61, 0x1A}); // i8x16.neg + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x62, 0x1A}); // i8x16.popcnt + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x63, 0x1A}); // i8x16.all_true + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x64, 0x1A}); // i8x16.bitmask + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6B, 0x1A}); // i8x16.shl + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6C, 0x1A}); // i8x16.shr_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6D, 0x1A}); // i8x16.shr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x6E, 0x1A}); // i8x16.add + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x6F, 0x1A}); // i8x16.add_sat_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x70, 0x1A}); // i8x16.add_sat_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x71, 0x1A}); // i8x16.sub + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x72, 0x1A}); // i8x16.sub_sat_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x73, 0x1A}); // i8x16.sub_sat_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x76, 0x1A}); // i8x16.min_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x77, 0x1A}); // i8x16.min_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x78, 0x1A}); // i8x16.max_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x79, 0x1A}); // i8x16.max_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x7B, 0x1A}); // i8x16.avgr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x80, 0x01, 0x1A}); // i16x8.abs + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x81, 0x01, 0x1A}); // i16x8.neg + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x83, 0x01, 0x1A}); // i16x8.all_true + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x84, 0x01, 0x1A}); // i16x8.bitmask + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8B, 0x01, 0x1A}); // i16x8.shl + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8C, 0x01, 0x1A}); // i16x8.shr_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8D, 0x01, 0x1A}); // i16x8.shr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x8E, 0x01, 0x1A}); // i16x8.add + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x8F, 0x01, 0x1A}); // i16x8.add_sat_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x90, 0x01, 0x1A}); // i16x8.add_sat_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x91, 0x01, 0x1A}); // i16x8.sub + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x92, 0x01, 0x1A}); // i16x8.sub_sat_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x93, 0x01, 0x1A}); // i16x8.sub_sat_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x95, 0x01, 0x1A}); // i16x8.mul + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x96, 0x01, 0x1A}); // i16x8.min_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x97, 0x01, 0x1A}); // i16x8.min_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x98, 0x01, 0x1A}); // i16x8.max_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x99, 0x01, 0x1A}); // i16x8.max_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x9B, 0x01, 0x1A}); // i16x8.avgr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA0, 0x01, 0x1A}); // i32x4.abs + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA1, 0x01, 0x1A}); // i32x4.neg + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA3, 0x01, 0x1A}); // i32x4.all_true + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA4, 0x01, 0x1A}); // i32x4.bitmask + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAB, 0x01, 0x1A}); // i32x4.shl + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAC, 0x01, 0x1A}); // i32x4.shr_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAD, 0x01, 0x1A}); // i32x4.shr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xAE, 0x01, 0x1A}); // i32x4.add + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB1, 0x01, 0x1A}); // i32x4.sub + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB5, 0x01, 0x1A}); // i32x4.mul + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB6, 0x01, 0x1A}); // i32x4.min_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB7, 0x01, 0x1A}); // i32x4.min_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB8, 0x01, 0x1A}); // i32x4.max_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB9, 0x01, 0x1A}); // i32x4.max_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBA, 0x01, 0x1A}); // i32x4.dot_i16x8_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC0, 0x01, 0x1A}); // i64x2.abs + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC1, 0x01, 0x1A}); // i64x2.neg + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC3, 0x01, 0x1A}); // i64x2.all_true + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC4, 0x01, 0x1A}); // i64x2.bitmask + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCB, 0x01, 0x1A}); // i64x2.shl + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCC, 0x01, 0x1A}); // i64x2.shr_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCD, 0x01, 0x1A}); // i64x2.shr_u + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xCE, 0x01, 0x1A}); // i64x2.add + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD1, 0x01, 0x1A}); // i64x2.sub + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD5, 0x01, 0x1A}); // i64x2.mul + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD6, 0x01, 0x1A}); // i64x2.eq + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD7, 0x01, 0x1A}); // i64x2.ne + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD8, 0x01, 0x1A}); // i64x2.lt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD9, 0x01, 0x1A}); // i64x2.gt_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xDA, 0x01, 0x1A}); // i64x2.le_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xDB, 0x01, 0x1A}); // i64x2.ge_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF8, 0x01, 0x1A}); // i32x4.trunc_sat_f32x4_s + testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF9, 0x01, 0x1A}); // i32x4.trunc_sat_f32x4_u + + // clang-format on + } + } + void run() override { @@ -932,10 +1521,9 @@ struct Wasm_test : public beast::unit_test::suite testWasmLib(); testBadWasm(); testWasmLedgerSqn(); + testImpExp(); testWasmFib(); - testWasmSha(); - testWasmB58(); testHFCost(); testEscrowWasmDN(); @@ -952,12 +1540,13 @@ struct Wasm_test : public beast::unit_test::suite testWasmSectionCorruption(); testStartFunctionLoop(); - testBadAlloc(); testBadAlign(); testReturnType(); testSwapBytes(); + testManyParams(); + testParameterType(); - // perfTest(); + testOpcodes(); } }; diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 2921e10ae4..b90ff49a84 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -84,7 +84,7 @@ struct SEnv template SEnv& - multiTx(jtx::JValueVec&& jvv, FN const&... fN) + multiTx(jtx::JValueVec const& jvv, FN const&... fN) { for (auto const& jv : jvv) env_(jv, fN...); @@ -277,8 +277,9 @@ struct BalanceTransfer bool payees_received(STAmount const& reward) const { - return std::all_of( - reward_accounts.begin(), reward_accounts.end(), [&](balance const& b) { return b.diff() == reward; }); + return std::all_of(reward_accounts.begin(), reward_accounts.end(), [&](balance const& b) { + return b.diff() == reward; + }); } bool @@ -291,7 +292,8 @@ struct BalanceTransfer has_happened(STAmount const& amt, STAmount const& reward, bool check_payer = true) { auto reward_cost = multiply(reward, STAmount(reward_accounts.size()), reward.issue()); - return check_most_balances(amt, reward) && (!check_payer || payer_.diff() == -(reward_cost + txFees_)); + return check_most_balances(amt, reward) && + (!check_payer || payer_.diff() == -(reward_cost + txFees_)); } bool @@ -325,9 +327,13 @@ struct BridgeDef return {}; return minAccountCreate; }(); - mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate)).tx(jtx::signers(doorA, quorum, signers)).close(); + mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate)) + .tx(jtx::signers(doorA, quorum, signers)) + .close(); - scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate)).tx(jtx::signers(doorB, quorum, signers)).close(); + scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate)) + .tx(jtx::signers(doorB, quorum, signers)) + .close(); } }; @@ -394,7 +400,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this).tx(create_bridge(mcDoor)).close().tx(create_bridge(mcDoor), ter(tecDUPLICATE)); // Create USD bridge Alice -> Bob ... should succeed - XEnv(*this).tx(create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])), ter(tesSUCCESS)); + XEnv(*this).tx( + create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])), + ter(tesSUCCESS)); // Create USD bridge, Alice is both the locking door and locking issue, // ... should fail. @@ -404,7 +412,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Bridge where the two door accounts are equal. XEnv(*this).tx( - create_bridge(mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])), ter(temXCHAIN_EQUAL_DOOR_ACCOUNTS)); + create_bridge(mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])), + ter(temXCHAIN_EQUAL_DOOR_ACCOUNTS)); // Both door accounts are on the same chain. This is not allowed. // Although it doesn't violate any invariants, it's not a useful thing @@ -412,7 +421,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this) .tx(create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]))) .close() - .tx(create_bridge(mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])), ter(tecDUPLICATE)) + .tx(create_bridge(mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])), + ter(tecDUPLICATE)) .close(); // Create a bridge on an account with exactly enough balance to @@ -430,10 +440,12 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(create_bridge(mcuDoor, jvub), ter(tecINSUFFICIENT_RESERVE)); // Reward amount is non-xrp - XEnv(*this).tx(create_bridge(mcDoor, jvb, mcUSD(1)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); + XEnv(*this).tx( + create_bridge(mcDoor, jvb, mcUSD(1)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); // Reward amount is XRP and negative - XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(-1)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); + XEnv(*this).tx( + create_bridge(mcDoor, jvb, XRP(-1)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); // Reward amount is 1 xrp => should succeed XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(1)), ter(tesSUCCESS)); @@ -443,14 +455,18 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Min create amount is non-xrp XEnv(*this).tx( - create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // Min create amount is zero (should fail, currently succeeds) - XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(1), XRP(0)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + XEnv(*this).tx( + create_bridge(mcDoor, jvb, XRP(1), XRP(0)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // Min create amount is negative XEnv(*this).tx( - create_bridge(mcDoor, jvb, XRP(1), XRP(-1)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + create_bridge(mcDoor, jvb, XRP(1), XRP(-1)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // coverage test: BridgeCreate::preflight() - create bridge when feature // disabled. @@ -461,13 +477,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER. XEnv(*this).tx( - create_bridge(mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])), ter(tecNO_ISSUER)); + create_bridge(mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])), + ter(tecNO_ISSUER)); // coverage test: create_bridge transaction with incorrect flag XEnv(*this).tx(create_bridge(mcAlice, jvb), txflags(tfFillOrKill), ter(temINVALID_FLAG)); // coverage test: create_bridge transaction with xchain feature disabled - XEnv(*this).disableFeature(featureXChainBridge).tx(create_bridge(mcAlice, jvb), ter(temDISABLED)); + XEnv(*this) + .disableFeature(featureXChainBridge) + .tx(create_bridge(mcAlice, jvb), ter(temDISABLED)); } void @@ -573,7 +592,11 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE)).close(); // Test case 6 and 7, commits - env.tx(trust(C, BUSD(1000))).tx(trust(A, BUSD(1000))).close().tx(pay(B, C, BUSD(1000))).close(); + env.tx(trust(C, BUSD(1000))) + .tx(trust(A, BUSD(1000))) + .close() + .tx(pay(B, C, BUSD(1000))) + .close(); auto const aBalanceStart = env.balance(A, BUSD); auto const cBalanceStart = env.balance(C, BUSD); env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close(); @@ -789,7 +812,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv.tx(create_bridge(b, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.second))); TER scTER = scEnv.env_.ter(); - bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS; + bool pass = isTesSuccess(mcTER) && isTesSuccess(scTER); test_result.emplace_back(mcTER, scTER, pass); }; @@ -808,7 +831,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::cout << "Markdown output for matrix test: " << fname << "\n"; auto print_res = [](auto tup) -> std::string { - std::string status = std::string(transToken(std::get<0>(tup))) + " / " + transToken(std::get<1>(tup)); + std::string status = + std::string(transToken(std::get<0>(tup))) + " / " + transToken(std::get<1>(tup)); if (std::get<2>(tup)) return status; @@ -830,7 +854,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj res += "\n"; res += "| :--- | "; - std::apply([&](auto const&... ic) { (((void)ic.first, res += ":---: | "), ...); }, ics); + std::apply( + [&](auto const&... ic) { (((void)ic.first, res += ":---: | "), ...); }, ics); res += "\n"; auto output = [&](auto const& lc, auto const& ic) { @@ -860,8 +885,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::ofstream ofs(ter_fname); for (auto& t : test_result) { - ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", " << std::string(transToken(std::get<1>(t))) - << "}\n,"; + ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", " + << std::string(transToken(std::get<1>(t))) << "}\n,"; } #endif } @@ -874,7 +899,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Changing a non-existent bridge should fail XEnv(*this).tx( - bridge_modify(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]), XRP(2), std::nullopt), + bridge_modify( + mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]), XRP(2), std::nullopt), ter(tecNO_ENTRY)); // must change something @@ -890,21 +916,27 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(bridge_modify(mcDoor, jvb, {}, {}), ter(temMALFORMED)); // Reward amount is non-xrp - XEnv(*this).tx(bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); + XEnv(*this).tx( + bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); // Reward amount is XRP and negative - XEnv(*this).tx(bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); + XEnv(*this).tx( + bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)); // Min create amount is non-xrp XEnv(*this).tx( - bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // Min create amount is zero - XEnv(*this).tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(0)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + XEnv(*this).tx( + bridge_modify(mcDoor, jvb, XRP(2), XRP(0)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // Min create amount is negative XEnv(*this).tx( - bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)), ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); + bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)), + ter(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT)); // First check the regular claim process (without bridge_modify) for (auto withClaim : {false, true}) @@ -926,9 +958,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) @@ -968,9 +1008,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10))).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) @@ -1013,18 +1061,28 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv.tx(jtx::signers(Account::master, quorum, alt_signers)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); // submit claim using outdated signers - should fail scEnv .multiTx( - claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers), + claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers), ter(tecNO_PERMISSION)) .close(); if (withClaim) { // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } // make sure transfer has not happened as we sent attestations @@ -1032,7 +1090,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // submit claim using current signers - should succeed - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, alt_signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, alt_signers)) .close(); if (withClaim) { @@ -1051,7 +1111,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this) .tx(create_bridge(mcDoor, jvb)) .close() - .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)), txflags(tfFillOrKill), ter(temINVALID_FLAG)); + .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)), + txflags(tfFillOrKill), + ter(temINVALID_FLAG)); // coverage test: bridge_modify transaction with xchain feature // disabled @@ -1078,7 +1140,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .close() .tx(sidechain_xchain_account_create(mcAlice, jvb, scuAlice, XRP(100), reward)) .close() - .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)), txflags(tfClearAccountCreateAmount), ter(temMALFORMED)) + .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)), + txflags(tfClearAccountCreateAmount), + ter(temMALFORMED)) .close() .tx(bridge_modify(mcDoor, jvb, XRP(3), {}), txflags(tfClearAccountCreateAmount)) .close() @@ -1120,7 +1184,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // Non-existent bridge XEnv(*this, true) - .tx(xchain_create_claim_id(scAlice, bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]), reward, mcAlice), + .tx(xchain_create_claim_id( + scAlice, bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]), reward, mcAlice), ter(tecNO_ENTRY)) .close(); @@ -1129,7 +1194,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(create_bridge(Account::master, jvb)) .fund(res1 - xrp_dust, scuAlice) // barely not enough .close() - .tx(xchain_create_claim_id(scuAlice, jvb, reward, mcAlice), ter(tecINSUFFICIENT_RESERVE)) + .tx(xchain_create_claim_id(scuAlice, jvb, reward, mcAlice), + ter(tecINSUFFICIENT_RESERVE)) .close(); // The specified reward doesn't match the reward on the bridge (test @@ -1138,14 +1204,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this, true) .tx(create_bridge(Account::master, jvb)) .close() - .tx(xchain_create_claim_id(scAlice, jvb, split_reward_quorum, mcAlice), ter(tecXCHAIN_REWARD_MISMATCH)) + .tx(xchain_create_claim_id(scAlice, jvb, split_reward_quorum, mcAlice), + ter(tecXCHAIN_REWARD_MISMATCH)) .close(); // A reward amount that isn't XRP XEnv(*this, true) .tx(create_bridge(Account::master, jvb)) .close() - .tx(xchain_create_claim_id(scAlice, jvb, mcUSD(1), mcAlice), ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)) + .tx(xchain_create_claim_id(scAlice, jvb, mcUSD(1), mcAlice), + ter(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT)) .close(); // coverage test: xchain_create_claim_id transaction with incorrect @@ -1153,7 +1221,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this, true) .tx(create_bridge(Account::master, jvb)) .close() - .tx(xchain_create_claim_id(scAlice, jvb, reward, mcAlice), txflags(tfFillOrKill), ter(temINVALID_FLAG)) + .tx(xchain_create_claim_id(scAlice, jvb, reward, mcAlice), + txflags(tfFillOrKill), + ter(temINVALID_FLAG)) .close(); // coverage test: xchain_create_claim_id transaction with xchain @@ -1185,7 +1255,10 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance alice_bal(xenv, mcAlice); auto const amt = XRP(1000); - xenv.tx(create_bridge(mcDoor, jvb)).close().tx(xchain_commit(mcAlice, jvb, 1, amt, scBob)).close(); + xenv.tx(create_bridge(mcDoor, jvb)) + .close() + .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob)) + .close(); STAmount claim_cost = amt; BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee)); @@ -1277,7 +1350,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this) .tx(create_bridge(mcDoor)) .close() - .tx(xchain_commit(mcAlice, jvb, 1, one_xrp, scBob), txflags(tfFillOrKill), ter(temINVALID_FLAG)); + .tx(xchain_commit(mcAlice, jvb, 1, one_xrp, scBob), + txflags(tfFillOrKill), + ter(temINVALID_FLAG)); // coverage test: xchain_commit transaction with xchain feature // disabled @@ -1297,7 +1372,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XRPAmount res0 = reserve(0); XRPAmount tx_fee = txFee(); - auto multiTtxFee = [&](std::uint32_t m) -> STAmount { return multiply(tx_fee, STAmount(m), xrpIssue()); }; + auto multiTtxFee = [&](std::uint32_t m) -> STAmount { + return multiply(tx_fee, STAmount(m), xrpIssue()); + }; // Add an attestation to a claim id that has already reached quorum. // This should succeed and share in the reward. @@ -1329,7 +1406,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv .multiTx(claim_attestations( - scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, UT_XCHAIN_DEFAULT_QUORUM)) + scAttester, + jvb, + mcAlice, + amt, + payees, + true, + claimID, + dst, + signers, + UT_XCHAIN_DEFAULT_QUORUM)) .close(); scEnv .tx(claim_attestation( @@ -1399,9 +1485,12 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 3, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[0], 3, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 3)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 3)) .close(); if (withClaim) @@ -1438,7 +1527,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj } return result; }(); - STAmount const split_reward_ = divide(reward, STAmount(signers_.size()), reward.issue()); + STAmount const split_reward_ = + divide(reward, STAmount(signers_.size()), reward.issue()); mcEnv.tx(create_bridge(mcDoor, jvb)).close(); @@ -1455,9 +1545,12 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[2], 2, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[2], 2, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 2)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 2)) .close(); if (withClaim) @@ -1510,16 +1603,22 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const amt = XRP(1000); mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 2, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[0], 2, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2)) .close(); if (withClaim) { BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present @@ -1564,9 +1663,12 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[1], 2, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[1], 2, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 1)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 1)) .close(); if (withClaim) @@ -1574,7 +1676,10 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present @@ -1602,7 +1707,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(sidechain_xchain_account_create(mcCarol, jvb, scuCarol, amt, reward)) .close(); - BEAST_EXPECT(door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee)); + BEAST_EXPECT( + door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee)); BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee)); } @@ -1747,7 +1853,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); Balance carol(mcEnv, mcCarol); - mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, amt, reward)).close(); + mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, amt, reward)) + .close(); BEAST_EXPECT(door.diff() == amt_plus_reward); BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee)); @@ -1788,7 +1895,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); Balance carol(mcEnv, mcCarol); - mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)).close(); + mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)) + .close(); BEAST_EXPECT(door.diff() == amt_plus_reward); BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee)); @@ -1830,7 +1938,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); Balance carol(mcEnv, mcCarol); - mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)).close(); + mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scAlice, amt, reward)) + .close(); BEAST_EXPECT(door.diff() == amt_plus_reward); BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee)); @@ -1882,7 +1991,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(sidechain_xchain_account_create(mcCarol, jvb, scuCarol, amt, reward)) .close(); // and Carol will get claim #3 - BEAST_EXPECT(door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee)); + BEAST_EXPECT( + door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee)); BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee)); } @@ -1937,7 +2047,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created"); scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3)) - .multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 3), ter(tecXCHAIN_ACCOUNT_CREATE_PAST)) + .multiTx( + att_create_acct_vec(1, amt, scuAlice, 1, 3), + ter(tecXCHAIN_ACCOUNT_CREATE_PAST)) .close(); txCount += 2; @@ -1954,7 +2066,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // because of the division of the rewards among attesters, // sometimes a couple drops are left over unspent in the // door account (here 2 drops) - BEAST_EXPECT(multiply(amt_plus_reward, STAmount(3), xrpIssue()) + door.diff() < drops(3)); + BEAST_EXPECT( + multiply(amt_plus_reward, STAmount(3), xrpIssue()) + door.diff() < drops(3)); BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount)); BEAST_EXPECT(scEnv.balance(scuAlice) == amt); BEAST_EXPECT(scEnv.balance(scuBob) == amt); @@ -1972,7 +2085,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv.tx(create_bridge(Account::master, jvb)) .tx(jtx::signers(Account::master, quorum, signers)) .close() - .tx(claim_attestation(scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]), + .tx(claim_attestation( + scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]), txflags(tfFillOrKill), ter(temINVALID_FLAG)) .close(); @@ -1986,7 +2100,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(jtx::signers(Account::master, quorum, signers)) .disableFeature(featureXChainBridge) .close() - .tx(claim_attestation(scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]), + .tx(claim_attestation( + scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]), ter(temDISABLED)) .close(); } @@ -2022,19 +2137,27 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj for (int i = 0; i < signers.size(); ++i) { - auto const att = - claim_attestation(scAttester, jvb, mcAlice, amt, payees[i], true, claimID, dst, signers[i]); + auto const att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[i], true, claimID, dst, signers[i]); TER const expectedTER = i < quorum ? tesSUCCESS : TER{tecXCHAIN_NO_CLAIM_ID}; if (i + 1 == quorum) + { scEnv.tx(att, ter(expectedTER)).close(); + } else + { scEnv.tx(att, ter(expectedTER)).close(); + } if (i + 1 < quorum) + { BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst)); + } else + { BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst)); + } } BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst)); } @@ -2078,8 +2201,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj { // G1: master key - auto att = - claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, alt_signers[0]); + auto att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, alt_signers[0]); scEnv.tx(att).close(); } { @@ -2087,8 +2210,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // alt_signers[0] is the regular key of alt_signers[1] // There should be 2 attestations after the transaction scEnv.tx(jtx::regkey(alt_signers[1].account, alt_signers[0].account)).close(); - auto att = - claim_attestation(scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, alt_signers[0]); + auto att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, alt_signers[0]); att[sfAttestationSignerAccount.getJsonName()] = alt_signers[1].account.human(); scEnv.tx(att).close(); } @@ -2117,27 +2240,36 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::vector tempSignerList = {signers[0]}; scEnv.tx(jtx::signers(alt_signers[2].account, 1, tempSignerList)); auto att = claim_attestation( - scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, tempSignerList.front()); + scAttester, + jvb, + mcAlice, + amt, + payees[2], + true, + claimID, + dst, + tempSignerList.front()); att[sfAttestationSignerAccount.getJsonName()] = alt_signers[2].account.human(); scEnv.tx(att, ter(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR)).close(); } { // B1: disabled master key scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0)).close(); - auto att = - claim_attestation(scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, alt_signers[2]); + auto att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, alt_signers[2]); scEnv.tx(att, ter(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR)).close(); } { // --B4: not on signer list - auto att = claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]); + auto att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]); scEnv.tx(att, ter(tecNO_PERMISSION)).close(); } { // --B5: missing sfAttestationSignerAccount field // Then submit the one with the field. Should reach quorum. - auto att = - claim_attestation(scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, alt_signers[3]); + auto att = claim_attestation( + scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, alt_signers[3]); att.removeMember(sfAttestationSignerAccount.getJsonName()); scEnv.tx(att, ter(temMALFORMED)).close(); BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst)); @@ -2197,14 +2329,28 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj for (int i = 0; i < signers.size(); ++i) { auto const att = create_account_attestation( - signers[0].account, xrp_b.jvb, a, amt, xrp_b.reward, signers[i].account, true, 1, ua, signers[i]); - TER const expectedTER = i < xrp_b.quorum ? tesSUCCESS : TER{tecXCHAIN_ACCOUNT_CREATE_PAST}; + signers[0].account, + xrp_b.jvb, + a, + amt, + xrp_b.reward, + signers[i].account, + true, + 1, + ua, + signers[i]); + TER const expectedTER = + i < xrp_b.quorum ? tesSUCCESS : TER{tecXCHAIN_ACCOUNT_CREATE_PAST}; scEnv.tx(att, ter(expectedTER)).close(); if (i + 1 < xrp_b.quorum) + { BEAST_EXPECT(!scEnv.env_.le(ua)); + } else + { BEAST_EXPECT(scEnv.env_.le(ua)); + } } BEAST_EXPECT(scEnv.env_.le(ua)); } @@ -2242,9 +2388,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) { @@ -2279,11 +2433,21 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const claimID = 1; mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); jtx::signer master_signer(Account::master); scEnv - .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, master_signer), + .tx(claim_attestation( + scAttester, + jvb, + mcAlice, + amt, + payees[0], + true, + claimID, + dst, + master_signer), ter(tecXCHAIN_NO_SIGNERS_LIST)) .close(); @@ -2314,11 +2478,21 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const claimID = 1; mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); - BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); + BalanceTransfer transfer( + scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim); jtx::signer master_signer(payees[0]); scEnv - .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, master_signer), + .tx(claim_attestation( + scAttester, + jvb, + mcAlice, + amt, + payees[0], + true, + claimID, + dst, + master_signer), ter(tecXCHAIN_NO_SIGNERS_LIST)) .close(); @@ -2345,11 +2519,21 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto dst(withClaim ? std::nullopt : std::optional{scBob}); auto const amt = XRP(1000); std::uint32_t const claimID = 1; - mcEnv.tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst), ter(tecNO_ENTRY)).close(); + mcEnv.tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst), ter(tecNO_ENTRY)) + .close(); BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim); scEnv - .tx(claim_attestation(scAttester, jvb_unknown, mcAlice, amt, payees[0], true, claimID, dst, signers[0]), + .tx(claim_attestation( + scAttester, + jvb_unknown, + mcAlice, + amt, + payees[0], + true, + claimID, + dst, + signers[0]), ter(tecNO_ENTRY)) .close(); @@ -2358,7 +2542,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob), ter(tecNO_ENTRY)).close(); + scEnv.tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob), ter(tecNO_ENTRY)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2388,7 +2573,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // attest using non-existent claim id scEnv - .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, 999, dst, signers[0]), + .tx(claim_attestation( + scAttester, jvb, mcAlice, amt, payees[0], true, 999, dst, signers[0]), ter(tecXCHAIN_NO_CLAIM_ID)) .close(); if (withClaim) @@ -2396,7 +2582,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // claim using non-existent claim id - scEnv.tx(xchain_claim(scAlice, jvb, 999, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, 999, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2423,9 +2610,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) { @@ -2433,7 +2628,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // submit a claim transaction with the wrong account (scGw // instead of scAlice) - scEnv.tx(xchain_claim(scGw, jvb, claimID, amt, scBob), ter(tecXCHAIN_BAD_CLAIM_ID)).close(); + scEnv.tx(xchain_claim(scGw, jvb, claimID, amt, scBob), ter(tecXCHAIN_BAD_CLAIM_ID)) + .close(); BEAST_EXPECT(transfer.has_not_happened()); } else @@ -2471,7 +2667,10 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2502,14 +2701,18 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto tooFew = quorum - 1; scEnv - .multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, tooFew)) + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, tooFew)) .close(); if (withClaim) { BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2539,7 +2742,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj scEnv .multiTx( - claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, 0, dst, signers), + claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, 0, dst, signers), ter(tecXCHAIN_NO_CLAIM_ID)) .close(); if (withClaim) @@ -2547,7 +2751,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, 0, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, 0, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2577,9 +2782,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) @@ -2587,7 +2800,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob), ter(temBAD_AMOUNT)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob), ter(temBAD_AMOUNT)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2614,9 +2829,17 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); if (withClaim) { @@ -2653,20 +2876,30 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); if (withClaim) { - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) + scEnv + .multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)) .close(); BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)) + .close(); } else { - auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); + auto txns = claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i) { scEnv.tx(txns[i]).close(); @@ -2676,7 +2909,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // The attestation should succeed, because it adds an // attestation, but the claim should fail with insufficient // funds - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2698,7 +2932,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj res0 + reward, scuAlice) // just not enough because of fees .close() - .tx(xchain_create_claim_id(scuAlice, jvb, reward, mcAlice), ter(tecINSUFFICIENT_RESERVE)) + .tx(xchain_create_claim_id(scuAlice, jvb, reward, mcAlice), + ter(tecINSUFFICIENT_RESERVE)) .close(); auto dst(withClaim ? std::nullopt : std::optional{scBob}); @@ -2709,7 +2944,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BalanceTransfer transfer(scEnv, Account::master, scBob, scuAlice, payees, withClaim); scEnv - .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]), + .tx(claim_attestation( + scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]), ter(tecXCHAIN_NO_CLAIM_ID)) .close(); if (withClaim) @@ -2717,7 +2953,10 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)).close(); + scEnv + .tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob), + ter(tecXCHAIN_NO_CLAIM_ID)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2745,8 +2984,15 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); - auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); + auto txns = claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i) { scEnv.tx(txns[i]).close(); @@ -2758,7 +3004,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)) + .close(); // the transfer failed, but check that we can still use the // claimID with a different account @@ -2770,7 +3017,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj else { scEnv.tx(txns.back()).close(); - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)) + .close(); // A way would be to remove deposit auth and resubmit the // attestations (even though the witness servers won't do // it) @@ -2806,8 +3054,15 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); - auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); + auto txns = claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers); for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i) { scEnv.tx(txns[i]).close(); @@ -2818,7 +3073,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj BEAST_EXPECT(transfer.has_not_happened()); // need to submit a claim transactions - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)) + .close(); // the transfer failed, but check that we can still use the // claimID with a different account @@ -2830,7 +3086,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj else { scEnv.tx(txns.back()).close(); - scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)).close(); + scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)) + .close(); // A way would be to remove the destination tag requirement // and resubmit the attestations (even though the witness // servers won't do it) @@ -2871,7 +3128,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // should not occur because dest account has deposit auth set Balance scBob_bal(scEnv, scBob); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); BEAST_EXPECT(scBob_bal.diff() == STAmount(0)); // Check that check that we still can use the claimID to transfer @@ -2903,14 +3161,24 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); if (withClaim) { BEAST_EXPECT(transfer.has_not_happened()); // claim wrong amount - scEnv.tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close(); + scEnv + .tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob), + ter(tecXCHAIN_CLAIM_NO_QUORUM)) + .close(); } BEAST_EXPECT(transfer.has_not_happened()); @@ -2938,9 +3206,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); Balance scAlice_bal(scEnv, scAlice); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); STAmount claim_cost = reward; @@ -2979,9 +3254,16 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM, + withClaim); Balance scAlice_bal(scEnv, scAlice); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); STAmount claim_cost = tiny_reward; if (withClaim) @@ -3024,8 +3306,15 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close(); BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM - 1, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, alt_payees, true, claimID, dst, signers)); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM - 1, + withClaim); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, alt_payees, true, claimID, dst, signers)); if (withClaim) { @@ -3067,8 +3356,15 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj // split_reward BalanceTransfer transfer( - scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM - 1, withClaim); - scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); + scEnv, + Account::master, + scBob, + scAlice, + &payees[0], + UT_XCHAIN_DEFAULT_QUORUM - 1, + withClaim); + scEnv.multiTx(claim_attestations( + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); if (withClaim) { @@ -3091,7 +3387,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XEnv(*this, true) .tx(create_bridge(Account::master, jvb)) .close() - .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob), txflags(tfFillOrKill), ter(temINVALID_FLAG)) + .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob), + txflags(tfFillOrKill), + ter(temINVALID_FLAG)) .close(); // coverage test: xchain_claim transaction with xchain feature @@ -3125,7 +3423,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj auto const amt = XRP(111); auto const amt_plus_reward = amt + reward; - scEnv.tx(create_bridge(Account::master, jvb)).tx(jtx::signers(Account::master, quorum, signers)).close(); + scEnv.tx(create_bridge(Account::master, jvb)) + .tx(jtx::signers(Account::master, quorum, signers)) + .close(); Balance door(scEnv, Account::master); @@ -3190,7 +3490,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); mcEnv.disableFeature(featureXChainBridge) - .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), reward), ter(temDISABLED)) + .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), reward), + ter(temDISABLED)) .close(); BEAST_EXPECT(door.diff() == STAmount(0)); @@ -3204,7 +3505,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); - mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(-20), reward), ter(temBAD_AMOUNT)) + mcEnv + .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(-20), reward), + ter(temBAD_AMOUNT)) .close(); BEAST_EXPECT(door.diff() == STAmount(0)); @@ -3218,7 +3521,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); - mcEnv.tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), XRP(-1)), ter(temBAD_AMOUNT)) + mcEnv + .tx(sidechain_xchain_account_create(mcCarol, jvb, scuAlice, XRP(20), XRP(-1)), + ter(temBAD_AMOUNT)) .close(); BEAST_EXPECT(door.diff() == STAmount(0)); @@ -3233,7 +3538,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Balance door(mcEnv, mcDoor); mcEnv - .tx(sidechain_xchain_account_create(mcDoor, jvb, scuAlice, XRP(20), XRP(1)), ter(tecXCHAIN_SELF_COMMIT)) + .tx(sidechain_xchain_account_create(mcDoor, jvb, scuAlice, XRP(20), XRP(1)), + ter(tecXCHAIN_SELF_COMMIT)) .close(); BEAST_EXPECT(door.diff() == -tx_fee); @@ -3288,7 +3594,8 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate)) .fund(res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice) .close() - .tx(sidechain_xchain_account_create(mcuAlice, jvb, scuAlice, minAccountCreate, reward), ter(tesSUCCESS)); + .tx(sidechain_xchain_account_create(mcuAlice, jvb, scuAlice, minAccountCreate, reward), + ter(tesSUCCESS)); // account create commit where the commit dips into the reserve, // this should fail @@ -3355,7 +3662,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj std::uint32_t const claimID = 1; std::optional dst{scBob}; auto const amt = XRP(1000); - scEnv.tx(create_bridge(Account::master, jvb)).tx(jtx::signers(Account::master, quorum, signers)).close(); + scEnv.tx(create_bridge(Account::master, jvb)) + .tx(jtx::signers(Account::master, quorum, signers)) + .close(); scEnv.tx(xchain_create_claim_id(scAlice, jvb, reward, mcAlice)).close(); auto jvAtt = claim_attestation( scAttester, @@ -3383,7 +3692,9 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj Account dst{scBob}; auto const amt = XRP(1000); auto const rewardAmt = XRP(1); - scEnv.tx(create_bridge(Account::master, jvb)).tx(jtx::signers(Account::master, quorum, signers)).close(); + scEnv.tx(create_bridge(Account::master, jvb)) + .tx(jtx::signers(Account::master, quorum, signers)) + .close(); auto jvAtt = create_account_attestation( scAttester, jvb, @@ -3527,7 +3838,7 @@ private: void sendAttestations() { - bool callback_called; + bool callback_called = false; // we have this "do {} while" loop because we want to process // all the account create which can reach quorum at this time @@ -3547,7 +3858,8 @@ private: auto num_attns = create_claims.size(); if (num_attns) { - c.num_create_attn_sent += sendCreateAttestations(i, bridge, create_claims); + c.num_create_attn_sent += + sendCreateAttestations(i, bridge, create_claims); } assert(claims.create_claims[c.claim_count].empty()); } @@ -3595,7 +3907,9 @@ private: { if (amt.issue() != xrpIssue()) return; - receive(acct, times == 1 ? -amt : -multiply(amt, STAmount(amt.issue(), times), amt.issue())); + receive( + acct, + times == 1 ? -amt : -multiply(amt, STAmount(amt.issue(), times), amt.issue())); } void @@ -3615,8 +3929,10 @@ private: verify() const { for (auto const& [acct, state] : accounts) + { if (!state.verify(env, acct)) return false; + } return true; } @@ -3695,12 +4011,12 @@ private: template class SmBase { - public: SmBase(std::shared_ptr const& chainstate, BridgeDef const& bridge) : bridge_(bridge), st_(chainstate) { } + public: ChainStateTrack& srcState() { @@ -3728,6 +4044,8 @@ private: protected: BridgeDef const& bridge_; std::shared_ptr st_; + + friend T; }; // -------------------------------------------------- @@ -3740,7 +4058,7 @@ private: std::shared_ptr const& chainstate, BridgeDef const& bridge, AccountCreate create) - : Base(chainstate, bridge), sm_state(st_initial), cr(std::move(create)) + : Base(chainstate, bridge), cr(std::move(create)) { } @@ -3756,7 +4074,8 @@ private: ChainStateTrack& st = srcState(); jtx::Account const& srcdoor = srcDoor(); - st.env.tx(sidechain_xchain_account_create(cr.from, bridge_.jvb, cr.to, cr.amt, cr.reward)) + st.env + .tx(sidechain_xchain_account_create(cr.from, bridge_.jvb, cr.to, cr.amt, cr.reward)) .close(); // needed for claim_id sequence to be correct' st.spendFee(cr.from); st.transfer(cr.from, srcdoor, cr.amt); @@ -3771,7 +4090,7 @@ private: ChainStateTrack& st = destState(); // check all signers, but start at a random one - size_t i; + size_t i = 0; for (i = 0; i < num_signers; ++i) { size_t signer_idx = (rnd + i) % num_signers; @@ -3781,8 +4100,9 @@ private: // enqueue one attestation for this signer cr.attested[signer_idx] = true; - st.signers_attns[signer_idx][&bridge_].create_claims[cr.claim_id - 1].emplace_back( - create_account_attestation( + st.signers_attns[signer_idx][&bridge_] + .create_claims[cr.claim_id - 1] + .emplace_back(create_account_attestation( bridge_.signers[signer_idx].account, bridge_.jvb, cr.from, @@ -3851,7 +4171,7 @@ private: } private: - SmState sm_state; + SmState sm_state{st_initial}; AccountCreate cr; }; @@ -3861,8 +4181,11 @@ private: public: using Base = SmBase; - SmTransfer(std::shared_ptr const& chainstate, BridgeDef const& bridge, Transfer xfer) - : Base(chainstate, bridge), xfer(std::move(xfer)), sm_state(st_initial) + SmTransfer( + std::shared_ptr const& chainstate, + BridgeDef const& bridge, + Transfer xfer) + : Base(chainstate, bridge), xfer(std::move(xfer)) { } @@ -3900,7 +4223,8 @@ private: bridge_.jvb, xfer.claim_id, xfer.amt, - xfer.with_claim == WithClaim::yes ? std::nullopt : std::optional(xfer.finaldest))); + xfer.with_claim == WithClaim::yes ? std::nullopt + : std::optional(xfer.finaldest))); st.spendFee(xfer.from); st.transfer(xfer.from, srcdoor, xfer.amt); } @@ -3933,22 +4257,26 @@ private: // enqueue one attestation for this signer xfer.attested[signer_idx] = true; - st.signers_attns[signer_idx][&bridge_].xfer_claims.emplace_back(claim_attestation( - bridge_.signers[signer_idx].account, - bridge_.jvb, - xfer.from, - xfer.amt, - bridge_.signers[signer_idx].account, - xfer.a2b, - xfer.claim_id, - xfer.with_claim == WithClaim::yes ? std::nullopt : std::optional(xfer.finaldest), - bridge_.signers[signer_idx])); + st.signers_attns[signer_idx][&bridge_].xfer_claims.emplace_back( + claim_attestation( + bridge_.signers[signer_idx].account, + bridge_.jvb, + xfer.from, + xfer.amt, + bridge_.signers[signer_idx].account, + xfer.a2b, + xfer.claim_id, + xfer.with_claim == WithClaim::yes + ? std::nullopt + : std::optional(xfer.finaldest), + bridge_.signers[signer_idx])); break; } } // return true if quorum was reached, false otherwise - bool quorum = std::count(xfer.attested.begin(), xfer.attested.end(), true) >= bridge_.quorum; + bool quorum = + std::count(xfer.attested.begin(), xfer.attested.end(), true) >= bridge_.quorum; if (quorum && xfer.with_claim == WithClaim::no) { distribute_reward(st); @@ -3983,8 +4311,14 @@ private: break; case st_attesting: - sm_state = attest(time, rnd) ? (xfer.with_claim == WithClaim::yes ? st_attested : st_completed) - : st_attesting; + if (attest(time, rnd)) + { + sm_state = xfer.with_claim == WithClaim::yes ? st_attested : st_completed; + } + else + { + sm_state = st_attesting; + } break; case st_attested: @@ -4003,7 +4337,7 @@ private: private: Transfer xfer; - SmState sm_state; + SmState sm_state{st_initial}; }; // -------------------------------------------------- @@ -4023,7 +4357,10 @@ private: } void - ac(uint64_t time, std::shared_ptr const& chainstate, BridgeDef const& bridge, AccountCreate ac) + ac(uint64_t time, + std::shared_ptr const& chainstate, + BridgeDef const& bridge, + AccountCreate ac) { sm_.emplace_back(time, SmCreateAccount(chainstate, bridge, std::move(ac))); } @@ -4048,9 +4385,13 @@ public: }; auto& [t, sm] = *it; if (t <= time && std::visit(vis, sm) == st_completed) + { it = sm_.erase(it); + } else + { ++it; + } } // send attestations @@ -4081,7 +4422,8 @@ public: // create 10 accounts + door funded on both chains, and store // in ChainStateTracker the initial amount of these accounts - Account doorXRPLocking("doorXRPLocking"), doorUSDLocking("doorUSDLocking"), doorUSDIssuing("doorUSDIssuing"); + Account doorXRPLocking("doorXRPLocking"), doorUSDLocking("doorUSDLocking"), + doorUSDIssuing("doorUSDIssuing"); constexpr size_t num_acct = 10; auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() { @@ -4089,7 +4431,10 @@ public: std::vector result; result.reserve(num_acct); for (int i = 0; i < num_acct; ++i) - result.emplace_back("a"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + { + result.emplace_back( + "a"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + } result.emplace_back("doorXRPLocking"); doorXRPLocking = result.back(); result.emplace_back("doorUSDLocking"); @@ -4138,7 +4483,10 @@ public: std::vector result; result.reserve(num_ua); for (int i = 0; i < num_ua; ++i) - result.emplace_back("ua"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + { + result.emplace_back( + "ua"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + } return result; }(); @@ -4152,14 +4500,30 @@ public: // create XRP -> XRP bridge // ------------------------ BridgeDef xrp_b{ - doorXRPLocking, xrpIssue(), Account::master, xrpIssue(), XRP(1), XRP(20), quorum, signers, Json::nullValue}; + doorXRPLocking, + xrpIssue(), + Account::master, + xrpIssue(), + XRP(1), + XRP(20), + quorum, + signers, + Json::nullValue}; initBridge(xrp_b); // create USD -> USD bridge // ------------------------ BridgeDef usd_b{ - doorUSDLocking, usdLocking, doorUSDIssuing, usdIssuing, XRP(1), XRP(20), quorum, signers, Json::nullValue}; + doorUSDLocking, + usdLocking, + doorUSDIssuing, + usdIssuing, + XRP(1), + XRP(20), + quorum, + signers, + Json::nullValue}; initBridge(usd_b); diff --git a/src/test/app/tx/apply_test.cpp b/src/test/app/tx/apply_test.cpp index fada168112..10b0544f49 100644 --- a/src/test/app/tx/apply_test.cpp +++ b/src/test/app/tx/apply_test.cpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include namespace xrpl { @@ -32,18 +31,16 @@ public: "8B62E6440848314BB85996936E4F595287774684DC2AC6266024BEF"; auto ret = strUnHex(non_fully_canonical_tx); - SerialIter sitTrans(makeSlice(*ret)); + SerialIter sitTrans(makeSlice(*ret)); // NOLINT(bugprone-unchecked-optional-access) STTx const tx = *std::make_shared(std::ref(sitTrans)); { test::jtx::Env fully_canonical(*this, test::jtx::testable_amendments()); - Validity valid = checkValidity( - fully_canonical.app().getHashRouter(), - tx, - fully_canonical.current()->rules(), - fully_canonical.app().config()) - .first; + Validity valid = + checkValidity( + fully_canonical.app().getHashRouter(), tx, fully_canonical.current()->rules()) + .first; if (valid == Validity::Valid) fail("Non-Fully canonical signature was permitted"); } diff --git a/src/test/app/wasm_fixtures/b58.c b/src/test/app/wasm_fixtures/b58.c deleted file mode 100644 index 56defdbb32..0000000000 --- a/src/test/app/wasm_fixtures/b58.c +++ /dev/null @@ -1,72 +0,0 @@ -#include - -static char const b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - -uint8_t e_data[32 * 1024]; - -void* -allocate(int sz) -{ - static int idx = 0; - if (idx >= 32) - return 0; - if (sz > 1024) - return 0; - return &e_data[idx++ << 10]; -} - -void -deallocate(void* p) -{ -} - -extern int32_t -b58enco(char* b58, int32_t b58sz, void const* data, int32_t binsz) -{ - uint8_t const* bin = data; - int32_t carry; - int32_t i, j, high, zcount = 0; - int32_t size; - - while (zcount < binsz && !bin[zcount]) - ++zcount; - - size = (binsz - zcount) * 138 / 100 + 1; - uint8_t* buf = allocate(size); - if (!buf) - return 0; - // memset(buf, 0, size); - for (i = 0; i < size; ++i) - buf[i] = 0; - - for (i = zcount, high = size - 1; i < binsz; ++i, high = j) - { - for (carry = bin[i], j = size - 1; (j > high) || carry; --j) - { - carry += 256 * buf[j]; - buf[j] = carry % 58; - carry /= 58; - if (!j) - break; - } - } - - for (j = 0; j < size && !buf[j]; ++j) - ; - - if (b58sz <= zcount + size - j) - return 0; - - if (zcount) - { - // memset(b58, '1', zcount); - for (i = 0; i < zcount; ++i) - b58[i] = '1'; - } - - for (i = zcount; j < size; ++i, ++j) - b58[i] = b58digits_ordered[buf[j]]; - b58[i] = '\0'; - - return i + 1; -} diff --git a/src/test/app/wasm_fixtures/bad_align.c b/src/test/app/wasm_fixtures/bad_align.c index edb045560d..cc291c70f1 100644 --- a/src/test/app/wasm_fixtures/bad_align.c +++ b/src/test/app/wasm_fixtures/bad_align.c @@ -1,47 +1,43 @@ #include -int32_t -float_from_uint(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); -int32_t -check_keylet(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t); +int32_t float_from_uint(uint8_t const *, int32_t, uint8_t *, int32_t, int32_t); +int32_t check_keylet(uint8_t const *, int32_t, uint8_t const *, int32_t, + uint8_t *, int32_t); uint8_t e_data1[32 * 1024]; uint8_t e_data2[32 * 1024]; -int32_t -test1() +int32_t test1() { - e_data1[1] = 0xFF; - e_data1[2] = 0xFF; - e_data1[3] = 0xFF; - e_data1[4] = 0xFF; - e_data1[5] = 0xFF; - e_data1[6] = 0xFF; - e_data1[7] = 0xFF; - e_data1[8] = 0xFF; - int32_t result = float_from_uint(&e_data1[1], 8, &e_data1[35], 12, 0); - return result >= 0 ? *((int32_t*)(&e_data1[36])) : result; + e_data1[1] = 0xFF; + e_data1[2] = 0xFF; + e_data1[3] = 0xFF; + e_data1[4] = 0xFF; + e_data1[5] = 0xFF; + e_data1[6] = 0xFF; + e_data1[7] = 0xFF; + e_data1[8] = 0xFF; + int32_t result = float_from_uint(&e_data1[1], 8, &e_data1[35], 12, 0); + return result >= 0 ? *((int32_t *)(&e_data1[36])) : result; } -int32_t -test2() +int32_t test2() { - // Set up misaligned uint32 (seq) at offset 1 - e_data2[1] = 0xFF; - e_data2[2] = 0xFF; - e_data2[3] = 0xFF; - e_data2[4] = 0xFF; - // Set up valid non-zero AccountID (20 bytes) at offset 10 - for (int i = 0; i < 20; i++) - e_data2[10 + i] = i + 1; - // Call check_keylet with misaligned uint32 at &e_data2[1] to hit line 72 in HostFuncWrapper.cpp - int32_t result = check_keylet(&e_data2[10], 20, &e_data2[1], 4, &e_data2[35], 32); - // Return the misaligned value directly to validate it was read correctly (-1 if all 0xFF) - return result >= 0 ? *((int32_t*)(&e_data2[36])) : result; + // Set up misaligned uint32 (seq) at offset 1 + e_data2[1] = 0xFF; + e_data2[2] = 0xFF; + e_data2[3] = 0xFF; + e_data2[4] = 0xFF; + // Set up valid non-zero AccountID (20 bytes) at offset 10 + for (int i = 0; i < 20; i++) + e_data2[10 + i] = i + 1; + // Call check_keylet with misaligned uint32 at &e_data2[1] to hit line 72 in + // HostFuncWrapper.cpp + int32_t result = + check_keylet(&e_data2[10], 20, &e_data2[1], 4, &e_data2[35], 32); + // Return the misaligned value directly to validate it was read correctly (-1 + // if all 0xFF) + return result >= 0 ? *((int32_t *)(&e_data2[36])) : result; } -int32_t -test() -{ - return test1() + test2(); -} +int32_t test() { return test1() + test2(); } diff --git a/src/test/app/wasm_fixtures/bad_alloc.c b/src/test/app/wasm_fixtures/bad_alloc.c deleted file mode 100644 index 9a0233e8a5..0000000000 --- a/src/test/app/wasm_fixtures/bad_alloc.c +++ /dev/null @@ -1,27 +0,0 @@ -#include - -char buf[1024]; - -void* -allocate(int sz) -{ - if (!sz) - return 0; - - if (sz == 1) - return ((void*)(8 * 1024 * 1024)); - if (sz == 2) - return 0; - if (sz == 3) - return ((void*)(0xFFFFFFFF)); - - return &buf[sz]; -} - -int32_t -test(char* p, int32_t sz) -{ - if (!sz) - return 0; - return p[0]; -} diff --git a/src/test/app/wasm_fixtures/codecov_tests/src/lib.rs b/src/test/app/wasm_fixtures/codecov_tests/src/lib.rs index 59ff5bc164..67e16c5cec 100644 --- a/src/test/app/wasm_fixtures/codecov_tests/src/lib.rs +++ b/src/test/app/wasm_fixtures/codecov_tests/src/lib.rs @@ -1777,6 +1777,19 @@ pub extern "C" fn finish() -> i32 { "mptoken_keylet_mptid_wrong_length", ) }); + check_result( + unsafe { + host::trace( + message.as_ptr(), + message.len(), + locator.as_ptr(), + locator.len(), + 2, + ) + }, + error_codes::INVALID_PARAMS, + "trace_invalid_as_hex", + ); // ensure that the Slice index desync issue is fixed let empty: &[u8] = b""; diff --git a/src/test/app/wasm_fixtures/fib.c b/src/test/app/wasm_fixtures/fib.c index 4410923054..e45cc4fe6c 100644 --- a/src/test/app/wasm_fixtures/fib.c +++ b/src/test/app/wasm_fixtures/fib.c @@ -1,12 +1,11 @@ // typedef long long mint; typedef int mint; -mint -fib(mint n) +mint fib(mint n) { - if (!n) - return 0; - if (n <= 2) - return 1; - return fib(n - 1) + fib(n - 2); + if (!n) + return 0; + if (n <= 2) + return 1; + return fib(n - 1) + fib(n - 2); } diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index 9e1783ef50..da1c0fa04d 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -57,7 +57,8 @@ std::vector generateCodeBlob(uint32_t num_instructions) { std::vector wasm; - wasm.reserve(sizeof(WASM_HEADER) + sizeof(TYPE_EMPTY_FUNC) + sizeof(FUNC_TYPE0) + sizeof(EXPORT_FINISH)); + wasm.reserve( + sizeof(WASM_HEADER) + sizeof(TYPE_EMPTY_FUNC) + sizeof(FUNC_TYPE0) + sizeof(EXPORT_FINISH)); appendBytes(wasm, WASM_HEADER); appendBytes(wasm, TYPE_EMPTY_FUNC); appendBytes(wasm, FUNC_TYPE0); @@ -122,953 +123,919 @@ generateDataBlob(uint32_t data_size) } // namespace wasm_constants extern std::string const fibWasmHex = - "0061736d0100000001090260000060017f017f0303020001071b02115f5f" - "7761736d5f63616c6c5f63746f727300000366696200010a440202000b3f" - "01017f200045044041000f0b2000410348044041010f0b200041026a2100" - "0340200041036b100120016a2101200041026b220041044a0d000b200141" - "016a0b"; - -extern std::string const b58WasmHex = - "0061736d0100000001150460000060017f017f60017f0060047f7f7f7f01" - "7f0305040001020305030100020607017f0041d0080b074906066d656d6f" - "72790200115f5f7761736d5f63616c6c5f63746f7273000008616c6c6f63" - "617465000106655f6461746103000a6465616c6c6f636174650002076235" - "38656e636f00030ae8050402000b3401017f024020004180084a0d0041c0" - "082802002200411f4a0d0041c008200041016a3602002000410a7441d008" - "6a21010b20010b02000baa0501097f41d088060240200341004c0d000340" - "200220066a2d00000d012003200641016a2206470d000b200321060b2003" - "20066b220c418a016c41e4006d220741106a4170716b21090240200c4100" - "480d00200741016a220a410771210541002104200741074f0440200a41f8" - "ffff1f71210a0340200420096a4200370300200a200441086a2204470d00" - "0b0b2005450d00200420096a21040340200441003a0000200441016a2104" - "200541016b22050d000b0b200320064a0440200621052007210403402002" - "20056a2d0000210802402004220a20074e0440200721042008450d010b20" - "0721040340200420096a220b200b2d000041087420086a220b200b413a6d" - "2208413a6c6b3a00002004450440410021040c020b200441016b2204200a" - "4a0d00200b413a6b418d7f490d000b0b200541016a22052003470d000b0b" - "200741016a22022103410021040240200c4100480d000340200420096a2d" - "00000d012002200441016a2204470d000b200221040b2001200320066a20" - "046b4a047f02402006450d002006410771210841002105200641084f0440" - "200641787121010340200020056a42b1e2c48993a6cc9831370000200120" - "0541086a2205470d000b0b2008450d00200020056a21050340200541313a" - "0000200541016a2105200841016b22080d000b0b0240200420074a0d0020" - "0420076a410171047f200405200020066a200420096a2d00004180086a2d" - "00003a0000200641016a2106200441016a0b210520042007460d00200520" - "096a2101200720056b2102200020066a2103417f21040340200320046a22" - "0741016a200120046a220541016a2d00004180086a2d00003a0000200741" - "026a200541026a2d00004180086a2d00003a00002002200441026a220447" - "0d000b200420066a41016a21060b200020066a41003a0000200641016a05" - "41000b0b0b4101004180080b3a3132333435363738394142434445464748" - "4a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f" - "707172737475767778797a00490f7461726765745f666561747572657304" - "2b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f72" - "65666572656e63652d74797065732b0a6d756c746976616c7565"; - -extern std::string const sha512PureWasmHex = - "0061736d0100000001130460000060017f017f60017f0060027f7f017f03" - "05040001020305030100020607017f0041800d0b075006066d656d6f7279" - "0200115f5f7761736d5f63616c6c5f63746f7273000008616c6c6f636174" - "65000106655f6461746103000a6465616c6c6f6361746500020e73686135" - "31325f70726f6365737300030aa4060402000b0f0041800d410020004180" - "80024d1b0b02000b8b0602147e037f200141ff006a41ff014f044041c08c" - "06211620014180016d41016bac210b41808d02290300210c41888d022903" - "00210d41908d02290300210e41988d02290300210f41a08d022903002110" - "41a88d02290300211141b08d02290300211241b88d022903002113034041" - "002101200c21092013210a201221082011210520102103200f2115200e21" - "06200d2102034020022107200120166a200020016a290000220242388620" - "024280fe0383422886842002428080fc0783421886200242808080f80f83" - "4208868484200242088842808080f80f832002421888428080fc07838420" - "024228884280fe03832002423888848484220237030020014180086a2903" - "00200a200322044232892004422e89852004421789857c2008220a200442" - "7f858320052208200483847c7c20027c2203200922024224892002421e89" - "85200242198985200220072006221485832006200783857c7c2109200320" - "157c2103200421052006211520072106200141086a2201418001470d000b" - "200020016a21004180092117411e210103402002210620162001410e6b41" - "0f714103746a221820182903002016200141056b410f714103746a290300" - "20162001410d6b410f714103746a2903002202423f892002423889852002" - "420788857c7c20162001410f714103746a2903002202422d892002420389" - "852002420688857c22023703002017290300200a20032205423289200542" - "2e89852005421789857c2005427f852008832004200583847c7c20027c22" - "03200922024224892002421e898520024219898520022006200785832006" - "200783857c7c2109200320147c2103201741086a21172008210a20042108" - "200521042007211420062107200141016a220141de00470d000b41b88d02" - "200a20137c221337030041b08d02200820127c221237030041a88d022004" - "20117c221137030041a08d02200320107c221037030041988d02200f2014" - "7c220f37030041908d022007200e7c220e37030041888d022002200d7c22" - "0d37030041808d022009200c7c220c370300200b420052200b42017d210b" - "0d000b0b41808d020b0b880501004180080b800522ae28d7982f8a42cd65" - "ef23914437712f3b4deccffbc0b5bcdb8981a5dbb5e938b548f35bc25639" - "19d005b6f111f1599b4f19afa4823f9218816ddad55e1cab420203a398aa" - "07d8be6f7045015b83128cb2e44ebe853124e2b4ffd5c37d0c556f897bf2" - "745dbe72b196163bfeb1de803512c725a706dc9b942669cf74f19bc1d24a" - "f19ec1699be4e3254f388647beefb5d58c8bc69dc10f659cac77cca10c24" - "75022b596f2ce92d83e4a66eaa84744ad4fb41bddca9b05cb5531183da88" - "f976abdf66ee52513e981032b42d6dc631a83f21fb98c82703b0e40eefbe" - "c77f59bfc28fa83df30be0c625a70a934791a7d56f8203e05163ca06706e" - "0e0a67292914fc2fd246850ab72726c9265c38211b2eed2ac45afc6d2c4d" - "dfb3959d130d3853de63af8b54730a65a8b2773cbb0a6a76e6aeed472ec9" - "c2813b358214852c72926403f14ca1e8bfa2013042bc4b661aa89197f8d0" - "708b4bc230be5406a3516cc71852efd619e892d110a96555240699d62a20" - "715785350ef4b8d1bb3270a06a10c8d0d2b816c1a41953ab4151086c371e" - "99eb8edf4c774827a8489be1b5bcb034635ac9c5b30c1c39cb8a41e34aaa" - "d84e73e363774fca9c5ba3b8b2d6f36f2e68fcb2ef5dee828f74602f1743" - "6f63a57872abf0a11478c884ec39641a0802c78c281e6323faffbe90e9bd" - "82deeb6c50a41579c6b2f7a3f9be2b5372e3f27871c69c6126eace3e27ca" - "07c2c021c7b886d11eebe0cdd67ddaea78d16eee7f4f7df5ba6f1772aa67" - "f006a698c8a2c57d630aae0df9be04983f111b471c13350b711b847d0423" - "f577db289324c7407babca32bcbec9150abe9e3c4c0d109cc4671d43b642" - "3ecbbed4c54c2a7e65fc9c297f59ecfad63aab6fcb5f1758474a8c19446c" - "00490f7461726765745f6665617475726573042b0f6d757461626c652d67" - "6c6f62616c732b087369676e2d6578742b0f7265666572656e63652d7479" - "7065732b0a6d756c746976616c7565"; + "0061736d0100000001090260000060017f017f0303020001071b02115f5f7761736d5f63616c6c5f63746f72730000" + "0366696200010a440202000b3f01017f200045044041000f0b2000410348044041010f0b200041026a210003402000" + "41036b100120016a2101200041026b220041044a0d000b200141016a0b"; extern std::string const ledgerSqnWasmHex = - "0061736d01000000010e0360027f7f017f6000006000017f02160103656e760e6765745f6c65646765725f73716e0000030302010205030100" - "02063f0a7f01418088040b7f004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f00418080080b7f" - "0041000b7f0041010b07aa010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f727300010666696e69736800020c5f5f64736f" - "5f68616e646c6503010a5f5f646174615f656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f" - "62616c5f6261736503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f6261736503080c5f5f74" - "61626c655f6261736503090a3d0202000b3801037f230041106b220024002000410c6a410410002101200028020c2102200041106a24002001" - "41054100200241054f1b20014100481b0b007f0970726f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e35" - "2d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a65637420616234623561326462" - "353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f6665617475726573042b0f6d757461" - "626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; + "0061736d01000000010e0360027f7f017f6000006000017f02160103656e760e6765745f6c65646765725f73716e00" + "0003030201020503010002063f0a7f01418088040b7f004180080b7f004180080b7f004180080b7f00418088040b7f" + "004180080b7f00418088040b7f00418080080b7f0041000b7f0041010b07aa010c066d656d6f72790200115f5f7761" + "736d5f63616c6c5f63746f727300010666696e69736800020c5f5f64736f5f68616e646c6503010a5f5f646174615f" + "656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f62616c5f626173" + "6503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f6261736503080c" + "5f5f7461626c655f6261736503090a3d0202000b3801037f230041106b220024002000410c6a410410002101200028" + "020c2102200041106a2400200141054100200241054f1b20014100481b0b007f0970726f647563657273010c70726f" + "6365737365642d62790105636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875" + "622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235613264623538323935386166316565333038" + "61373930636664623432626432343732302900490f7461726765745f6665617475726573042b0f6d757461626c652d" + "676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; extern std::string const allHostFunctionsWasmHex = - "0061736d0100000001540c60027f7f017f60037f7f7f017f60047f7f7f7f017f60017f017f60067f7f7f7f7f7f017f60037f7f7f0060057f7f" - "7f7f7f017f60037f7f7e017f60087f7f7f7f7f7f7f7f017f60017f0060027f7f006000017f02ae061a08686f73745f6c69620c6765745f7478" - "5f6669656c64000108686f73745f6c69620974726163655f6e756d000708686f73745f6c6962057472616365000608686f73745f6c69620e67" - "65745f6c65646765725f73716e000008686f73745f6c6962166765745f706172656e745f6c65646765725f74696d65000008686f73745f6c69" - "62166765745f706172656e745f6c65646765725f68617368000008686f73745f6c6962136765745f74785f6e65737465645f6669656c640002" - "08686f73745f6c6962106765745f74785f61727261795f6c656e000308686f73745f6c6962176765745f74785f6e65737465645f6172726179" - "5f6c656e000008686f73745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669656c64000108686f73745f6c69622367" - "65745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c64000208686f73745f6c6962206765745f63757272656e74" - "5f6c65646765725f6f626a5f61727261795f6c656e000308686f73745f6c6962276765745f63757272656e745f6c65646765725f6f626a5f6e" - "65737465645f61727261795f6c656e000008686f73745f6c69621063616368655f6c65646765725f6f626a000108686f73745f6c6962116372" - "6564656e7469616c5f6b65796c6574000808686f73745f6c69620d657363726f775f6b65796c6574000408686f73745f6c69620d6f7261636c" - "655f6b65796c6574000408686f73745f6c696213636f6d707574655f7368613531325f68616c66000208686f73745f6c6962076765745f6e66" - "74000408686f73745f6c69620b7570646174655f64617461000008686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c6400" - "0208686f73745f6c69621b6765745f6c65646765725f6f626a5f6e65737465645f6669656c64000608686f73745f6c6962186765745f6c6564" - "6765725f6f626a5f61727261795f6c656e000008686f73745f6c69621f6765745f6c65646765725f6f626a5f6e65737465645f61727261795f" - "6c656e000108686f73745f6c69620e6163636f756e745f6b65796c6574000208686f73745f6c69620d74726163655f6163636f756e74000203" - "0c0b090a05050b05000101030005030100110619037f01418080c0000b7f0041af99c0000b7f0041b099c0000b072e04066d656d6f72790200" - "0666696e697368001e0a5f5f646174615f656e6403010b5f5f686561705f6261736503020ac61d0b990101027f230041306b22012400200002" - "7f418180202001411c6a4114100022024114470440417f20022002417f4e1b210241010c010b200020012f001c3b0001200041036a2001411e" - "6a2d00003a0000200120012900233703082001200141286a29000037000d200128001f21022000410d6a200129000d37000020002001290308" - "37020841000b3a000020002002360204200141306a24000b460020012d00004101460440418080c000410b20013402041001000b2000200129" - "0001370000200041106a200141116a280000360000200041086a200141096a2900003700000b1900200241094f0440000b2000200236020420" - "0020013602000b1900200241214f0440000b20002002360204200020013602000bde1a01097f230041b0036b22002400418b80c000411b4101" - "4100410010021a41a680c000411941014100410010021a41e780c000412b41014100410010021a200041003602700240024002400240024002" - "4002400240200041f0006a220741041003220141004a0440419281c00041172000280270220141187420014180fe0371410874722001410876" - "4180fe037120014118767272ad10011a200041003602900120004190016a220341041004220141004c0d0141a981c000411320002802900122" - "0141187420014180fe03714108747220014108764180fe037120014118767272ad10011a200041c8016a22024200370300200041c0016a2205" - "4200370300200041b8016a22044200370300200042003703b001200041b0016a22064120100522014120470d0241bc81c00041132006412041" - "0110021a41cf81c000412041014100410010021a41dc82c000412e41014100410010021a200041a0016a410036020020004198016a42003703" - "0020004200370390014181802020034114100022014114470d03418a83c00041142003101f2000420037037041888018200741081000220141" - "08470d04419e83c0004117420810011a41b583c000412820074108410110021a2000410036024841848008200041c8006a2203410410002201" - "4104470d0541dd83c000411520034104410110021a200041013b0034200242003703002005420037030020044200370300200042003703b001" - "0240200041346a4102200641201006220141004e044041f283c00041142001ad10011a200041286a20062001101d418684c000410d20002802" - "28200028022c410110021a0c010b419384c00041292001ac10011a0b41bc84c00041154183803c1007ac10011a41d184c00041134189803c10" - "07ac10011a0240200041346a41021008220141004e044041e484c00041142001ad10011a0c010b41f884c000412d2001ac10011a0b41a585c0" - "00412341014100410010021a41de86c000413341014100410010021a2000420037037041828018200041f0006a220141081009220341004c0d" - "0620034108460440419187c000412b420810011a41bc87c000412f20014108410110021a0c080b41eb87c000412f2003ad10011a200041206a" - "200041f0006a2003101c419a88c000411720002802202000280224410110021a0c070b41bf82c000411d2001ac10011a419b7f21020c070b41" - "9a82c00041252001ac10011a419a7f21020c060b41ef81c000412b2001ac10011a41997f21020c050b41b486c000412a2001ac10011a41b77e" - "21020c040b41f385c00041c1002001ac10011a41b67e21020c030b41c885c000412b2001ac10011a41b57e21020c020b41b188c00041c50020" - "03ac10011a0b200041a0016a410036020020004198016a4200370300200042003703900102404181802020004190016a220341141009220141" - "004a044041f688c000411e2003101f0c010b419489c00041332001ac10011a0b200041013b0048200041c8016a4200370300200041c0016a42" - "00370300200041b8016a4200370300200042003703b0010240200041c8006a4102200041b0016a22014120100a220341004e044041c789c000" - "411c2003ad10011a200041186a20012003101d41e389c00041152000280218200028021c410110021a0c010b41f889c00041392003ac10011a" - "0b41b18ac00041244183803c100bac10011a0240200041c8006a4102100c220141004e044041d58ac000411c2001ad10011a0c010b41f18ac0" - "00413d2001ac10011a0b41ae8bc000412841014100410010021a41d68bc000412f41014100410010021a200041b0016a2203101a200041f000" - "6a22012003101b200041a8016a4200370300200041a0016a420037030020004198016a42003703002000420037039001024002400240024002" - "40200120004190016a2203102022014120460440200341204100100d220441004a044041858cc00041232004ad10011a200042003703482004" - "200041c8006a220141081021220341004c0d022003410846044041a88cc000412a420810011a41d28cc000412e20014108410110021a0c060b" - "41808dc000412e2003ad10011a200041106a200041c8006a2003101c41ae8dc000411620002802102000280214410110021a0c050b41e68fc0" - "00413c2004ac10011a200041c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b0014101200041b001" - "6a4120102122014100480d020c030b41ba92c000412e2001ac10011a41ef7c21020c050b41c48dc000412b2003ac10011a0c020b41a290c000" - "41c1002001ac10011a0b200041013b00484101200041c8006a200041b0016a10222201410048044041e390c00041352001ac10011a0b410110" - "2322014100480440419891c00041322001ac10011a0b4101200041c8006a10242201410048044041ca91c00041392001ac10011a0b418392c0" - "00413741014100410010021a0c010b200041013b0034200041c8016a4200370300200041c0016a4200370300200041b8016a42003703002000" - "42003703b00102402004200041346a200041b0016a22011022220341004e044041ef8dc000411b2003ad10011a200041086a20012003101d41" - "8a8ec00041142000280208200028020c410110021a0c010b419e8ec00041312003ac10011a0b41cf8ec000412320041023ac10011a02402004" - "200041346a1024220141004e044041f28ec000411b2001ad10011a0c010b418d8fc00041352001ac10011a0b41c28fc0004124410141004100" - "10021a0b41e892c000412f41014100410010021a200041b0016a2201101a200041346a22042001101b200041e0006a4200370300200041d800" - "6a4200370300200041d0006a420037030020004200370348024002400240024002402004200041c8006a2203102022014120460440419793c0" - "00410f20034120410110021a20004188016a420037030020004180016a4200370300200041f8006a4200370300200042003703700240200441" - "142004411441a693c0004109200041f0006a22014120100e220341004a0440200020012003101d41ae93c00041122000280200200028020441" - "0110021a0c010b41c093c000413c2003ac10011a0b200041a8016a22064200370300200041a0016a2202420037030020004198016a22054200" - "370300200042003703900120004180808cc07e360268200041346a22034114200041e8006a410420004190016a22084120100f22014120470d" - "0141fc93c000410e20084120410110021a200041c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b0" - "01200041808080d00236026c20034114200041ec006a4104200041b0016a22044120101022014120470d02418a94c000410e20044120410110" - "021a419894c000412441014100410010021a419195c000412541014100410010021a20004188016a420037030020004180016a420037030020" - "0041f8006a42003703002000420037037041b695c0004117200041f0006a22034120101122014120470d0341cd95c000410b41b695c0004117" - "410110021a41d895c000411120034120410110021a2004101a200041c8006a22072004101b2006420037030020024200370300200542003703" - "00200042003703900102404100200422026b410371220320026a220520024d0d0020030440200321010340200241003a0000200241016a2102" - "200141016b22010d000b0b200341016b4107490d000340200241003a0000200241076a41003a0000200241066a41003a0000200241056a4100" - "3a0000200241046a41003a0000200241036a41003a0000200241026a41003a0000200241016a41003a0000200241086a22022005470d000b0b" - "200541800220036b2201417c716a220220054b0440034020054100360200200541046a22052002490d000b0b02402002200141037122012002" - "6a22034f0d002001220504400340200241003a0000200241016a2102200541016b22050d000b0b200141016b4107490d000340200241003a00" - "00200241076a41003a0000200241066a41003a0000200241056a41003a0000200241046a41003a0000200241036a41003a0000200241026a41" - "003a0000200241016a41003a0000200241086a22022003470d000b0b0240200741142008412020044180021012220141004a044041e995c000" - "41102001ad10011a20014181024f0d0641f995c000410920042001410110021a0c010b418296c000412e2001ac10011a0b41b096c000411241" - "c296c00041074101100222014100480d0541c996c000411d2001ad10011a41e696c0004111422a1001410048044041ad97c000411a42a47b10" - "011a41a47b21020c070b41f796c000411c420010011a41012102419397c000411a41014100410010021a41ff97c00041294101410041001002" - "1a41a898c000412810132201412846044041d098c000412741a898c0004128410110021a41f798c000411e41014100410010021a41bf80c000" - "412841014100410010021a0c070b419599c000411a2001ac10011a41c37a21020c060b41f494c000411d2001ac10011a418b7c21020c050b41" - "d894c000411c2001ac10011a41897c21020c040b41bc94c000411c2001ac10011a41887c21020c030b41dd97c00041222001ac10011a41a77b" - "21020c020b000b41c797c00041162001ac10011a41a57b21020b200041b0036a240020020b0d00200020012002411410191a0b0c0020004114" - "2001412010180b0e002000418280182001200210140b0e002000200141022002412010150b0a0020004183803c10160b0a0020002001410210" - "170b0bb9190100418080c0000baf196572726f725f636f64653d3d3d3d20484f53542046554e4354494f4e532054455354203d3d3d54657374" - "696e6720323620686f73742066756e6374696f6e73535543434553533a20416c6c20686f73742066756e6374696f6e20746573747320706173" - "736564212d2d2d2043617465676f727920313a204c6564676572204865616465722046756e6374696f6e73202d2d2d4c656467657220736571" - "75656e6365206e756d6265723a506172656e74206c65646765722074696d653a506172656e74206c656467657220686173683a535543434553" - "533a204c6564676572206865616465722066756e6374696f6e734552524f523a206765745f706172656e745f6c65646765725f686173682077" - "726f6e67206c656e6774683a4552524f523a206765745f706172656e745f6c65646765725f74696d65206661696c65643a4552524f523a2067" - "65745f6c65646765725f73716e206661696c65643a2d2d2d2043617465676f727920323a205472616e73616374696f6e20446174612046756e" - "6374696f6e73202d2d2d5472616e73616374696f6e204163636f756e743a5472616e73616374696f6e20466565206c656e6774683a5472616e" - "73616374696f6e20466565202873657269616c697a65642058525020616d6f756e74293a5472616e73616374696f6e2053657175656e63653a" - "4e6573746564206669656c64206c656e6774683a4e6573746564206669656c643a494e464f3a206765745f74785f6e65737465645f6669656c" - "64206e6f74206170706c696361626c653a5369676e657273206172726179206c656e6774683a4d656d6f73206172726179206c656e6774683a" - "4e6573746564206172726179206c656e6774683a494e464f3a206765745f74785f6e65737465645f61727261795f6c656e206e6f7420617070" - "6c696361626c653a535543434553533a205472616e73616374696f6e20646174612066756e6374696f6e734552524f523a206765745f74785f" - "6669656c642853657175656e6365292077726f6e67206c656e6774683a4552524f523a206765745f74785f6669656c6428466565292077726f" - "6e67206c656e67746820286578706563746564203820627974657320666f7220585250293a4552524f523a206765745f74785f6669656c6428" - "4163636f756e74292077726f6e67206c656e6774683a2d2d2d2043617465676f727920333a2043757272656e74204c6564676572204f626a65" - "63742046756e6374696f6e73202d2d2d43757272656e74206f626a6563742062616c616e6365206c656e677468202858525020616d6f756e74" - "293a43757272656e74206f626a6563742062616c616e6365202873657269616c697a65642058525020616d6f756e74293a43757272656e7420" - "6f626a6563742062616c616e6365206c656e67746820286e6f6e2d58525020616d6f756e74293a43757272656e74206f626a6563742062616c" - "616e63653a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6669656c642842616c616e636529206661696c65642028" - "6d6179206265206578706563746564293a43757272656e74206c6564676572206f626a656374206163636f756e743a494e464f3a206765745f" - "63757272656e745f6c65646765725f6f626a5f6669656c64284163636f756e7429206661696c65643a43757272656e74206e65737465642066" - "69656c64206c656e6774683a43757272656e74206e6573746564206669656c643a494e464f3a206765745f63757272656e745f6c6564676572" - "5f6f626a5f6e65737465645f6669656c64206e6f74206170706c696361626c653a43757272656e74206f626a656374205369676e6572732061" - "72726179206c656e6774683a43757272656e74206e6573746564206172726179206c656e6774683a494e464f3a206765745f63757272656e74" - "5f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c696361626c653a535543434553533a2043757272" - "656e74206c6564676572206f626a6563742066756e6374696f6e732d2d2d2043617465676f727920343a20416e79204c6564676572204f626a" - "6563742046756e6374696f6e73202d2d2d5375636365737366756c6c7920636163686564206f626a65637420696e20736c6f743a4361636865" - "64206f626a6563742062616c616e6365206c656e677468202858525020616d6f756e74293a436163686564206f626a6563742062616c616e63" - "65202873657269616c697a65642058525020616d6f756e74293a436163686564206f626a6563742062616c616e6365206c656e67746820286e" - "6f6e2d58525020616d6f756e74293a436163686564206f626a6563742062616c616e63653a494e464f3a206765745f6c65646765725f6f626a" - "5f6669656c642842616c616e636529206661696c65643a436163686564206e6573746564206669656c64206c656e6774683a43616368656420" - "6e6573746564206669656c643a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206e6f74206170706c6963" - "61626c653a436163686564206f626a656374205369676e657273206172726179206c656e6774683a436163686564206e657374656420617272" - "6179206c656e6774683a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c69" - "6361626c653a535543434553533a20416e79206c6564676572206f626a6563742066756e6374696f6e73494e464f3a2063616368655f6c6564" - "6765725f6f626a206661696c65642028657870656374656420776974682074657374206669787475726573293a494e464f3a206765745f6c65" - "646765725f6f626a5f6669656c64206661696c656420617320657870656374656420286e6f20636163686564206f626a656374293a494e464f" - "3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206661696c65642061732065787065637465643a494e464f3a206765" - "745f6c65646765725f6f626a5f61727261795f6c656e206661696c65642061732065787065637465643a494e464f3a206765745f6c65646765" - "725f6f626a5f6e65737465645f61727261795f6c656e206661696c65642061732065787065637465643a535543434553533a20416e79206c65" - "64676572206f626a6563742066756e6374696f6e732028696e7465726661636520746573746564294552524f523a206163636f756e745f6b65" - "796c6574206661696c656420666f722063616368696e6720746573743a2d2d2d2043617465676f727920353a204b65796c65742047656e6572" - "6174696f6e2046756e6374696f6e73202d2d2d4163636f756e74206b65796c65743a546573745479706543726564656e7469616c206b65796c" - "65743a494e464f3a2063726564656e7469616c5f6b65796c6574206661696c656420286578706563746564202d20696e746572666163652069" - "73737565293a457363726f77206b65796c65743a4f7261636c65206b65796c65743a535543434553533a204b65796c65742067656e65726174" - "696f6e2066756e6374696f6e734552524f523a206f7261636c655f6b65796c6574206661696c65643a4552524f523a20657363726f775f6b65" - "796c6574206661696c65643a4552524f523a206163636f756e745f6b65796c6574206661696c65643a2d2d2d2043617465676f727920363a20" - "5574696c6974792046756e6374696f6e73202d2d2d48656c6c6f2c205852504c205741534d20776f726c6421496e70757420646174613a5348" - "413531322068616c6620686173683a4e46542064617461206c656e6774683a4e465420646174613a494e464f3a206765745f6e667420666169" - "6c656420286578706563746564202d206e6f2073756368204e4654293a54657374207472616365206d6573736167657061796c6f6164547261" - "63652066756e6374696f6e206279746573207772697474656e3a54657374206e756d62657220747261636554726163655f6e756d2066756e63" - "74696f6e20737563636565646564535543434553533a205574696c6974792066756e6374696f6e734552524f523a2074726163655f6e756d28" - "29206661696c65643a4552524f523a2074726163652829206661696c65643a4552524f523a20636f6d707574655f7368613531325f68616c66" - "206661696c65643a2d2d2d2043617465676f727920373a2044617461205570646174652046756e6374696f6e73202d2d2d5570646174656420" - "6c656467657220656e74727920646174612066726f6d205741534d20746573745375636365737366756c6c792075706461746564206c656467" - "657220656e74727920776974683a535543434553533a2044617461207570646174652066756e6374696f6e734552524f523a20757064617465" - "5f64617461206661696c65643a004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d62790105" - "72757374631d312e38372e30202831373036376539616320323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d" - "757461626c652d676c6f62616c732b087369676e2d657874"; + "0061736d0100000001540c60027f7f017f60037f7f7f017f60047f7f7f7f017f60017f017f60067f7f7f7f7f7f017f" + "60037f7f7f0060057f7f7f7f7f017f60037f7f7e017f60087f7f7f7f7f7f7f7f017f60017f0060027f7f006000017f" + "02ae061a08686f73745f6c69620c6765745f74785f6669656c64000108686f73745f6c69620974726163655f6e756d" + "000708686f73745f6c6962057472616365000608686f73745f6c69620e6765745f6c65646765725f73716e00000868" + "6f73745f6c6962166765745f706172656e745f6c65646765725f74696d65000008686f73745f6c6962166765745f70" + "6172656e745f6c65646765725f68617368000008686f73745f6c6962136765745f74785f6e65737465645f6669656c" + "64000208686f73745f6c6962106765745f74785f61727261795f6c656e000308686f73745f6c6962176765745f7478" + "5f6e65737465645f61727261795f6c656e000008686f73745f6c69621c6765745f63757272656e745f6c6564676572" + "5f6f626a5f6669656c64000108686f73745f6c6962236765745f63757272656e745f6c65646765725f6f626a5f6e65" + "737465645f6669656c64000208686f73745f6c6962206765745f63757272656e745f6c65646765725f6f626a5f6172" + "7261795f6c656e000308686f73745f6c6962276765745f63757272656e745f6c65646765725f6f626a5f6e65737465" + "645f61727261795f6c656e000008686f73745f6c69621063616368655f6c65646765725f6f626a000108686f73745f" + "6c69621163726564656e7469616c5f6b65796c6574000808686f73745f6c69620d657363726f775f6b65796c657400" + "0408686f73745f6c69620d6f7261636c655f6b65796c6574000408686f73745f6c696213636f6d707574655f736861" + "3531325f68616c66000208686f73745f6c6962076765745f6e6674000408686f73745f6c69620b7570646174655f64" + "617461000008686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000208686f73745f6c69621b" + "6765745f6c65646765725f6f626a5f6e65737465645f6669656c64000608686f73745f6c6962186765745f6c656467" + "65725f6f626a5f61727261795f6c656e000008686f73745f6c69621f6765745f6c65646765725f6f626a5f6e657374" + "65645f61727261795f6c656e000108686f73745f6c69620e6163636f756e745f6b65796c6574000208686f73745f6c" + "69620d74726163655f6163636f756e740002030c0b090a05050b05000101030005030100110619037f01418080c000" + "0b7f0041af99c0000b7f0041b099c0000b072e04066d656d6f727902000666696e697368001e0a5f5f646174615f65" + "6e6403010b5f5f686561705f6261736503020ac61d0b990101027f230041306b220124002000027f41818020200141" + "1c6a4114100022024114470440417f20022002417f4e1b210241010c010b200020012f001c3b0001200041036a2001" + "411e6a2d00003a0000200120012900233703082001200141286a29000037000d200128001f21022000410d6a200129" + "000d3700002000200129030837020841000b3a000020002002360204200141306a24000b460020012d000041014604" + "40418080c000410b20013402041001000b20002001290001370000200041106a200141116a28000036000020004108" + "6a200141096a2900003700000b1900200241094f0440000b20002002360204200020013602000b1900200241214f04" + "40000b20002002360204200020013602000bde1a01097f230041b0036b22002400418b80c000411b41014100410010" + "021a41a680c000411941014100410010021a41e780c000412b41014100410010021a20004100360270024002400240" + "02400240024002400240200041f0006a220741041003220141004a0440419281c00041172000280270220141187420" + "014180fe03714108747220014108764180fe037120014118767272ad10011a200041003602900120004190016a2203" + "41041004220141004c0d0141a981c0004113200028029001220141187420014180fe03714108747220014108764180" + "fe037120014118767272ad10011a200041c8016a22024200370300200041c0016a22054200370300200041b8016a22" + "044200370300200042003703b001200041b0016a22064120100522014120470d0241bc81c000411320064120410110" + "021a41cf81c000412041014100410010021a41dc82c000412e41014100410010021a200041a0016a41003602002000" + "4198016a420037030020004200370390014181802020034114100022014114470d03418a83c00041142003101f2000" + "42003703704188801820074108100022014108470d04419e83c0004117420810011a41b583c0004128200741084101" + "10021a2000410036024841848008200041c8006a22034104100022014104470d0541dd83c000411520034104410110" + "021a200041013b0034200242003703002005420037030020044200370300200042003703b0010240200041346a4102" + "200641201006220141004e044041f283c00041142001ad10011a200041286a20062001101d418684c000410d200028" + "0228200028022c410110021a0c010b419384c00041292001ac10011a0b41bc84c00041154183803c1007ac10011a41" + "d184c00041134189803c1007ac10011a0240200041346a41021008220141004e044041e484c00041142001ad10011a" + "0c010b41f884c000412d2001ac10011a0b41a585c000412341014100410010021a41de86c000413341014100410010" + "021a2000420037037041828018200041f0006a220141081009220341004c0d0620034108460440419187c000412b42" + "0810011a41bc87c000412f20014108410110021a0c080b41eb87c000412f2003ad10011a200041206a200041f0006a" + "2003101c419a88c000411720002802202000280224410110021a0c070b41bf82c000411d2001ac10011a419b7f2102" + "0c070b419a82c00041252001ac10011a419a7f21020c060b41ef81c000412b2001ac10011a41997f21020c050b41b4" + "86c000412a2001ac10011a41b77e21020c040b41f385c00041c1002001ac10011a41b67e21020c030b41c885c00041" + "2b2001ac10011a41b57e21020c020b41b188c00041c5002003ac10011a0b200041a0016a410036020020004198016a" + "4200370300200042003703900102404181802020004190016a220341141009220141004a044041f688c000411e2003" + "101f0c010b419489c00041332001ac10011a0b200041013b0048200041c8016a4200370300200041c0016a42003703" + "00200041b8016a4200370300200042003703b0010240200041c8006a4102200041b0016a22014120100a220341004e" + "044041c789c000411c2003ad10011a200041186a20012003101d41e389c00041152000280218200028021c41011002" + "1a0c010b41f889c00041392003ac10011a0b41b18ac00041244183803c100bac10011a0240200041c8006a4102100c" + "220141004e044041d58ac000411c2001ad10011a0c010b41f18ac000413d2001ac10011a0b41ae8bc0004128410141" + "00410010021a41d68bc000412f41014100410010021a200041b0016a2203101a200041f0006a22012003101b200041" + "a8016a4200370300200041a0016a420037030020004198016a42003703002000420037039001024002400240024002" + "40200120004190016a2203102022014120460440200341204100100d220441004a044041858cc00041232004ad1001" + "1a200042003703482004200041c8006a220141081021220341004c0d022003410846044041a88cc000412a42081001" + "1a41d28cc000412e20014108410110021a0c060b41808dc000412e2003ad10011a200041106a200041c8006a200310" + "1c41ae8dc000411620002802102000280214410110021a0c050b41e68fc000413c2004ac10011a200041c8016a4200" + "370300200041c0016a4200370300200041b8016a4200370300200042003703b0014101200041b0016a412010212201" + "4100480d020c030b41ba92c000412e2001ac10011a41ef7c21020c050b41c48dc000412b2003ac10011a0c020b41a2" + "90c00041c1002001ac10011a0b200041013b00484101200041c8006a200041b0016a10222201410048044041e390c0" + "0041352001ac10011a0b4101102322014100480440419891c00041322001ac10011a0b4101200041c8006a10242201" + "410048044041ca91c00041392001ac10011a0b418392c000413741014100410010021a0c010b200041013b00342000" + "41c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b0010240200420004134" + "6a200041b0016a22011022220341004e044041ef8dc000411b2003ad10011a200041086a20012003101d418a8ec000" + "41142000280208200028020c410110021a0c010b419e8ec00041312003ac10011a0b41cf8ec000412320041023ac10" + "011a02402004200041346a1024220141004e044041f28ec000411b2001ad10011a0c010b418d8fc00041352001ac10" + "011a0b41c28fc000412441014100410010021a0b41e892c000412f41014100410010021a200041b0016a2201101a20" + "0041346a22042001101b200041e0006a4200370300200041d8006a4200370300200041d0006a420037030020004200" + "370348024002400240024002402004200041c8006a2203102022014120460440419793c000410f2003412041011002" + "1a20004188016a420037030020004180016a4200370300200041f8006a420037030020004200370370024020044114" + "2004411441a693c0004109200041f0006a22014120100e220341004a0440200020012003101d41ae93c00041122000" + "2802002000280204410110021a0c010b41c093c000413c2003ac10011a0b200041a8016a22064200370300200041a0" + "016a2202420037030020004198016a22054200370300200042003703900120004180808cc07e360268200041346a22" + "034114200041e8006a410420004190016a22084120100f22014120470d0141fc93c000410e20084120410110021a20" + "0041c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b001200041808080d0" + "0236026c20034114200041ec006a4104200041b0016a22044120101022014120470d02418a94c000410e2004412041" + "0110021a419894c000412441014100410010021a419195c000412541014100410010021a20004188016a4200370300" + "20004180016a4200370300200041f8006a42003703002000420037037041b695c0004117200041f0006a2203412010" + "1122014120470d0341cd95c000410b41b695c0004117410110021a41d895c000411120034120410110021a2004101a" + "200041c8006a22072004101b2006420037030020024200370300200542003703002000420037039001024041002004" + "22026b410371220320026a220520024d0d0020030440200321010340200241003a0000200241016a2102200141016b" + "22010d000b0b200341016b4107490d000340200241003a0000200241076a41003a0000200241066a41003a00002002" + "41056a41003a0000200241046a41003a0000200241036a41003a0000200241026a41003a0000200241016a41003a00" + "00200241086a22022005470d000b0b200541800220036b2201417c716a220220054b04400340200541003602002005" + "41046a22052002490d000b0b024020022001410371220120026a22034f0d002001220504400340200241003a000020" + "0241016a2102200541016b22050d000b0b200141016b4107490d000340200241003a0000200241076a41003a000020" + "0241066a41003a0000200241056a41003a0000200241046a41003a0000200241036a41003a0000200241026a41003a" + "0000200241016a41003a0000200241086a22022003470d000b0b024020074114200841202004418002101222014100" + "4a044041e995c00041102001ad10011a20014181024f0d0641f995c000410920042001410110021a0c010b418296c0" + "00412e2001ac10011a0b41b096c000411241c296c00041074101100222014100480d0541c996c000411d2001ad1001" + "1a41e696c0004111422a1001410048044041ad97c000411a42a47b10011a41a47b21020c070b41f796c000411c4200" + "10011a41012102419397c000411a41014100410010021a41ff97c000412941014100410010021a41a898c000412810" + "132201412846044041d098c000412741a898c0004128410110021a41f798c000411e41014100410010021a41bf80c0" + "00412841014100410010021a0c070b419599c000411a2001ac10011a41c37a21020c060b41f494c000411d2001ac10" + "011a418b7c21020c050b41d894c000411c2001ac10011a41897c21020c040b41bc94c000411c2001ac10011a41887c" + "21020c030b41dd97c00041222001ac10011a41a77b21020c020b000b41c797c00041162001ac10011a41a57b21020b" + "200041b0036a240020020b0d00200020012002411410191a0b0c00200041142001412010180b0e0020004182801820" + "01200210140b0e002000200141022002412010150b0a0020004183803c10160b0a0020002001410210170b0bb91901" + "00418080c0000baf196572726f725f636f64653d3d3d3d20484f53542046554e4354494f4e532054455354203d3d3d" + "54657374696e6720323620686f73742066756e6374696f6e73535543434553533a20416c6c20686f73742066756e63" + "74696f6e20746573747320706173736564212d2d2d2043617465676f727920313a204c656467657220486561646572" + "2046756e6374696f6e73202d2d2d4c65646765722073657175656e6365206e756d6265723a506172656e74206c6564" + "6765722074696d653a506172656e74206c656467657220686173683a535543434553533a204c656467657220686561" + "6465722066756e6374696f6e734552524f523a206765745f706172656e745f6c65646765725f686173682077726f6e" + "67206c656e6774683a4552524f523a206765745f706172656e745f6c65646765725f74696d65206661696c65643a45" + "52524f523a206765745f6c65646765725f73716e206661696c65643a2d2d2d2043617465676f727920323a20547261" + "6e73616374696f6e20446174612046756e6374696f6e73202d2d2d5472616e73616374696f6e204163636f756e743a" + "5472616e73616374696f6e20466565206c656e6774683a5472616e73616374696f6e20466565202873657269616c69" + "7a65642058525020616d6f756e74293a5472616e73616374696f6e2053657175656e63653a4e657374656420666965" + "6c64206c656e6774683a4e6573746564206669656c643a494e464f3a206765745f74785f6e65737465645f6669656c" + "64206e6f74206170706c696361626c653a5369676e657273206172726179206c656e6774683a4d656d6f7320617272" + "6179206c656e6774683a4e6573746564206172726179206c656e6774683a494e464f3a206765745f74785f6e657374" + "65645f61727261795f6c656e206e6f74206170706c696361626c653a535543434553533a205472616e73616374696f" + "6e20646174612066756e6374696f6e734552524f523a206765745f74785f6669656c642853657175656e6365292077" + "726f6e67206c656e6774683a4552524f523a206765745f74785f6669656c6428466565292077726f6e67206c656e67" + "746820286578706563746564203820627974657320666f7220585250293a4552524f523a206765745f74785f666965" + "6c64284163636f756e74292077726f6e67206c656e6774683a2d2d2d2043617465676f727920333a2043757272656e" + "74204c6564676572204f626a6563742046756e6374696f6e73202d2d2d43757272656e74206f626a6563742062616c" + "616e6365206c656e677468202858525020616d6f756e74293a43757272656e74206f626a6563742062616c616e6365" + "202873657269616c697a65642058525020616d6f756e74293a43757272656e74206f626a6563742062616c616e6365" + "206c656e67746820286e6f6e2d58525020616d6f756e74293a43757272656e74206f626a6563742062616c616e6365" + "3a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6669656c642842616c616e636529206661" + "696c656420286d6179206265206578706563746564293a43757272656e74206c6564676572206f626a656374206163" + "636f756e743a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6669656c64284163636f756e" + "7429206661696c65643a43757272656e74206e6573746564206669656c64206c656e6774683a43757272656e74206e" + "6573746564206669656c643a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6e6573746564" + "5f6669656c64206e6f74206170706c696361626c653a43757272656e74206f626a656374205369676e657273206172" + "726179206c656e6774683a43757272656e74206e6573746564206172726179206c656e6774683a494e464f3a206765" + "745f63757272656e745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c69" + "6361626c653a535543434553533a2043757272656e74206c6564676572206f626a6563742066756e6374696f6e732d" + "2d2d2043617465676f727920343a20416e79204c6564676572204f626a6563742046756e6374696f6e73202d2d2d53" + "75636365737366756c6c7920636163686564206f626a65637420696e20736c6f743a436163686564206f626a656374" + "2062616c616e6365206c656e677468202858525020616d6f756e74293a436163686564206f626a6563742062616c61" + "6e6365202873657269616c697a65642058525020616d6f756e74293a436163686564206f626a6563742062616c616e" + "6365206c656e67746820286e6f6e2d58525020616d6f756e74293a436163686564206f626a6563742062616c616e63" + "653a494e464f3a206765745f6c65646765725f6f626a5f6669656c642842616c616e636529206661696c65643a4361" + "63686564206e6573746564206669656c64206c656e6774683a436163686564206e6573746564206669656c643a494e" + "464f3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206e6f74206170706c696361626c653a" + "436163686564206f626a656374205369676e657273206172726179206c656e6774683a436163686564206e65737465" + "64206172726179206c656e6774683a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f61727261" + "795f6c656e206e6f74206170706c696361626c653a535543434553533a20416e79206c6564676572206f626a656374" + "2066756e6374696f6e73494e464f3a2063616368655f6c65646765725f6f626a206661696c65642028657870656374" + "656420776974682074657374206669787475726573293a494e464f3a206765745f6c65646765725f6f626a5f666965" + "6c64206661696c656420617320657870656374656420286e6f20636163686564206f626a656374293a494e464f3a20" + "6765745f6c65646765725f6f626a5f6e65737465645f6669656c64206661696c65642061732065787065637465643a" + "494e464f3a206765745f6c65646765725f6f626a5f61727261795f6c656e206661696c656420617320657870656374" + "65643a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e206661696c6564" + "2061732065787065637465643a535543434553533a20416e79206c6564676572206f626a6563742066756e6374696f" + "6e732028696e7465726661636520746573746564294552524f523a206163636f756e745f6b65796c6574206661696c" + "656420666f722063616368696e6720746573743a2d2d2d2043617465676f727920353a204b65796c65742047656e65" + "726174696f6e2046756e6374696f6e73202d2d2d4163636f756e74206b65796c65743a546573745479706543726564" + "656e7469616c206b65796c65743a494e464f3a2063726564656e7469616c5f6b65796c6574206661696c6564202865" + "78706563746564202d20696e74657266616365206973737565293a457363726f77206b65796c65743a4f7261636c65" + "206b65796c65743a535543434553533a204b65796c65742067656e65726174696f6e2066756e6374696f6e73455252" + "4f523a206f7261636c655f6b65796c6574206661696c65643a4552524f523a20657363726f775f6b65796c65742066" + "61696c65643a4552524f523a206163636f756e745f6b65796c6574206661696c65643a2d2d2d2043617465676f7279" + "20363a205574696c6974792046756e6374696f6e73202d2d2d48656c6c6f2c205852504c205741534d20776f726c64" + "21496e70757420646174613a5348413531322068616c6620686173683a4e46542064617461206c656e6774683a4e46" + "5420646174613a494e464f3a206765745f6e6674206661696c656420286578706563746564202d206e6f2073756368" + "204e4654293a54657374207472616365206d6573736167657061796c6f616454726163652066756e6374696f6e2062" + "79746573207772697474656e3a54657374206e756d62657220747261636554726163655f6e756d2066756e6374696f" + "6e20737563636565646564535543434553533a205574696c6974792066756e6374696f6e734552524f523a20747261" + "63655f6e756d2829206661696c65643a4552524f523a2074726163652829206661696c65643a4552524f523a20636f" + "6d707574655f7368613531325f68616c66206661696c65643a2d2d2d2043617465676f727920373a20446174612055" + "70646174652046756e6374696f6e73202d2d2d55706461746564206c656467657220656e7472792064617461206672" + "6f6d205741534d20746573745375636365737366756c6c792075706461746564206c656467657220656e7472792077" + "6974683a535543434553533a2044617461207570646174652066756e6374696f6e734552524f523a20757064617465" + "5f64617461206661696c65643a004d0970726f64756365727302086c616e6775616765010452757374000c70726f63" + "65737365642d6279010572757374631d312e38372e30202831373036376539616320323032352d30352d303929002c" + "0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d657874"; extern std::string const deepRecursionHex = - "0061736d010000000105016000017f030201000608017f0141c0843d0b070a010666696e69" - "736800000a16011400230045044041010f0b230041016b240010000b"; - -extern std::string const hfPerfTest = - "0061736d0100000001190460057f7f7f7f7f017f60047f7f7f7f017f6000006000017f0236" - "0303656e7609666c6f61745f6c6f67000003656e76057472616365000003656e7612747261" - "63655f6f70617175655f666c6f617400010303020203050301000206aa011c7f0041b0090b" - "7f004193090b7f0041b0080b7f0041c0080b7f0041e0080b7f004180090b7f004180080b7f" - "004184080b7f004188080b7f00418c080b7f004190080b7f004194080b7f004198080b7f00" - "419c080b7f0041a0080b7f0041a4080b7f0041a8080b7f0041ac080b7f00419b090b7f0041" - "80080b7f0041b0110b7f0041b0110b7f0041b091040b7f004180080b7f0041b091040b7f00" - "418080080b7f0041000b7f0041010b0785031f066d656d6f72790200115f5f7761736d5f63" - "616c6c5f63746f727300030666696e697368000403627566030001610301086572725f6865" - "61640302096572725f6461746131030305696e707574030406726573756c74030508495445" - "525f4d4158030609484153485f53495a450307084143435f53495a4503080d43555252454e" - "43595f53495a4503090b4b45594c45545f53495a45030a0a53465f4163636f756e74030b0e" - "53465f44657374696e6174696f6e030c0853465f4d656d6f73030d0753465f4d656d6f030e" - "0b53465f4d656d6f44617461030f0753465f4461746103101753465f417574686f72697a65" - "43726564656e7469616c730311016203120c5f5f64736f5f68616e646c6503130a5f5f6461" - "74615f656e6403140b5f5f737461636b5f6c6f7703150c5f5f737461636b5f686967680316" - "0d5f5f676c6f62616c5f6261736503170b5f5f686561705f6261736503180a5f5f68656170" - "5f656e6403190d5f5f6d656d6f72795f62617365031a0c5f5f7461626c655f62617365031b" - "0a7c0202000b7701017f41807821000340200041b0116a4200370300200041086a22000d00" - "0b41c0843d210002400340419309410841b009418008410010004108460440200041016b22" - "000d010c020b0b41b008410f41c0084113410010011a0b41e0084111419309410810021a41" - "8009411241b009410810021a41010b0b9f0104004180080b53809698002000000014000000" - "1400000020000000010008000300080009000f000a000e000d0007001b0007001a000f0066" - "6c6f61745f6c6f67206572726f7200696e76616c69642072657475726e2073697a650041e0" - "080b11666c6f61745f6c6f6720696e7075743a20004180090b12666c6f61745f6c6f672072" - "6573756c743a20004193090b10d48b29430a256d21d920c49ba5e353f8007f0970726f6475" - "63657273010c70726f6365737365642d62790105636c616e675f31392e312e352d77617369" - "2d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d7072" - "6f6a6563742061623462356132646235383239353861663165653330386137393063666462" - "3432626432343732302900490f7461726765745f6665617475726573042b0f6d757461626c" - "652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b" - "0a6d756c746976616c7565"; + "0061736d010000000105016000017f030201000608017f0141c0843d0b070a010666696e69736800000a1601140023" + "0045044041010f0b230041016b240010000b"; extern std::string const allKeyletsWasmHex = - "0061736d0100000001480960067f7f7f7f7f7f017f60047f7f7f7f017f60087f7f7f7f7f7f7f7f017f60037f7f7f017f60037f7f7e017f6005" - "7f7f7f7f7f017f60057f7f7f7f7f006000017f60037f7f7f000284051808686f73745f6c69620974726163655f6e756d000408686f73745f6c" - "6962057472616365000508686f73745f6c69621063616368655f6c65646765725f6f626a000308686f73745f6c6962146765745f6c65646765" - "725f6f626a5f6669656c64000108686f73745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669656c64000308686f73" - "745f6c69620d74726163655f6163636f756e74000108686f73745f6c69620e6163636f756e745f6b65796c6574000108686f73745f6c69620b" - "6c696e655f6b65796c6574000208686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c69620c636865636b5f6b65796c6574" - "000008686f73745f6c69621163726564656e7469616c5f6b65796c6574000208686f73745f6c69620f64656c65676174655f6b65796c657400" - "0008686f73745f6c6962166465706f7369745f707265617574685f6b65796c6574000008686f73745f6c69620a6469645f6b65796c65740001" - "08686f73745f6c69620d657363726f775f6b65796c6574000008686f73745f6c6962136d70745f69737375616e63655f6b65796c6574000008" - "686f73745f6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c6962106e66745f6f666665725f6b65796c6574000008686f73" - "745f6c69620c6f666665725f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b65796c6574000208686f73745f6c69621a70" - "65726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73745f6c69620e7369676e6572735f6b65796c6574000108686f73" - "745f6c69620d7469636b65745f6b65796c6574000008686f73745f6c69620c7661756c745f6b65796c65740000030403060708050301001106" - "19037f01418080c0000b7f0041ad8ac0000b7f0041b08ac0000b073e05066d656d6f727902000d6f626a6563745f6578697374730018066669" - "6e69736800190a5f5f646174615f656e6403010b5f5f686561705f6261736503020a953403a80502017f017e230041a0016b22052400024020" - "012d0000410146044041c280c000411620012802042201ac10001a200041013a0000200020013602040c010b200541186a200141196a290000" - "370300200541106a200141116a290000370300200541086a200141096a29000037030020052001290001370300200220032005412041011001" - "1a02402005412041001002220141004a04402004450440418b80c000410f4285801410001a20014185801420054180016a4120100322014120" - "47044041a680c0004115417f20012001417f4e1b2201ac10001a200041013a0000200020013602040c040b200541c2006a20054182016a2d00" - "003a0000200541f0006a20054197016a2900002206370300200541286a22012005418f016a290000370300200541306a220220063703002005" - "41386a22032005419f016a2d00003a0000200520052f0080013b014020052005290087013703202005200528008301360043200541df006a20" - "032d00003a0000200541d7006a2002290300370000200541cf006a200129030037000020052005290320370047419a80c000410c200541406b" - "4120410110011a0c020b418b80c000410f2004ac10001a2001200420054180016a411410032201411447044041a680c0004115417f20012001" - "417f4e1b2201ac10001a200041013a0000200020013602040c030b200541c2006a20054182016a2d00003a0000200520052900870137036020" - "052005418c016a290000370065200520052f0080013b0140200520052903603703202005200529006537002520052005280083013600432005" - "41cc006a200529002537000020052005290320370047419a80c000410c200541406b4114410110011a0c010b41bb80c00041072001ac10001a" - "200041013a0000200020013602040c010b20004180023b01000b200541a0016a24000be92902087f027e23004180076b2200240041d880c000" - "412341014100410010011a02402000027f02404181802020004188016a22024114100422014114460440200041066a2000418a016a22032d00" - "003a00002000200029008f013703e001200020004194016a22042900003700e501200020002f0088013b0104200020002903e0013703d80620" - "0020002900e5013700dd062000200028008b01360007200041106a20002900dd06370000200020002903d80637000b41fb80c0004108200041" - "046a2205411410051a4183802020024114100422014114470d032000411a6a20032d00003a00002000200029008f013703e001200020042900" - "003700e501200020002f0088013b0118200020002903e0013703d806200020002900e5013700dd062000200028008b0136001b200041246a20" - "002900dd06370000200020002903d80637001f418381c000410c200041186a411410051a200041a0016a2203420037030020004198016a2204" - "420037030020004190016a420037030020004200370388012005411420024120100622014120460d0102402001410048044020002001360230" - "0c010b2000417f3602300b41010c020b0c020b200041c5006a20032903003700002000413d6a2004290300370000200041356a20004190016a" - "290300370000200020002903880137002d41000b3a002c20004188016a2000412c6a418f81c0004107418180201018024020002d0088014101" - "460440200028028c01210141878ac0004112420510001a0c010b41002101419681c000413541014100410010011a200041de006a41c4003a00" - "00200041d8006a4100360200200041e3006a41003a0000200041d5a6013b015c200042003703502000410036005f200041a0016a2203420037" - "030020004198016a2204420037030020004190016a2205420037030020004200370388010240200041046a4114200041186a4114200041d000" - "6a411420004188016a4120100722024120470440024020024100480440200020023602680c010b2000417f3602680b410121010c010b200041" - "fd006a2003290300370000200041f5006a2004290300370000200041ed006a200529030037000020002000290388013700650b200020013a00" - "6420004188016a200041e4006a41cb81c00041094100101820002d0088014101460440200028028c01210141878ac0004112420510001a0c01" - "0b41d481c000413741014100410010011a200041f0016a200041286a2203280100360200200041e8016a200041206a22042901003703002000" - "41fc016a200041d8006a290300220837020020004184026a200041e0006a2802002202360200200020002901183703e0012000200029035022" - "093702f401200041e8066a22012002360200200041e0066a22022008370300200020093703d806200041f4066a2004290100370200200041fc" - "066a2003280100360200200020002901183702ec0620004188026a200041d8066a22034128101a2000418c016a200041e0016a41d000101a20" - "00410136028801200041f0066a220442003703002001420037030020024200370300200042003703d8062000027f41998ac0004114200041b4" - "016a412820034120100822034120470440024020034100480440200020033602e4010c010b2000417f3602e4010b41010c010b200041f9016a" - "2004290300370000200041f1016a2001290300370000200041e9016a2002290300370000200020002903d8063700e10141000b3a00e0012000" - "41b4026a200041e0016a418b82c000410341818020101820002d00b402410146044020002802b802210141878ac0004112420610001a0c010b" - "41002101418e82c000413141014100410010011a200041063602d806200041f8016a22034200370300200041f0016a22044200370300200041" - "e8016a22054200370300200042003703e0010240200041046a4114200041d8066a4104200041e0016a41201009220241204704400240200241" - "00480440200020023602c0020c010b2000417f3602c0020b410121010c010b200041d5026a2003290300370000200041cd026a200429030037" - "0000200041c5026a2005290300370000200020002903e0013700bd020b200020013a00bc02200041e0016a200041bc026a41bf82c000410541" - "818020101820002d00e001410146044020002802e401210141878ac0004112420610001a0c010b4100210141c482c000413341014100410010" - "011a200041f8016a22034200370300200041f0016a22044200370300200041e8016a22054200370300200042003703e0010240200041046a22" - "0241142002411441f782c0004112200041e0016a4120100a22024120470440024020024100480440200020023602e4020c010b2000417f3602" - "e4020b410121010c010b200041f9026a2003290300370000200041f1026a2004290300370000200041e9026a20052903003700002000200029" - "03e0013700e1020b200020013a00e002200041e0016a200041e0026a418983c000410a41988020101820002d00e001410146044020002802e4" - "01210141878ac0004112420710001a0c010b41002101419383c000413841014100410010011a200041f8016a22034200370300200041f0016a" - "22044200370300200041e8016a22054200370300200042003703e0010240200041046a4114200041186a4114200041e0016a4120100b220241" - "2047044002402002410048044020002002360288030c010b2000417f360288030b410121010c010b2000419d036a2003290300370000200041" - "95036a20042903003700002000418d036a2005290300370000200020002903e001370085030b200020013a008403200041e0016a2000418403" - "6a41cb83c000410841818020101820002d00e001410146044020002802e401210141878ac0004112420810001a0c010b41d383c00041364101" - "4100410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141086a220642003703002001420037" - "0300200041a8036a2202027f200041046a4114200041186a411420014120100c22034120470440024020034100480440200220033602040c01" - "0b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a2005290300370000200241096a2006" - "29030037000041000b3a0000200141206a2400200041e0016a2002418984c000410e41818020101820002d00e001410146044020002802e401" - "210141878ac0004112420910001a0c010b419784c000413c41014100410010011a230041206b22012400200141186a22044200370300200141" - "106a22054200370300200141086a2206420037030020014200370300200041cc036a2202027f200041046a411420014120100d220341204704" - "40024020034100480440200220033602040c010b2002417f3602040b41010c010b20022001290300370001200241196a200429030037000020" - "0241116a2005290300370000200241096a200629030037000041000b3a0000200141206a2400200041e0016a200241d384c000410341818020" - "101820002d00e001410146044020002802e401210141878ac0004112420a10001a0c010b41d684c000413141014100410010011a230041306b" - "220124002001410b36020c200141286a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041" - "f0036a2202027f200041046a41142001410c6a4104200141106a4120100e22034120470440024020034100480440200220033602040c010b20" - "02417f3602040b41010c010b20022001290310370001200241196a2004290300370000200241116a2005290300370000200241096a20062903" - "0037000041000b3a0000200141306a2400200041e0016a2002418785c000410641818020101820002d00e001410146044020002802e4012101" - "41878ac0004112420b10001a0c010b418d85c000413441014100410010011a230041306b220124002001410c36020c200141286a2204420037" - "0300200141206a22054200370300200141186a220642003703002001420037031020004194046a2202027f200041046a41142001410c6a4104" - "200141106a4120100f22034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b200220012903103700" - "01200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a2400200041f4" - "016a200041146a280100360200200041ec016a2000410c6a290100370200200020002901043702e401200041808080e0003602e001200041d8" - "066a200241c185c000410b41848020101820002d00d806410146044020002802dc06210141878ac0004112420c10001a0c010b41cc85c00041" - "3941014100410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141086a220642003703002001" - "4200370300200041b8046a2202027f200041e0016a4118200041186a4114200141201010220341204704400240200341004804402002200336" - "02040c010b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a2005290300370000200241" - "096a200629030037000041000b3a0000200141206a2400200041d8066a2002418586c000410741818020101820002d00d80641014604402000" - "2802dc06210141878ac0004112420d10001a0c010b418c86c000413541014100410010011a230041306b220124002001410636020c20014128" - "6a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041dc046a2202027f200041186a411420" - "01410c6a4104200141106a4120101122034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b200220" - "01290310370001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a" - "2400200041d8066a200241c186c000410c41828020101820002d00d806410146044020002802dc06210141878ac0004112420d10001a0c010b" - "41cd86c000413a41014100410010011a230041306b220124002001410d36020c200141286a22044200370300200141206a2205420037030020" - "0141186a220642003703002001420037031020004180056a2202027f200041046a41142001410c6a4104200141106a41201012220341204704" - "40024020034100480440200220033602040c010b2002417f3602040b41010c010b20022001290310370001200241196a200429030037000020" - "0241116a2005290300370000200241096a200629030037000041000b3a0000200141306a2400200041d8066a2002418787c000410541818020" - "101820002d00d806410146044020002802dc06210141878ac0004112420d10001a0c010b418c87c000413341014100410010011a230041306b" - "220124002001410e36020c200141286a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041" - "a4056a2202027f200041046a4114200041186a41142001410c6a4104200141106a412010132203412047044002402003410048044020022003" - "3602040c010b2002417f3602040b41010c010b20022001290310370001200241196a2004290300370000200241116a20052903003700002002" - "41096a200629030037000041000b3a0000200141306a2400200041d8066a200241bf87c000410a41818020101820002d00d806410146044020" - "002802dc06210141878ac0004112420e10001a0c010b41c987c000413841014100410010011a230041306b220124002001410f36020c200141" - "286a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041c8056a2202027f200041046a4114" - "2001410c6a4104200141106a4120101422034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b2002" - "2001290310370001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a000020014130" - "6a2400200041d8066a2002418188c000411241828020101820002d00d806410146044020002802dc06210141878ac0004112420f10001a0c01" - "0b419388c00041c00041014100410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141086a22" - "06420037030020014200370300200041ec056a2202027f200041046a4114200141201015220341204704400240200341004804402002200336" - "02040c010b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a2005290300370000200241" - "096a200629030037000041000b3a0000200141206a2400200041d8066a200241d388c000410a4100101820002d00d806410146044020002802" - "dc06210141878ac0004112421010001a0c010b41dd88c000413841014100410010011a230041306b220124002001411236020c200141286a22" - "044200370300200141206a22054200370300200141186a220642003703002001420037031020004190066a2202027f200041046a4114200141" - "0c6a4104200141106a4120101622034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b2002200129" - "0310370001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a2400" - "200041d8066a2002419589c000410641818020101820002d00d806410146044020002802dc06210141878ac0004112421210001a0c010b4101" - "2101419b89c000413441014100410010011a230041306b220224002002411336020c200241286a22054200370300200241206a220642003703" - "00200241186a2207420037030020024200370310200041b4066a2203027f200041046a41142002410c6a4104200241106a4120101722044120" - "470440024020044100480440200320043602040c010b2003417f3602040b41010c010b20032002290310370001200341196a20052903003700" - "00200341116a2006290300370000200341096a200729030037000041000b3a0000200241306a2400200041d8066a200341cf89c00041054181" - "8020101820002d00d806410146044020002802dc06210141878ac0004112421310001a0c010b41d489c000413341014100410010011a0b2000" - "4180076a240020010f0b418080c000410b417f20012001417f4e1bac1000000bfd0401067f200241104f0440024020002000410020006b4103" - "7122056a22044f0d002001210320050440200521060340200020032d00003a0000200341016a2103200041016a2100200641016b22060d000b" - "0b200541016b4107490d000340200020032d00003a0000200041016a200341016a2d00003a0000200041026a200341026a2d00003a00002000" - "41036a200341036a2d00003a0000200041046a200341046a2d00003a0000200041056a200341056a2d00003a0000200041066a200341066a2d" - "00003a0000200041076a200341076a2d00003a0000200341086a2103200041086a22002004470d000b0b2004200220056b2207417c7122086a" - "21000240200120056a2206410371450440200020044d0d0120062101034020042001280200360200200141046a2101200441046a2204200049" - "0d000b0c010b200020044d0d002006410374220541187121032006417c71220241046a2101410020056b411871210520022802002102034020" - "0420022003762001280200220220057472360200200141046a2101200441046a22042000490d000b0b20074103712102200620086a21010b02" - "402000200020026a22064f0d002002410771220304400340200020012d00003a0000200141016a2101200041016a2100200341016b22030d00" - "0b0b200241016b4107490d000340200020012d00003a0000200041016a200141016a2d00003a0000200041026a200141026a2d00003a000020" - "0041036a200141036a2d00003a0000200041046a200141046a2d00003a0000200041056a200141056a2d00003a0000200041066a200141066a" - "2d00003a0000200041076a200141076a2d00003a0000200141086a2101200041086a22002006470d000b0b0b0ba30a0100418080c0000b990a" - "6572726f725f636f64653d47657474696e67206669656c643a204669656c6420646174613a204572726f722067657474696e67206669656c64" - "3a204572726f723a204572726f722067657474696e67206b65796c65743a202424242424205354415254494e47205741534d20455845435554" - "494f4e2024242424244163636f756e743a44657374696e6174696f6e3a4163636f756e744163636f756e74206f626a65637420657869737473" - "2c2070726f63656564696e67207769746820657363726f772066696e6973682e54727573746c696e6554727573746c696e65206f626a656374" - "206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e414d4d414d4d206f626a6563742065786973" - "74732c2070726f63656564696e67207769746820657363726f772066696e6973682e436865636b436865636b206f626a656374206578697374" - "732c2070726f63656564696e67207769746820657363726f772066696e6973682e7465726d73616e64636f6e646974696f6e7343726564656e" - "7469616c43726564656e7469616c206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e69" - "73682e44656c656761746544656c6567617465206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f77" - "2066696e6973682e4465706f736974507265617574684465706f73697450726561757468206f626a656374206578697374732c2070726f6365" - "6564696e67207769746820657363726f772066696e6973682e444944444944206f626a656374206578697374732c2070726f63656564696e67" - "207769746820657363726f772066696e6973682e457363726f77457363726f77206f626a656374206578697374732c2070726f63656564696e" - "67207769746820657363726f772066696e6973682e4d505449737375616e63654d505449737375616e6365206f626a65637420657869737473" - "2c2070726f63656564696e67207769746820657363726f772066696e6973682e4d50546f6b656e4d50546f6b656e206f626a65637420657869" - "7374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4e46546f6b656e4f666665724e46546f6b656e4f6666" - "6572206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4f666665724f666665" - "72206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e5061794368616e6e656c" - "5061794368616e6e656c206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e50" - "65726d697373696f6e6564446f6d61696e5065726d697373696f6e6564446f6d61696e206f626a656374206578697374732c2070726f636565" - "64696e67207769746820657363726f772066696e6973682e5369676e65724c6973745369676e65724c697374206f626a656374206578697374" - "732c2070726f63656564696e67207769746820657363726f772066696e6973682e5469636b65745469636b6574206f626a6563742065786973" - "74732c2070726f63656564696e67207769746820657363726f772066696e6973682e5661756c745661756c74206f626a656374206578697374" - "732c2070726f63656564696e67207769746820657363726f772066696e6973682e43757272656e74207365712076616c75653a004d0970726f" - "64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e38372e3020283137303637" - "6539616320323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b08736967" - "6e2d657874"; + "0061736d0100000001480960067f7f7f7f7f7f017f60047f7f7f7f017f60087f7f7f7f7f7f7f7f017f60037f7f7f01" + "7f60037f7f7e017f60057f7f7f7f7f017f60057f7f7f7f7f006000017f60037f7f7f000284051808686f73745f6c69" + "620974726163655f6e756d000408686f73745f6c6962057472616365000508686f73745f6c69621063616368655f6c" + "65646765725f6f626a000308686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000108686f73" + "745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669656c64000308686f73745f6c69620d74" + "726163655f6163636f756e74000108686f73745f6c69620e6163636f756e745f6b65796c6574000108686f73745f6c" + "69620b6c696e655f6b65796c6574000208686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c6962" + "0c636865636b5f6b65796c6574000008686f73745f6c69621163726564656e7469616c5f6b65796c6574000208686f" + "73745f6c69620f64656c65676174655f6b65796c6574000008686f73745f6c6962166465706f7369745f7072656175" + "74685f6b65796c6574000008686f73745f6c69620a6469645f6b65796c6574000108686f73745f6c69620d65736372" + "6f775f6b65796c6574000008686f73745f6c6962136d70745f69737375616e63655f6b65796c6574000008686f7374" + "5f6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c6962106e66745f6f666665725f6b65796c6574" + "000008686f73745f6c69620c6f666665725f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b6579" + "6c6574000208686f73745f6c69621a7065726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73" + "745f6c69620e7369676e6572735f6b65796c6574000108686f73745f6c69620d7469636b65745f6b65796c65740000" + "08686f73745f6c69620c7661756c745f6b65796c6574000003040306070805030100110619037f01418080c0000b7f" + "0041ad8ac0000b7f0041b08ac0000b073e05066d656d6f727902000d6f626a6563745f65786973747300180666696e" + "69736800190a5f5f646174615f656e6403010b5f5f686561705f6261736503020a953403a80502017f017e230041a0" + "016b22052400024020012d0000410146044041c280c000411620012802042201ac10001a200041013a000020002001" + "3602040c010b200541186a200141196a290000370300200541106a200141116a290000370300200541086a20014109" + "6a290000370300200520012900013703002002200320054120410110011a02402005412041001002220141004a0440" + "2004450440418b80c000410f4285801410001a20014185801420054180016a412010032201412047044041a680c000" + "4115417f20012001417f4e1b2201ac10001a200041013a0000200020013602040c040b200541c2006a20054182016a" + "2d00003a0000200541f0006a20054197016a2900002206370300200541286a22012005418f016a2900003703002005" + "41306a22022006370300200541386a22032005419f016a2d00003a0000200520052f0080013b014020052005290087" + "013703202005200528008301360043200541df006a20032d00003a0000200541d7006a2002290300370000200541cf" + "006a200129030037000020052005290320370047419a80c000410c200541406b4120410110011a0c020b418b80c000" + "410f2004ac10001a2001200420054180016a411410032201411447044041a680c0004115417f20012001417f4e1b22" + "01ac10001a200041013a0000200020013602040c030b200541c2006a20054182016a2d00003a000020052005290087" + "0137036020052005418c016a290000370065200520052f0080013b0140200520052903603703202005200529006537" + "00252005200528008301360043200541cc006a200529002537000020052005290320370047419a80c000410c200541" + "406b4114410110011a0c010b41bb80c00041072001ac10001a200041013a0000200020013602040c010b2000418002" + "3b01000b200541a0016a24000be92902087f027e23004180076b2200240041d880c000412341014100410010011a02" + "402000027f02404181802020004188016a22024114100422014114460440200041066a2000418a016a22032d00003a" + "00002000200029008f013703e001200020004194016a22042900003700e501200020002f0088013b01042000200029" + "03e0013703d806200020002900e5013700dd062000200028008b01360007200041106a20002900dd06370000200020" + "002903d80637000b41fb80c0004108200041046a2205411410051a4183802020024114100422014114470d03200041" + "1a6a20032d00003a00002000200029008f013703e001200020042900003700e501200020002f0088013b0118200020" + "002903e0013703d806200020002900e5013700dd062000200028008b0136001b200041246a20002900dd0637000020" + "0020002903d80637001f418381c000410c200041186a411410051a200041a0016a2203420037030020004198016a22" + "04420037030020004190016a420037030020004200370388012005411420024120100622014120460d010240200141" + "00480440200020013602300c010b2000417f3602300b41010c020b0c020b200041c5006a2003290300370000200041" + "3d6a2004290300370000200041356a20004190016a290300370000200020002903880137002d41000b3a002c200041" + "88016a2000412c6a418f81c0004107418180201018024020002d0088014101460440200028028c01210141878ac000" + "4112420510001a0c010b41002101419681c000413541014100410010011a200041de006a41c4003a0000200041d800" + "6a4100360200200041e3006a41003a0000200041d5a6013b015c200042003703502000410036005f200041a0016a22" + "03420037030020004198016a2204420037030020004190016a2205420037030020004200370388010240200041046a" + "4114200041186a4114200041d0006a411420004188016a412010072202412047044002402002410048044020002002" + "3602680c010b2000417f3602680b410121010c010b200041fd006a2003290300370000200041f5006a200429030037" + "0000200041ed006a200529030037000020002000290388013700650b200020013a006420004188016a200041e4006a" + "41cb81c00041094100101820002d0088014101460440200028028c01210141878ac0004112420510001a0c010b41d4" + "81c000413741014100410010011a200041f0016a200041286a2203280100360200200041e8016a200041206a220429" + "0100370300200041fc016a200041d8006a290300220837020020004184026a200041e0006a28020022023602002000" + "20002901183703e0012000200029035022093702f401200041e8066a22012002360200200041e0066a220220083703" + "00200020093703d806200041f4066a2004290100370200200041fc066a2003280100360200200020002901183702ec" + "0620004188026a200041d8066a22034128101a2000418c016a200041e0016a41d000101a2000410136028801200041" + "f0066a220442003703002001420037030020024200370300200042003703d8062000027f41998ac0004114200041b4" + "016a412820034120100822034120470440024020034100480440200020033602e4010c010b2000417f3602e4010b41" + "010c010b200041f9016a2004290300370000200041f1016a2001290300370000200041e9016a200229030037000020" + "0020002903d8063700e10141000b3a00e001200041b4026a200041e0016a418b82c000410341818020101820002d00" + "b402410146044020002802b802210141878ac0004112420610001a0c010b41002101418e82c0004131410141004100" + "10011a200041063602d806200041f8016a22034200370300200041f0016a22044200370300200041e8016a22054200" + "370300200042003703e0010240200041046a4114200041d8066a4104200041e0016a41201009220241204704400240" + "20024100480440200020023602c0020c010b2000417f3602c0020b410121010c010b200041d5026a20032903003700" + "00200041cd026a2004290300370000200041c5026a2005290300370000200020002903e0013700bd020b200020013a" + "00bc02200041e0016a200041bc026a41bf82c000410541818020101820002d00e001410146044020002802e4012101" + "41878ac0004112420610001a0c010b4100210141c482c000413341014100410010011a200041f8016a220342003703" + "00200041f0016a22044200370300200041e8016a22054200370300200042003703e0010240200041046a2202411420" + "02411441f782c0004112200041e0016a4120100a22024120470440024020024100480440200020023602e4020c010b" + "2000417f3602e4020b410121010c010b200041f9026a2003290300370000200041f1026a2004290300370000200041" + "e9026a2005290300370000200020002903e0013700e1020b200020013a00e002200041e0016a200041e0026a418983" + "c000410a41988020101820002d00e001410146044020002802e401210141878ac0004112420710001a0c010b410021" + "01419383c000413841014100410010011a200041f8016a22034200370300200041f0016a22044200370300200041e8" + "016a22054200370300200042003703e0010240200041046a4114200041186a4114200041e0016a4120100b22024120" + "47044002402002410048044020002002360288030c010b2000417f360288030b410121010c010b2000419d036a2003" + "29030037000020004195036a20042903003700002000418d036a2005290300370000200020002903e001370085030b" + "200020013a008403200041e0016a20004184036a41cb83c000410841818020101820002d00e0014101460440200028" + "02e401210141878ac0004112420810001a0c010b41d383c000413641014100410010011a230041206b220124002001" + "41186a22044200370300200141106a22054200370300200141086a2206420037030020014200370300200041a8036a" + "2202027f200041046a4114200041186a411420014120100c2203412047044002402003410048044020022003360204" + "0c010b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a200529" + "0300370000200241096a200629030037000041000b3a0000200141206a2400200041e0016a2002418984c000410e41" + "818020101820002d00e001410146044020002802e401210141878ac0004112420910001a0c010b419784c000413c41" + "014100410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141086a22" + "06420037030020014200370300200041cc036a2202027f200041046a411420014120100d2203412047044002402003" + "4100480440200220033602040c010b2002417f3602040b41010c010b20022001290300370001200241196a20042903" + "00370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141206a2400200041e0" + "016a200241d384c000410341818020101820002d00e001410146044020002802e401210141878ac0004112420a1000" + "1a0c010b41d684c000413141014100410010011a230041306b220124002001410b36020c200141286a220442003703" + "00200141206a22054200370300200141186a2206420037030020014200370310200041f0036a2202027f200041046a" + "41142001410c6a4104200141106a4120100e22034120470440024020034100480440200220033602040c010b200241" + "7f3602040b41010c010b20022001290310370001200241196a2004290300370000200241116a200529030037000020" + "0241096a200629030037000041000b3a0000200141306a2400200041e0016a2002418785c000410641818020101820" + "002d00e001410146044020002802e401210141878ac0004112420b10001a0c010b418d85c000413441014100410010" + "011a230041306b220124002001410c36020c200141286a22044200370300200141206a22054200370300200141186a" + "220642003703002001420037031020004194046a2202027f200041046a41142001410c6a4104200141106a4120100f" + "22034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b2002200129031037" + "0001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000" + "200141306a2400200041f4016a200041146a280100360200200041ec016a2000410c6a290100370200200020002901" + "043702e401200041808080e0003602e001200041d8066a200241c185c000410b41848020101820002d00d806410146" + "044020002802dc06210141878ac0004112420c10001a0c010b41cc85c000413941014100410010011a230041206b22" + "012400200141186a22044200370300200141106a22054200370300200141086a220642003703002001420037030020" + "0041b8046a2202027f200041e0016a4118200041186a41142001412010102203412047044002402003410048044020" + "0220033602040c010b2002417f3602040b41010c010b20022001290300370001200241196a20042903003700002002" + "41116a2005290300370000200241096a200629030037000041000b3a0000200141206a2400200041d8066a20024185" + "86c000410741818020101820002d00d806410146044020002802dc06210141878ac0004112420d10001a0c010b418c" + "86c000413541014100410010011a230041306b220124002001410636020c200141286a22044200370300200141206a" + "22054200370300200141186a2206420037030020014200370310200041dc046a2202027f200041186a41142001410c" + "6a4104200141106a4120101122034120470440024020034100480440200220033602040c010b2002417f3602040b41" + "010c010b20022001290310370001200241196a2004290300370000200241116a2005290300370000200241096a2006" + "29030037000041000b3a0000200141306a2400200041d8066a200241c186c000410c41828020101820002d00d80641" + "0146044020002802dc06210141878ac0004112420d10001a0c010b41cd86c000413a41014100410010011a23004130" + "6b220124002001410d36020c200141286a22044200370300200141206a22054200370300200141186a220642003703" + "002001420037031020004180056a2202027f200041046a41142001410c6a4104200141106a41201012220341204704" + "40024020034100480440200220033602040c010b2002417f3602040b41010c010b2002200129031037000120024119" + "6a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a24" + "00200041d8066a2002418787c000410541818020101820002d00d806410146044020002802dc06210141878ac00041" + "12420d10001a0c010b418c87c000413341014100410010011a230041306b220124002001410e36020c200141286a22" + "044200370300200141206a22054200370300200141186a2206420037030020014200370310200041a4056a2202027f" + "200041046a4114200041186a41142001410c6a4104200141106a412010132203412047044002402003410048044020" + "0220033602040c010b2002417f3602040b41010c010b20022001290310370001200241196a20042903003700002002" + "41116a2005290300370000200241096a200629030037000041000b3a0000200141306a2400200041d8066a200241bf" + "87c000410a41818020101820002d00d806410146044020002802dc06210141878ac0004112420e10001a0c010b41c9" + "87c000413841014100410010011a230041306b220124002001410f36020c200141286a22044200370300200141206a" + "22054200370300200141186a2206420037030020014200370310200041c8056a2202027f200041046a41142001410c" + "6a4104200141106a4120101422034120470440024020034100480440200220033602040c010b2002417f3602040b41" + "010c010b20022001290310370001200241196a2004290300370000200241116a2005290300370000200241096a2006" + "29030037000041000b3a0000200141306a2400200041d8066a2002418188c000411241828020101820002d00d80641" + "0146044020002802dc06210141878ac0004112420f10001a0c010b419388c00041c00041014100410010011a230041" + "206b22012400200141186a22044200370300200141106a22054200370300200141086a220642003703002001420037" + "0300200041ec056a2202027f200041046a411420014120101522034120470440024020034100480440200220033602" + "040c010b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a2005" + "290300370000200241096a200629030037000041000b3a0000200141206a2400200041d8066a200241d388c000410a" + "4100101820002d00d806410146044020002802dc06210141878ac0004112421010001a0c010b41dd88c00041384101" + "4100410010011a230041306b220124002001411236020c200141286a22044200370300200141206a22054200370300" + "200141186a220642003703002001420037031020004190066a2202027f200041046a41142001410c6a410420014110" + "6a4120101622034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b200220" + "01290310370001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041" + "000b3a0000200141306a2400200041d8066a2002419589c000410641818020101820002d00d8064101460440200028" + "02dc06210141878ac0004112421210001a0c010b41012101419b89c000413441014100410010011a230041306b2202" + "24002002411336020c200241286a22054200370300200241206a22064200370300200241186a220742003703002002" + "4200370310200041b4066a2203027f200041046a41142002410c6a4104200241106a41201017220441204704400240" + "20044100480440200320043602040c010b2003417f3602040b41010c010b20032002290310370001200341196a2005" + "290300370000200341116a2006290300370000200341096a200729030037000041000b3a0000200241306a24002000" + "41d8066a200341cf89c000410541818020101820002d00d806410146044020002802dc06210141878ac00041124213" + "10001a0c010b41d489c000413341014100410010011a0b20004180076a240020010f0b418080c000410b417f200120" + "01417f4e1bac1000000bfd0401067f200241104f0440024020002000410020006b41037122056a22044f0d00200121" + "0320050440200521060340200020032d00003a0000200341016a2103200041016a2100200641016b22060d000b0b20" + "0541016b4107490d000340200020032d00003a0000200041016a200341016a2d00003a0000200041026a200341026a" + "2d00003a0000200041036a200341036a2d00003a0000200041046a200341046a2d00003a0000200041056a20034105" + "6a2d00003a0000200041066a200341066a2d00003a0000200041076a200341076a2d00003a0000200341086a210320" + "0041086a22002004470d000b0b2004200220056b2207417c7122086a21000240200120056a22064103714504402000" + "20044d0d0120062101034020042001280200360200200141046a2101200441046a22042000490d000b0c010b200020" + "044d0d002006410374220541187121032006417c71220241046a2101410020056b4118712105200228020021020340" + "200420022003762001280200220220057472360200200141046a2101200441046a22042000490d000b0b2007410371" + "2102200620086a21010b02402000200020026a22064f0d002002410771220304400340200020012d00003a00002001" + "41016a2101200041016a2100200341016b22030d000b0b200241016b4107490d000340200020012d00003a00002000" + "41016a200141016a2d00003a0000200041026a200141026a2d00003a0000200041036a200141036a2d00003a000020" + "0041046a200141046a2d00003a0000200041056a200141056a2d00003a0000200041066a200141066a2d00003a0000" + "200041076a200141076a2d00003a0000200141086a2101200041086a22002006470d000b0b0b0ba30a0100418080c0" + "000b990a6572726f725f636f64653d47657474696e67206669656c643a204669656c6420646174613a204572726f72" + "2067657474696e67206669656c643a204572726f723a204572726f722067657474696e67206b65796c65743a202424" + "242424205354415254494e47205741534d20455845435554494f4e2024242424244163636f756e743a44657374696e" + "6174696f6e3a4163636f756e744163636f756e74206f626a656374206578697374732c2070726f63656564696e6720" + "7769746820657363726f772066696e6973682e54727573746c696e6554727573746c696e65206f626a656374206578" + "697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e414d4d414d4d206f626a65" + "6374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e436865636b43" + "6865636b206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e69" + "73682e7465726d73616e64636f6e646974696f6e7343726564656e7469616c43726564656e7469616c206f626a6563" + "74206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e44656c65676174" + "6544656c6567617465206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f77" + "2066696e6973682e4465706f736974507265617574684465706f73697450726561757468206f626a65637420657869" + "7374732c2070726f63656564696e67207769746820657363726f772066696e6973682e444944444944206f626a6563" + "74206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e457363726f7745" + "7363726f77206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e" + "6973682e4d505449737375616e63654d505449737375616e6365206f626a656374206578697374732c2070726f6365" + "6564696e67207769746820657363726f772066696e6973682e4d50546f6b656e4d50546f6b656e206f626a65637420" + "6578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4e46546f6b656e4f66" + "6665724e46546f6b656e4f66666572206f626a656374206578697374732c2070726f63656564696e67207769746820" + "657363726f772066696e6973682e4f666665724f66666572206f626a656374206578697374732c2070726f63656564" + "696e67207769746820657363726f772066696e6973682e5061794368616e6e656c5061794368616e6e656c206f626a" + "656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e5065726d69" + "7373696f6e6564446f6d61696e5065726d697373696f6e6564446f6d61696e206f626a656374206578697374732c20" + "70726f63656564696e67207769746820657363726f772066696e6973682e5369676e65724c6973745369676e65724c" + "697374206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973" + "682e5469636b65745469636b6574206f626a656374206578697374732c2070726f63656564696e6720776974682065" + "7363726f772066696e6973682e5661756c745661756c74206f626a656374206578697374732c2070726f6365656469" + "6e67207769746820657363726f772066696e6973682e43757272656e74207365712076616c75653a004d0970726f64" + "756365727302086c616e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e3837" + "2e30202831373036376539616320323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d" + "757461626c652d676c6f62616c732b087369676e2d657874"; extern std::string const codecovTestsWasmHex = - "0061736d0100000001570b60067f7f7f7f7f7f017f60047f7f7f7f017f60027f7f017f60057f7f7f7f7f017f60037f7f7f017f60077f7f7f7f" - "7f7f7f017f60087f7f7f7f7f7f7f7f017f60017f017f60037f7f7e017f60047f7f7f7f006000017f02990d3c08686f73745f6c696205747261" - "6365000308686f73745f6c69620974726163655f6e756d000808686f73745f6c69620e6765745f6c65646765725f73716e000208686f73745f" - "6c6962166765745f706172656e745f6c65646765725f74696d65000208686f73745f6c6962166765745f706172656e745f6c65646765725f68" - "617368000208686f73745f6c69620c6765745f626173655f666565000208686f73745f6c696211616d656e646d656e745f656e61626c656400" - "0208686f73745f6c69620c6765745f74785f6669656c64000408686f73745f6c69620e6163636f756e745f6b65796c6574000108686f73745f" - "6c69621063616368655f6c65646765725f6f626a000408686f73745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669" - "656c64000408686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000108686f73745f6c6962136765745f74785f6e6573" - "7465645f6669656c64000108686f73745f6c6962236765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c6400" - "0108686f73745f6c69621b6765745f6c65646765725f6f626a5f6e65737465645f6669656c64000308686f73745f6c6962106765745f74785f" - "61727261795f6c656e000708686f73745f6c6962206765745f63757272656e745f6c65646765725f6f626a5f61727261795f6c656e00070868" - "6f73745f6c6962186765745f6c65646765725f6f626a5f61727261795f6c656e000208686f73745f6c6962176765745f74785f6e6573746564" - "5f61727261795f6c656e000208686f73745f6c6962276765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6172726179" - "5f6c656e000208686f73745f6c69621f6765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e000408686f73745f6c69" - "620b7570646174655f64617461000208686f73745f6c696213636f6d707574655f7368613531325f68616c66000108686f73745f6c69620963" - "6865636b5f736967000008686f73745f6c6962076765745f6e6674000008686f73745f6c69620e6765745f6e66745f69737375657200010868" - "6f73745f6c69620d6765745f6e66745f7461786f6e000108686f73745f6c69620d6765745f6e66745f666c616773000208686f73745f6c6962" - "146765745f6e66745f7472616e736665725f666565000208686f73745f6c69620e6765745f6e66745f73657269616c000108686f73745f6c69" - "620d74726163655f6163636f756e74000108686f73745f6c69620c74726163655f616d6f756e74000108686f73745f6c69620c636865636b5f" - "6b65796c6574000008686f73745f6c69620f666c6f61745f66726f6d5f75696e74000308686f73745f6c69620b6c696e655f6b65796c657400" - "0608686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c69621163726564656e7469616c5f6b65796c6574000608686f7374" - "5f6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c69621274726163655f6f70617175655f666c6f6174000108686f73745f" - "6c69620d666c6f61745f636f6d70617265000108686f73745f6c696209666c6f61745f616464000508686f73745f6c69620e666c6f61745f73" - "75627472616374000508686f73745f6c69620e666c6f61745f6d756c7469706c79000508686f73745f6c69620c666c6f61745f646976696465" - "000508686f73745f6c69620a666c6f61745f726f6f74000008686f73745f6c696209666c6f61745f706f77000008686f73745f6c696209666c" - "6f61745f6c6f67000308686f73745f6c69620d657363726f775f6b65796c6574000008686f73745f6c6962136d70745f69737375616e63655f" - "6b65796c6574000008686f73745f6c6962106e66745f6f666665725f6b65796c6574000008686f73745f6c69620c6f666665725f6b65796c65" - "74000008686f73745f6c69620d6f7261636c655f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b65796c6574000608686f" - "73745f6c69621a7065726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73745f6c69620d7469636b65745f6b65796c65" - "74000008686f73745f6c69620c7661756c745f6b65796c6574000008686f73745f6c69620f64656c65676174655f6b65796c6574000008686f" - "73745f6c6962166465706f7369745f707265617574685f6b65796c6574000008686f73745f6c69620a6469645f6b65796c6574000108686f73" - "745f6c69620e7369676e6572735f6b65796c65740001030302090a05030100110619037f01418080c0000b7f00418ea1c0000b7f004190a1c0" - "000b072e04066d656d6f727902000666696e697368003d0a5f5f646174615f656e6403010b5f5f686561705f6261736503020aa12c02460002" - "40200020014704402002200341014100410010001a20004100480d01418b80c000410b2000ad1001000b200220032000ac10011a0f0b418b80" - "c000410b2000ac1001000bd72b020a7f017e23004190026b22002400419680c000412341014100410010001a20004100360260200041e0006a" - "220241041002410441a88cc000410e103c20004100360260200241041003410441b68cc0004116103c200041f8006a22044200370300200041" - "f0006a22014200370300200041e8006a2205420037030020004200370360200241201004412041cc8cc0004116103c20004100360260200241" - "041005410441e28cc000410c103c200041106a2207428182848890a0c08001370300200041186a2206428182848890a0c08001370300200041" - "206a2209428182848890a0c080013703002000428182848890a0c0800137030841b980c000410e1006410141c780c0004111103c200041086a" - "41201006410141c780c0004111103c418180202002411410072203411446044002402000412e6a200041e2006a2d00003a0000200020002900" - "673703e8012000200041ec006a2900003700ed01200020002f00603b012c200020002903e8013703a801200020002900ed013700ad01200020" - "0028006336002f200041386a20002900ad01370000200020002903a80137003320044200370300200142003703002005420037030020004200" - "3703602000412c6a2204411420024120100822034120470d00200041c2006a20002d00623a0000200041f0016a2203200041ef006a29000022" - "0a370300200041cf006a200a370000200041d7006a200041f7006a290000370000200041df006a200041ff006a2d00003a0000200020002f01" - "603b01402000200028006336004320002000290067370047200041406b412041001009410141d880c0004110103c2001410036020020054200" - "370300200042003703604181802020024114100a411441ee8cc000411c103c2001410036020020054200370300200042003703604101418180" - "2020024114100b4114418a8dc0004114103c200041043602a001200041818020360260200041f8016a22054100360200200342003703002000" - "42003703e80120024104200041e8016a22014114100c4114419e8dc0004113103c2005410036020020034200370300200042003703e8012002" - "20002802a00120014114100d411441b18dc0004123103c2005410036020020034200370300200042003703e8014101200220002802a0012001" - "4114100e411441d48dc000411b103c4189803c100f412041e880c0004110103c4189803c1010412041f880c0004120103c41014189803c1011" - "4120419881c0004118103c200220002802a0011012412041b081c0004117103c200220002802a0011013412041c781c0004127103c41012002" - "20002802a0011014412041ee81c000411f103c2004411410154114418d82c000410b103c20004180026a220842003703002005420037030020" - "034200370300200042003703e801200220002802a001200141201016412041ef8dc0004113103c419882c000410c41a482c000410b41af82c0" - "00410e1017410141bd82c0004109103c200041c0016a2009290300370300200041b8016a2006290300370300200041b0016a20072903003703" - "00200020002903083703a801200541003b010020034200370300200042003703e80120044114200041a8016a22074120200141121018411241" - "828ec0004107103c2005410036020020034200370300200042003703e80120074120200141141019411441898ec000410e103c200041003602" - "e8012007412020014104101a410441978ec000410d103c20074120101b410841c682c000410d103c20074120101c410a41d382c0004114103c" - "200041003602e8012007412020014104101d410441a48ec000410e103c41e782c000410d20044114101e410041f482c000410d103c41e782c0" - "00410d418183c0004108101f4100418983c000410c103c41e782c000410d419583c0004108101f4100419d83c0004111103c417f4104100441" - "7141ae83c000411e103c200041003602e8012001417f1004417141b28ec000411e103c200041ea016a41003a0000200041003b01e801200141" - "031004417d41d08ec0004124103c200041003602e8012001418094ebdc031004417341f48ec0004123103c4102100f416f41cc83c000411f10" - "3c417f20002802a0011012417141eb83c000411f103c2002417f10124171418a84c000411f103c20024181201012417441a984c0004120103c" - "200041e094ebdc036a220620002802a0011012417341c984c000411f103c200842003703002005420037030020034200370300200042003703" - "e8012004411420064108200141201020417341978fc0004118103c200842003703002005420037030020034200370300200042003703e80120" - "04411420044114200141201020417141af8fc000411a103c200842003703002005420037030020034200370300200042003703e80120064108" - "2001412041001021417341c98fc0004117103c200842003703002005420037030020034200370300200042003703e801200220002802a00120" - "01412041001021417141e08fc0004120103c200620002802a00141011009417341e884c0004118103c200220002802a0014101100941714180" - "85c000411a103c200842003703002005420037030020034200370300200042003703e801200620002802a0012001412010084173418090c000" - "4116103c200842003703002005420037030020034200370300200042003703e801200220002802a0012001412010084171419690c000411810" - "3c200842003703002005420037030020034200370300200042003703e8012004411420044114200620002802a001200141201022417341ae90" - "c000411c103c200842003703002005420037030020034200370300200042003703e8012004411420044114200220002802a001200141201022" - "417141ca90c000411e103c200842003703002005420037030020034200370300200042003703e80141faa0c0004114200620002802a0012001" - "41201023417341e890c0004119103c200842003703002005420037030020034200370300200042003703e80141faa0c0004114200220002802" - "a0012001412010234171418191c000411f103c200842003703002005420037030020034200370300200042003703e80141faa0c0004114419a" - "85c0004114200141201023417141a091c0004129103c200842003703002005420037030020034200370300200042003703e80141ae85c00041" - "2841faa0c0004114200141201023417141c991c0004125103c200041dc016a2000413c6a280100360200200041d4016a200041346a29010037" - "02002000200029012c3702cc01200041808080083602c801200041003b01e801200041c8016a2209411841faa0c00041142001410210234171" - "41ee91c000410e103c200620002802a001422a1001417341d685c0004111103c200041003b01e8014102200141021007416f41fc91c000411b" - "103c200041003b01e801410220014102100a416f419792c000412b103c200041003b01e8014101410220014102100b416f41c292c000412310" - "3c4102100f416f41cc83c000411f103c41021010416f41e785c000412f103c410141021011416f419686c0004127103c41b980c00041812010" - "06417441bd86c000411f103c41b980c00041c1001006417441dc86c000411a103c200041003b01e801200241812020014102100c417441e592" - "c0004121103c200041003b01e801200241812020014102100d4174418693c0004131103c200041003b01e8014101200241812020014102100e" - "417441b793c0004129103c20024181201012417441f686c0004125103c200241812010134174419b87c0004135103c41012002418120101441" - "7441d087c000412d103c20024181201015417441fd87c0004119103c41e782c00041812041a482c000410b41af82c000410e1017417441bd82" - "c0004109103c41e782c000410d41a482c00041812041af82c000410e1017417441bd82c0004109103c41e782c000410d41a482c000410b41af" - "82c0004181201017417441bd82c0004109103c200041003b01e8012002418120200141021016417441e093c0004121103c200041003b01e801" - "41faa0c00041812041faa0c00041142001410210234174418194c0004118103c200041003b01e8012004411420044114200241812020014102" - "10244174419994c000411f103c200041003b01e801200941812020044114200141021025417441b894c0004122103c41e782c000410d200620" - "002802a001410010004173419688c000410f103c200042d487b6f4c7d4b1c0003700e00141e782c000410d200041e095ebdc036a2207410810" - "26417341a588c000411c103c41e782c000410d200620002802a001101f417341c188c0004116103c20074108200041e0016a22064108102741" - "7341d788c0004118103c20064108200741081027417341ef88c0004118103c200041003b01e801200741082006410820014102410010284173" - "41da94c0004114103c200041003b01e80120064108200741082001410241001028417341ee94c0004114103c200041003b01e8012007410820" - "06410820014102410010294173418295c0004119103c200041003b01e801200641082007410820014102410010294173419b95c0004119103c" - "200041003b01e8012007410820064108200141024100102a417341b495c0004119103c200041003b01e8012006410820074108200141024100" - "102a417341cd95c0004119103c200041003b01e8012007410820064108200141024100102b417341e695c0004117103c200041003b01e80120" - "06410820074108200141024100102b417341fd95c0004117103c200041003b01e801200741084103200141024100102c4173419496c0004114" - "103c200041003b01e801200741084103200141024100102d417341a896c0004113103c200041003b01e80120074108200141024100102e4173" - "41bb96c0004113103c200842003703002005420037030020034200370300200042003703e801200441142004411420014120102f417141ce96" - "c000411f103c200842003703002005420037030020034200370300200042003703e8012004411420044114200141201030417141ed96c00041" - "25103c200842003703002005420037030020034200370300200042003703e80120044114200441142001412010314171419297c0004122103c" - "200842003703002005420037030020034200370300200042003703e8012004411420044114200141201032417141b497c000411e103c200842" - "003703002005420037030020034200370300200042003703e8012004411420044114200141201033417141d297c000411f103c200842003703" - "002005420037030020034200370300200042003703e801200441142004411420044114200141201034417141f197c0004120103c2008420037" - "03002005420037030020034200370300200042003703e80120044114200441142001412010354171419198c000412c103c2008420037030020" - "05420037030020034200370300200042003703e8012004411420044114200141201036417141bd98c000411f103c2008420037030020054200" - "37030020034200370300200042003703e8012004411420044114200141201037417141dc98c000411e103c200220002802a001410010094171" - "418789c0004123103c200041003b01e80120044114200220002802a001200141021018417141fa98c000411a103c200041003b01e801200220" - "002802a0012001410210194171419499c0004121103c200041003b01e801200220002802a00120014102101a417141b599c0004120103c2002" - "20002802a001101b417141aa89c0004120103c200220002802a001101c417141ca89c0004127103c200041003602e801200220002802a00120" - "014104101d417141d599c0004121103c200041003b01e801200220002802a001200141021008417141f699c0004123103c2000418080800836" - "02e801200041003b018e02200220002802a001200141042000418e026a220341021020417141999ac0004121103c200041003b018e02200220" - "002802a00122052004411420022005200341021024417141ba9ac0004127103c200041003b018e0220044114200220002802a0012205200220" - "05200341021024417141e19ac0004127103c200041003b018e02200220002802a00120044114200341021038417141889bc0004125103c2000" - "41003b018e0220044114200220002802a001200341021038417141ad9bc0004125103c200041003b018e02200220002802a001200441142003" - "41021039417141d29bc000412c103c200041003b018e0220044114200220002802a001200341021039417141fe9bc000412c103c200041003b" - "018e02200220002802a00120034102103a417141aa9cc000411f103c200041003b018e02200220002802a0012001410420034102102f417141" - "c99cc0004122103c200041003b018e02200220002802a00120044114419a85c0004114200341021022417141eb9cc0004121103c200041003b" - "018e0220044114200220002802a001419a85c00041142003410210224171418c9dc0004121103c200041003b018e02200220002802a0012001" - "4104200341021030417141ad9dc0004128103c200041003b018e0220094118200220002802a001200341021025417141d59dc0004123103c20" - "0041003b018e02200220002802a00120014104200341021031417141f89dc0004125103c200041003b018e02200220002802a0012001410420" - "03410210324171419d9ec0004121103c200041003b018e02200220002802a00120014104200341021033417141be9ec0004122103c20004100" - "3b018e02200220002802a0012004411420014104200341021034417141e09ec0004124103c200041003b018e0220044114200220002802a001" - "20014104200341021034417141849fc0004124103c200041003b018e02200220002802a00120014104200341021035417141a89fc000412f10" - "3c200041003b018e02200220002802a00120034102103b417141d79fc0004123103c200041003b018e02200220002802a00120014104200341" - "021036417141fa9fc0004122103c200041003b018e02200220002802a001200141042003410210374171419ca0c0004121103c200041003b01" - "8e02200220002802a00141f189c0004120200341021018417141bda0c000411c103c41e782c000410d200220002802a001101e417141918ac0" - "004122103c41e796abdd03410d41f189c000412041001000417341b38ac0004110103c41e796abdd03410d200641081026417341c38ac00041" - "1d103c41e796abdd03410d20044114101e417341e08ac0004118103c41e796abdd03410d419583c0004108101f417341f88ac0004117103c20" - "0220002802a0012002418120410010004174418f8bc000410e103c2002418120420110014174419d8bc0004112103c41e782c0004181202006" - "41081026417441af8bc000411b103c41e782c00041812020044114101e417441ca8bc0004116103c41e782c000418120419583c0004108101f" - "417441e08bc0004115103c41e782c000410d200220002802a001101f417141f58bc0004119103c200041003b018e02200220002802a0012004" - "4114200341021025417141d9a0c0004121103c4101410020044114101e4100418e8cc000411a103c20004190026a240041010f0b0b418080c0" - "00410b417f20032003417f4e1bac1001000b0be5200200418080c0000bae056572726f725f636f64653d54455354204641494c454424242424" - "24205354415254494e47205741534d20455845435554494f4e202424242424746573745f616d656e646d656e74616d656e646d656e745f656e" - "61626c656463616368655f6c65646765725f6f626a6765745f74785f61727261795f6c656e6765745f63757272656e745f6c65646765725f6f" - "626a5f61727261795f6c656e6765745f6c65646765725f6f626a5f61727261795f6c656e6765745f74785f6e65737465645f61727261795f6c" - "656e6765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e6765745f6c65646765725f6f626a5f6e" - "65737465645f61727261795f6c656e7570646174655f6461746174657374206d65737361676574657374207075626b65797465737420736967" - "6e6174757265636865636b5f7369676765745f6e66745f666c6167736765745f6e66745f7472616e736665725f66656574657374696e672074" - "7261636574726163655f6163636f756e74400000000000005f74726163655f616d6f756e74400000000000000074726163655f616d6f756e74" - "5f7a65726f6765745f706172656e745f6c65646765725f686173685f6e65675f7074726765745f74785f61727261795f6c656e5f696e76616c" - "69645f736669656c646765745f74785f6e65737465645f61727261795f6c656e5f6e65675f7074726765745f74785f6e65737465645f617272" - "61795f6c656e5f6e65675f6c656e6765745f74785f6e65737465645f61727261795f6c656e5f746f6f5f6c6f6e676765745f74785f6e657374" - "65645f61727261795f6c656e5f7074725f6f6f6263616368655f6c65646765725f6f626a5f7074725f6f6f6263616368655f6c65646765725f" - "6f626a5f77726f6e675f6c656e55534430303030303030303030303030303030300041d685c0000ba41b74726163655f6e756d5f6f6f625f73" - "74726765745f63757272656e745f6c65646765725f6f626a5f61727261795f6c656e5f696e76616c69645f736669656c646765745f6c656467" - "65725f6f626a5f61727261795f6c656e5f696e76616c69645f736669656c64616d656e646d656e745f656e61626c65645f746f6f5f6269675f" - "736c696365616d656e646d656e745f656e61626c65645f746f6f5f6c6f6e676765745f74785f6e65737465645f61727261795f6c656e5f746f" - "6f5f6269675f736c6963656765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e5f746f6f5f6269" - "675f736c6963656765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e5f746f6f5f6269675f736c6963657570646174" - "655f646174615f746f6f5f6269675f736c69636574726163655f6f6f625f736c69636574726163655f6f70617175655f666c6f61745f6f6f62" - "5f736c69636574726163655f616d6f756e745f6f6f625f736c696365666c6f61745f636f6d706172655f6f6f625f736c69636531666c6f6174" - "5f636f6d706172655f6f6f625f736c6963653263616368655f6c65646765725f6f626a5f77726f6e675f73697a655f75696e74323536676574" - "5f6e66745f666c6167735f77726f6e675f73697a655f75696e743235366765745f6e66745f7472616e736665725f6665655f77726f6e675f73" - "697a655f75696e74323536303030303030303030303030303030303030303030303030303030303030303174726163655f6163636f756e745f" - "77726f6e675f73697a655f6163636f756e74696474726163655f6f6f625f737472696e6774726163655f6f70617175655f666c6f61745f6f6f" - "625f737472696e6774726163655f6163636f756e745f6f6f625f737472696e6774726163655f616d6f756e745f6f6f625f737472696e677472" - "6163655f746f6f5f6c6f6e6774726163655f6e756d5f746f6f5f6c6f6e6774726163655f6f70617175655f666c6f61745f746f6f5f6c6f6e67" - "74726163655f6163636f756e745f746f6f5f6c6f6e6774726163655f616d6f756e745f746f6f5f6c6f6e6774726163655f616d6f756e745f77" - "726f6e675f6c656e67746874726163655f6163636f756e745f636865636b5f646573796e636765745f6c65646765725f73716e6765745f7061" - "72656e745f6c65646765725f74696d656765745f706172656e745f6c65646765725f686173686765745f626173655f6665656765745f637572" - "72656e745f6c65646765725f6f626a5f6669656c646765745f6c65646765725f6f626a5f6669656c646765745f74785f6e65737465645f6669" - "656c646765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c646765745f6c65646765725f6f626a5f6e657374" - "65645f6669656c64636f6d707574655f7368613531325f68616c666765745f6e66746765745f6e66745f6973737565726765745f6e66745f74" - "61786f6e6765745f6e66745f73657269616c6765745f706172656e745f6c65646765725f686173685f6e65675f6c656e6765745f706172656e" - "745f6c65646765725f686173685f6275665f746f6f5f736d616c6c6765745f706172656e745f6c65646765725f686173685f6c656e5f746f6f" - "5f6c6f6e67636865636b5f6b65796c65745f6f6f625f6c656e5f753332636865636b5f6b65796c65745f77726f6e675f6c656e5f753332666c" - "6f61745f66726f6d5f75696e745f6c656e5f6f6f62666c6f61745f66726f6d5f75696e745f77726f6e675f6c656e5f75696e7436346163636f" - "756e745f6b65796c65745f6c656e5f6f6f626163636f756e745f6b65796c65745f77726f6e675f6c656e6c696e655f6b65796c65745f6c656e" - "5f6f6f625f63757272656e63796c696e655f6b65796c65745f77726f6e675f6c656e5f63757272656e6379616d6d5f6b65796c65745f6c656e" - "5f6f6f625f617373657432616d6d5f6b65796c65745f6c656e5f77726f6e675f6c656e5f617373657432616d6d5f6b65796c65745f6c656e5f" - "77726f6e675f6e6f6e5f7872705f63757272656e63795f6c656e616d6d5f6b65796c65745f6c656e5f77726f6e675f7872705f63757272656e" - "63795f6c656e616d6d5f6b65796c65745f6d70746765745f74785f6669656c645f696e76616c69645f736669656c646765745f63757272656e" - "745f6c65646765725f6f626a5f6669656c645f696e76616c69645f736669656c646765745f6c65646765725f6f626a5f6669656c645f696e76" - "616c69645f736669656c646765745f74785f6e65737465645f6669656c645f746f6f5f6269675f736c6963656765745f63757272656e745f6c" - "65646765725f6f626a5f6e65737465645f6669656c645f746f6f5f6269675f736c6963656765745f6c65646765725f6f626a5f6e6573746564" - "5f6669656c645f746f6f5f6269675f736c696365636f6d707574655f7368613531325f68616c665f746f6f5f6269675f736c696365616d6d5f" - "6b65796c65745f746f6f5f6269675f736c69636563726564656e7469616c5f6b65796c65745f746f6f5f6269675f736c6963656d70746f6b65" - "6e5f6b65796c65745f746f6f5f6269675f736c6963655f6d70746964666c6f61745f6164645f6f6f625f736c69636531666c6f61745f616464" - "5f6f6f625f736c69636532666c6f61745f73756274726163745f6f6f625f736c69636531666c6f61745f73756274726163745f6f6f625f736c" - "69636532666c6f61745f6d756c7469706c795f6f6f625f736c69636531666c6f61745f6d756c7469706c795f6f6f625f736c69636532666c6f" - "61745f6469766964655f6f6f625f736c69636531666c6f61745f6469766964655f6f6f625f736c69636532666c6f61745f726f6f745f6f6f62" - "5f736c696365666c6f61745f706f775f6f6f625f736c696365666c6f61745f6c6f675f6f6f625f736c696365657363726f775f6b65796c6574" - "5f77726f6e675f73697a655f75696e7433326d70745f69737375616e63655f6b65796c65745f77726f6e675f73697a655f75696e7433326e66" - "745f6f666665725f6b65796c65745f77726f6e675f73697a655f75696e7433326f666665725f6b65796c65745f77726f6e675f73697a655f75" - "696e7433326f7261636c655f6b65796c65745f77726f6e675f73697a655f75696e7433327061796368616e5f6b65796c65745f77726f6e675f" - "73697a655f75696e7433327065726d697373696f6e65645f646f6d61696e5f6b65796c65745f77726f6e675f73697a655f75696e7433327469" - "636b65745f6b65796c65745f77726f6e675f73697a655f75696e7433327661756c745f6b65796c65745f77726f6e675f73697a655f75696e74" - "33326765745f6e66745f77726f6e675f73697a655f75696e743235366765745f6e66745f6973737565725f77726f6e675f73697a655f75696e" - "743235366765745f6e66745f7461786f6e5f77726f6e675f73697a655f75696e743235366765745f6e66745f73657269616c5f77726f6e675f" - "73697a655f75696e743235366163636f756e745f6b65796c65745f77726f6e675f73697a655f6163636f756e746964636865636b5f6b65796c" - "65745f77726f6e675f73697a655f6163636f756e74696463726564656e7469616c5f6b65796c65745f77726f6e675f73697a655f6163636f75" - "6e7469643163726564656e7469616c5f6b65796c65745f77726f6e675f73697a655f6163636f756e7469643264656c65676174655f6b65796c" - "65745f77726f6e675f73697a655f6163636f756e7469643164656c65676174655f6b65796c65745f77726f6e675f73697a655f6163636f756e" - "746964326465706f7369745f707265617574685f6b65796c65745f77726f6e675f73697a655f6163636f756e746964316465706f7369745f70" - "7265617574685f6b65796c65745f77726f6e675f73697a655f6163636f756e746964326469645f6b65796c65745f77726f6e675f73697a655f" - "6163636f756e746964657363726f775f6b65796c65745f77726f6e675f73697a655f6163636f756e7469646c696e655f6b65796c65745f7772" - "6f6e675f73697a655f6163636f756e746964316c696e655f6b65796c65745f77726f6e675f73697a655f6163636f756e746964326d70745f69" - "737375616e63655f6b65796c65745f77726f6e675f73697a655f6163636f756e7469646d70746f6b656e5f6b65796c65745f77726f6e675f73" - "697a655f6163636f756e7469646e66745f6f666665725f6b65796c65745f77726f6e675f73697a655f6163636f756e7469646f666665725f6b" - "65796c65745f77726f6e675f73697a655f6163636f756e7469646f7261636c655f6b65796c65745f77726f6e675f73697a655f6163636f756e" - "7469647061796368616e5f6b65796c65745f77726f6e675f73697a655f6163636f756e746964317061796368616e5f6b65796c65745f77726f" - "6e675f73697a655f6163636f756e746964327065726d697373696f6e65645f646f6d61696e5f6b65796c65745f77726f6e675f73697a655f61" - "63636f756e7469647369676e6572735f6b65796c65745f77726f6e675f73697a655f6163636f756e7469647469636b65745f6b65796c65745f" - "77726f6e675f73697a655f6163636f756e7469647661756c745f6b65796c65745f77726f6e675f73697a655f6163636f756e7469646765745f" - "6e66745f77726f6e675f73697a655f6163636f756e7469646d70746f6b656e5f6b65796c65745f6d707469645f77726f6e675f6c656e677468" - "004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e38372e3020" - "2831373036376539616320323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c" - "732b087369676e2d657874"; + "0061736d0100000001570b60067f7f7f7f7f7f017f60047f7f7f7f017f60027f7f017f60057f7f7f7f7f017f60037f" + "7f7f017f60077f7f7f7f7f7f7f017f60087f7f7f7f7f7f7f7f017f60017f017f60037f7f7e017f60047f7f7f7f0060" + "00017f02990d3c08686f73745f6c6962057472616365000308686f73745f6c69620974726163655f6e756d00080868" + "6f73745f6c69620e6765745f6c65646765725f73716e000208686f73745f6c6962166765745f706172656e745f6c65" + "646765725f74696d65000208686f73745f6c6962166765745f706172656e745f6c65646765725f6861736800020868" + "6f73745f6c69620c6765745f626173655f666565000208686f73745f6c696211616d656e646d656e745f656e61626c" + "6564000208686f73745f6c69620c6765745f74785f6669656c64000408686f73745f6c69620e6163636f756e745f6b" + "65796c6574000108686f73745f6c69621063616368655f6c65646765725f6f626a000408686f73745f6c69621c6765" + "745f63757272656e745f6c65646765725f6f626a5f6669656c64000408686f73745f6c6962146765745f6c65646765" + "725f6f626a5f6669656c64000108686f73745f6c6962136765745f74785f6e65737465645f6669656c64000108686f" + "73745f6c6962236765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c64000108686f" + "73745f6c69621b6765745f6c65646765725f6f626a5f6e65737465645f6669656c64000308686f73745f6c69621067" + "65745f74785f61727261795f6c656e000708686f73745f6c6962206765745f63757272656e745f6c65646765725f6f" + "626a5f61727261795f6c656e000708686f73745f6c6962186765745f6c65646765725f6f626a5f61727261795f6c65" + "6e000208686f73745f6c6962176765745f74785f6e65737465645f61727261795f6c656e000208686f73745f6c6962" + "276765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e000208686f7374" + "5f6c69621f6765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e000408686f73745f6c6962" + "0b7570646174655f64617461000208686f73745f6c696213636f6d707574655f7368613531325f68616c6600010868" + "6f73745f6c696209636865636b5f736967000008686f73745f6c6962076765745f6e6674000008686f73745f6c6962" + "0e6765745f6e66745f697373756572000108686f73745f6c69620d6765745f6e66745f7461786f6e000108686f7374" + "5f6c69620d6765745f6e66745f666c616773000208686f73745f6c6962146765745f6e66745f7472616e736665725f" + "666565000208686f73745f6c69620e6765745f6e66745f73657269616c000108686f73745f6c69620d74726163655f" + "6163636f756e74000108686f73745f6c69620c74726163655f616d6f756e74000108686f73745f6c69620c63686563" + "6b5f6b65796c6574000008686f73745f6c69620f666c6f61745f66726f6d5f75696e74000308686f73745f6c69620b" + "6c696e655f6b65796c6574000608686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c6962116372" + "6564656e7469616c5f6b65796c6574000608686f73745f6c69620e6d70746f6b656e5f6b65796c6574000008686f73" + "745f6c69621274726163655f6f70617175655f666c6f6174000108686f73745f6c69620d666c6f61745f636f6d7061" + "7265000108686f73745f6c696209666c6f61745f616464000508686f73745f6c69620e666c6f61745f737562747261" + "6374000508686f73745f6c69620e666c6f61745f6d756c7469706c79000508686f73745f6c69620c666c6f61745f64" + "6976696465000508686f73745f6c69620a666c6f61745f726f6f74000008686f73745f6c696209666c6f61745f706f" + "77000008686f73745f6c696209666c6f61745f6c6f67000308686f73745f6c69620d657363726f775f6b65796c6574" + "000008686f73745f6c6962136d70745f69737375616e63655f6b65796c6574000008686f73745f6c6962106e66745f" + "6f666665725f6b65796c6574000008686f73745f6c69620c6f666665725f6b65796c6574000008686f73745f6c6962" + "0d6f7261636c655f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b65796c6574000608686f7374" + "5f6c69621a7065726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73745f6c69620d7469636b" + "65745f6b65796c6574000008686f73745f6c69620c7661756c745f6b65796c6574000008686f73745f6c69620f6465" + "6c65676174655f6b65796c6574000008686f73745f6c6962166465706f7369745f707265617574685f6b65796c6574" + "000008686f73745f6c69620a6469645f6b65796c6574000108686f73745f6c69620e7369676e6572735f6b65796c65" + "740001030302090a05030100110619037f01418080c0000b7f0041bba1c0000b7f0041c0a1c0000b072e04066d656d" + "6f727902000666696e697368003d0a5f5f646174615f656e6403010b5f5f686561705f6261736503020abf2c024600" + "0240200020014704402002200341014100410010001a20004100480d01418b80c000410b2000ad1001000b20022003" + "2000ac10011a0f0b418b80c000410b2000ac1001000bf52b020a7f017e23004190026b22002400419680c000412341" + "014100410010001a20004100360260200041e0006a220241041002410441bd8cc000410e103c200041003602602002" + "41041003410441cb8cc0004116103c200041f8006a22044200370300200041f0006a22014200370300200041e8006a" + "2205420037030020004200370360200241201004412041e18cc0004116103c20004100360260200241041005410441" + "f78cc000410c103c200041106a2207428182848890a0c08001370300200041186a2206428182848890a0c080013703" + "00200041206a2209428182848890a0c080013703002000428182848890a0c0800137030841b980c000410e10064101" + "41c780c0004111103c200041086a41201006410141c780c0004111103c418180202002411410072203411446044002" + "402000412e6a200041e2006a2d00003a0000200020002900673703e8012000200041ec006a2900003700ed01200020" + "002f00603b012c200020002903e8013703a801200020002900ed013700ad012000200028006336002f200041386a20" + "002900ad01370000200020002903a80137003320044200370300200142003703002005420037030020004200370360" + "2000412c6a2204411420024120100822034120470d00200041c2006a20002d00623a0000200041f0016a2203200041" + "ef006a290000220a370300200041cf006a200a370000200041d7006a200041f7006a290000370000200041df006a20" + "0041ff006a2d00003a0000200020002f01603b01402000200028006336004320002000290067370047200041406b41" + "2041001009410141d880c0004110103c2001410036020020054200370300200042003703604181802020024114100a" + "411441838dc000411c103c20014100360200200542003703002000420037036041014181802020024114100b411441" + "9f8dc0004114103c200041043602a001200041818020360260200041f8016a22054100360200200342003703002000" + "42003703e80120024104200041e8016a22014114100c411441b38dc0004113103c2005410036020020034200370300" + "200042003703e801200220002802a00120014114100d411441c68dc0004123103c2005410036020020034200370300" + "200042003703e8014101200220002802a00120014114100e411441e98dc000411b103c4189803c100f412041e880c0" + "004110103c4189803c1010412041f880c0004120103c41014189803c10114120419881c0004118103c200220002802" + "a0011012412041b081c0004117103c200220002802a0011013412041c781c0004127103c4101200220002802a00110" + "14412041ee81c000411f103c2004411410154114418d82c000410b103c20004180026a220842003703002005420037" + "030020034200370300200042003703e801200220002802a001200141201016412041848ec0004113103c419882c000" + "410c41a482c000410b41af82c000410e1017410141bd82c0004109103c200041c0016a2009290300370300200041b8" + "016a2006290300370300200041b0016a2007290300370300200020002903083703a801200541003b01002003420037" + "0300200042003703e80120044114200041a8016a22074120200141121018411241978ec0004107103c200541003602" + "0020034200370300200042003703e801200741202001411410194114419e8ec000410e103c200041003602e8012007" + "412020014104101a410441ac8ec000410d103c20074120101b410841c682c000410d103c20074120101c410a41d382" + "c0004114103c200041003602e8012007412020014104101d410441b98ec000410e103c41e782c000410d2004411410" + "1e410041f482c000410d103c41e782c000410d418183c0004108101f4100418983c000410c103c41e782c000410d41" + "9583c0004108101f4100419d83c0004111103c417f41041004417141ae83c000411e103c200041003602e801200141" + "7f1004417141c78ec000411e103c200041ea016a41003a0000200041003b01e801200141031004417d41e58ec00041" + "24103c200041003602e8012001418094ebdc031004417341898fc0004123103c4102100f416f41cc83c000411f103c" + "417f20002802a0011012417141eb83c000411f103c2002417f10124171418a84c000411f103c200241812010124174" + "41a984c0004120103c200041e094ebdc036a220620002802a0011012417341c984c000411f103c2008420037030020" + "05420037030020034200370300200042003703e8012004411420064108200141201020417341ac8fc0004118103c20" + "0842003703002005420037030020034200370300200042003703e8012004411420044114200141201020417141c48f" + "c000411a103c200842003703002005420037030020034200370300200042003703e801200641082001412041001021" + "417341de8fc0004117103c200842003703002005420037030020034200370300200042003703e801200220002802a0" + "012001412041001021417141f58fc0004120103c200620002802a00141011009417341e884c0004118103c20022000" + "2802a001410110094171418085c000411a103c200842003703002005420037030020034200370300200042003703e8" + "01200620002802a0012001412010084173419590c0004116103c200842003703002005420037030020034200370300" + "200042003703e801200220002802a001200141201008417141ab90c0004118103c2008420037030020054200370300" + "20034200370300200042003703e8012004411420044114200620002802a001200141201022417341c390c000411c10" + "3c200842003703002005420037030020034200370300200042003703e8012004411420044114200220002802a00120" + "0141201022417141df90c000411e103c200842003703002005420037030020034200370300200042003703e80141a7" + "a1c0004114200620002802a001200141201023417341fd90c0004119103c2008420037030020054200370300200342" + "00370300200042003703e80141a7a1c0004114200220002802a0012001412010234171419691c000411f103c200842" + "003703002005420037030020034200370300200042003703e80141a7a1c0004114419a85c000411420014120102341" + "7141b591c0004129103c200842003703002005420037030020034200370300200042003703e80141ae85c000412841" + "a7a1c0004114200141201023417141de91c0004125103c200041dc016a2000413c6a280100360200200041d4016a20" + "0041346a2901003702002000200029012c3702cc01200041808080083602c801200041003b01e801200041c8016a22" + "09411841a7a1c00041142001410210234171418392c000410e103c200620002802a001422a1001417341d685c00041" + "11103c200041003b01e8014102200141021007416f419192c000411b103c200041003b01e801410220014102100a41" + "6f41ac92c000412b103c200041003b01e8014101410220014102100b416f41d792c0004123103c4102100f416f41cc" + "83c000411f103c41021010416f41e785c000412f103c410141021011416f419686c0004127103c41b980c000418120" + "1006417441bd86c000411f103c41b980c00041c1001006417441dc86c000411a103c200041003b01e8012002418120" + "20014102100c417441fa92c0004121103c200041003b01e801200241812020014102100d4174419b93c0004131103c" + "200041003b01e8014101200241812020014102100e417441cc93c0004129103c20024181201012417441f686c00041" + "25103c200241812010134174419b87c0004135103c410120024181201014417441d087c000412d103c200241812010" + "15417441fd87c0004119103c41e782c00041812041a482c000410b41af82c000410e1017417441bd82c0004109103c" + "41e782c000410d41a482c00041812041af82c000410e1017417441bd82c0004109103c41e782c000410d41a482c000" + "410b41af82c0004181201017417441bd82c0004109103c200041003b01e8012002418120200141021016417441f593" + "c0004121103c200041003b01e80141a7a1c00041812041a7a1c00041142001410210234174419694c0004118103c20" + "0041003b01e80120044114200441142002418120200141021024417441ae94c000411f103c200041003b01e8012009" + "41812020044114200141021025417441cd94c0004122103c41e782c000410d200620002802a0014100100041734196" + "88c000410f103c200042d487b6f4c7d4b1c0003700e00141e782c000410d200041e095ebdc036a2207410810264173" + "41a588c000411c103c41e782c000410d200620002802a001101f417341c188c0004116103c20074108200041e0016a" + "220641081027417341d788c0004118103c20064108200741081027417341ef88c0004118103c200041003b01e80120" + "074108200641082001410241001028417341ef94c0004114103c200041003b01e80120064108200741082001410241" + "0010284173418395c0004114103c200041003b01e801200741082006410820014102410010294173419795c0004119" + "103c200041003b01e80120064108200741082001410241001029417341b095c0004119103c200041003b01e8012007" + "410820064108200141024100102a417341c995c0004119103c200041003b01e8012006410820074108200141024100" + "102a417341e295c0004119103c200041003b01e8012007410820064108200141024100102b417341fb95c000411710" + "3c200041003b01e8012006410820074108200141024100102b4173419296c0004117103c200041003b01e801200741" + "084103200141024100102c417341a996c0004114103c200041003b01e801200741084103200141024100102d417341" + "bd96c0004113103c200041003b01e80120074108200141024100102e417341d096c0004113103c2008420037030020" + "05420037030020034200370300200042003703e801200441142004411420014120102f417141e396c000411f103c20" + "0842003703002005420037030020034200370300200042003703e80120044114200441142001412010304171418297" + "c0004125103c200842003703002005420037030020034200370300200042003703e801200441142004411420014120" + "1031417141a797c0004122103c200842003703002005420037030020034200370300200042003703e8012004411420" + "044114200141201032417141c997c000411e103c200842003703002005420037030020034200370300200042003703" + "e8012004411420044114200141201033417141e797c000411f103c2008420037030020054200370300200342003703" + "00200042003703e8012004411420044114200441142001412010344171418698c0004120103c200842003703002005" + "420037030020034200370300200042003703e8012004411420044114200141201035417141a698c000412c103c2008" + "42003703002005420037030020034200370300200042003703e8012004411420044114200141201036417141d298c0" + "00411f103c200842003703002005420037030020034200370300200042003703e80120044114200441142001412010" + "37417141f198c000411e103c200220002802a001410010094171418789c0004123103c200041003b01e80120044114" + "200220002802a0012001410210184171418f99c000411a103c200041003b01e801200220002802a001200141021019" + "417141a999c0004121103c200041003b01e801200220002802a00120014102101a417141ca99c0004120103c200220" + "002802a001101b417141aa89c0004120103c200220002802a001101c417141ca89c0004127103c200041003602e801" + "200220002802a00120014104101d417141ea99c0004121103c200041003b01e801200220002802a001200141021008" + "4171418b9ac0004124103c200041808080083602e801200041003b018e02200220002802a001200141042000418e02" + "6a220341021020417141af9ac0004122103c200041003b018e02200220002802a00122052004411420022005200341" + "021024417141d19ac0004128103c200041003b018e0220044114200220002802a00122052002200520034102102441" + "7141f99ac0004128103c200041003b018e02200220002802a00120044114200341021038417141a19bc0004126103c" + "200041003b018e0220044114200220002802a001200341021038417141c79bc0004126103c200041003b018e022002" + "20002802a00120044114200341021039417141ed9bc000412d103c200041003b018e0220044114200220002802a001" + "2003410210394171419a9cc000412d103c200041003b018e02200220002802a00120034102103a417141c79cc00041" + "20103c200041003b018e02200220002802a0012001410420034102102f417141e79cc0004123103c200041003b018e" + "02200220002802a00120044114419a85c00041142003410210224171418a9dc0004122103c200041003b018e022004" + "4114200220002802a001419a85c0004114200341021022417141ac9dc0004122103c200041003b018e022002200028" + "02a00120014104200341021030417141ce9dc0004129103c200041003b018e0220094118200220002802a001200341" + "021025417141f79dc0004124103c200041003b018e02200220002802a001200141042003410210314171419b9ec000" + "4126103c200041003b018e02200220002802a00120014104200341021032417141c19ec0004122103c200041003b01" + "8e02200220002802a00120014104200341021033417141e39ec0004123103c200041003b018e02200220002802a001" + "2004411420014104200341021034417141869fc0004125103c200041003b018e0220044114200220002802a0012001" + "4104200341021034417141ab9fc0004125103c200041003b018e02200220002802a001200141042003410210354171" + "41d09fc0004130103c200041003b018e02200220002802a00120034102103b41714180a0c0004124103c200041003b" + "018e02200220002802a00120014104200341021036417141a4a0c0004123103c200041003b018e02200220002802a0" + "0120014104200341021037417141c7a0c0004122103c200041003b018e02200220002802a00141f189c00041202003" + "41021018417141e9a0c000411d103c41e782c000410d200220002802a001101e417141918ac0004123103c41e796ab" + "dd03410d41f189c000412041001000417341b48ac0004110103c41e796abdd03410d200641081026417341c48ac000" + "411d103c41e796abdd03410d20044114101e417341e18ac0004118103c41e796abdd03410d419583c0004108101f41" + "7341f98ac0004117103c200220002802a001200241812041001000417441908bc000410e103c200241812042011001" + "4174419e8bc0004112103c41e782c000418120200641081026417441b08bc000411b103c41e782c000418120200441" + "14101e417441cb8bc0004116103c41e782c000418120419583c0004108101f417441e18bc0004115103c41e782c000" + "410d200220002802a001101f417141f68bc0004119103c200041003b018e02200220002802a0012004411420034102" + "102541714186a1c0004121103c41e782c000410d200220002802a001410210004171418f8cc0004114103c41014100" + "20044114101e410041a38cc000411a103c20004190026a240041010f0b0b418080c000410b417f20032003417f4e1b" + "ac1001000b0b92210200418080c0000bae056572726f725f636f64653d54455354204641494c454424242424242053" + "54415254494e47205741534d20455845435554494f4e202424242424746573745f616d656e646d656e74616d656e64" + "6d656e745f656e61626c656463616368655f6c65646765725f6f626a6765745f74785f61727261795f6c656e676574" + "5f63757272656e745f6c65646765725f6f626a5f61727261795f6c656e6765745f6c65646765725f6f626a5f617272" + "61795f6c656e6765745f74785f6e65737465645f61727261795f6c656e6765745f63757272656e745f6c6564676572" + "5f6f626a5f6e65737465645f61727261795f6c656e6765745f6c65646765725f6f626a5f6e65737465645f61727261" + "795f6c656e7570646174655f6461746174657374206d65737361676574657374207075626b65797465737420736967" + "6e6174757265636865636b5f7369676765745f6e66745f666c6167736765745f6e66745f7472616e736665725f6665" + "6574657374696e6720747261636574726163655f6163636f756e74400000000000005f74726163655f616d6f756e74" + "400000000000000074726163655f616d6f756e745f7a65726f6765745f706172656e745f6c65646765725f68617368" + "5f6e65675f7074726765745f74785f61727261795f6c656e5f696e76616c69645f736669656c646765745f74785f6e" + "65737465645f61727261795f6c656e5f6e65675f7074726765745f74785f6e65737465645f61727261795f6c656e5f" + "6e65675f6c656e6765745f74785f6e65737465645f61727261795f6c656e5f746f6f5f6c6f6e676765745f74785f6e" + "65737465645f61727261795f6c656e5f7074725f6f6f6263616368655f6c65646765725f6f626a5f7074725f6f6f62" + "63616368655f6c65646765725f6f626a5f77726f6e675f6c656e555344303030303030303030303030303030303000" + "41d685c0000bd11b74726163655f6e756d5f6f6f625f7374726765745f63757272656e745f6c65646765725f6f626a" + "5f61727261795f6c656e5f696e76616c69645f736669656c646765745f6c65646765725f6f626a5f61727261795f6c" + "656e5f696e76616c69645f736669656c64616d656e646d656e745f656e61626c65645f746f6f5f6269675f736c6963" + "65616d656e646d656e745f656e61626c65645f746f6f5f6c6f6e676765745f74785f6e65737465645f61727261795f" + "6c656e5f746f6f5f6269675f736c6963656765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f" + "61727261795f6c656e5f746f6f5f6269675f736c6963656765745f6c65646765725f6f626a5f6e65737465645f6172" + "7261795f6c656e5f746f6f5f6269675f736c6963657570646174655f646174615f746f6f5f6269675f736c69636574" + "726163655f6f6f625f736c69636574726163655f6f70617175655f666c6f61745f6f6f625f736c6963657472616365" + "5f616d6f756e745f6f6f625f736c696365666c6f61745f636f6d706172655f6f6f625f736c69636531666c6f61745f" + "636f6d706172655f6f6f625f736c6963653263616368655f6c65646765725f6f626a5f77726f6e675f73697a655f75" + "696e743235366765745f6e66745f666c6167735f77726f6e675f73697a655f75696e743235366765745f6e66745f74" + "72616e736665725f6665655f77726f6e675f73697a655f75696e743235363030303030303030303030303030303030" + "30303030303030303030303030303174726163655f6163636f756e745f77726f6e675f73697a655f6163636f756e74" + "5f696474726163655f6f6f625f737472696e6774726163655f6f70617175655f666c6f61745f6f6f625f737472696e" + "6774726163655f6163636f756e745f6f6f625f737472696e6774726163655f616d6f756e745f6f6f625f737472696e" + "6774726163655f746f6f5f6c6f6e6774726163655f6e756d5f746f6f5f6c6f6e6774726163655f6f70617175655f66" + "6c6f61745f746f6f5f6c6f6e6774726163655f6163636f756e745f746f6f5f6c6f6e6774726163655f616d6f756e74" + "5f746f6f5f6c6f6e6774726163655f616d6f756e745f77726f6e675f6c656e67746874726163655f696e76616c6964" + "5f61735f68657874726163655f6163636f756e745f636865636b5f646573796e636765745f6c65646765725f73716e" + "6765745f706172656e745f6c65646765725f74696d656765745f706172656e745f6c65646765725f68617368676574" + "5f626173655f6665656765745f63757272656e745f6c65646765725f6f626a5f6669656c646765745f6c6564676572" + "5f6f626a5f6669656c646765745f74785f6e65737465645f6669656c646765745f63757272656e745f6c6564676572" + "5f6f626a5f6e65737465645f6669656c646765745f6c65646765725f6f626a5f6e65737465645f6669656c64636f6d" + "707574655f7368613531325f68616c666765745f6e66746765745f6e66745f6973737565726765745f6e66745f7461" + "786f6e6765745f6e66745f73657269616c6765745f706172656e745f6c65646765725f686173685f6e65675f6c656e" + "6765745f706172656e745f6c65646765725f686173685f6275665f746f6f5f736d616c6c6765745f706172656e745f" + "6c65646765725f686173685f6c656e5f746f6f5f6c6f6e67636865636b5f6b65796c65745f6f6f625f6c656e5f7533" + "32636865636b5f6b65796c65745f77726f6e675f6c656e5f753332666c6f61745f66726f6d5f75696e745f6c656e5f" + "6f6f62666c6f61745f66726f6d5f75696e745f77726f6e675f6c656e5f75696e7436346163636f756e745f6b65796c" + "65745f6c656e5f6f6f626163636f756e745f6b65796c65745f77726f6e675f6c656e6c696e655f6b65796c65745f6c" + "656e5f6f6f625f63757272656e63796c696e655f6b65796c65745f77726f6e675f6c656e5f63757272656e6379616d" + "6d5f6b65796c65745f6c656e5f6f6f625f617373657432616d6d5f6b65796c65745f6c656e5f77726f6e675f6c656e" + "5f617373657432616d6d5f6b65796c65745f6c656e5f77726f6e675f6e6f6e5f7872705f63757272656e63795f6c65" + "6e616d6d5f6b65796c65745f6c656e5f77726f6e675f7872705f63757272656e63795f6c656e616d6d5f6b65796c65" + "745f6d70746765745f74785f6669656c645f696e76616c69645f736669656c646765745f63757272656e745f6c6564" + "6765725f6f626a5f6669656c645f696e76616c69645f736669656c646765745f6c65646765725f6f626a5f6669656c" + "645f696e76616c69645f736669656c646765745f74785f6e65737465645f6669656c645f746f6f5f6269675f736c69" + "63656765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c645f746f6f5f6269675f73" + "6c6963656765745f6c65646765725f6f626a5f6e65737465645f6669656c645f746f6f5f6269675f736c696365636f" + "6d707574655f7368613531325f68616c665f746f6f5f6269675f736c696365616d6d5f6b65796c65745f746f6f5f62" + "69675f736c69636563726564656e7469616c5f6b65796c65745f746f6f5f6269675f736c6963656d70746f6b656e5f" + "6b65796c65745f746f6f5f6269675f736c6963655f6d70746964666c6f61745f6164645f6f6f625f736c6963653166" + "6c6f61745f6164645f6f6f625f736c69636532666c6f61745f73756274726163745f6f6f625f736c69636531666c6f" + "61745f73756274726163745f6f6f625f736c69636532666c6f61745f6d756c7469706c795f6f6f625f736c69636531" + "666c6f61745f6d756c7469706c795f6f6f625f736c69636532666c6f61745f6469766964655f6f6f625f736c696365" + "31666c6f61745f6469766964655f6f6f625f736c69636532666c6f61745f726f6f745f6f6f625f736c696365666c6f" + "61745f706f775f6f6f625f736c696365666c6f61745f6c6f675f6f6f625f736c696365657363726f775f6b65796c65" + "745f77726f6e675f73697a655f75696e7433326d70745f69737375616e63655f6b65796c65745f77726f6e675f7369" + "7a655f75696e7433326e66745f6f666665725f6b65796c65745f77726f6e675f73697a655f75696e7433326f666665" + "725f6b65796c65745f77726f6e675f73697a655f75696e7433326f7261636c655f6b65796c65745f77726f6e675f73" + "697a655f75696e7433327061796368616e5f6b65796c65745f77726f6e675f73697a655f75696e7433327065726d69" + "7373696f6e65645f646f6d61696e5f6b65796c65745f77726f6e675f73697a655f75696e7433327469636b65745f6b" + "65796c65745f77726f6e675f73697a655f75696e7433327661756c745f6b65796c65745f77726f6e675f73697a655f" + "75696e7433326765745f6e66745f77726f6e675f73697a655f75696e743235366765745f6e66745f6973737565725f" + "77726f6e675f73697a655f75696e743235366765745f6e66745f7461786f6e5f77726f6e675f73697a655f75696e74" + "3235366765745f6e66745f73657269616c5f77726f6e675f73697a655f75696e743235366163636f756e745f6b6579" + "6c65745f77726f6e675f73697a655f6163636f756e745f6964636865636b5f6b65796c65745f77726f6e675f73697a" + "655f6163636f756e745f696463726564656e7469616c5f6b65796c65745f77726f6e675f73697a655f6163636f756e" + "745f69643163726564656e7469616c5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964326465" + "6c65676174655f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69643164656c65676174655f6b65" + "796c65745f77726f6e675f73697a655f6163636f756e745f6964326465706f7369745f707265617574685f6b65796c" + "65745f77726f6e675f73697a655f6163636f756e745f6964316465706f7369745f707265617574685f6b65796c6574" + "5f77726f6e675f73697a655f6163636f756e745f6964326469645f6b65796c65745f77726f6e675f73697a655f6163" + "636f756e745f6964657363726f775f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69646c696e65" + "5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964316c696e655f6b65796c65745f77726f6e67" + "5f73697a655f6163636f756e745f6964326d70745f69737375616e63655f6b65796c65745f77726f6e675f73697a65" + "5f6163636f756e745f69646d70746f6b656e5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964" + "6e66745f6f666665725f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69646f666665725f6b6579" + "6c65745f77726f6e675f73697a655f6163636f756e745f69646f7261636c655f6b65796c65745f77726f6e675f7369" + "7a655f6163636f756e745f69647061796368616e5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f" + "6964317061796368616e5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964327065726d697373" + "696f6e65645f646f6d61696e5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69647369676e6572" + "735f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69647469636b65745f6b65796c65745f77726f" + "6e675f73697a655f6163636f756e745f69647661756c745f6b65796c65745f77726f6e675f73697a655f6163636f75" + "6e745f69646765745f6e66745f77726f6e675f73697a655f6163636f756e745f69646d70746f6b656e5f6b65796c65" + "745f6d707469645f77726f6e675f6c656e677468004d0970726f64756365727302086c616e67756167650104527573" + "74000c70726f6365737365642d6279010572757374631d312e38372e30202831373036376539616320323032352d30" + "352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b087369676e" + "2d657874"; extern std::string const floatTestsWasmHex = - "0061736d0100000001430860077f7f7f7f7f7f7f017f60057f7f7f7f7f017f60047f7f7f7f017f60067f7f7f7f7f7f017f60047e7f7f7f017f" - "60057f7e7f7f7f017f60037f7f7e017f6000017f02c9020e08686f73745f6c6962057472616365000108686f73745f6c69620e666c6f61745f" - "66726f6d5f696e74000408686f73745f6c69621274726163655f6f70617175655f666c6f6174000208686f73745f6c69620f666c6f61745f66" - "726f6d5f75696e74000108686f73745f6c696209666c6f61745f736574000508686f73745f6c69620d666c6f61745f636f6d70617265000208" - "686f73745f6c696209666c6f61745f616464000008686f73745f6c69620e666c6f61745f7375627472616374000008686f73745f6c69620e66" - "6c6f61745f6d756c7469706c79000008686f73745f6c69620c666c6f61745f646976696465000008686f73745f6c696209666c6f61745f706f" - "77000308686f73745f6c69620974726163655f6e756d000608686f73745f6c69620a666c6f61745f726f6f74000308686f73745f6c69620966" - "6c6f61745f6c6f6700010302010705030100110619037f01418080c0000b7f0041d28ac0000b7f0041e08ac0000b072e04066d656d6f727902" - "000666696e697368000e0a5f5f646174615f656e6403010b5f5f686561705f6261736503020af20e01ef0e01047f230041206b220124004180" - "80c000411d41014100410010001a200142003703100240428ce000200141106a22004108410010014108460440419d80c00041172000410810" - "021a41b480c000411e20004108410110001a0c010b41d280c000411e41014100410010001a0b2001428ce0003703180240200141186a410820" - "0141106a2200410841001003410846044041f080c00041172000410810021a0c010b418781c000411e41014100410010001a0b0240410242fb" - "00200141106a2200410841001004410846044041a581c00041212000410810021a0c010b41c681c000412641014100410010001a0b41ec81c0" - "004115418182c000410810021a419182c0004116418982c000410810021a41a782c000411b41014100410010001a2001420037031802404201" - "200141186a2200410841001001410846044041c282c000410f2000410810021a0c010b41d182c000411641014100410010001a0b0240200141" - "186a4108418182c0004108100545044041e782c000411b41014100410010001a0c010b418283c000411b41014100410010001a0b0240200141" - "186a4108418982c000410810054101460440419d83c000412341014100410010001a0c010b41c083c000412441014100410010001a0b024041" - "8982c0004108200141186a41081005410246044041e483c000412341014100410010001a0c010b418784c000412441014100410010001a0b41" - "ab84c000412041014100410010001a200142d487b6f4c7d4b1c000370310410921000340200141106a22024108418182c00041082002410841" - "0010061a200041016b22000d000b20014200370318420a200141186a22004108410010011a02402000410820024108100545044041cb84c000" - "411441014100410010001a0c010b41df84c000411341014100410010001a0b410b21000340200141106a22024108418182c000410820024108" - "410010071a200041016b22000d000b024020024108418982c0004108100545044041f284c000411941014100410010001a0c010b418b85c000" - "411841014100410010001a0b41a385c000412341014100410010001a20014200370300420a20014108410010011a200142d487b6f4c7d4b1c0" - "00370308410621000340200141086a220241082001410820024108410010081a200041016b22000d000b2001420037031042c0843d20014110" - "6a22004108410010011a02402000410820024108100545044041c685c000411941014100410010001a0c010b41df85c0004118410141004100" - "10001a0b410721000340200141086a220241082001410820024108410010091a200041016b22000d000b20014200370318417f420120014118" - "6a22004108410010041a02402002410820004108100545044041f785c000411741014100410010001a0c010b418e86c0004116410141004100" - "10001a0b41a486c000411741014100410010001a20014200370308418182c00041084103200141086a220041084100100a1a41bb86c0004112" - "2000410810021a418982c00041084106200041084100100a1a41cd86c00041182000410810021a200142003703104209200141106a22024108" - "410010011a200241084102200041084100100a1a41e586c00041142000410810021a200241084100200041084100100a1a41f986c000411720" - "00410810021a200142003703184200200141186a22034108410010011a200341084102200041084100100a1a419087c0004114200041081002" - "1a41a487c0004138200341084100200041084100100aac100b1a41dc87c000411841014100410010001a200142003703084209200041084100" - "10011a20014200370310200041084102200241084100100c1a41f487c00041122002410810021a200041084103200241084100100c1a418688" - "c00041122002410810021a2001420037031842c0843d20034108410010011a200341084103200241084100100c1a419888c000411820024108" - "10021a200341084106200241084100100c1a41b088c000411c2002410810021a41cc88c000411741014100410010001a2001420037031042c0" - "843d20024108410010011a2001420037031820024108200341084100100d1a41e388c00041142003410810021a41f788c000411a4101410041" - "0010001a20014200370318418182c0004108418982c000410820034108410010081a0240418982c0004108200341081005450440419189c000" - "411641014100410010001a0c010b41a789c000411541014100410010001a0b418982c0004108418982c0004108200141186a22004108410010" - "081a0240418182c000410820004108100545044041bc89c000411741014100410010001a0c010b41d389c000411641014100410010001a0b41" - "e989c000411a41014100410010001a2001420037031020014200370318420a200141186a22024108410010011a418182c00041082002410820" - "0141106a22004108410010091a41838ac00041192000410810021a418182c00041082000410820004108410010091a419c8ac000410f200041" - "0810021a02402002410820004108100545044041ab8ac000411441014100410010001a0c010b41bf8ac000411341014100410010001a0b2001" - "41206a240041010b0bdc0a0100418080c0000bd20a0a24242420746573745f666c6f61745f66726f6d5f7761736d202424242020666c6f6174" - "2066726f6d206936342031323330303a2020666c6f61742066726f6d20693634203132333030206173204845583a2020666c6f61742066726f" - "6d206936342031323330303a206661696c65642020666c6f61742066726f6d207536342031323330303a2020666c6f61742066726f6d207536" - "342031323330303a206661696c65642020666c6f61742066726f6d2065787020322c206d616e7469737361203132333a2020666c6f61742066" - "726f6d2065787020322c206d616e746973736120333a206661696c65642020666c6f61742066726f6d20636f6e737420313ad4838d7ea4c680" - "0094838d7ea4c680002020666c6f61742066726f6d20636f6e7374202d313a0a24242420746573745f666c6f61745f636f6d70617265202424" - "242020666c6f61742066726f6d20313a2020666c6f61742066726f6d20313a206661696c65642020666c6f61742066726f6d2031203d3d2046" - "4c4f41545f4f4e452020666c6f61742066726f6d203120213d20464c4f41545f4f4e452020666c6f61742066726f6d2031203e20464c4f4154" - "5f4e454741544956455f4f4e452020666c6f61742066726f6d203120213e20464c4f41545f4e454741544956455f4f4e452020464c4f41545f" - "4e454741544956455f4f4e45203c20666c6f61742066726f6d20312020464c4f41545f4e454741544956455f4f4e4520213c20666c6f617420" - "66726f6d20310a24242420746573745f666c6f61745f6164645f73756274726163742024242420207265706561746564206164643a20676f6f" - "6420207265706561746564206164643a20626164202072657065617465642073756274726163743a20676f6f64202072657065617465642073" - "756274726163743a206261640a24242420746573745f666c6f61745f6d756c7469706c795f6469766964652024242420207265706561746564" - "206d756c7469706c793a20676f6f6420207265706561746564206d756c7469706c793a2062616420207265706561746564206469766964653a" - "20676f6f6420207265706561746564206469766964653a206261640a24242420746573745f666c6f61745f706f77202424242020666c6f6174" - "2063756265206f6620313a2020666c6f61742036746820706f776572206f66202d313a2020666c6f617420737175617265206f6620393a2020" - "666c6f61742030746820706f776572206f6620393a2020666c6f617420737175617265206f6620303a2020666c6f61742030746820706f7765" - "72206f6620302028657870656374696e6720494e56414c49445f504152414d53206572726f72293a0a24242420746573745f666c6f61745f72" - "6f6f74202424242020666c6f61742073717274206f6620393a2020666c6f61742063627274206f6620393a2020666c6f61742063627274206f" - "6620313030303030303a2020666c6f61742036746820726f6f74206f6620313030303030303a0a24242420746573745f666c6f61745f6c6f67" - "2024242420206c6f675f3130206f6620313030303030303a0a24242420746573745f666c6f61745f6e65676174652024242420206e65676174" - "6520636f6e737420313a20676f6f6420206e656761746520636f6e737420313a2062616420206e656761746520636f6e7374202d313a20676f" - "6f6420206e656761746520636f6e7374202d313a206261640a24242420746573745f666c6f61745f696e76657274202424242020696e766572" - "74206120666c6f61742066726f6d2031303a2020696e7665727420616761696e3a2020696e766572742074776963653a20676f6f642020696e" - "766572742074776963653a20626164004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279" - "010572757374631d312e38372e30202831373036376539616320323032352d30352d303929002c0f7461726765745f6665617475726573022b" - "0f6d757461626c652d676c6f62616c732b087369676e2d657874"; + "0061736d0100000001430860077f7f7f7f7f7f7f017f60057f7f7f7f7f017f60047f7f7f7f017f60067f7f7f7f7f7f" + "017f60047e7f7f7f017f60057f7e7f7f7f017f60037f7f7e017f6000017f02c9020e08686f73745f6c696205747261" + "6365000108686f73745f6c69620e666c6f61745f66726f6d5f696e74000408686f73745f6c69621274726163655f6f" + "70617175655f666c6f6174000208686f73745f6c69620f666c6f61745f66726f6d5f75696e74000108686f73745f6c" + "696209666c6f61745f736574000508686f73745f6c69620d666c6f61745f636f6d70617265000208686f73745f6c69" + "6209666c6f61745f616464000008686f73745f6c69620e666c6f61745f7375627472616374000008686f73745f6c69" + "620e666c6f61745f6d756c7469706c79000008686f73745f6c69620c666c6f61745f646976696465000008686f7374" + "5f6c696209666c6f61745f706f77000308686f73745f6c69620974726163655f6e756d000608686f73745f6c69620a" + "666c6f61745f726f6f74000308686f73745f6c696209666c6f61745f6c6f6700010302010705030100110619037f01" + "418080c0000b7f0041d28ac0000b7f0041e08ac0000b072e04066d656d6f727902000666696e697368000e0a5f5f64" + "6174615f656e6403010b5f5f686561705f6261736503020af20e01ef0e01047f230041206b22012400418080c00041" + "1d41014100410010001a200142003703100240428ce000200141106a22004108410010014108460440419d80c00041" + "172000410810021a41b480c000411e20004108410110001a0c010b41d280c000411e41014100410010001a0b200142" + "8ce0003703180240200141186a4108200141106a2200410841001003410846044041f080c00041172000410810021a" + "0c010b418781c000411e41014100410010001a0b0240410242fb00200141106a2200410841001004410846044041a5" + "81c00041212000410810021a0c010b41c681c000412641014100410010001a0b41ec81c0004115418182c000410810" + "021a419182c0004116418982c000410810021a41a782c000411b41014100410010001a200142003703180240420120" + "0141186a2200410841001001410846044041c282c000410f2000410810021a0c010b41d182c0004116410141004100" + "10001a0b0240200141186a4108418182c0004108100545044041e782c000411b41014100410010001a0c010b418283" + "c000411b41014100410010001a0b0240200141186a4108418982c000410810054101460440419d83c0004123410141" + "00410010001a0c010b41c083c000412441014100410010001a0b0240418982c0004108200141186a41081005410246" + "044041e483c000412341014100410010001a0c010b418784c000412441014100410010001a0b41ab84c00041204101" + "4100410010001a200142d487b6f4c7d4b1c000370310410921000340200141106a22024108418182c0004108200241" + "08410010061a200041016b22000d000b20014200370318420a200141186a22004108410010011a0240200041082002" + "4108100545044041cb84c000411441014100410010001a0c010b41df84c000411341014100410010001a0b410b2100" + "0340200141106a22024108418182c000410820024108410010071a200041016b22000d000b024020024108418982c0" + "004108100545044041f284c000411941014100410010001a0c010b418b85c000411841014100410010001a0b41a385" + "c000412341014100410010001a20014200370300420a20014108410010011a200142d487b6f4c7d4b1c00037030841" + "0621000340200141086a220241082001410820024108410010081a200041016b22000d000b2001420037031042c084" + "3d200141106a22004108410010011a02402000410820024108100545044041c685c000411941014100410010001a0c" + "010b41df85c000411841014100410010001a0b410721000340200141086a220241082001410820024108410010091a" + "200041016b22000d000b20014200370318417f4201200141186a22004108410010041a024020024108200041081005" + "45044041f785c000411741014100410010001a0c010b418e86c000411641014100410010001a0b41a486c000411741" + "014100410010001a20014200370308418182c00041084103200141086a220041084100100a1a41bb86c00041122000" + "410810021a418982c00041084106200041084100100a1a41cd86c00041182000410810021a20014200370310420920" + "0141106a22024108410010011a200241084102200041084100100a1a41e586c00041142000410810021a2002410841" + "00200041084100100a1a41f986c00041172000410810021a200142003703184200200141186a22034108410010011a" + "200341084102200041084100100a1a419087c00041142000410810021a41a487c00041382003410841002000410841" + "00100aac100b1a41dc87c000411841014100410010001a20014200370308420920004108410010011a200142003703" + "10200041084102200241084100100c1a41f487c00041122002410810021a200041084103200241084100100c1a4186" + "88c00041122002410810021a2001420037031842c0843d20034108410010011a200341084103200241084100100c1a" + "419888c00041182002410810021a200341084106200241084100100c1a41b088c000411c2002410810021a41cc88c0" + "00411741014100410010001a2001420037031042c0843d20024108410010011a200142003703182002410820034108" + "4100100d1a41e388c00041142003410810021a41f788c000411a41014100410010001a20014200370318418182c000" + "4108418982c000410820034108410010081a0240418982c0004108200341081005450440419189c000411641014100" + "410010001a0c010b41a789c000411541014100410010001a0b418982c0004108418982c0004108200141186a220041" + "08410010081a0240418182c000410820004108100545044041bc89c000411741014100410010001a0c010b41d389c0" + "00411641014100410010001a0b41e989c000411a41014100410010001a2001420037031020014200370318420a2001" + "41186a22024108410010011a418182c000410820024108200141106a22004108410010091a41838ac0004119200041" + "0810021a418182c00041082000410820004108410010091a419c8ac000410f2000410810021a024020024108200041" + "08100545044041ab8ac000411441014100410010001a0c010b41bf8ac000411341014100410010001a0b200141206a" + "240041010b0bdc0a0100418080c0000bd20a0a24242420746573745f666c6f61745f66726f6d5f7761736d20242424" + "2020666c6f61742066726f6d206936342031323330303a2020666c6f61742066726f6d206936342031323330302061" + "73204845583a2020666c6f61742066726f6d206936342031323330303a206661696c65642020666c6f61742066726f" + "6d207536342031323330303a2020666c6f61742066726f6d207536342031323330303a206661696c65642020666c6f" + "61742066726f6d2065787020322c206d616e7469737361203132333a2020666c6f61742066726f6d2065787020322c" + "206d616e746973736120333a206661696c65642020666c6f61742066726f6d20636f6e737420313ad4838d7ea4c680" + "0094838d7ea4c680002020666c6f61742066726f6d20636f6e7374202d313a0a24242420746573745f666c6f61745f" + "636f6d70617265202424242020666c6f61742066726f6d20313a2020666c6f61742066726f6d20313a206661696c65" + "642020666c6f61742066726f6d2031203d3d20464c4f41545f4f4e452020666c6f61742066726f6d203120213d2046" + "4c4f41545f4f4e452020666c6f61742066726f6d2031203e20464c4f41545f4e454741544956455f4f4e452020666c" + "6f61742066726f6d203120213e20464c4f41545f4e454741544956455f4f4e452020464c4f41545f4e454741544956" + "455f4f4e45203c20666c6f61742066726f6d20312020464c4f41545f4e454741544956455f4f4e4520213c20666c6f" + "61742066726f6d20310a24242420746573745f666c6f61745f6164645f737562747261637420242424202072657065" + "61746564206164643a20676f6f6420207265706561746564206164643a206261642020726570656174656420737562" + "74726163743a20676f6f64202072657065617465642073756274726163743a206261640a24242420746573745f666c" + "6f61745f6d756c7469706c795f6469766964652024242420207265706561746564206d756c7469706c793a20676f6f" + "6420207265706561746564206d756c7469706c793a2062616420207265706561746564206469766964653a20676f6f" + "6420207265706561746564206469766964653a206261640a24242420746573745f666c6f61745f706f772024242420" + "20666c6f61742063756265206f6620313a2020666c6f61742036746820706f776572206f66202d313a2020666c6f61" + "7420737175617265206f6620393a2020666c6f61742030746820706f776572206f6620393a2020666c6f6174207371" + "75617265206f6620303a2020666c6f61742030746820706f776572206f6620302028657870656374696e6720494e56" + "414c49445f504152414d53206572726f72293a0a24242420746573745f666c6f61745f726f6f74202424242020666c" + "6f61742073717274206f6620393a2020666c6f61742063627274206f6620393a2020666c6f61742063627274206f66" + "20313030303030303a2020666c6f61742036746820726f6f74206f6620313030303030303a0a24242420746573745f" + "666c6f61745f6c6f672024242420206c6f675f3130206f6620313030303030303a0a24242420746573745f666c6f61" + "745f6e65676174652024242420206e656761746520636f6e737420313a20676f6f6420206e656761746520636f6e73" + "7420313a2062616420206e656761746520636f6e7374202d313a20676f6f6420206e656761746520636f6e7374202d" + "313a206261640a24242420746573745f666c6f61745f696e76657274202424242020696e76657274206120666c6f61" + "742066726f6d2031303a2020696e7665727420616761696e3a2020696e766572742074776963653a20676f6f642020" + "696e766572742074776963653a20626164004d0970726f64756365727302086c616e6775616765010452757374000c" + "70726f6365737365642d6279010572757374631d312e38372e30202831373036376539616320323032352d30352d30" + "3929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d6578" + "74"; extern std::string const float0Hex = - "0061736d0100000001290560057f7f7f7f7f017f60047e7f7f7f017f6007" - "7f7f7f7f7f7f7f017f60047f7f7f7f017f6000017f025f0408686f73745f" - "6c6962057472616365000008686f73745f6c69620e666c6f61745f66726f" - "6d5f696e74000108686f73745f6c69620e666c6f61745f73756274726163" - "74000208686f73745f6c69620d666c6f61745f636f6d7061726500030302" - "010405030100110619037f01418080c0000b7f00419281c0000b7f0041a0" - "81c0000b072e04066d656d6f727902000666696e69736800040a5f5f6461" - "74615f656e6403010b5f5f686561705f6261736503020abe0201bb020101" - "7f23808080800041206b2200248080808000418080c08000411541004100" - "41001080808080001a200042003703084200200041086a41084100108180" - "8080001a20004200370310420a200041106a410841001081808080001a20" - "0042003703180240200041106a4108200041106a4108200041186a410841" - "001082808080004108460d00419580c08000411541004100410010808080" - "80001a0b02400240200041086a4108200041186a41081083808080000d00" - "41aa80c0800041174100410041001080808080001a0c010b41c180c08000" - "41164100410041001080808080001a0b02400240200041086a410841d780" - "c0800041081083808080000d0041df80c08000411a410041004100108080" - "8080001a0c010b41f980c0800041194100410041001080808080001a0b20" - "0041206a24808080800041010b0b9c010100418080c0000b92010a242424" - "20746573745f666c6f61745f30202424242020666c6f61742031302d3130" - "3a206661696c65642020666c6f6174203020636f6d706172653a20676f6f" - "642020666c6f6174203020636f6d706172653a2062616480000000000000" - "002020464c4f41545f5a45524f20636f6d706172653a20676f6f64202046" - "4c4f41545f5a45524f20636f6d706172653a20626164009502046e616d65" - "001110666c6f61745f74657374732e7761736d01da0105002b5f5a4e3878" - "72706c5f73746434686f7374357472616365313768616338383262323664" - "656162656436364501355f5a4e387872706c5f73746434686f7374313466" - "6c6f61745f66726f6d5f696e743137683032343066386533613839643139" - "39654502355f5a4e387872706c5f73746434686f73743134666c6f61745f" - "737562747261637431376864363430633135323334353432393563450334" - "5f5a4e387872706c5f73746434686f73743133666c6f61745f636f6d7061" - "72653137683663386465656231323864393638386645040666696e697368" - "071201000f5f5f737461636b5f706f696e746572090a0100072e726f6461" - "7461004d0970726f64756365727302086c616e6775616765010452757374" - "000c70726f6365737365642d6279010572757374631d312e38392e302028" - "32393438333838336520323032352d30382d3034290094010f7461726765" - "745f6665617475726573082b0b62756c6b2d6d656d6f72792b0f62756c6b" - "2d6d656d6f72792d6f70742b1663616c6c2d696e6469726563742d6f7665" - "726c6f6e672b0a6d756c746976616c75652b0f6d757461626c652d676c6f" - "62616c732b136e6f6e7472617070696e672d6670746f696e742b0f726566" - "6572656e63652d74797065732b087369676e2d657874"; + "0061736d0100000001290560057f7f7f7f7f017f60047e7f7f7f017f60077f7f7f7f7f7f7f017f60047f7f7f7f017f" + "6000017f025f0408686f73745f6c6962057472616365000008686f73745f6c69620e666c6f61745f66726f6d5f696e" + "74000108686f73745f6c69620e666c6f61745f7375627472616374000208686f73745f6c69620d666c6f61745f636f" + "6d7061726500030302010405030100110619037f01418080c0000b7f00419281c0000b7f0041a081c0000b072e0406" + "6d656d6f727902000666696e69736800040a5f5f646174615f656e6403010b5f5f686561705f6261736503020abe02" + "01bb0201017f23808080800041206b2200248080808000418080c0800041154100410041001080808080001a200042" + "003703084200200041086a410841001081808080001a20004200370310420a200041106a410841001081808080001a" + "200042003703180240200041106a4108200041106a4108200041186a410841001082808080004108460d00419580c0" + "800041154100410041001080808080001a0b02400240200041086a4108200041186a41081083808080000d0041aa80" + "c0800041174100410041001080808080001a0c010b41c180c0800041164100410041001080808080001a0b02400240" + "200041086a410841d780c0800041081083808080000d0041df80c08000411a4100410041001080808080001a0c010b" + "41f980c0800041194100410041001080808080001a0b200041206a24808080800041010b0b9c010100418080c0000b" + "92010a24242420746573745f666c6f61745f30202424242020666c6f61742031302d31303a206661696c6564202066" + "6c6f6174203020636f6d706172653a20676f6f642020666c6f6174203020636f6d706172653a206261648000000000" + "0000002020464c4f41545f5a45524f20636f6d706172653a20676f6f642020464c4f41545f5a45524f20636f6d7061" + "72653a20626164009502046e616d65001110666c6f61745f74657374732e7761736d01da0105002b5f5a4e38787270" + "6c5f73746434686f7374357472616365313768616338383262323664656162656436364501355f5a4e387872706c5f" + "73746434686f73743134666c6f61745f66726f6d5f696e74313768303234306638653361383964313939654502355f" + "5a4e387872706c5f73746434686f73743134666c6f61745f7375627472616374313768643634306331353233343534" + "323935634503345f5a4e387872706c5f73746434686f73743133666c6f61745f636f6d706172653137683663386465" + "656231323864393638386645040666696e697368071201000f5f5f737461636b5f706f696e746572090a0100072e72" + "6f64617461004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279" + "010572757374631d312e38392e30202832393438333838336520323032352d30382d3034290094010f746172676574" + "5f6665617475726573082b0b62756c6b2d6d656d6f72792b0f62756c6b2d6d656d6f72792d6f70742b1663616c6c2d" + "696e6469726563742d6f7665726c6f6e672b0a6d756c746976616c75652b0f6d757461626c652d676c6f62616c732" + "b136e6f6e7472617070696e672d6670746f696e742b0f7265666572656e63652d74797065732b087369676e2d65787" + "4"; extern std::string const disabledFloatHex = - "0061736d010000000108026000006000017f03030200010503010002063e" - "0a7f004180080b7f004180080b7f004180100b7f004180100b7f00418090" - "040b7f004180080b7f00418090040b7f00418080080b7f0041000b7f0041" - "010b07b0010d066d656d6f72790200115f5f7761736d5f63616c6c5f6374" - "6f727300000666696e69736800010362756603000c5f5f64736f5f68616e" - "646c6503010a5f5f646174615f656e6403020b5f5f737461636b5f6c6f77" - "03030c5f5f737461636b5f6869676803040d5f5f676c6f62616c5f626173" - "6503050b5f5f686561705f6261736503060a5f5f686561705f656e640307" - "0d5f5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503" - "090a150202000b100043000000c54300200045921a41010b"; + "0061736d010000000108026000006000017f03030200010503010002063e0a7f004180080b7f004180080b7f004180" + "100b7f004180100b7f00418090040b7f004180080b7f00418090040b7f00418080080b7f0041000b7f0041010b07b0" + "010d066d656d6f72790200115f5f7761736d5f63616c6c5f63746f727300000666696e69736800010362756603000c" + "5f5f64736f5f68616e646c6503010a5f5f646174615f656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461" + "636b5f6869676803040d5f5f676c6f62616c5f6261736503050b5f5f686561705f6261736503060a5f5f686561705f" + "656e6403070d5f5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503090a150202000b1000430000" + "00c54300200045921a41010b"; extern std::string const memoryPointerAtLimitHex = - "0061736d010000000105016000017f030201000503010001070a010666696e69736800000a" - "0e010c0041ffff032d00001a41010b"; + "0061736d010000000105016000017f030201000503010001070a010666696e69736800000a0e010c0041ffff032d00" + "001a41010b"; extern std::string const memoryPointerOverLimitHex = - "0061736d010000000105016000017f030201000503010001070a010666696e69736800000a" - "0e010c00418080042d00001a41010b"; + "0061736d010000000105016000017f030201000503010001070a010666696e69736800000a0e010c00418080042d00" + "001a41010b"; extern std::string const memoryOffsetOverLimitHex = - "0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006" - "66696e69736800000a0e010c00410028028080041a41010b"; + "0061736d010000000105016000017f030201000503010001071302066d656d6f727902000666696e69736800000a0e" + "010c00410028028080041a41010b"; extern std::string const memoryEndOfWordOverLimitHex = - "0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006" - "66696e69736800000a0e010c0041feff032802001a41010b"; + "0061736d010000000105016000017f030201000503010001071302066d656d6f727902000666696e69736800000a0e" + "010c0041feff032802001a41010b"; extern std::string const memoryGrow0To1PageHex = - "0061736d010000000105016000017f030201000503010000071302066d656d6f7279020006" - "66696e69736800000a0b010900410140001a41010b"; + "0061736d010000000105016000017f030201000503010000071302066d656d6f727902000666696e69736800000a0b" + "010900410140001a41010b"; extern std::string const memoryGrow1To0PageHex = - "0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006" - "66696e69736800000a13011100417f4000417f460440417f0f0b41010b"; + "0061736d010000000105016000017f030201000503010001071302066d656d6f727902000666696e69736800000a13" + "011100417f4000417f460440417f0f0b41010b"; extern std::string const memoryLastByteOf8MBHex = - "0061736d010000000105016000017f030201000506010180018001071302066d656d6f7279" - "02000666696e69736800000a0f010d0041ffffff032d00001a41010b"; + "0061736d010000000105016000017f030201000506010180018001071302066d656d6f727902000666696e69736800" + "000a0f010d0041ffffff032d00001a41010b"; extern std::string const memoryGrow1MoreThan8MBHex = - "0061736d010000000105016000017f03020100050401008001071302066d656d6f72790200" - "0666696e69736800000a1301110041014000417f460440417f0f0b41010b"; + "0061736d010000000105016000017f03020100050401008001071302066d656d6f727902000666696e69736800000a" + "1301110041014000417f460440417f0f0b41010b"; extern std::string const memoryGrow0MoreThan8MBHex = - "0061736d010000000105016000017f03020100050401008001071302066d656d6f72790200" - "0666696e69736800000a1301110041004000417f460440417f0f0b41010b"; + "0061736d010000000105016000017f03020100050401008001071302066d656d6f727902000666696e69736800000a" + "1301110041004000417f460440417f0f0b41010b"; extern std::string const memoryInit1MoreThan8MBHex = - "0061736d010000000105016000017f030201000506010181018101071302066d656d6f7279" - "02000666696e69736800000a0f010d0041ffffff032d00001a41010b"; + "0061736d010000000105016000017f030201000506010181018101071302066d656d6f727902000666696e69736800" + "000a0f010d0041ffffff032d00001a41010b"; extern std::string const memoryNegativeAddressHex = - "0061736d010000000105016000017f030201000506010180018001071302066d656d6f7279" - "02000666696e69736800000a0c010a00417f2d00001a41010b"; + "0061736d010000000105016000017f030201000506010180018001071302066d656d6f727902000666696e69736800" + "000a0c010a00417f2d00001a41010b"; extern std::string const table64ElementsHex = - "0061736d010000000108026000006000017f0303020001040401700040070a010666696e69" - "736800010946010041000b4000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000000000000000000000000000" - "00000a090202000b040041010b"; + "0061736d010000000108026000006000017f0303020001040401700040070a010666696e6973680001094601004100" + "0b40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000a090202000b040041010b"; extern std::string const table65ElementsHex = - "0061736d010000000108026000006000017f0303020001040401700041070a010666696e69" - "736800010947010041000b4100000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000000000000000000000000000" - "0000000a090202000b040041010b"; + "0061736d010000000108026000006000017f0303020001040401700041070a010666696e6973680001094701004100" + "0b41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000a090202000b040041010b"; extern std::string const table2TablesHex = - "0061736d010000000108026000006000017f03030200010409027001010170010101070a01" - "0666696e6973680001090f020041000b0100020141000b0001000a090202000b040041010" - "b"; + "0061736d010000000108026000006000017f03030200010409027001010170010101070a010666696e697368000109" + "0f020041000b0100020141000b0001000a090202000b040041010b"; extern std::string const table0ElementsHex = - "0061736d010000000105016000017f03020100040401700000070a010666696e6973680000" - "0a0601040041010b"; + "0061736d010000000105016000017f03020100040401700000070a010666696e69736800000a0601040041010b"; extern std::string const tableUintMaxHex = - "0061736d010000000105016000017f030201000408017000ffffffff0f070a010666696e69" - "736800000a0601040041010b"; + "0061736d010000000105016000017f030201000408017000ffffffff0f070a010666696e69736800000a0601040041" + "010b"; extern std::string const proposalMutableGlobalHex = - "0061736d010000000105016000017f030201000606017f0141000b07140207636f756e7465" - "7203000666696e69736800000a0d010b00230041016a240041010b"; + "0061736d010000000105016000017f030201000606017f0141000b07140207636f756e74657203000666696e697368" + "00000a0d010b00230041016a240041010b"; extern std::string const proposalGcStructNewHex = - "0061736d01000000010b026000017f5f027f017f0103020100070a010666696e6973680000" - "0a0a010800fb01011a41010b"; + "0061736d01000000010b026000017f5f027f017f0103020100070a010666696e69736800000a0a010800fb01011a41" + "010b"; extern std::string const proposalMultiValueHex = - "0061736d010000000110036000027f7f6000017f60027f7f017f0303020001070a01066669" - "6e69736800010a14020600410a41140b0b00100002026a411e460b0b"; + "0061736d010000000110036000027f7f6000017f60027f7f017f0303020001070a010666696e69736800010a140206" + "00410a41140b0b00100002026a411e460b0b"; extern std::string const proposalSignExtHex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a0b01090041" - "ff01c0417f460b"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a0b01090041ff01c0417f460b"; extern std::string const proposalFloatToIntHex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a1201100043" - "f9021550fc0041ffffffff07460b"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a1201100043f9021550fc0041ffffff" + "ff07460b"; extern std::string const proposalBulkMemoryHex = - "0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006" - "66696e69736800000a1f011d004100412a3a000041e40041004101fc0a000041e4002d0000" - "412a460b"; + "0061736d010000000105016000017f030201000503010001071302066d656d6f727902000666696e69736800000a1f" + "011d004100412a3a000041e40041004101fc0a000041e4002d0000412a460b"; extern std::string const proposalRefTypesHex = - "0061736d010000000105016000017f020f0103656e76057461626c65016f00010302010007" - "0a010666696e69736800000a0c010a004100d06f260041010b"; + "0061736d010000000105016000017f020f0103656e76057461626c65016f000103020100070a010666696e69736800" + "000a0c010a004100d06f260041010b"; extern std::string const proposalTailCallHex = - "0061736d010000000105016000017f0303020000070a010666696e69736800010a0b020400" - "41010b040012000b"; + "0061736d010000000105016000017f0303020000070a010666696e69736800010a0b02040041010b040012000b"; extern std::string const proposalExtendedConstHex = - "0061736d010000000105016000017f030201000609017f00410a41206a0b070a010666696e" - "69736800000a090107002300412a460b"; + "0061736d010000000105016000017f030201000609017f00410a41206a0b070a010666696e69736800000a09010700" + "2300412a460b"; extern std::string const proposalMultiMemoryHex = - "0061736d010000000105016000017f0302010005050200000001070a010666696e69736800" - "000a060104003f010b"; + "0061736d010000000105016000017f0302010005050200000001070a010666696e69736800000a060104003f010b"; extern std::string const proposalCustomPageSizesHex = - "0061736d010000000105016000017f0302010005040108010a070a010666696e6973680000" - "0a0601040041010b0010046e616d65010901000666696e697368"; + "0061736d010000000105016000017f0302010005040108010a070a010666696e69736800000a0601040041010b0010" + "046e616d65010901000666696e697368"; extern std::string const proposalMemory64Hex = - "0061736d010000000105016000017f030201000503010401070a010666696e69736800000a" - "10010e004200412a3a00003f004201510b0010046e616d65010901000666696e697368"; + "0061736d010000000105016000017f030201000503010401070a010666696e69736800000a10010e004200412a3a00" + "003f004201510b0010046e616d65010901000666696e697368"; extern std::string const proposalWideArithmeticHex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a0e010c0042" - "014202fc161a1a41010b0010046e616d65010901000666696e697368"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a0e010c0042014202fc161a1a41010b" + "0010046e616d65010901000666696e697368"; extern std::string const trapDivideBy0Hex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a0c010a0041" - "2a41006d1a41010b"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a0c010a00412a41006d1a41010b"; extern std::string const trapIntOverflowHex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a0d010b0041" - "8080808078417f6d0b"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a0d010b00418080808078417f6d0b"; extern std::string const trapUnreachableHex = - "0061736d010000000105016000017f03020100070a010666696e69736800000a0701050000" - "41010b"; + "0061736d010000000105016000017f03020100070a010666696e69736800000a070105000041010b"; extern std::string const trapNullCallHex = - "0061736d010000000105016000017f03020100040401700001070a010666696e6973680000" - "0a0901070041001100000b"; + "0061736d010000000105016000017f03020100040401700001070a010666696e69736800000a090107004100110000" + "0b"; extern std::string const trapFuncSigMismatchHex = - "0061736d010000000108026000006000017f0303020001040401700001070a010666696e69" - "736800010907010041000b01000a0d020300010b070041001101000b"; + "0061736d010000000108026000006000017f0303020001040401700001070a010666696e6973680001090701004100" + "0b01000a0d020300010b070041001101000b"; extern std::string const wasiGetTimeHex = - "0061736d01000000010c0260037f7e7f017f6000017f02290116776173695f736e61707368" - "6f745f70726576696577310e636c6f636b5f74696d655f6765740000030201010503010001" - "071302066d656d6f727902000666696e69736800010a16011400410042e807410010004504" - "7f410105417f0b0b"; + "0061736d01000000010c0260037f7e7f017f6000017f02290116776173695f736e617073686f745f70726576696577" + "310e636c6f636b5f74696d655f6765740000030201010503010001071302066d656d6f727902000666696e69736800" + "010a16011400410042e8074100100045047f410105417f0b0b"; extern std::string const wasiPrintHex = - "0061736d01000000010d0260047f7f7f7f017f6000017f02230116776173695f736e617073" - "686f745f70726576696577310866645f77726974650000030201010503010001071302066d" - "656d6f727902000666696e69736800010a1d011b01017f4118210041014100410120001000" - "45047f410105417f0b0b0b1e030041100b0648656c6c6f0a0041000b04100000000041040b" - "0406000000"; + "0061736d01000000010d0260047f7f7f7f017f6000017f02230116776173695f736e617073686f745f707265766965" + "77310866645f77726974650000030201010503010001071302066d656d6f727902000666696e69736800010a1d011b" + "01017f411821004101410041012000100045047f410105417f0b0b0b1e030041100b0648656c6c6f0a0041000b0410" + "0000000041040b0406000000"; // The following several wasm hex strings are for testing wasm section // corruption cases. They are illegal hence do not have corresponding @@ -1097,9 +1064,9 @@ extern std::string const lyingHeaderHex = "0061736d01000000018080808008"; // # Vector count: Infinite stream of 0x80 (100 bytes) // data += b'\x80' * 100 extern std::string const neverEndingNumberHex = - "0061736d010000000105808080808080808080808080808080808080808080808080808080" - "80808080808080808080808080808080808080808080808080808080808080808080808080" - "808080808080808080808080808080808080808080808080808080808080808080808080"; + "0061736d01000000010580808080808080808080808080808080808080808080808080808080808080808080808080" + "8080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080" + "80808080808080808080808080808080"; // Corruption Test: vectorLie // Scenario: A vector declares it has 4 billion items, but provides none. @@ -1181,62 +1148,4647 @@ extern std::string const invalidSectionIdHex = "0061736d01000000ff0100"; // data += b'\xff\xff\xff\xff\x0f\x7f' // # Instruction: end (0x0b) // data += b'\x0b' -extern std::string const localVariableBombHex = "0061736d01000000010401600000030201000a0f010d01ffffffff0f7f0b"; +extern std::string const localVariableBombHex = + "0061736d01000000010401600000030201000a0f010d01ffffffff0f7f0b"; extern std::string const infiniteLoopWasmHex = - "0061736d010000000108026000006000017f030302000105030100020638097f004180080b" - "7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f00418080" - "080b7f0041000b7f0041010b07a8010c066d656d6f72790200115f5f7761736d5f63616c6c" - "5f63746f72730000046c6f6f7000010c5f5f64736f5f68616e646c6503000a5f5f64617461" - "5f656e6403010b5f5f737461636b5f6c6f7703020c5f5f737461636b5f6869676803030d5f" - "5f676c6f62616c5f6261736503040b5f5f686561705f6261736503050a5f5f686561705f65" - "6e6403060d5f5f6d656d6f72795f6261736503070c5f5f7461626c655f6261736503080a27" - "0202000b220041fc87044100360200034041fc870441fc870428020041016a3602000c000b" - "000b007f0970726f647563657273010c70726f6365737365642d62790105636c616e675f31" - "392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c" - "766d2f6c6c766d2d70726f6a65637420616234623561326462353832393538616631656533" - "303861373930636664623432626432343732302900490f7461726765745f66656174757265" - "73042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f726566657265" - "6e63652d74797065732b0a6d756c746976616c7565"; + "0061736d010000000108026000006000017f030302000105030100020638097f004180080b7f004180080b7f004180" + "080b7f00418088040b7f004180080b7f00418088040b7f00418080080b7f0041000b7f0041010b07a8010c066d656d" + "6f72790200115f5f7761736d5f63616c6c5f63746f72730000046c6f6f7000010c5f5f64736f5f68616e646c650300" + "0a5f5f646174615f656e6403010b5f5f737461636b5f6c6f7703020c5f5f737461636b5f6869676803030d5f5f676c" + "6f62616c5f6261736503040b5f5f686561705f6261736503050a5f5f686561705f656e6403060d5f5f6d656d6f7279" + "5f6261736503070c5f5f7461626c655f6261736503080a270202000b220041fc87044100360200034041fc870441fc" + "870428020041016a3602000c000b000b007f0970726f647563657273010c70726f6365737365642d62790105636c61" + "6e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c76" + "6d2d70726f6a6563742061623462356132646235383239353861663165653330386137393063666462343262643234" + "3732302900490f7461726765745f6665617475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d" + "6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; extern std::string const startLoopHex = - "0061736d010000000108026000006000017f03030200010712020573746172740000066669" - "6e69736800010801000a0e02070003400c000b0b040041010b"; - -extern std::string const badAllocHex = - "0061736d01000000010f0360000060017f017f60027f7f017f030403000102050301000206" - "3e0a7f004180080b7f004180080b7f004180100b7f004180100b7f00418090040b7f004180" - "080b7f00418090040b7f00418080080b7f0041000b7f0041010b07b9010e066d656d6f7279" - "0200115f5f7761736d5f63616c6c5f63746f7273000008616c6c6f63617465000103627566" - "0300047465737400020c5f5f64736f5f68616e646c6503010a5f5f646174615f656e640302" - "0b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f6261" - "6c5f6261736503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f" - "5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503090a420302000b2c01" - "017f024002400240024020000e0403000301020b41808080040f0b417f0f0b20004180086a" - "21010b20010b1000200145044041000f0b20002c00000b007f0970726f647563657273010c" - "70726f6365737365642d62790105636c616e675f31392e312e352d776173692d73646b2028" - "68747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a65637420" - "61623462356132646235383239353861663165653330386137393063666462343262643234" - "3732302900490f7461726765745f6665617475726573042b0f6d757461626c652d676c6f62" - "616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c7469" - "76616c7565"; + "0061736d010000000108026000006000017f030302000107120205737461727400000666696e69736800010801000a" + "0e02070003400c000b0b040041010b"; extern std::string const badAlignWasmHex = - "0061736d01000000011b046000017f60057f7f7f7f7f017f60067f7f7f7f7f7f017f600000022a0203656e760f666c6f61745f66726f6d5f75" - "696e74000103656e760c636865636b5f6b65796c6574000203050403000000050301000306470b7f004180080b7f00418088020b7f00418008" - "0b7f00418088040b7f00418088040b7f00418088080b7f004180080b7f00418088080b7f004180800c0b7f0041000b7f0041010b07cc011006" - "6d656d6f72790200115f5f7761736d5f63616c6c5f63746f72730002057465737431000307655f64617461310300057465737432000407655f" - "64617461320301047465737400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f737461636b5f6c6f7703040c" - "5f5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f6261736503070a5f5f686561705f656e640308" - "0d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a99020402000b2801017f418108427f370000418108410841a3" - "08410c41001000220041a40828020020004100481b0b5f01017f419a88024191a4cca00136010041928802428994ace0d0c1c3871037010041" - "8a88024281848ca0d0c0c1830837010041818802417f360000418a8802411441818802410441a3880241201001220041a48802280200200041" - "00481b0b8a0101037f418108427f370000418108410841a308410c410010002100419a88024191a4cca00136010041928802428994ace0d0c1" - "c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a4082802002101418a8802411441818802410441a3880241" - "201001220241a4880228020020024100481b2000200120004100481b6a0b007f0970726f647563657273010c70726f6365737365642d627901" - "05636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a" - "65637420616234623561326462353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f6665" - "617475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c74" - "6976616c7565"; + "0061736d01000000011b046000017f60057f7f7f7f7f017f60067f7f7f7f7f7f017f600000022a0203656e760f666c" + "6f61745f66726f6d5f75696e74000103656e760c636865636b5f6b65796c6574000203050403000000050301000306" + "470b7f004180080b7f00418088020b7f004180080b7f00418088040b7f00418088040b7f00418088080b7f00418008" + "0b7f00418088080b7f004180800c0b7f0041000b7f0041010b07cc0110066d656d6f72790200115f5f7761736d5f63" + "616c6c5f63746f72730002057465737431000307655f64617461310300057465737432000407655f64617461320301" + "047465737400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f737461636b5f6c6f77" + "03040c5f5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f626173650307" + "0a5f5f686561705f656e6403080d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a9902" + "0402000b2801017f418108427f370000418108410841a308410c41001000220041a40828020020004100481b0b5f01" + "017f419a88024191a4cca00136010041928802428994ace0d0c1c38710370100418a88024281848ca0d0c0c1830837" + "010041818802417f360000418a8802411441818802410441a3880241201001220041a4880228020020004100481b0b" + "8a0101037f418108427f370000418108410841a308410c410010002100419a88024191a4cca0013601004192880242" + "8994ace0d0c1c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a408280200210141" + "8a8802411441818802410441a3880241201001220241a4880228020020024100481b2000200120004100481b6a0b00" + "7f0970726f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e352d776173692d7364" + "6b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a6563742061623462356132" + "6462353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f666561" + "7475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d7479" + "7065732b0a6d756c746976616c7565"; + +extern std::string const thousandParamsHex = + "0061736d0100000001f1070260000060e8077f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f017f030302000105030100020638097f" + "004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f00418080080b7f0041" + "000b7f0041010b07a8010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f7273000004746573740001" + "0c5f5f64736f5f68616e646c6503000a5f5f646174615f656e6403010b5f5f737461636b5f6c6f7703020c5f5f7374" + "61636b5f6869676803030d5f5f676c6f62616c5f6261736503040b5f5f686561705f6261736503050a5f5f68656170" + "5f656e6403060d5f5f6d656d6f72795f6261736503070c5f5f7461626c655f6261736503080aa71e0202000ba11e00" + "200020016a20026a20036a20046a20056a20066a20076a20086a20096a200a6a200b6a200c6a200d6a200e6a200f6a" + "20106a20116a20126a20136a20146a20156a20166a20176a20186a20196a201a6a201b6a201c6a201d6a201e6a201f" + "6a20206a20216a20226a20236a20246a20256a20266a20276a20286a20296a202a6a202b6a202c6a202d6a202e6a20" + "2f6a20306a20316a20326a20336a20346a20356a20366a20376a20386a20396a203a6a203b6a203c6a203d6a203e6a" + "203f6a20406a20416a20426a20436a20446a20456a20466a20476a20486a20496a204a6a204b6a204c6a204d6a204e" + "6a204f6a20506a20516a20526a20536a20546a20556a20566a20576a20586a20596a205a6a205b6a205c6a205d6a20" + "5e6a205f6a20606a20616a20626a20636a20646a20656a20666a20676a20686a20696a206a6a206b6a206c6a206d6a" + "206e6a206f6a20706a20716a20726a20736a20746a20756a20766a20776a20786a20796a207a6a207b6a207c6a207d" + "6a207e6a207f6a2080016a2081016a2082016a2083016a2084016a2085016a2086016a2087016a2088016a2089016a" + "208a016a208b016a208c016a208d016a208e016a208f016a2090016a2091016a2092016a2093016a2094016a209501" + "6a2096016a2097016a2098016a2099016a209a016a209b016a209c016a209d016a209e016a209f016a20a0016a20a1" + "016a20a2016a20a3016a20a4016a20a5016a20a6016a20a7016a20a8016a20a9016a20aa016a20ab016a20ac016a20" + "ad016a20ae016a20af016a20b0016a20b1016a20b2016a20b3016a20b4016a20b5016a20b6016a20b7016a20b8016a" + "20b9016a20ba016a20bb016a20bc016a20bd016a20be016a20bf016a20c0016a20c1016a20c2016a20c3016a20c401" + "6a20c5016a20c6016a20c7016a20c8016a20c9016a20ca016a20cb016a20cc016a20cd016a20ce016a20cf016a20d0" + "016a20d1016a20d2016a20d3016a20d4016a20d5016a20d6016a20d7016a20d8016a20d9016a20da016a20db016a20" + "dc016a20dd016a20de016a20df016a20e0016a20e1016a20e2016a20e3016a20e4016a20e5016a20e6016a20e7016a" + "20e8016a20e9016a20ea016a20eb016a20ec016a20ed016a20ee016a20ef016a20f0016a20f1016a20f2016a20f301" + "6a20f4016a20f5016a20f6016a20f7016a20f8016a20f9016a20fa016a20fb016a20fc016a20fd016a20fe016a20ff" + "016a2080026a2081026a2082026a2083026a2084026a2085026a2086026a2087026a2088026a2089026a208a026a20" + "8b026a208c026a208d026a208e026a208f026a2090026a2091026a2092026a2093026a2094026a2095026a2096026a" + "2097026a2098026a2099026a209a026a209b026a209c026a209d026a209e026a209f026a20a0026a20a1026a20a202" + "6a20a3026a20a4026a20a5026a20a6026a20a7026a20a8026a20a9026a20aa026a20ab026a20ac026a20ad026a20ae" + "026a20af026a20b0026a20b1026a20b2026a20b3026a20b4026a20b5026a20b6026a20b7026a20b8026a20b9026a20" + "ba026a20bb026a20bc026a20bd026a20be026a20bf026a20c0026a20c1026a20c2026a20c3026a20c4026a20c5026a" + "20c6026a20c7026a20c8026a20c9026a20ca026a20cb026a20cc026a20cd026a20ce026a20cf026a20d0026a20d102" + "6a20d2026a20d3026a20d4026a20d5026a20d6026a20d7026a20d8026a20d9026a20da026a20db026a20dc026a20dd" + "026a20de026a20df026a20e0026a20e1026a20e2026a20e3026a20e4026a20e5026a20e6026a20e7026a20e8026a20" + "e9026a20ea026a20eb026a20ec026a20ed026a20ee026a20ef026a20f0026a20f1026a20f2026a20f3026a20f4026a" + "20f5026a20f6026a20f7026a20f8026a20f9026a20fa026a20fb026a20fc026a20fd026a20fe026a20ff026a208003" + "6a2081036a2082036a2083036a2084036a2085036a2086036a2087036a2088036a2089036a208a036a208b036a208c" + "036a208d036a208e036a208f036a2090036a2091036a2092036a2093036a2094036a2095036a2096036a2097036a20" + "98036a2099036a209a036a209b036a209c036a209d036a209e036a209f036a20a0036a20a1036a20a2036a20a3036a" + "20a4036a20a5036a20a6036a20a7036a20a8036a20a9036a20aa036a20ab036a20ac036a20ad036a20ae036a20af03" + "6a20b0036a20b1036a20b2036a20b3036a20b4036a20b5036a20b6036a20b7036a20b8036a20b9036a20ba036a20bb" + "036a20bc036a20bd036a20be036a20bf036a20c0036a20c1036a20c2036a20c3036a20c4036a20c5036a20c6036a20" + "c7036a20c8036a20c9036a20ca036a20cb036a20cc036a20cd036a20ce036a20cf036a20d0036a20d1036a20d2036a" + "20d3036a20d4036a20d5036a20d6036a20d7036a20d8036a20d9036a20da036a20db036a20dc036a20dd036a20de03" + "6a20df036a20e0036a20e1036a20e2036a20e3036a20e4036a20e5036a20e6036a20e7036a20e8036a20e9036a20ea" + "036a20eb036a20ec036a20ed036a20ee036a20ef036a20f0036a20f1036a20f2036a20f3036a20f4036a20f5036a20" + "f6036a20f7036a20f8036a20f9036a20fa036a20fb036a20fc036a20fd036a20fe036a20ff036a2080046a2081046a" + "2082046a2083046a2084046a2085046a2086046a2087046a2088046a2089046a208a046a208b046a208c046a208d04" + "6a208e046a208f046a2090046a2091046a2092046a2093046a2094046a2095046a2096046a2097046a2098046a2099" + "046a209a046a209b046a209c046a209d046a209e046a209f046a20a0046a20a1046a20a2046a20a3046a20a4046a20" + "a5046a20a6046a20a7046a20a8046a20a9046a20aa046a20ab046a20ac046a20ad046a20ae046a20af046a20b0046a" + "20b1046a20b2046a20b3046a20b4046a20b5046a20b6046a20b7046a20b8046a20b9046a20ba046a20bb046a20bc04" + "6a20bd046a20be046a20bf046a20c0046a20c1046a20c2046a20c3046a20c4046a20c5046a20c6046a20c7046a20c8" + "046a20c9046a20ca046a20cb046a20cc046a20cd046a20ce046a20cf046a20d0046a20d1046a20d2046a20d3046a20" + "d4046a20d5046a20d6046a20d7046a20d8046a20d9046a20da046a20db046a20dc046a20dd046a20de046a20df046a" + "20e0046a20e1046a20e2046a20e3046a20e4046a20e5046a20e6046a20e7046a20e8046a20e9046a20ea046a20eb04" + "6a20ec046a20ed046a20ee046a20ef046a20f0046a20f1046a20f2046a20f3046a20f4046a20f5046a20f6046a20f7" + "046a20f8046a20f9046a20fa046a20fb046a20fc046a20fd046a20fe046a20ff046a2080056a2081056a2082056a20" + "83056a2084056a2085056a2086056a2087056a2088056a2089056a208a056a208b056a208c056a208d056a208e056a" + "208f056a2090056a2091056a2092056a2093056a2094056a2095056a2096056a2097056a2098056a2099056a209a05" + "6a209b056a209c056a209d056a209e056a209f056a20a0056a20a1056a20a2056a20a3056a20a4056a20a5056a20a6" + "056a20a7056a20a8056a20a9056a20aa056a20ab056a20ac056a20ad056a20ae056a20af056a20b0056a20b1056a20" + "b2056a20b3056a20b4056a20b5056a20b6056a20b7056a20b8056a20b9056a20ba056a20bb056a20bc056a20bd056a" + "20be056a20bf056a20c0056a20c1056a20c2056a20c3056a20c4056a20c5056a20c6056a20c7056a20c8056a20c905" + "6a20ca056a20cb056a20cc056a20cd056a20ce056a20cf056a20d0056a20d1056a20d2056a20d3056a20d4056a20d5" + "056a20d6056a20d7056a20d8056a20d9056a20da056a20db056a20dc056a20dd056a20de056a20df056a20e0056a20" + "e1056a20e2056a20e3056a20e4056a20e5056a20e6056a20e7056a20e8056a20e9056a20ea056a20eb056a20ec056a" + "20ed056a20ee056a20ef056a20f0056a20f1056a20f2056a20f3056a20f4056a20f5056a20f6056a20f7056a20f805" + "6a20f9056a20fa056a20fb056a20fc056a20fd056a20fe056a20ff056a2080066a2081066a2082066a2083066a2084" + "066a2085066a2086066a2087066a2088066a2089066a208a066a208b066a208c066a208d066a208e066a208f066a20" + "90066a2091066a2092066a2093066a2094066a2095066a2096066a2097066a2098066a2099066a209a066a209b066a" + "209c066a209d066a209e066a209f066a20a0066a20a1066a20a2066a20a3066a20a4066a20a5066a20a6066a20a706" + "6a20a8066a20a9066a20aa066a20ab066a20ac066a20ad066a20ae066a20af066a20b0066a20b1066a20b2066a20b3" + "066a20b4066a20b5066a20b6066a20b7066a20b8066a20b9066a20ba066a20bb066a20bc066a20bd066a20be066a20" + "bf066a20c0066a20c1066a20c2066a20c3066a20c4066a20c5066a20c6066a20c7066a20c8066a20c9066a20ca066a" + "20cb066a20cc066a20cd066a20ce066a20cf066a20d0066a20d1066a20d2066a20d3066a20d4066a20d5066a20d606" + "6a20d7066a20d8066a20d9066a20da066a20db066a20dc066a20dd066a20de066a20df066a20e0066a20e1066a20e2" + "066a20e3066a20e4066a20e5066a20e6066a20e7066a20e8066a20e9066a20ea066a20eb066a20ec066a20ed066a20" + "ee066a20ef066a20f0066a20f1066a20f2066a20f3066a20f4066a20f5066a20f6066a20f7066a20f8066a20f9066a" + "20fa066a20fb066a20fc066a20fd066a20fe066a20ff066a2080076a2081076a2082076a2083076a2084076a208507" + "6a2086076a2087076a2088076a2089076a208a076a208b076a208c076a208d076a208e076a208f076a2090076a2091" + "076a2092076a2093076a2094076a2095076a2096076a2097076a2098076a2099076a209a076a209b076a209c076a20" + "9d076a209e076a209f076a20a0076a20a1076a20a2076a20a3076a20a4076a20a5076a20a6076a20a7076a20a8076a" + "20a9076a20aa076a20ab076a20ac076a20ad076a20ae076a20af076a20b0076a20b1076a20b2076a20b3076a20b407" + "6a20b5076a20b6076a20b7076a20b8076a20b9076a20ba076a20bb076a20bc076a20bd076a20be076a20bf076a20c0" + "076a20c1076a20c2076a20c3076a20c4076a20c5076a20c6076a20c7076a20c8076a20c9076a20ca076a20cb076a20" + "cc076a20cd076a20ce076a20cf076a20d0076a20d1076a20d2076a20d3076a20d4076a20d5076a20d6076a20d7076a" + "20d8076a20d9076a20da076a20db076a20dc076a20dd076a20de076a20df076a20e0076a20e1076a20e2076a20e307" + "6a20e4076a20e5076a20e6076a20e7076a0b007f0970726f647563657273010c70726f6365737365642d6279010563" + "6c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c" + "6c766d2d70726f6a656374206162346235613264623538323935386166316565333038613739306366646234326264" + "32343732302900490f7461726765745f6665617475726573042b0f6d757461626c652d676c6f62616c732b08736967" + "6e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; + +extern std::string const thousand1ParamsHex = + "0061736d0100000001f2070260000060e9077f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f017f03030200010503010002063809" + "7f004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f00418080080b7f00" + "41000b7f0041010b07a8010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f72730000047465737400" + "010c5f5f64736f5f68616e646c6503000a5f5f646174615f656e6403010b5f5f737461636b5f6c6f7703020c5f5f73" + "7461636b5f6869676803030d5f5f676c6f62616c5f6261736503040b5f5f686561705f6261736503050a5f5f686561" + "705f656e6403060d5f5f6d656d6f72795f6261736503070c5f5f7461626c655f6261736503080aab1e0202000ba51e" + "00200020016a20026a20036a20046a20056a20066a20076a20086a20096a200a6a200b6a200c6a200d6a200e6a200f" + "6a20106a20116a20126a20136a20146a20156a20166a20176a20186a20196a201a6a201b6a201c6a201d6a201e6a20" + "1f6a20206a20216a20226a20236a20246a20256a20266a20276a20286a20296a202a6a202b6a202c6a202d6a202e6a" + "202f6a20306a20316a20326a20336a20346a20356a20366a20376a20386a20396a203a6a203b6a203c6a203d6a203e" + "6a203f6a20406a20416a20426a20436a20446a20456a20466a20476a20486a20496a204a6a204b6a204c6a204d6a20" + "4e6a204f6a20506a20516a20526a20536a20546a20556a20566a20576a20586a20596a205a6a205b6a205c6a205d6a" + "205e6a205f6a20606a20616a20626a20636a20646a20656a20666a20676a20686a20696a206a6a206b6a206c6a206d" + "6a206e6a206f6a20706a20716a20726a20736a20746a20756a20766a20776a20786a20796a207a6a207b6a207c6a20" + "7d6a207e6a207f6a2080016a2081016a2082016a2083016a2084016a2085016a2086016a2087016a2088016a208901" + "6a208a016a208b016a208c016a208d016a208e016a208f016a2090016a2091016a2092016a2093016a2094016a2095" + "016a2096016a2097016a2098016a2099016a209a016a209b016a209c016a209d016a209e016a209f016a20a0016a20" + "a1016a20a2016a20a3016a20a4016a20a5016a20a6016a20a7016a20a8016a20a9016a20aa016a20ab016a20ac016a" + "20ad016a20ae016a20af016a20b0016a20b1016a20b2016a20b3016a20b4016a20b5016a20b6016a20b7016a20b801" + "6a20b9016a20ba016a20bb016a20bc016a20bd016a20be016a20bf016a20c0016a20c1016a20c2016a20c3016a20c4" + "016a20c5016a20c6016a20c7016a20c8016a20c9016a20ca016a20cb016a20cc016a20cd016a20ce016a20cf016a20" + "d0016a20d1016a20d2016a20d3016a20d4016a20d5016a20d6016a20d7016a20d8016a20d9016a20da016a20db016a" + "20dc016a20dd016a20de016a20df016a20e0016a20e1016a20e2016a20e3016a20e4016a20e5016a20e6016a20e701" + "6a20e8016a20e9016a20ea016a20eb016a20ec016a20ed016a20ee016a20ef016a20f0016a20f1016a20f2016a20f3" + "016a20f4016a20f5016a20f6016a20f7016a20f8016a20f9016a20fa016a20fb016a20fc016a20fd016a20fe016a20" + "ff016a2080026a2081026a2082026a2083026a2084026a2085026a2086026a2087026a2088026a2089026a208a026a" + "208b026a208c026a208d026a208e026a208f026a2090026a2091026a2092026a2093026a2094026a2095026a209602" + "6a2097026a2098026a2099026a209a026a209b026a209c026a209d026a209e026a209f026a20a0026a20a1026a20a2" + "026a20a3026a20a4026a20a5026a20a6026a20a7026a20a8026a20a9026a20aa026a20ab026a20ac026a20ad026a20" + "ae026a20af026a20b0026a20b1026a20b2026a20b3026a20b4026a20b5026a20b6026a20b7026a20b8026a20b9026a" + "20ba026a20bb026a20bc026a20bd026a20be026a20bf026a20c0026a20c1026a20c2026a20c3026a20c4026a20c502" + "6a20c6026a20c7026a20c8026a20c9026a20ca026a20cb026a20cc026a20cd026a20ce026a20cf026a20d0026a20d1" + "026a20d2026a20d3026a20d4026a20d5026a20d6026a20d7026a20d8026a20d9026a20da026a20db026a20dc026a20" + "dd026a20de026a20df026a20e0026a20e1026a20e2026a20e3026a20e4026a20e5026a20e6026a20e7026a20e8026a" + "20e9026a20ea026a20eb026a20ec026a20ed026a20ee026a20ef026a20f0026a20f1026a20f2026a20f3026a20f402" + "6a20f5026a20f6026a20f7026a20f8026a20f9026a20fa026a20fb026a20fc026a20fd026a20fe026a20ff026a2080" + "036a2081036a2082036a2083036a2084036a2085036a2086036a2087036a2088036a2089036a208a036a208b036a20" + "8c036a208d036a208e036a208f036a2090036a2091036a2092036a2093036a2094036a2095036a2096036a2097036a" + "2098036a2099036a209a036a209b036a209c036a209d036a209e036a209f036a20a0036a20a1036a20a2036a20a303" + "6a20a4036a20a5036a20a6036a20a7036a20a8036a20a9036a20aa036a20ab036a20ac036a20ad036a20ae036a20af" + "036a20b0036a20b1036a20b2036a20b3036a20b4036a20b5036a20b6036a20b7036a20b8036a20b9036a20ba036a20" + "bb036a20bc036a20bd036a20be036a20bf036a20c0036a20c1036a20c2036a20c3036a20c4036a20c5036a20c6036a" + "20c7036a20c8036a20c9036a20ca036a20cb036a20cc036a20cd036a20ce036a20cf036a20d0036a20d1036a20d203" + "6a20d3036a20d4036a20d5036a20d6036a20d7036a20d8036a20d9036a20da036a20db036a20dc036a20dd036a20de" + "036a20df036a20e0036a20e1036a20e2036a20e3036a20e4036a20e5036a20e6036a20e7036a20e8036a20e9036a20" + "ea036a20eb036a20ec036a20ed036a20ee036a20ef036a20f0036a20f1036a20f2036a20f3036a20f4036a20f5036a" + "20f6036a20f7036a20f8036a20f9036a20fa036a20fb036a20fc036a20fd036a20fe036a20ff036a2080046a208104" + "6a2082046a2083046a2084046a2085046a2086046a2087046a2088046a2089046a208a046a208b046a208c046a208d" + "046a208e046a208f046a2090046a2091046a2092046a2093046a2094046a2095046a2096046a2097046a2098046a20" + "99046a209a046a209b046a209c046a209d046a209e046a209f046a20a0046a20a1046a20a2046a20a3046a20a4046a" + "20a5046a20a6046a20a7046a20a8046a20a9046a20aa046a20ab046a20ac046a20ad046a20ae046a20af046a20b004" + "6a20b1046a20b2046a20b3046a20b4046a20b5046a20b6046a20b7046a20b8046a20b9046a20ba046a20bb046a20bc" + "046a20bd046a20be046a20bf046a20c0046a20c1046a20c2046a20c3046a20c4046a20c5046a20c6046a20c7046a20" + "c8046a20c9046a20ca046a20cb046a20cc046a20cd046a20ce046a20cf046a20d0046a20d1046a20d2046a20d3046a" + "20d4046a20d5046a20d6046a20d7046a20d8046a20d9046a20da046a20db046a20dc046a20dd046a20de046a20df04" + "6a20e0046a20e1046a20e2046a20e3046a20e4046a20e5046a20e6046a20e7046a20e8046a20e9046a20ea046a20eb" + "046a20ec046a20ed046a20ee046a20ef046a20f0046a20f1046a20f2046a20f3046a20f4046a20f5046a20f6046a20" + "f7046a20f8046a20f9046a20fa046a20fb046a20fc046a20fd046a20fe046a20ff046a2080056a2081056a2082056a" + "2083056a2084056a2085056a2086056a2087056a2088056a2089056a208a056a208b056a208c056a208d056a208e05" + "6a208f056a2090056a2091056a2092056a2093056a2094056a2095056a2096056a2097056a2098056a2099056a209a" + "056a209b056a209c056a209d056a209e056a209f056a20a0056a20a1056a20a2056a20a3056a20a4056a20a5056a20" + "a6056a20a7056a20a8056a20a9056a20aa056a20ab056a20ac056a20ad056a20ae056a20af056a20b0056a20b1056a" + "20b2056a20b3056a20b4056a20b5056a20b6056a20b7056a20b8056a20b9056a20ba056a20bb056a20bc056a20bd05" + "6a20be056a20bf056a20c0056a20c1056a20c2056a20c3056a20c4056a20c5056a20c6056a20c7056a20c8056a20c9" + "056a20ca056a20cb056a20cc056a20cd056a20ce056a20cf056a20d0056a20d1056a20d2056a20d3056a20d4056a20" + "d5056a20d6056a20d7056a20d8056a20d9056a20da056a20db056a20dc056a20dd056a20de056a20df056a20e0056a" + "20e1056a20e2056a20e3056a20e4056a20e5056a20e6056a20e7056a20e8056a20e9056a20ea056a20eb056a20ec05" + "6a20ed056a20ee056a20ef056a20f0056a20f1056a20f2056a20f3056a20f4056a20f5056a20f6056a20f7056a20f8" + "056a20f9056a20fa056a20fb056a20fc056a20fd056a20fe056a20ff056a2080066a2081066a2082066a2083066a20" + "84066a2085066a2086066a2087066a2088066a2089066a208a066a208b066a208c066a208d066a208e066a208f066a" + "2090066a2091066a2092066a2093066a2094066a2095066a2096066a2097066a2098066a2099066a209a066a209b06" + "6a209c066a209d066a209e066a209f066a20a0066a20a1066a20a2066a20a3066a20a4066a20a5066a20a6066a20a7" + "066a20a8066a20a9066a20aa066a20ab066a20ac066a20ad066a20ae066a20af066a20b0066a20b1066a20b2066a20" + "b3066a20b4066a20b5066a20b6066a20b7066a20b8066a20b9066a20ba066a20bb066a20bc066a20bd066a20be066a" + "20bf066a20c0066a20c1066a20c2066a20c3066a20c4066a20c5066a20c6066a20c7066a20c8066a20c9066a20ca06" + "6a20cb066a20cc066a20cd066a20ce066a20cf066a20d0066a20d1066a20d2066a20d3066a20d4066a20d5066a20d6" + "066a20d7066a20d8066a20d9066a20da066a20db066a20dc066a20dd066a20de066a20df066a20e0066a20e1066a20" + "e2066a20e3066a20e4066a20e5066a20e6066a20e7066a20e8066a20e9066a20ea066a20eb066a20ec066a20ed066a" + "20ee066a20ef066a20f0066a20f1066a20f2066a20f3066a20f4066a20f5066a20f6066a20f7066a20f8066a20f906" + "6a20fa066a20fb066a20fc066a20fd066a20fe066a20ff066a2080076a2081076a2082076a2083076a2084076a2085" + "076a2086076a2087076a2088076a2089076a208a076a208b076a208c076a208d076a208e076a208f076a2090076a20" + "91076a2092076a2093076a2094076a2095076a2096076a2097076a2098076a2099076a209a076a209b076a209c076a" + "209d076a209e076a209f076a20a0076a20a1076a20a2076a20a3076a20a4076a20a5076a20a6076a20a7076a20a807" + "6a20a9076a20aa076a20ab076a20ac076a20ad076a20ae076a20af076a20b0076a20b1076a20b2076a20b3076a20b4" + "076a20b5076a20b6076a20b7076a20b8076a20b9076a20ba076a20bb076a20bc076a20bd076a20be076a20bf076a20" + "c0076a20c1076a20c2076a20c3076a20c4076a20c5076a20c6076a20c7076a20c8076a20c9076a20ca076a20cb076a" + "20cc076a20cd076a20ce076a20cf076a20d0076a20d1076a20d2076a20d3076a20d4076a20d5076a20d6076a20d707" + "6a20d8076a20d9076a20da076a20db076a20dc076a20dd076a20de076a20df076a20e0076a20e1076a20e2076a20e3" + "076a20e4076a20e5076a20e6076a20e7076a20e8076a0b007f0970726f647563657273010c70726f6365737365642d" + "62790105636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c" + "6c766d2f6c6c766d2d70726f6a65637420616234623561326462353832393538616631656533303861373930636664" + "623432626432343732302900490f7461726765745f6665617475726573042b0f6d757461626c652d676c6f62616c73" + "2b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; + +extern std::string const locals10kHex = + "0061736d0100000001070160027f7f017f03020100070801047465737400000a9b8a0601978a06018e4e7f20002001" + "6a2102200120026a2103200220036a2104200320046a2105200420056a2106200520066a2107200620076a21082007" + "20086a2109200820096a210a2009200a6a210b200a200b6a210c200b200c6a210d200c200d6a210e200d200e6a210f" + "200e200f6a2110200f20106a2111201020116a2112201120126a2113201220136a2114201320146a2115201420156a" + "2116201520166a2117201620176a2118201720186a2119201820196a211a2019201a6a211b201a201b6a211c201b20" + "1c6a211d201c201d6a211e201d201e6a211f201e201f6a2120201f20206a2121202020216a2122202120226a212320" + "2220236a2124202320246a2125202420256a2126202520266a2127202620276a2128202720286a2129202820296a21" + "2a2029202a6a212b202a202b6a212c202b202c6a212d202c202d6a212e202d202e6a212f202e202f6a2130202f2030" + "6a2131203020316a2132203120326a2133203220336a2134203320346a2135203420356a2136203520366a21372036" + "20376a2138203720386a2139203820396a213a2039203a6a213b203a203b6a213c203b203c6a213d203c203d6a213e" + "203d203e6a213f203e203f6a2140203f20406a2141204020416a2142204120426a2143204220436a2144204320446a" + "2145204420456a2146204520466a2147204620476a2148204720486a2149204820496a214a2049204a6a214b204a20" + "4b6a214c204b204c6a214d204c204d6a214e204d204e6a214f204e204f6a2150204f20506a2151205020516a215220" + "5120526a2153205220536a2154205320546a2155205420556a2156205520566a2157205620576a2158205720586a21" + "59205820596a215a2059205a6a215b205a205b6a215c205b205c6a215d205c205d6a215e205d205e6a215f205e205f" + "6a2160205f20606a2161206020616a2162206120626a2163206220636a2164206320646a2165206420656a21662065" + "20666a2167206620676a2168206720686a2169206820696a216a2069206a6a216b206a206b6a216c206b206c6a216d" + "206c206d6a216e206d206e6a216f206e206f6a2170206f20706a2171207020716a2172207120726a2173207220736a" + "2174207320746a2175207420756a2176207520766a2177207620776a2178207720786a2179207820796a217a207920" + "7a6a217b207a207b6a217c207b207c6a217d207c207d6a217e207d207e6a217f207e207f6a218001207f2080016a21" + "81012080012081016a2182012081012082016a2183012082012083016a2184012083012084016a2185012084012085" + "016a2186012085012086016a2187012086012087016a2188012087012088016a2189012088012089016a218a012089" + "01208a016a218b01208a01208b016a218c01208b01208c016a218d01208c01208d016a218e01208d01208e016a218f" + "01208e01208f016a219001208f012090016a2191012090012091016a2192012091012092016a219301209201209301" + "6a2194012093012094016a2195012094012095016a2196012095012096016a2197012096012097016a219801209701" + "2098016a2199012098012099016a219a01209901209a016a219b01209a01209b016a219c01209b01209c016a219d01" + "209c01209d016a219e01209d01209e016a219f01209e01209f016a21a001209f0120a0016a21a10120a00120a1016a" + "21a20120a10120a2016a21a30120a20120a3016a21a40120a30120a4016a21a50120a40120a5016a21a60120a50120" + "a6016a21a70120a60120a7016a21a80120a70120a8016a21a90120a80120a9016a21aa0120a90120aa016a21ab0120" + "aa0120ab016a21ac0120ab0120ac016a21ad0120ac0120ad016a21ae0120ad0120ae016a21af0120ae0120af016a21" + "b00120af0120b0016a21b10120b00120b1016a21b20120b10120b2016a21b30120b20120b3016a21b40120b30120b4" + "016a21b50120b40120b5016a21b60120b50120b6016a21b70120b60120b7016a21b80120b70120b8016a21b90120b8" + "0120b9016a21ba0120b90120ba016a21bb0120ba0120bb016a21bc0120bb0120bc016a21bd0120bc0120bd016a21be" + "0120bd0120be016a21bf0120be0120bf016a21c00120bf0120c0016a21c10120c00120c1016a21c20120c10120c201" + "6a21c30120c20120c3016a21c40120c30120c4016a21c50120c40120c5016a21c60120c50120c6016a21c70120c601" + "20c7016a21c80120c70120c8016a21c90120c80120c9016a21ca0120c90120ca016a21cb0120ca0120cb016a21cc01" + "20cb0120cc016a21cd0120cc0120cd016a21ce0120cd0120ce016a21cf0120ce0120cf016a21d00120cf0120d0016a" + "21d10120d00120d1016a21d20120d10120d2016a21d30120d20120d3016a21d40120d30120d4016a21d50120d40120" + "d5016a21d60120d50120d6016a21d70120d60120d7016a21d80120d70120d8016a21d90120d80120d9016a21da0120" + "d90120da016a21db0120da0120db016a21dc0120db0120dc016a21dd0120dc0120dd016a21de0120dd0120de016a21" + "df0120de0120df016a21e00120df0120e0016a21e10120e00120e1016a21e20120e10120e2016a21e30120e20120e3" + "016a21e40120e30120e4016a21e50120e40120e5016a21e60120e50120e6016a21e70120e60120e7016a21e80120e7" + "0120e8016a21e90120e80120e9016a21ea0120e90120ea016a21eb0120ea0120eb016a21ec0120eb0120ec016a21ed" + "0120ec0120ed016a21ee0120ed0120ee016a21ef0120ee0120ef016a21f00120ef0120f0016a21f10120f00120f101" + "6a21f20120f10120f2016a21f30120f20120f3016a21f40120f30120f4016a21f50120f40120f5016a21f60120f501" + "20f6016a21f70120f60120f7016a21f80120f70120f8016a21f90120f80120f9016a21fa0120f90120fa016a21fb01" + "20fa0120fb016a21fc0120fb0120fc016a21fd0120fc0120fd016a21fe0120fd0120fe016a21ff0120fe0120ff016a" + "21800220ff012080026a2181022080022081026a2182022081022082026a2183022082022083026a21840220830220" + "84026a2185022084022085026a2186022085022086026a2187022086022087026a2188022087022088026a21890220" + "88022089026a218a02208902208a026a218b02208a02208b026a218c02208b02208c026a218d02208c02208d026a21" + "8e02208d02208e026a218f02208e02208f026a219002208f022090026a2191022090022091026a2192022091022092" + "026a2193022092022093026a2194022093022094026a2195022094022095026a2196022095022096026a2197022096" + "022097026a2198022097022098026a2199022098022099026a219a02209902209a026a219b02209a02209b026a219c" + "02209b02209c026a219d02209c02209d026a219e02209d02209e026a219f02209e02209f026a21a002209f0220a002" + "6a21a10220a00220a1026a21a20220a10220a2026a21a30220a20220a3026a21a40220a30220a4026a21a50220a402" + "20a5026a21a60220a50220a6026a21a70220a60220a7026a21a80220a70220a8026a21a90220a80220a9026a21aa02" + "20a90220aa026a21ab0220aa0220ab026a21ac0220ab0220ac026a21ad0220ac0220ad026a21ae0220ad0220ae026a" + "21af0220ae0220af026a21b00220af0220b0026a21b10220b00220b1026a21b20220b10220b2026a21b30220b20220" + "b3026a21b40220b30220b4026a21b50220b40220b5026a21b60220b50220b6026a21b70220b60220b7026a21b80220" + "b70220b8026a21b90220b80220b9026a21ba0220b90220ba026a21bb0220ba0220bb026a21bc0220bb0220bc026a21" + "bd0220bc0220bd026a21be0220bd0220be026a21bf0220be0220bf026a21c00220bf0220c0026a21c10220c00220c1" + "026a21c20220c10220c2026a21c30220c20220c3026a21c40220c30220c4026a21c50220c40220c5026a21c60220c5" + "0220c6026a21c70220c60220c7026a21c80220c70220c8026a21c90220c80220c9026a21ca0220c90220ca026a21cb" + "0220ca0220cb026a21cc0220cb0220cc026a21cd0220cc0220cd026a21ce0220cd0220ce026a21cf0220ce0220cf02" + "6a21d00220cf0220d0026a21d10220d00220d1026a21d20220d10220d2026a21d30220d20220d3026a21d40220d302" + "20d4026a21d50220d40220d5026a21d60220d50220d6026a21d70220d60220d7026a21d80220d70220d8026a21d902" + "20d80220d9026a21da0220d90220da026a21db0220da0220db026a21dc0220db0220dc026a21dd0220dc0220dd026a" + "21de0220dd0220de026a21df0220de0220df026a21e00220df0220e0026a21e10220e00220e1026a21e20220e10220" + "e2026a21e30220e20220e3026a21e40220e30220e4026a21e50220e40220e5026a21e60220e50220e6026a21e70220" + "e60220e7026a21e80220e70220e8026a21e90220e80220e9026a21ea0220e90220ea026a21eb0220ea0220eb026a21" + "ec0220eb0220ec026a21ed0220ec0220ed026a21ee0220ed0220ee026a21ef0220ee0220ef026a21f00220ef0220f0" + "026a21f10220f00220f1026a21f20220f10220f2026a21f30220f20220f3026a21f40220f30220f4026a21f50220f4" + "0220f5026a21f60220f50220f6026a21f70220f60220f7026a21f80220f70220f8026a21f90220f80220f9026a21fa" + "0220f90220fa026a21fb0220fa0220fb026a21fc0220fb0220fc026a21fd0220fc0220fd026a21fe0220fd0220fe02" + "6a21ff0220fe0220ff026a21800320ff022080036a2181032080032081036a2182032081032082036a218303208203" + "2083036a2184032083032084036a2185032084032085036a2186032085032086036a2187032086032087036a218803" + "2087032088036a2189032088032089036a218a03208903208a036a218b03208a03208b036a218c03208b03208c036a" + "218d03208c03208d036a218e03208d03208e036a218f03208e03208f036a219003208f032090036a21910320900320" + "91036a2192032091032092036a2193032092032093036a2194032093032094036a2195032094032095036a21960320" + "95032096036a2197032096032097036a2198032097032098036a2199032098032099036a219a03209903209a036a21" + "9b03209a03209b036a219c03209b03209c036a219d03209c03209d036a219e03209d03209e036a219f03209e03209f" + "036a21a003209f0320a0036a21a10320a00320a1036a21a20320a10320a2036a21a30320a20320a3036a21a40320a3" + "0320a4036a21a50320a40320a5036a21a60320a50320a6036a21a70320a60320a7036a21a80320a70320a8036a21a9" + "0320a80320a9036a21aa0320a90320aa036a21ab0320aa0320ab036a21ac0320ab0320ac036a21ad0320ac0320ad03" + "6a21ae0320ad0320ae036a21af0320ae0320af036a21b00320af0320b0036a21b10320b00320b1036a21b20320b103" + "20b2036a21b30320b20320b3036a21b40320b30320b4036a21b50320b40320b5036a21b60320b50320b6036a21b703" + "20b60320b7036a21b80320b70320b8036a21b90320b80320b9036a21ba0320b90320ba036a21bb0320ba0320bb036a" + "21bc0320bb0320bc036a21bd0320bc0320bd036a21be0320bd0320be036a21bf0320be0320bf036a21c00320bf0320" + "c0036a21c10320c00320c1036a21c20320c10320c2036a21c30320c20320c3036a21c40320c30320c4036a21c50320" + "c40320c5036a21c60320c50320c6036a21c70320c60320c7036a21c80320c70320c8036a21c90320c80320c9036a21" + "ca0320c90320ca036a21cb0320ca0320cb036a21cc0320cb0320cc036a21cd0320cc0320cd036a21ce0320cd0320ce" + "036a21cf0320ce0320cf036a21d00320cf0320d0036a21d10320d00320d1036a21d20320d10320d2036a21d30320d2" + "0320d3036a21d40320d30320d4036a21d50320d40320d5036a21d60320d50320d6036a21d70320d60320d7036a21d8" + "0320d70320d8036a21d90320d80320d9036a21da0320d90320da036a21db0320da0320db036a21dc0320db0320dc03" + "6a21dd0320dc0320dd036a21de0320dd0320de036a21df0320de0320df036a21e00320df0320e0036a21e10320e003" + "20e1036a21e20320e10320e2036a21e30320e20320e3036a21e40320e30320e4036a21e50320e40320e5036a21e603" + "20e50320e6036a21e70320e60320e7036a21e80320e70320e8036a21e90320e80320e9036a21ea0320e90320ea036a" + "21eb0320ea0320eb036a21ec0320eb0320ec036a21ed0320ec0320ed036a21ee0320ed0320ee036a21ef0320ee0320" + "ef036a21f00320ef0320f0036a21f10320f00320f1036a21f20320f10320f2036a21f30320f20320f3036a21f40320" + "f30320f4036a21f50320f40320f5036a21f60320f50320f6036a21f70320f60320f7036a21f80320f70320f8036a21" + "f90320f80320f9036a21fa0320f90320fa036a21fb0320fa0320fb036a21fc0320fb0320fc036a21fd0320fc0320fd" + "036a21fe0320fd0320fe036a21ff0320fe0320ff036a21800420ff032080046a2181042080042081046a2182042081" + "042082046a2183042082042083046a2184042083042084046a2185042084042085046a2186042085042086046a2187" + "042086042087046a2188042087042088046a2189042088042089046a218a04208904208a046a218b04208a04208b04" + "6a218c04208b04208c046a218d04208c04208d046a218e04208d04208e046a218f04208e04208f046a219004208f04" + "2090046a2191042090042091046a2192042091042092046a2193042092042093046a2194042093042094046a219504" + "2094042095046a2196042095042096046a2197042096042097046a2198042097042098046a2199042098042099046a" + "219a04209904209a046a219b04209a04209b046a219c04209b04209c046a219d04209c04209d046a219e04209d0420" + "9e046a219f04209e04209f046a21a004209f0420a0046a21a10420a00420a1046a21a20420a10420a2046a21a30420" + "a20420a3046a21a40420a30420a4046a21a50420a40420a5046a21a60420a50420a6046a21a70420a60420a7046a21" + "a80420a70420a8046a21a90420a80420a9046a21aa0420a90420aa046a21ab0420aa0420ab046a21ac0420ab0420ac" + "046a21ad0420ac0420ad046a21ae0420ad0420ae046a21af0420ae0420af046a21b00420af0420b0046a21b10420b0" + "0420b1046a21b20420b10420b2046a21b30420b20420b3046a21b40420b30420b4046a21b50420b40420b5046a21b6" + "0420b50420b6046a21b70420b60420b7046a21b80420b70420b8046a21b90420b80420b9046a21ba0420b90420ba04" + "6a21bb0420ba0420bb046a21bc0420bb0420bc046a21bd0420bc0420bd046a21be0420bd0420be046a21bf0420be04" + "20bf046a21c00420bf0420c0046a21c10420c00420c1046a21c20420c10420c2046a21c30420c20420c3046a21c404" + "20c30420c4046a21c50420c40420c5046a21c60420c50420c6046a21c70420c60420c7046a21c80420c70420c8046a" + "21c90420c80420c9046a21ca0420c90420ca046a21cb0420ca0420cb046a21cc0420cb0420cc046a21cd0420cc0420" + "cd046a21ce0420cd0420ce046a21cf0420ce0420cf046a21d00420cf0420d0046a21d10420d00420d1046a21d20420" + "d10420d2046a21d30420d20420d3046a21d40420d30420d4046a21d50420d40420d5046a21d60420d50420d6046a21" + "d70420d60420d7046a21d80420d70420d8046a21d90420d80420d9046a21da0420d90420da046a21db0420da0420db" + "046a21dc0420db0420dc046a21dd0420dc0420dd046a21de0420dd0420de046a21df0420de0420df046a21e00420df" + "0420e0046a21e10420e00420e1046a21e20420e10420e2046a21e30420e20420e3046a21e40420e30420e4046a21e5" + "0420e40420e5046a21e60420e50420e6046a21e70420e60420e7046a21e80420e70420e8046a21e90420e80420e904" + "6a21ea0420e90420ea046a21eb0420ea0420eb046a21ec0420eb0420ec046a21ed0420ec0420ed046a21ee0420ed04" + "20ee046a21ef0420ee0420ef046a21f00420ef0420f0046a21f10420f00420f1046a21f20420f10420f2046a21f304" + "20f20420f3046a21f40420f30420f4046a21f50420f40420f5046a21f60420f50420f6046a21f70420f60420f7046a" + "21f80420f70420f8046a21f90420f80420f9046a21fa0420f90420fa046a21fb0420fa0420fb046a21fc0420fb0420" + "fc046a21fd0420fc0420fd046a21fe0420fd0420fe046a21ff0420fe0420ff046a21800520ff042080056a21810520" + "80052081056a2182052081052082056a2183052082052083056a2184052083052084056a2185052084052085056a21" + "86052085052086056a2187052086052087056a2188052087052088056a2189052088052089056a218a05208905208a" + "056a218b05208a05208b056a218c05208b05208c056a218d05208c05208d056a218e05208d05208e056a218f05208e" + "05208f056a219005208f052090056a2191052090052091056a2192052091052092056a2193052092052093056a2194" + "052093052094056a2195052094052095056a2196052095052096056a2197052096052097056a219805209705209805" + "6a2199052098052099056a219a05209905209a056a219b05209a05209b056a219c05209b05209c056a219d05209c05" + "209d056a219e05209d05209e056a219f05209e05209f056a21a005209f0520a0056a21a10520a00520a1056a21a205" + "20a10520a2056a21a30520a20520a3056a21a40520a30520a4056a21a50520a40520a5056a21a60520a50520a6056a" + "21a70520a60520a7056a21a80520a70520a8056a21a90520a80520a9056a21aa0520a90520aa056a21ab0520aa0520" + "ab056a21ac0520ab0520ac056a21ad0520ac0520ad056a21ae0520ad0520ae056a21af0520ae0520af056a21b00520" + "af0520b0056a21b10520b00520b1056a21b20520b10520b2056a21b30520b20520b3056a21b40520b30520b4056a21" + "b50520b40520b5056a21b60520b50520b6056a21b70520b60520b7056a21b80520b70520b8056a21b90520b80520b9" + "056a21ba0520b90520ba056a21bb0520ba0520bb056a21bc0520bb0520bc056a21bd0520bc0520bd056a21be0520bd" + "0520be056a21bf0520be0520bf056a21c00520bf0520c0056a21c10520c00520c1056a21c20520c10520c2056a21c3" + "0520c20520c3056a21c40520c30520c4056a21c50520c40520c5056a21c60520c50520c6056a21c70520c60520c705" + "6a21c80520c70520c8056a21c90520c80520c9056a21ca0520c90520ca056a21cb0520ca0520cb056a21cc0520cb05" + "20cc056a21cd0520cc0520cd056a21ce0520cd0520ce056a21cf0520ce0520cf056a21d00520cf0520d0056a21d105" + "20d00520d1056a21d20520d10520d2056a21d30520d20520d3056a21d40520d30520d4056a21d50520d40520d5056a" + "21d60520d50520d6056a21d70520d60520d7056a21d80520d70520d8056a21d90520d80520d9056a21da0520d90520" + "da056a21db0520da0520db056a21dc0520db0520dc056a21dd0520dc0520dd056a21de0520dd0520de056a21df0520" + "de0520df056a21e00520df0520e0056a21e10520e00520e1056a21e20520e10520e2056a21e30520e20520e3056a21" + "e40520e30520e4056a21e50520e40520e5056a21e60520e50520e6056a21e70520e60520e7056a21e80520e70520e8" + "056a21e90520e80520e9056a21ea0520e90520ea056a21eb0520ea0520eb056a21ec0520eb0520ec056a21ed0520ec" + "0520ed056a21ee0520ed0520ee056a21ef0520ee0520ef056a21f00520ef0520f0056a21f10520f00520f1056a21f2" + "0520f10520f2056a21f30520f20520f3056a21f40520f30520f4056a21f50520f40520f5056a21f60520f50520f605" + "6a21f70520f60520f7056a21f80520f70520f8056a21f90520f80520f9056a21fa0520f90520fa056a21fb0520fa05" + "20fb056a21fc0520fb0520fc056a21fd0520fc0520fd056a21fe0520fd0520fe056a21ff0520fe0520ff056a218006" + "20ff052080066a2181062080062081066a2182062081062082066a2183062082062083066a2184062083062084066a" + "2185062084062085066a2186062085062086066a2187062086062087066a2188062087062088066a21890620880620" + "89066a218a06208906208a066a218b06208a06208b066a218c06208b06208c066a218d06208c06208d066a218e0620" + "8d06208e066a218f06208e06208f066a219006208f062090066a2191062090062091066a2192062091062092066a21" + "93062092062093066a2194062093062094066a2195062094062095066a2196062095062096066a2197062096062097" + "066a2198062097062098066a2199062098062099066a219a06209906209a066a219b06209a06209b066a219c06209b" + "06209c066a219d06209c06209d066a219e06209d06209e066a219f06209e06209f066a21a006209f0620a0066a21a1" + "0620a00620a1066a21a20620a10620a2066a21a30620a20620a3066a21a40620a30620a4066a21a50620a40620a506" + "6a21a60620a50620a6066a21a70620a60620a7066a21a80620a70620a8066a21a90620a80620a9066a21aa0620a906" + "20aa066a21ab0620aa0620ab066a21ac0620ab0620ac066a21ad0620ac0620ad066a21ae0620ad0620ae066a21af06" + "20ae0620af066a21b00620af0620b0066a21b10620b00620b1066a21b20620b10620b2066a21b30620b20620b3066a" + "21b40620b30620b4066a21b50620b40620b5066a21b60620b50620b6066a21b70620b60620b7066a21b80620b70620" + "b8066a21b90620b80620b9066a21ba0620b90620ba066a21bb0620ba0620bb066a21bc0620bb0620bc066a21bd0620" + "bc0620bd066a21be0620bd0620be066a21bf0620be0620bf066a21c00620bf0620c0066a21c10620c00620c1066a21" + "c20620c10620c2066a21c30620c20620c3066a21c40620c30620c4066a21c50620c40620c5066a21c60620c50620c6" + "066a21c70620c60620c7066a21c80620c70620c8066a21c90620c80620c9066a21ca0620c90620ca066a21cb0620ca" + "0620cb066a21cc0620cb0620cc066a21cd0620cc0620cd066a21ce0620cd0620ce066a21cf0620ce0620cf066a21d0" + "0620cf0620d0066a21d10620d00620d1066a21d20620d10620d2066a21d30620d20620d3066a21d40620d30620d406" + "6a21d50620d40620d5066a21d60620d50620d6066a21d70620d60620d7066a21d80620d70620d8066a21d90620d806" + "20d9066a21da0620d90620da066a21db0620da0620db066a21dc0620db0620dc066a21dd0620dc0620dd066a21de06" + "20dd0620de066a21df0620de0620df066a21e00620df0620e0066a21e10620e00620e1066a21e20620e10620e2066a" + "21e30620e20620e3066a21e40620e30620e4066a21e50620e40620e5066a21e60620e50620e6066a21e70620e60620" + "e7066a21e80620e70620e8066a21e90620e80620e9066a21ea0620e90620ea066a21eb0620ea0620eb066a21ec0620" + "eb0620ec066a21ed0620ec0620ed066a21ee0620ed0620ee066a21ef0620ee0620ef066a21f00620ef0620f0066a21" + "f10620f00620f1066a21f20620f10620f2066a21f30620f20620f3066a21f40620f30620f4066a21f50620f40620f5" + "066a21f60620f50620f6066a21f70620f60620f7066a21f80620f70620f8066a21f90620f80620f9066a21fa0620f9" + "0620fa066a21fb0620fa0620fb066a21fc0620fb0620fc066a21fd0620fc0620fd066a21fe0620fd0620fe066a21ff" + "0620fe0620ff066a21800720ff062080076a2181072080072081076a2182072081072082076a218307208207208307" + "6a2184072083072084076a2185072084072085076a2186072085072086076a2187072086072087076a218807208707" + "2088076a2189072088072089076a218a07208907208a076a218b07208a07208b076a218c07208b07208c076a218d07" + "208c07208d076a218e07208d07208e076a218f07208e07208f076a219007208f072090076a2191072090072091076a" + "2192072091072092076a2193072092072093076a2194072093072094076a2195072094072095076a21960720950720" + "96076a2197072096072097076a2198072097072098076a2199072098072099076a219a07209907209a076a219b0720" + "9a07209b076a219c07209b07209c076a219d07209c07209d076a219e07209d07209e076a219f07209e07209f076a21" + "a007209f0720a0076a21a10720a00720a1076a21a20720a10720a2076a21a30720a20720a3076a21a40720a30720a4" + "076a21a50720a40720a5076a21a60720a50720a6076a21a70720a60720a7076a21a80720a70720a8076a21a90720a8" + "0720a9076a21aa0720a90720aa076a21ab0720aa0720ab076a21ac0720ab0720ac076a21ad0720ac0720ad076a21ae" + "0720ad0720ae076a21af0720ae0720af076a21b00720af0720b0076a21b10720b00720b1076a21b20720b10720b207" + "6a21b30720b20720b3076a21b40720b30720b4076a21b50720b40720b5076a21b60720b50720b6076a21b70720b607" + "20b7076a21b80720b70720b8076a21b90720b80720b9076a21ba0720b90720ba076a21bb0720ba0720bb076a21bc07" + "20bb0720bc076a21bd0720bc0720bd076a21be0720bd0720be076a21bf0720be0720bf076a21c00720bf0720c0076a" + "21c10720c00720c1076a21c20720c10720c2076a21c30720c20720c3076a21c40720c30720c4076a21c50720c40720" + "c5076a21c60720c50720c6076a21c70720c60720c7076a21c80720c70720c8076a21c90720c80720c9076a21ca0720" + "c90720ca076a21cb0720ca0720cb076a21cc0720cb0720cc076a21cd0720cc0720cd076a21ce0720cd0720ce076a21" + "cf0720ce0720cf076a21d00720cf0720d0076a21d10720d00720d1076a21d20720d10720d2076a21d30720d20720d3" + "076a21d40720d30720d4076a21d50720d40720d5076a21d60720d50720d6076a21d70720d60720d7076a21d80720d7" + "0720d8076a21d90720d80720d9076a21da0720d90720da076a21db0720da0720db076a21dc0720db0720dc076a21dd" + "0720dc0720dd076a21de0720dd0720de076a21df0720de0720df076a21e00720df0720e0076a21e10720e00720e107" + "6a21e20720e10720e2076a21e30720e20720e3076a21e40720e30720e4076a21e50720e40720e5076a21e60720e507" + "20e6076a21e70720e60720e7076a21e80720e70720e8076a21e90720e80720e9076a21ea0720e90720ea076a21eb07" + "20ea0720eb076a21ec0720eb0720ec076a21ed0720ec0720ed076a21ee0720ed0720ee076a21ef0720ee0720ef076a" + "21f00720ef0720f0076a21f10720f00720f1076a21f20720f10720f2076a21f30720f20720f3076a21f40720f30720" + "f4076a21f50720f40720f5076a21f60720f50720f6076a21f70720f60720f7076a21f80720f70720f8076a21f90720" + "f80720f9076a21fa0720f90720fa076a21fb0720fa0720fb076a21fc0720fb0720fc076a21fd0720fc0720fd076a21" + "fe0720fd0720fe076a21ff0720fe0720ff076a21800820ff072080086a2181082080082081086a2182082081082082" + "086a2183082082082083086a2184082083082084086a2185082084082085086a2186082085082086086a2187082086" + "082087086a2188082087082088086a2189082088082089086a218a08208908208a086a218b08208a08208b086a218c" + "08208b08208c086a218d08208c08208d086a218e08208d08208e086a218f08208e08208f086a219008208f08209008" + "6a2191082090082091086a2192082091082092086a2193082092082093086a2194082093082094086a219508209408" + "2095086a2196082095082096086a2197082096082097086a2198082097082098086a2199082098082099086a219a08" + "209908209a086a219b08209a08209b086a219c08209b08209c086a219d08209c08209d086a219e08209d08209e086a" + "219f08209e08209f086a21a008209f0820a0086a21a10820a00820a1086a21a20820a10820a2086a21a30820a20820" + "a3086a21a40820a30820a4086a21a50820a40820a5086a21a60820a50820a6086a21a70820a60820a7086a21a80820" + "a70820a8086a21a90820a80820a9086a21aa0820a90820aa086a21ab0820aa0820ab086a21ac0820ab0820ac086a21" + "ad0820ac0820ad086a21ae0820ad0820ae086a21af0820ae0820af086a21b00820af0820b0086a21b10820b00820b1" + "086a21b20820b10820b2086a21b30820b20820b3086a21b40820b30820b4086a21b50820b40820b5086a21b60820b5" + "0820b6086a21b70820b60820b7086a21b80820b70820b8086a21b90820b80820b9086a21ba0820b90820ba086a21bb" + "0820ba0820bb086a21bc0820bb0820bc086a21bd0820bc0820bd086a21be0820bd0820be086a21bf0820be0820bf08" + "6a21c00820bf0820c0086a21c10820c00820c1086a21c20820c10820c2086a21c30820c20820c3086a21c40820c308" + "20c4086a21c50820c40820c5086a21c60820c50820c6086a21c70820c60820c7086a21c80820c70820c8086a21c908" + "20c80820c9086a21ca0820c90820ca086a21cb0820ca0820cb086a21cc0820cb0820cc086a21cd0820cc0820cd086a" + "21ce0820cd0820ce086a21cf0820ce0820cf086a21d00820cf0820d0086a21d10820d00820d1086a21d20820d10820" + "d2086a21d30820d20820d3086a21d40820d30820d4086a21d50820d40820d5086a21d60820d50820d6086a21d70820" + "d60820d7086a21d80820d70820d8086a21d90820d80820d9086a21da0820d90820da086a21db0820da0820db086a21" + "dc0820db0820dc086a21dd0820dc0820dd086a21de0820dd0820de086a21df0820de0820df086a21e00820df0820e0" + "086a21e10820e00820e1086a21e20820e10820e2086a21e30820e20820e3086a21e40820e30820e4086a21e50820e4" + "0820e5086a21e60820e50820e6086a21e70820e60820e7086a21e80820e70820e8086a21e90820e80820e9086a21ea" + "0820e90820ea086a21eb0820ea0820eb086a21ec0820eb0820ec086a21ed0820ec0820ed086a21ee0820ed0820ee08" + "6a21ef0820ee0820ef086a21f00820ef0820f0086a21f10820f00820f1086a21f20820f10820f2086a21f30820f208" + "20f3086a21f40820f30820f4086a21f50820f40820f5086a21f60820f50820f6086a21f70820f60820f7086a21f808" + "20f70820f8086a21f90820f80820f9086a21fa0820f90820fa086a21fb0820fa0820fb086a21fc0820fb0820fc086a" + "21fd0820fc0820fd086a21fe0820fd0820fe086a21ff0820fe0820ff086a21800920ff082080096a21810920800920" + "81096a2182092081092082096a2183092082092083096a2184092083092084096a2185092084092085096a21860920" + "85092086096a2187092086092087096a2188092087092088096a2189092088092089096a218a09208909208a096a21" + "8b09208a09208b096a218c09208b09208c096a218d09208c09208d096a218e09208d09208e096a218f09208e09208f" + "096a219009208f092090096a2191092090092091096a2192092091092092096a2193092092092093096a2194092093" + "092094096a2195092094092095096a2196092095092096096a2197092096092097096a2198092097092098096a2199" + "092098092099096a219a09209909209a096a219b09209a09209b096a219c09209b09209c096a219d09209c09209d09" + "6a219e09209d09209e096a219f09209e09209f096a21a009209f0920a0096a21a10920a00920a1096a21a20920a109" + "20a2096a21a30920a20920a3096a21a40920a30920a4096a21a50920a40920a5096a21a60920a50920a6096a21a709" + "20a60920a7096a21a80920a70920a8096a21a90920a80920a9096a21aa0920a90920aa096a21ab0920aa0920ab096a" + "21ac0920ab0920ac096a21ad0920ac0920ad096a21ae0920ad0920ae096a21af0920ae0920af096a21b00920af0920" + "b0096a21b10920b00920b1096a21b20920b10920b2096a21b30920b20920b3096a21b40920b30920b4096a21b50920" + "b40920b5096a21b60920b50920b6096a21b70920b60920b7096a21b80920b70920b8096a21b90920b80920b9096a21" + "ba0920b90920ba096a21bb0920ba0920bb096a21bc0920bb0920bc096a21bd0920bc0920bd096a21be0920bd0920be" + "096a21bf0920be0920bf096a21c00920bf0920c0096a21c10920c00920c1096a21c20920c10920c2096a21c30920c2" + "0920c3096a21c40920c30920c4096a21c50920c40920c5096a21c60920c50920c6096a21c70920c60920c7096a21c8" + "0920c70920c8096a21c90920c80920c9096a21ca0920c90920ca096a21cb0920ca0920cb096a21cc0920cb0920cc09" + "6a21cd0920cc0920cd096a21ce0920cd0920ce096a21cf0920ce0920cf096a21d00920cf0920d0096a21d10920d009" + "20d1096a21d20920d10920d2096a21d30920d20920d3096a21d40920d30920d4096a21d50920d40920d5096a21d609" + "20d50920d6096a21d70920d60920d7096a21d80920d70920d8096a21d90920d80920d9096a21da0920d90920da096a" + "21db0920da0920db096a21dc0920db0920dc096a21dd0920dc0920dd096a21de0920dd0920de096a21df0920de0920" + "df096a21e00920df0920e0096a21e10920e00920e1096a21e20920e10920e2096a21e30920e20920e3096a21e40920" + "e30920e4096a21e50920e40920e5096a21e60920e50920e6096a21e70920e60920e7096a21e80920e70920e8096a21" + "e90920e80920e9096a21ea0920e90920ea096a21eb0920ea0920eb096a21ec0920eb0920ec096a21ed0920ec0920ed" + "096a21ee0920ed0920ee096a21ef0920ee0920ef096a21f00920ef0920f0096a21f10920f00920f1096a21f20920f1" + "0920f2096a21f30920f20920f3096a21f40920f30920f4096a21f50920f40920f5096a21f60920f50920f6096a21f7" + "0920f60920f7096a21f80920f70920f8096a21f90920f80920f9096a21fa0920f90920fa096a21fb0920fa0920fb09" + "6a21fc0920fb0920fc096a21fd0920fc0920fd096a21fe0920fd0920fe096a21ff0920fe0920ff096a21800a20ff09" + "20800a6a21810a20800a20810a6a21820a20810a20820a6a21830a20820a20830a6a21840a20830a20840a6a21850a" + "20840a20850a6a21860a20850a20860a6a21870a20860a20870a6a21880a20870a20880a6a21890a20880a20890a6a" + "218a0a20890a208a0a6a218b0a208a0a208b0a6a218c0a208b0a208c0a6a218d0a208c0a208d0a6a218e0a208d0a20" + "8e0a6a218f0a208e0a208f0a6a21900a208f0a20900a6a21910a20900a20910a6a21920a20910a20920a6a21930a20" + "920a20930a6a21940a20930a20940a6a21950a20940a20950a6a21960a20950a20960a6a21970a20960a20970a6a21" + "980a20970a20980a6a21990a20980a20990a6a219a0a20990a209a0a6a219b0a209a0a209b0a6a219c0a209b0a209c" + "0a6a219d0a209c0a209d0a6a219e0a209d0a209e0a6a219f0a209e0a209f0a6a21a00a209f0a20a00a6a21a10a20a0" + "0a20a10a6a21a20a20a10a20a20a6a21a30a20a20a20a30a6a21a40a20a30a20a40a6a21a50a20a40a20a50a6a21a6" + "0a20a50a20a60a6a21a70a20a60a20a70a6a21a80a20a70a20a80a6a21a90a20a80a20a90a6a21aa0a20a90a20aa0a" + "6a21ab0a20aa0a20ab0a6a21ac0a20ab0a20ac0a6a21ad0a20ac0a20ad0a6a21ae0a20ad0a20ae0a6a21af0a20ae0a" + "20af0a6a21b00a20af0a20b00a6a21b10a20b00a20b10a6a21b20a20b10a20b20a6a21b30a20b20a20b30a6a21b40a" + "20b30a20b40a6a21b50a20b40a20b50a6a21b60a20b50a20b60a6a21b70a20b60a20b70a6a21b80a20b70a20b80a6a" + "21b90a20b80a20b90a6a21ba0a20b90a20ba0a6a21bb0a20ba0a20bb0a6a21bc0a20bb0a20bc0a6a21bd0a20bc0a20" + "bd0a6a21be0a20bd0a20be0a6a21bf0a20be0a20bf0a6a21c00a20bf0a20c00a6a21c10a20c00a20c10a6a21c20a20" + "c10a20c20a6a21c30a20c20a20c30a6a21c40a20c30a20c40a6a21c50a20c40a20c50a6a21c60a20c50a20c60a6a21" + "c70a20c60a20c70a6a21c80a20c70a20c80a6a21c90a20c80a20c90a6a21ca0a20c90a20ca0a6a21cb0a20ca0a20cb" + "0a6a21cc0a20cb0a20cc0a6a21cd0a20cc0a20cd0a6a21ce0a20cd0a20ce0a6a21cf0a20ce0a20cf0a6a21d00a20cf" + "0a20d00a6a21d10a20d00a20d10a6a21d20a20d10a20d20a6a21d30a20d20a20d30a6a21d40a20d30a20d40a6a21d5" + "0a20d40a20d50a6a21d60a20d50a20d60a6a21d70a20d60a20d70a6a21d80a20d70a20d80a6a21d90a20d80a20d90a" + "6a21da0a20d90a20da0a6a21db0a20da0a20db0a6a21dc0a20db0a20dc0a6a21dd0a20dc0a20dd0a6a21de0a20dd0a" + "20de0a6a21df0a20de0a20df0a6a21e00a20df0a20e00a6a21e10a20e00a20e10a6a21e20a20e10a20e20a6a21e30a" + "20e20a20e30a6a21e40a20e30a20e40a6a21e50a20e40a20e50a6a21e60a20e50a20e60a6a21e70a20e60a20e70a6a" + "21e80a20e70a20e80a6a21e90a20e80a20e90a6a21ea0a20e90a20ea0a6a21eb0a20ea0a20eb0a6a21ec0a20eb0a20" + "ec0a6a21ed0a20ec0a20ed0a6a21ee0a20ed0a20ee0a6a21ef0a20ee0a20ef0a6a21f00a20ef0a20f00a6a21f10a20" + "f00a20f10a6a21f20a20f10a20f20a6a21f30a20f20a20f30a6a21f40a20f30a20f40a6a21f50a20f40a20f50a6a21" + "f60a20f50a20f60a6a21f70a20f60a20f70a6a21f80a20f70a20f80a6a21f90a20f80a20f90a6a21fa0a20f90a20fa" + "0a6a21fb0a20fa0a20fb0a6a21fc0a20fb0a20fc0a6a21fd0a20fc0a20fd0a6a21fe0a20fd0a20fe0a6a21ff0a20fe" + "0a20ff0a6a21800b20ff0a20800b6a21810b20800b20810b6a21820b20810b20820b6a21830b20820b20830b6a2184" + "0b20830b20840b6a21850b20840b20850b6a21860b20850b20860b6a21870b20860b20870b6a21880b20870b20880b" + "6a21890b20880b20890b6a218a0b20890b208a0b6a218b0b208a0b208b0b6a218c0b208b0b208c0b6a218d0b208c0b" + "208d0b6a218e0b208d0b208e0b6a218f0b208e0b208f0b6a21900b208f0b20900b6a21910b20900b20910b6a21920b" + "20910b20920b6a21930b20920b20930b6a21940b20930b20940b6a21950b20940b20950b6a21960b20950b20960b6a" + "21970b20960b20970b6a21980b20970b20980b6a21990b20980b20990b6a219a0b20990b209a0b6a219b0b209a0b20" + "9b0b6a219c0b209b0b209c0b6a219d0b209c0b209d0b6a219e0b209d0b209e0b6a219f0b209e0b209f0b6a21a00b20" + "9f0b20a00b6a21a10b20a00b20a10b6a21a20b20a10b20a20b6a21a30b20a20b20a30b6a21a40b20a30b20a40b6a21" + "a50b20a40b20a50b6a21a60b20a50b20a60b6a21a70b20a60b20a70b6a21a80b20a70b20a80b6a21a90b20a80b20a9" + "0b6a21aa0b20a90b20aa0b6a21ab0b20aa0b20ab0b6a21ac0b20ab0b20ac0b6a21ad0b20ac0b20ad0b6a21ae0b20ad" + "0b20ae0b6a21af0b20ae0b20af0b6a21b00b20af0b20b00b6a21b10b20b00b20b10b6a21b20b20b10b20b20b6a21b3" + "0b20b20b20b30b6a21b40b20b30b20b40b6a21b50b20b40b20b50b6a21b60b20b50b20b60b6a21b70b20b60b20b70b" + "6a21b80b20b70b20b80b6a21b90b20b80b20b90b6a21ba0b20b90b20ba0b6a21bb0b20ba0b20bb0b6a21bc0b20bb0b" + "20bc0b6a21bd0b20bc0b20bd0b6a21be0b20bd0b20be0b6a21bf0b20be0b20bf0b6a21c00b20bf0b20c00b6a21c10b" + "20c00b20c10b6a21c20b20c10b20c20b6a21c30b20c20b20c30b6a21c40b20c30b20c40b6a21c50b20c40b20c50b6a" + "21c60b20c50b20c60b6a21c70b20c60b20c70b6a21c80b20c70b20c80b6a21c90b20c80b20c90b6a21ca0b20c90b20" + "ca0b6a21cb0b20ca0b20cb0b6a21cc0b20cb0b20cc0b6a21cd0b20cc0b20cd0b6a21ce0b20cd0b20ce0b6a21cf0b20" + "ce0b20cf0b6a21d00b20cf0b20d00b6a21d10b20d00b20d10b6a21d20b20d10b20d20b6a21d30b20d20b20d30b6a21" + "d40b20d30b20d40b6a21d50b20d40b20d50b6a21d60b20d50b20d60b6a21d70b20d60b20d70b6a21d80b20d70b20d8" + "0b6a21d90b20d80b20d90b6a21da0b20d90b20da0b6a21db0b20da0b20db0b6a21dc0b20db0b20dc0b6a21dd0b20dc" + "0b20dd0b6a21de0b20dd0b20de0b6a21df0b20de0b20df0b6a21e00b20df0b20e00b6a21e10b20e00b20e10b6a21e2" + "0b20e10b20e20b6a21e30b20e20b20e30b6a21e40b20e30b20e40b6a21e50b20e40b20e50b6a21e60b20e50b20e60b" + "6a21e70b20e60b20e70b6a21e80b20e70b20e80b6a21e90b20e80b20e90b6a21ea0b20e90b20ea0b6a21eb0b20ea0b" + "20eb0b6a21ec0b20eb0b20ec0b6a21ed0b20ec0b20ed0b6a21ee0b20ed0b20ee0b6a21ef0b20ee0b20ef0b6a21f00b" + "20ef0b20f00b6a21f10b20f00b20f10b6a21f20b20f10b20f20b6a21f30b20f20b20f30b6a21f40b20f30b20f40b6a" + "21f50b20f40b20f50b6a21f60b20f50b20f60b6a21f70b20f60b20f70b6a21f80b20f70b20f80b6a21f90b20f80b20" + "f90b6a21fa0b20f90b20fa0b6a21fb0b20fa0b20fb0b6a21fc0b20fb0b20fc0b6a21fd0b20fc0b20fd0b6a21fe0b20" + "fd0b20fe0b6a21ff0b20fe0b20ff0b6a21800c20ff0b20800c6a21810c20800c20810c6a21820c20810c20820c6a21" + "830c20820c20830c6a21840c20830c20840c6a21850c20840c20850c6a21860c20850c20860c6a21870c20860c2087" + "0c6a21880c20870c20880c6a21890c20880c20890c6a218a0c20890c208a0c6a218b0c208a0c208b0c6a218c0c208b" + "0c208c0c6a218d0c208c0c208d0c6a218e0c208d0c208e0c6a218f0c208e0c208f0c6a21900c208f0c20900c6a2191" + "0c20900c20910c6a21920c20910c20920c6a21930c20920c20930c6a21940c20930c20940c6a21950c20940c20950c" + "6a21960c20950c20960c6a21970c20960c20970c6a21980c20970c20980c6a21990c20980c20990c6a219a0c20990c" + "209a0c6a219b0c209a0c209b0c6a219c0c209b0c209c0c6a219d0c209c0c209d0c6a219e0c209d0c209e0c6a219f0c" + "209e0c209f0c6a21a00c209f0c20a00c6a21a10c20a00c20a10c6a21a20c20a10c20a20c6a21a30c20a20c20a30c6a" + "21a40c20a30c20a40c6a21a50c20a40c20a50c6a21a60c20a50c20a60c6a21a70c20a60c20a70c6a21a80c20a70c20" + "a80c6a21a90c20a80c20a90c6a21aa0c20a90c20aa0c6a21ab0c20aa0c20ab0c6a21ac0c20ab0c20ac0c6a21ad0c20" + "ac0c20ad0c6a21ae0c20ad0c20ae0c6a21af0c20ae0c20af0c6a21b00c20af0c20b00c6a21b10c20b00c20b10c6a21" + "b20c20b10c20b20c6a21b30c20b20c20b30c6a21b40c20b30c20b40c6a21b50c20b40c20b50c6a21b60c20b50c20b6" + "0c6a21b70c20b60c20b70c6a21b80c20b70c20b80c6a21b90c20b80c20b90c6a21ba0c20b90c20ba0c6a21bb0c20ba" + "0c20bb0c6a21bc0c20bb0c20bc0c6a21bd0c20bc0c20bd0c6a21be0c20bd0c20be0c6a21bf0c20be0c20bf0c6a21c0" + "0c20bf0c20c00c6a21c10c20c00c20c10c6a21c20c20c10c20c20c6a21c30c20c20c20c30c6a21c40c20c30c20c40c" + "6a21c50c20c40c20c50c6a21c60c20c50c20c60c6a21c70c20c60c20c70c6a21c80c20c70c20c80c6a21c90c20c80c" + "20c90c6a21ca0c20c90c20ca0c6a21cb0c20ca0c20cb0c6a21cc0c20cb0c20cc0c6a21cd0c20cc0c20cd0c6a21ce0c" + "20cd0c20ce0c6a21cf0c20ce0c20cf0c6a21d00c20cf0c20d00c6a21d10c20d00c20d10c6a21d20c20d10c20d20c6a" + "21d30c20d20c20d30c6a21d40c20d30c20d40c6a21d50c20d40c20d50c6a21d60c20d50c20d60c6a21d70c20d60c20" + "d70c6a21d80c20d70c20d80c6a21d90c20d80c20d90c6a21da0c20d90c20da0c6a21db0c20da0c20db0c6a21dc0c20" + "db0c20dc0c6a21dd0c20dc0c20dd0c6a21de0c20dd0c20de0c6a21df0c20de0c20df0c6a21e00c20df0c20e00c6a21" + "e10c20e00c20e10c6a21e20c20e10c20e20c6a21e30c20e20c20e30c6a21e40c20e30c20e40c6a21e50c20e40c20e5" + "0c6a21e60c20e50c20e60c6a21e70c20e60c20e70c6a21e80c20e70c20e80c6a21e90c20e80c20e90c6a21ea0c20e9" + "0c20ea0c6a21eb0c20ea0c20eb0c6a21ec0c20eb0c20ec0c6a21ed0c20ec0c20ed0c6a21ee0c20ed0c20ee0c6a21ef" + "0c20ee0c20ef0c6a21f00c20ef0c20f00c6a21f10c20f00c20f10c6a21f20c20f10c20f20c6a21f30c20f20c20f30c" + "6a21f40c20f30c20f40c6a21f50c20f40c20f50c6a21f60c20f50c20f60c6a21f70c20f60c20f70c6a21f80c20f70c" + "20f80c6a21f90c20f80c20f90c6a21fa0c20f90c20fa0c6a21fb0c20fa0c20fb0c6a21fc0c20fb0c20fc0c6a21fd0c" + "20fc0c20fd0c6a21fe0c20fd0c20fe0c6a21ff0c20fe0c20ff0c6a21800d20ff0c20800d6a21810d20800d20810d6a" + "21820d20810d20820d6a21830d20820d20830d6a21840d20830d20840d6a21850d20840d20850d6a21860d20850d20" + "860d6a21870d20860d20870d6a21880d20870d20880d6a21890d20880d20890d6a218a0d20890d208a0d6a218b0d20" + "8a0d208b0d6a218c0d208b0d208c0d6a218d0d208c0d208d0d6a218e0d208d0d208e0d6a218f0d208e0d208f0d6a21" + "900d208f0d20900d6a21910d20900d20910d6a21920d20910d20920d6a21930d20920d20930d6a21940d20930d2094" + "0d6a21950d20940d20950d6a21960d20950d20960d6a21970d20960d20970d6a21980d20970d20980d6a21990d2098" + "0d20990d6a219a0d20990d209a0d6a219b0d209a0d209b0d6a219c0d209b0d209c0d6a219d0d209c0d209d0d6a219e" + "0d209d0d209e0d6a219f0d209e0d209f0d6a21a00d209f0d20a00d6a21a10d20a00d20a10d6a21a20d20a10d20a20d" + "6a21a30d20a20d20a30d6a21a40d20a30d20a40d6a21a50d20a40d20a50d6a21a60d20a50d20a60d6a21a70d20a60d" + "20a70d6a21a80d20a70d20a80d6a21a90d20a80d20a90d6a21aa0d20a90d20aa0d6a21ab0d20aa0d20ab0d6a21ac0d" + "20ab0d20ac0d6a21ad0d20ac0d20ad0d6a21ae0d20ad0d20ae0d6a21af0d20ae0d20af0d6a21b00d20af0d20b00d6a" + "21b10d20b00d20b10d6a21b20d20b10d20b20d6a21b30d20b20d20b30d6a21b40d20b30d20b40d6a21b50d20b40d20" + "b50d6a21b60d20b50d20b60d6a21b70d20b60d20b70d6a21b80d20b70d20b80d6a21b90d20b80d20b90d6a21ba0d20" + "b90d20ba0d6a21bb0d20ba0d20bb0d6a21bc0d20bb0d20bc0d6a21bd0d20bc0d20bd0d6a21be0d20bd0d20be0d6a21" + "bf0d20be0d20bf0d6a21c00d20bf0d20c00d6a21c10d20c00d20c10d6a21c20d20c10d20c20d6a21c30d20c20d20c3" + "0d6a21c40d20c30d20c40d6a21c50d20c40d20c50d6a21c60d20c50d20c60d6a21c70d20c60d20c70d6a21c80d20c7" + "0d20c80d6a21c90d20c80d20c90d6a21ca0d20c90d20ca0d6a21cb0d20ca0d20cb0d6a21cc0d20cb0d20cc0d6a21cd" + "0d20cc0d20cd0d6a21ce0d20cd0d20ce0d6a21cf0d20ce0d20cf0d6a21d00d20cf0d20d00d6a21d10d20d00d20d10d" + "6a21d20d20d10d20d20d6a21d30d20d20d20d30d6a21d40d20d30d20d40d6a21d50d20d40d20d50d6a21d60d20d50d" + "20d60d6a21d70d20d60d20d70d6a21d80d20d70d20d80d6a21d90d20d80d20d90d6a21da0d20d90d20da0d6a21db0d" + "20da0d20db0d6a21dc0d20db0d20dc0d6a21dd0d20dc0d20dd0d6a21de0d20dd0d20de0d6a21df0d20de0d20df0d6a" + "21e00d20df0d20e00d6a21e10d20e00d20e10d6a21e20d20e10d20e20d6a21e30d20e20d20e30d6a21e40d20e30d20" + "e40d6a21e50d20e40d20e50d6a21e60d20e50d20e60d6a21e70d20e60d20e70d6a21e80d20e70d20e80d6a21e90d20" + "e80d20e90d6a21ea0d20e90d20ea0d6a21eb0d20ea0d20eb0d6a21ec0d20eb0d20ec0d6a21ed0d20ec0d20ed0d6a21" + "ee0d20ed0d20ee0d6a21ef0d20ee0d20ef0d6a21f00d20ef0d20f00d6a21f10d20f00d20f10d6a21f20d20f10d20f2" + "0d6a21f30d20f20d20f30d6a21f40d20f30d20f40d6a21f50d20f40d20f50d6a21f60d20f50d20f60d6a21f70d20f6" + "0d20f70d6a21f80d20f70d20f80d6a21f90d20f80d20f90d6a21fa0d20f90d20fa0d6a21fb0d20fa0d20fb0d6a21fc" + "0d20fb0d20fc0d6a21fd0d20fc0d20fd0d6a21fe0d20fd0d20fe0d6a21ff0d20fe0d20ff0d6a21800e20ff0d20800e" + "6a21810e20800e20810e6a21820e20810e20820e6a21830e20820e20830e6a21840e20830e20840e6a21850e20840e" + "20850e6a21860e20850e20860e6a21870e20860e20870e6a21880e20870e20880e6a21890e20880e20890e6a218a0e" + "20890e208a0e6a218b0e208a0e208b0e6a218c0e208b0e208c0e6a218d0e208c0e208d0e6a218e0e208d0e208e0e6a" + "218f0e208e0e208f0e6a21900e208f0e20900e6a21910e20900e20910e6a21920e20910e20920e6a21930e20920e20" + "930e6a21940e20930e20940e6a21950e20940e20950e6a21960e20950e20960e6a21970e20960e20970e6a21980e20" + "970e20980e6a21990e20980e20990e6a219a0e20990e209a0e6a219b0e209a0e209b0e6a219c0e209b0e209c0e6a21" + "9d0e209c0e209d0e6a219e0e209d0e209e0e6a219f0e209e0e209f0e6a21a00e209f0e20a00e6a21a10e20a00e20a1" + "0e6a21a20e20a10e20a20e6a21a30e20a20e20a30e6a21a40e20a30e20a40e6a21a50e20a40e20a50e6a21a60e20a5" + "0e20a60e6a21a70e20a60e20a70e6a21a80e20a70e20a80e6a21a90e20a80e20a90e6a21aa0e20a90e20aa0e6a21ab" + "0e20aa0e20ab0e6a21ac0e20ab0e20ac0e6a21ad0e20ac0e20ad0e6a21ae0e20ad0e20ae0e6a21af0e20ae0e20af0e" + "6a21b00e20af0e20b00e6a21b10e20b00e20b10e6a21b20e20b10e20b20e6a21b30e20b20e20b30e6a21b40e20b30e" + "20b40e6a21b50e20b40e20b50e6a21b60e20b50e20b60e6a21b70e20b60e20b70e6a21b80e20b70e20b80e6a21b90e" + "20b80e20b90e6a21ba0e20b90e20ba0e6a21bb0e20ba0e20bb0e6a21bc0e20bb0e20bc0e6a21bd0e20bc0e20bd0e6a" + "21be0e20bd0e20be0e6a21bf0e20be0e20bf0e6a21c00e20bf0e20c00e6a21c10e20c00e20c10e6a21c20e20c10e20" + "c20e6a21c30e20c20e20c30e6a21c40e20c30e20c40e6a21c50e20c40e20c50e6a21c60e20c50e20c60e6a21c70e20" + "c60e20c70e6a21c80e20c70e20c80e6a21c90e20c80e20c90e6a21ca0e20c90e20ca0e6a21cb0e20ca0e20cb0e6a21" + "cc0e20cb0e20cc0e6a21cd0e20cc0e20cd0e6a21ce0e20cd0e20ce0e6a21cf0e20ce0e20cf0e6a21d00e20cf0e20d0" + "0e6a21d10e20d00e20d10e6a21d20e20d10e20d20e6a21d30e20d20e20d30e6a21d40e20d30e20d40e6a21d50e20d4" + "0e20d50e6a21d60e20d50e20d60e6a21d70e20d60e20d70e6a21d80e20d70e20d80e6a21d90e20d80e20d90e6a21da" + "0e20d90e20da0e6a21db0e20da0e20db0e6a21dc0e20db0e20dc0e6a21dd0e20dc0e20dd0e6a21de0e20dd0e20de0e" + "6a21df0e20de0e20df0e6a21e00e20df0e20e00e6a21e10e20e00e20e10e6a21e20e20e10e20e20e6a21e30e20e20e" + "20e30e6a21e40e20e30e20e40e6a21e50e20e40e20e50e6a21e60e20e50e20e60e6a21e70e20e60e20e70e6a21e80e" + "20e70e20e80e6a21e90e20e80e20e90e6a21ea0e20e90e20ea0e6a21eb0e20ea0e20eb0e6a21ec0e20eb0e20ec0e6a" + "21ed0e20ec0e20ed0e6a21ee0e20ed0e20ee0e6a21ef0e20ee0e20ef0e6a21f00e20ef0e20f00e6a21f10e20f00e20" + "f10e6a21f20e20f10e20f20e6a21f30e20f20e20f30e6a21f40e20f30e20f40e6a21f50e20f40e20f50e6a21f60e20" + "f50e20f60e6a21f70e20f60e20f70e6a21f80e20f70e20f80e6a21f90e20f80e20f90e6a21fa0e20f90e20fa0e6a21" + "fb0e20fa0e20fb0e6a21fc0e20fb0e20fc0e6a21fd0e20fc0e20fd0e6a21fe0e20fd0e20fe0e6a21ff0e20fe0e20ff" + "0e6a21800f20ff0e20800f6a21810f20800f20810f6a21820f20810f20820f6a21830f20820f20830f6a21840f2083" + "0f20840f6a21850f20840f20850f6a21860f20850f20860f6a21870f20860f20870f6a21880f20870f20880f6a2189" + "0f20880f20890f6a218a0f20890f208a0f6a218b0f208a0f208b0f6a218c0f208b0f208c0f6a218d0f208c0f208d0f" + "6a218e0f208d0f208e0f6a218f0f208e0f208f0f6a21900f208f0f20900f6a21910f20900f20910f6a21920f20910f" + "20920f6a21930f20920f20930f6a21940f20930f20940f6a21950f20940f20950f6a21960f20950f20960f6a21970f" + "20960f20970f6a21980f20970f20980f6a21990f20980f20990f6a219a0f20990f209a0f6a219b0f209a0f209b0f6a" + "219c0f209b0f209c0f6a219d0f209c0f209d0f6a219e0f209d0f209e0f6a219f0f209e0f209f0f6a21a00f209f0f20" + "a00f6a21a10f20a00f20a10f6a21a20f20a10f20a20f6a21a30f20a20f20a30f6a21a40f20a30f20a40f6a21a50f20" + "a40f20a50f6a21a60f20a50f20a60f6a21a70f20a60f20a70f6a21a80f20a70f20a80f6a21a90f20a80f20a90f6a21" + "aa0f20a90f20aa0f6a21ab0f20aa0f20ab0f6a21ac0f20ab0f20ac0f6a21ad0f20ac0f20ad0f6a21ae0f20ad0f20ae" + "0f6a21af0f20ae0f20af0f6a21b00f20af0f20b00f6a21b10f20b00f20b10f6a21b20f20b10f20b20f6a21b30f20b2" + "0f20b30f6a21b40f20b30f20b40f6a21b50f20b40f20b50f6a21b60f20b50f20b60f6a21b70f20b60f20b70f6a21b8" + "0f20b70f20b80f6a21b90f20b80f20b90f6a21ba0f20b90f20ba0f6a21bb0f20ba0f20bb0f6a21bc0f20bb0f20bc0f" + "6a21bd0f20bc0f20bd0f6a21be0f20bd0f20be0f6a21bf0f20be0f20bf0f6a21c00f20bf0f20c00f6a21c10f20c00f" + "20c10f6a21c20f20c10f20c20f6a21c30f20c20f20c30f6a21c40f20c30f20c40f6a21c50f20c40f20c50f6a21c60f" + "20c50f20c60f6a21c70f20c60f20c70f6a21c80f20c70f20c80f6a21c90f20c80f20c90f6a21ca0f20c90f20ca0f6a" + "21cb0f20ca0f20cb0f6a21cc0f20cb0f20cc0f6a21cd0f20cc0f20cd0f6a21ce0f20cd0f20ce0f6a21cf0f20ce0f20" + "cf0f6a21d00f20cf0f20d00f6a21d10f20d00f20d10f6a21d20f20d10f20d20f6a21d30f20d20f20d30f6a21d40f20" + "d30f20d40f6a21d50f20d40f20d50f6a21d60f20d50f20d60f6a21d70f20d60f20d70f6a21d80f20d70f20d80f6a21" + "d90f20d80f20d90f6a21da0f20d90f20da0f6a21db0f20da0f20db0f6a21dc0f20db0f20dc0f6a21dd0f20dc0f20dd" + "0f6a21de0f20dd0f20de0f6a21df0f20de0f20df0f6a21e00f20df0f20e00f6a21e10f20e00f20e10f6a21e20f20e1" + "0f20e20f6a21e30f20e20f20e30f6a21e40f20e30f20e40f6a21e50f20e40f20e50f6a21e60f20e50f20e60f6a21e7" + "0f20e60f20e70f6a21e80f20e70f20e80f6a21e90f20e80f20e90f6a21ea0f20e90f20ea0f6a21eb0f20ea0f20eb0f" + "6a21ec0f20eb0f20ec0f6a21ed0f20ec0f20ed0f6a21ee0f20ed0f20ee0f6a21ef0f20ee0f20ef0f6a21f00f20ef0f" + "20f00f6a21f10f20f00f20f10f6a21f20f20f10f20f20f6a21f30f20f20f20f30f6a21f40f20f30f20f40f6a21f50f" + "20f40f20f50f6a21f60f20f50f20f60f6a21f70f20f60f20f70f6a21f80f20f70f20f80f6a21f90f20f80f20f90f6a" + "21fa0f20f90f20fa0f6a21fb0f20fa0f20fb0f6a21fc0f20fb0f20fc0f6a21fd0f20fc0f20fd0f6a21fe0f20fd0f20" + "fe0f6a21ff0f20fe0f20ff0f6a21801020ff0f2080106a2181102080102081106a2182102081102082106a21831020" + "82102083106a2184102083102084106a2185102084102085106a2186102085102086106a2187102086102087106a21" + "88102087102088106a2189102088102089106a218a10208910208a106a218b10208a10208b106a218c10208b10208c" + "106a218d10208c10208d106a218e10208d10208e106a218f10208e10208f106a219010208f102090106a2191102090" + "102091106a2192102091102092106a2193102092102093106a2194102093102094106a2195102094102095106a2196" + "102095102096106a2197102096102097106a2198102097102098106a2199102098102099106a219a10209910209a10" + "6a219b10209a10209b106a219c10209b10209c106a219d10209c10209d106a219e10209d10209e106a219f10209e10" + "209f106a21a010209f1020a0106a21a11020a01020a1106a21a21020a11020a2106a21a31020a21020a3106a21a410" + "20a31020a4106a21a51020a41020a5106a21a61020a51020a6106a21a71020a61020a7106a21a81020a71020a8106a" + "21a91020a81020a9106a21aa1020a91020aa106a21ab1020aa1020ab106a21ac1020ab1020ac106a21ad1020ac1020" + "ad106a21ae1020ad1020ae106a21af1020ae1020af106a21b01020af1020b0106a21b11020b01020b1106a21b21020" + "b11020b2106a21b31020b21020b3106a21b41020b31020b4106a21b51020b41020b5106a21b61020b51020b6106a21" + "b71020b61020b7106a21b81020b71020b8106a21b91020b81020b9106a21ba1020b91020ba106a21bb1020ba1020bb" + "106a21bc1020bb1020bc106a21bd1020bc1020bd106a21be1020bd1020be106a21bf1020be1020bf106a21c01020bf" + "1020c0106a21c11020c01020c1106a21c21020c11020c2106a21c31020c21020c3106a21c41020c31020c4106a21c5" + "1020c41020c5106a21c61020c51020c6106a21c71020c61020c7106a21c81020c71020c8106a21c91020c81020c910" + "6a21ca1020c91020ca106a21cb1020ca1020cb106a21cc1020cb1020cc106a21cd1020cc1020cd106a21ce1020cd10" + "20ce106a21cf1020ce1020cf106a21d01020cf1020d0106a21d11020d01020d1106a21d21020d11020d2106a21d310" + "20d21020d3106a21d41020d31020d4106a21d51020d41020d5106a21d61020d51020d6106a21d71020d61020d7106a" + "21d81020d71020d8106a21d91020d81020d9106a21da1020d91020da106a21db1020da1020db106a21dc1020db1020" + "dc106a21dd1020dc1020dd106a21de1020dd1020de106a21df1020de1020df106a21e01020df1020e0106a21e11020" + "e01020e1106a21e21020e11020e2106a21e31020e21020e3106a21e41020e31020e4106a21e51020e41020e5106a21" + "e61020e51020e6106a21e71020e61020e7106a21e81020e71020e8106a21e91020e81020e9106a21ea1020e91020ea" + "106a21eb1020ea1020eb106a21ec1020eb1020ec106a21ed1020ec1020ed106a21ee1020ed1020ee106a21ef1020ee" + "1020ef106a21f01020ef1020f0106a21f11020f01020f1106a21f21020f11020f2106a21f31020f21020f3106a21f4" + "1020f31020f4106a21f51020f41020f5106a21f61020f51020f6106a21f71020f61020f7106a21f81020f71020f810" + "6a21f91020f81020f9106a21fa1020f91020fa106a21fb1020fa1020fb106a21fc1020fb1020fc106a21fd1020fc10" + "20fd106a21fe1020fd1020fe106a21ff1020fe1020ff106a21801120ff102080116a2181112080112081116a218211" + "2081112082116a2183112082112083116a2184112083112084116a2185112084112085116a2186112085112086116a" + "2187112086112087116a2188112087112088116a2189112088112089116a218a11208911208a116a218b11208a1120" + "8b116a218c11208b11208c116a218d11208c11208d116a218e11208d11208e116a218f11208e11208f116a21901120" + "8f112090116a2191112090112091116a2192112091112092116a2193112092112093116a2194112093112094116a21" + "95112094112095116a2196112095112096116a2197112096112097116a2198112097112098116a2199112098112099" + "116a219a11209911209a116a219b11209a11209b116a219c11209b11209c116a219d11209c11209d116a219e11209d" + "11209e116a219f11209e11209f116a21a011209f1120a0116a21a11120a01120a1116a21a21120a11120a2116a21a3" + "1120a21120a3116a21a41120a31120a4116a21a51120a41120a5116a21a61120a51120a6116a21a71120a61120a711" + "6a21a81120a71120a8116a21a91120a81120a9116a21aa1120a91120aa116a21ab1120aa1120ab116a21ac1120ab11" + "20ac116a21ad1120ac1120ad116a21ae1120ad1120ae116a21af1120ae1120af116a21b01120af1120b0116a21b111" + "20b01120b1116a21b21120b11120b2116a21b31120b21120b3116a21b41120b31120b4116a21b51120b41120b5116a" + "21b61120b51120b6116a21b71120b61120b7116a21b81120b71120b8116a21b91120b81120b9116a21ba1120b91120" + "ba116a21bb1120ba1120bb116a21bc1120bb1120bc116a21bd1120bc1120bd116a21be1120bd1120be116a21bf1120" + "be1120bf116a21c01120bf1120c0116a21c11120c01120c1116a21c21120c11120c2116a21c31120c21120c3116a21" + "c41120c31120c4116a21c51120c41120c5116a21c61120c51120c6116a21c71120c61120c7116a21c81120c71120c8" + "116a21c91120c81120c9116a21ca1120c91120ca116a21cb1120ca1120cb116a21cc1120cb1120cc116a21cd1120cc" + "1120cd116a21ce1120cd1120ce116a21cf1120ce1120cf116a21d01120cf1120d0116a21d11120d01120d1116a21d2" + "1120d11120d2116a21d31120d21120d3116a21d41120d31120d4116a21d51120d41120d5116a21d61120d51120d611" + "6a21d71120d61120d7116a21d81120d71120d8116a21d91120d81120d9116a21da1120d91120da116a21db1120da11" + "20db116a21dc1120db1120dc116a21dd1120dc1120dd116a21de1120dd1120de116a21df1120de1120df116a21e011" + "20df1120e0116a21e11120e01120e1116a21e21120e11120e2116a21e31120e21120e3116a21e41120e31120e4116a" + "21e51120e41120e5116a21e61120e51120e6116a21e71120e61120e7116a21e81120e71120e8116a21e91120e81120" + "e9116a21ea1120e91120ea116a21eb1120ea1120eb116a21ec1120eb1120ec116a21ed1120ec1120ed116a21ee1120" + "ed1120ee116a21ef1120ee1120ef116a21f01120ef1120f0116a21f11120f01120f1116a21f21120f11120f2116a21" + "f31120f21120f3116a21f41120f31120f4116a21f51120f41120f5116a21f61120f51120f6116a21f71120f61120f7" + "116a21f81120f71120f8116a21f91120f81120f9116a21fa1120f91120fa116a21fb1120fa1120fb116a21fc1120fb" + "1120fc116a21fd1120fc1120fd116a21fe1120fd1120fe116a21ff1120fe1120ff116a21801220ff112080126a2181" + "122080122081126a2182122081122082126a2183122082122083126a2184122083122084126a218512208412208512" + "6a2186122085122086126a2187122086122087126a2188122087122088126a2189122088122089126a218a12208912" + "208a126a218b12208a12208b126a218c12208b12208c126a218d12208c12208d126a218e12208d12208e126a218f12" + "208e12208f126a219012208f122090126a2191122090122091126a2192122091122092126a2193122092122093126a" + "2194122093122094126a2195122094122095126a2196122095122096126a2197122096122097126a21981220971220" + "98126a2199122098122099126a219a12209912209a126a219b12209a12209b126a219c12209b12209c126a219d1220" + "9c12209d126a219e12209d12209e126a219f12209e12209f126a21a012209f1220a0126a21a11220a01220a1126a21" + "a21220a11220a2126a21a31220a21220a3126a21a41220a31220a4126a21a51220a41220a5126a21a61220a51220a6" + "126a21a71220a61220a7126a21a81220a71220a8126a21a91220a81220a9126a21aa1220a91220aa126a21ab1220aa" + "1220ab126a21ac1220ab1220ac126a21ad1220ac1220ad126a21ae1220ad1220ae126a21af1220ae1220af126a21b0" + "1220af1220b0126a21b11220b01220b1126a21b21220b11220b2126a21b31220b21220b3126a21b41220b31220b412" + "6a21b51220b41220b5126a21b61220b51220b6126a21b71220b61220b7126a21b81220b71220b8126a21b91220b812" + "20b9126a21ba1220b91220ba126a21bb1220ba1220bb126a21bc1220bb1220bc126a21bd1220bc1220bd126a21be12" + "20bd1220be126a21bf1220be1220bf126a21c01220bf1220c0126a21c11220c01220c1126a21c21220c11220c2126a" + "21c31220c21220c3126a21c41220c31220c4126a21c51220c41220c5126a21c61220c51220c6126a21c71220c61220" + "c7126a21c81220c71220c8126a21c91220c81220c9126a21ca1220c91220ca126a21cb1220ca1220cb126a21cc1220" + "cb1220cc126a21cd1220cc1220cd126a21ce1220cd1220ce126a21cf1220ce1220cf126a21d01220cf1220d0126a21" + "d11220d01220d1126a21d21220d11220d2126a21d31220d21220d3126a21d41220d31220d4126a21d51220d41220d5" + "126a21d61220d51220d6126a21d71220d61220d7126a21d81220d71220d8126a21d91220d81220d9126a21da1220d9" + "1220da126a21db1220da1220db126a21dc1220db1220dc126a21dd1220dc1220dd126a21de1220dd1220de126a21df" + "1220de1220df126a21e01220df1220e0126a21e11220e01220e1126a21e21220e11220e2126a21e31220e21220e312" + "6a21e41220e31220e4126a21e51220e41220e5126a21e61220e51220e6126a21e71220e61220e7126a21e81220e712" + "20e8126a21e91220e81220e9126a21ea1220e91220ea126a21eb1220ea1220eb126a21ec1220eb1220ec126a21ed12" + "20ec1220ed126a21ee1220ed1220ee126a21ef1220ee1220ef126a21f01220ef1220f0126a21f11220f01220f1126a" + "21f21220f11220f2126a21f31220f21220f3126a21f41220f31220f4126a21f51220f41220f5126a21f61220f51220" + "f6126a21f71220f61220f7126a21f81220f71220f8126a21f91220f81220f9126a21fa1220f91220fa126a21fb1220" + "fa1220fb126a21fc1220fb1220fc126a21fd1220fc1220fd126a21fe1220fd1220fe126a21ff1220fe1220ff126a21" + "801320ff122080136a2181132080132081136a2182132081132082136a2183132082132083136a2184132083132084" + "136a2185132084132085136a2186132085132086136a2187132086132087136a2188132087132088136a2189132088" + "132089136a218a13208913208a136a218b13208a13208b136a218c13208b13208c136a218d13208c13208d136a218e" + "13208d13208e136a218f13208e13208f136a219013208f132090136a2191132090132091136a219213209113209213" + "6a2193132092132093136a2194132093132094136a2195132094132095136a2196132095132096136a219713209613" + "2097136a2198132097132098136a2199132098132099136a219a13209913209a136a219b13209a13209b136a219c13" + "209b13209c136a219d13209c13209d136a219e13209d13209e136a219f13209e13209f136a21a013209f1320a0136a" + "21a11320a01320a1136a21a21320a11320a2136a21a31320a21320a3136a21a41320a31320a4136a21a51320a41320" + "a5136a21a61320a51320a6136a21a71320a61320a7136a21a81320a71320a8136a21a91320a81320a9136a21aa1320" + "a91320aa136a21ab1320aa1320ab136a21ac1320ab1320ac136a21ad1320ac1320ad136a21ae1320ad1320ae136a21" + "af1320ae1320af136a21b01320af1320b0136a21b11320b01320b1136a21b21320b11320b2136a21b31320b21320b3" + "136a21b41320b31320b4136a21b51320b41320b5136a21b61320b51320b6136a21b71320b61320b7136a21b81320b7" + "1320b8136a21b91320b81320b9136a21ba1320b91320ba136a21bb1320ba1320bb136a21bc1320bb1320bc136a21bd" + "1320bc1320bd136a21be1320bd1320be136a21bf1320be1320bf136a21c01320bf1320c0136a21c11320c01320c113" + "6a21c21320c11320c2136a21c31320c21320c3136a21c41320c31320c4136a21c51320c41320c5136a21c61320c513" + "20c6136a21c71320c61320c7136a21c81320c71320c8136a21c91320c81320c9136a21ca1320c91320ca136a21cb13" + "20ca1320cb136a21cc1320cb1320cc136a21cd1320cc1320cd136a21ce1320cd1320ce136a21cf1320ce1320cf136a" + "21d01320cf1320d0136a21d11320d01320d1136a21d21320d11320d2136a21d31320d21320d3136a21d41320d31320" + "d4136a21d51320d41320d5136a21d61320d51320d6136a21d71320d61320d7136a21d81320d71320d8136a21d91320" + "d81320d9136a21da1320d91320da136a21db1320da1320db136a21dc1320db1320dc136a21dd1320dc1320dd136a21" + "de1320dd1320de136a21df1320de1320df136a21e01320df1320e0136a21e11320e01320e1136a21e21320e11320e2" + "136a21e31320e21320e3136a21e41320e31320e4136a21e51320e41320e5136a21e61320e51320e6136a21e71320e6" + "1320e7136a21e81320e71320e8136a21e91320e81320e9136a21ea1320e91320ea136a21eb1320ea1320eb136a21ec" + "1320eb1320ec136a21ed1320ec1320ed136a21ee1320ed1320ee136a21ef1320ee1320ef136a21f01320ef1320f013" + "6a21f11320f01320f1136a21f21320f11320f2136a21f31320f21320f3136a21f41320f31320f4136a21f51320f413" + "20f5136a21f61320f51320f6136a21f71320f61320f7136a21f81320f71320f8136a21f91320f81320f9136a21fa13" + "20f91320fa136a21fb1320fa1320fb136a21fc1320fb1320fc136a21fd1320fc1320fd136a21fe1320fd1320fe136a" + "21ff1320fe1320ff136a21801420ff132080146a2181142080142081146a2182142081142082146a21831420821420" + "83146a2184142083142084146a2185142084142085146a2186142085142086146a2187142086142087146a21881420" + "87142088146a2189142088142089146a218a14208914208a146a218b14208a14208b146a218c14208b14208c146a21" + "8d14208c14208d146a218e14208d14208e146a218f14208e14208f146a219014208f142090146a2191142090142091" + "146a2192142091142092146a2193142092142093146a2194142093142094146a2195142094142095146a2196142095" + "142096146a2197142096142097146a2198142097142098146a2199142098142099146a219a14209914209a146a219b" + "14209a14209b146a219c14209b14209c146a219d14209c14209d146a219e14209d14209e146a219f14209e14209f14" + "6a21a014209f1420a0146a21a11420a01420a1146a21a21420a11420a2146a21a31420a21420a3146a21a41420a314" + "20a4146a21a51420a41420a5146a21a61420a51420a6146a21a71420a61420a7146a21a81420a71420a8146a21a914" + "20a81420a9146a21aa1420a91420aa146a21ab1420aa1420ab146a21ac1420ab1420ac146a21ad1420ac1420ad146a" + "21ae1420ad1420ae146a21af1420ae1420af146a21b01420af1420b0146a21b11420b01420b1146a21b21420b11420" + "b2146a21b31420b21420b3146a21b41420b31420b4146a21b51420b41420b5146a21b61420b51420b6146a21b71420" + "b61420b7146a21b81420b71420b8146a21b91420b81420b9146a21ba1420b91420ba146a21bb1420ba1420bb146a21" + "bc1420bb1420bc146a21bd1420bc1420bd146a21be1420bd1420be146a21bf1420be1420bf146a21c01420bf1420c0" + "146a21c11420c01420c1146a21c21420c11420c2146a21c31420c21420c3146a21c41420c31420c4146a21c51420c4" + "1420c5146a21c61420c51420c6146a21c71420c61420c7146a21c81420c71420c8146a21c91420c81420c9146a21ca" + "1420c91420ca146a21cb1420ca1420cb146a21cc1420cb1420cc146a21cd1420cc1420cd146a21ce1420cd1420ce14" + "6a21cf1420ce1420cf146a21d01420cf1420d0146a21d11420d01420d1146a21d21420d11420d2146a21d31420d214" + "20d3146a21d41420d31420d4146a21d51420d41420d5146a21d61420d51420d6146a21d71420d61420d7146a21d814" + "20d71420d8146a21d91420d81420d9146a21da1420d91420da146a21db1420da1420db146a21dc1420db1420dc146a" + "21dd1420dc1420dd146a21de1420dd1420de146a21df1420de1420df146a21e01420df1420e0146a21e11420e01420" + "e1146a21e21420e11420e2146a21e31420e21420e3146a21e41420e31420e4146a21e51420e41420e5146a21e61420" + "e51420e6146a21e71420e61420e7146a21e81420e71420e8146a21e91420e81420e9146a21ea1420e91420ea146a21" + "eb1420ea1420eb146a21ec1420eb1420ec146a21ed1420ec1420ed146a21ee1420ed1420ee146a21ef1420ee1420ef" + "146a21f01420ef1420f0146a21f11420f01420f1146a21f21420f11420f2146a21f31420f21420f3146a21f41420f3" + "1420f4146a21f51420f41420f5146a21f61420f51420f6146a21f71420f61420f7146a21f81420f71420f8146a21f9" + "1420f81420f9146a21fa1420f91420fa146a21fb1420fa1420fb146a21fc1420fb1420fc146a21fd1420fc1420fd14" + "6a21fe1420fd1420fe146a21ff1420fe1420ff146a21801520ff142080156a2181152080152081156a218215208115" + "2082156a2183152082152083156a2184152083152084156a2185152084152085156a2186152085152086156a218715" + "2086152087156a2188152087152088156a2189152088152089156a218a15208915208a156a218b15208a15208b156a" + "218c15208b15208c156a218d15208c15208d156a218e15208d15208e156a218f15208e15208f156a219015208f1520" + "90156a2191152090152091156a2192152091152092156a2193152092152093156a2194152093152094156a21951520" + "94152095156a2196152095152096156a2197152096152097156a2198152097152098156a2199152098152099156a21" + "9a15209915209a156a219b15209a15209b156a219c15209b15209c156a219d15209c15209d156a219e15209d15209e" + "156a219f15209e15209f156a21a015209f1520a0156a21a11520a01520a1156a21a21520a11520a2156a21a31520a2" + "1520a3156a21a41520a31520a4156a21a51520a41520a5156a21a61520a51520a6156a21a71520a61520a7156a21a8" + "1520a71520a8156a21a91520a81520a9156a21aa1520a91520aa156a21ab1520aa1520ab156a21ac1520ab1520ac15" + "6a21ad1520ac1520ad156a21ae1520ad1520ae156a21af1520ae1520af156a21b01520af1520b0156a21b11520b015" + "20b1156a21b21520b11520b2156a21b31520b21520b3156a21b41520b31520b4156a21b51520b41520b5156a21b615" + "20b51520b6156a21b71520b61520b7156a21b81520b71520b8156a21b91520b81520b9156a21ba1520b91520ba156a" + "21bb1520ba1520bb156a21bc1520bb1520bc156a21bd1520bc1520bd156a21be1520bd1520be156a21bf1520be1520" + "bf156a21c01520bf1520c0156a21c11520c01520c1156a21c21520c11520c2156a21c31520c21520c3156a21c41520" + "c31520c4156a21c51520c41520c5156a21c61520c51520c6156a21c71520c61520c7156a21c81520c71520c8156a21" + "c91520c81520c9156a21ca1520c91520ca156a21cb1520ca1520cb156a21cc1520cb1520cc156a21cd1520cc1520cd" + "156a21ce1520cd1520ce156a21cf1520ce1520cf156a21d01520cf1520d0156a21d11520d01520d1156a21d21520d1" + "1520d2156a21d31520d21520d3156a21d41520d31520d4156a21d51520d41520d5156a21d61520d51520d6156a21d7" + "1520d61520d7156a21d81520d71520d8156a21d91520d81520d9156a21da1520d91520da156a21db1520da1520db15" + "6a21dc1520db1520dc156a21dd1520dc1520dd156a21de1520dd1520de156a21df1520de1520df156a21e01520df15" + "20e0156a21e11520e01520e1156a21e21520e11520e2156a21e31520e21520e3156a21e41520e31520e4156a21e515" + "20e41520e5156a21e61520e51520e6156a21e71520e61520e7156a21e81520e71520e8156a21e91520e81520e9156a" + "21ea1520e91520ea156a21eb1520ea1520eb156a21ec1520eb1520ec156a21ed1520ec1520ed156a21ee1520ed1520" + "ee156a21ef1520ee1520ef156a21f01520ef1520f0156a21f11520f01520f1156a21f21520f11520f2156a21f31520" + "f21520f3156a21f41520f31520f4156a21f51520f41520f5156a21f61520f51520f6156a21f71520f61520f7156a21" + "f81520f71520f8156a21f91520f81520f9156a21fa1520f91520fa156a21fb1520fa1520fb156a21fc1520fb1520fc" + "156a21fd1520fc1520fd156a21fe1520fd1520fe156a21ff1520fe1520ff156a21801620ff152080166a2181162080" + "162081166a2182162081162082166a2183162082162083166a2184162083162084166a2185162084162085166a2186" + "162085162086166a2187162086162087166a2188162087162088166a2189162088162089166a218a16208916208a16" + "6a218b16208a16208b166a218c16208b16208c166a218d16208c16208d166a218e16208d16208e166a218f16208e16" + "208f166a219016208f162090166a2191162090162091166a2192162091162092166a2193162092162093166a219416" + "2093162094166a2195162094162095166a2196162095162096166a2197162096162097166a2198162097162098166a" + "2199162098162099166a219a16209916209a166a219b16209a16209b166a219c16209b16209c166a219d16209c1620" + "9d166a219e16209d16209e166a219f16209e16209f166a21a016209f1620a0166a21a11620a01620a1166a21a21620" + "a11620a2166a21a31620a21620a3166a21a41620a31620a4166a21a51620a41620a5166a21a61620a51620a6166a21" + "a71620a61620a7166a21a81620a71620a8166a21a91620a81620a9166a21aa1620a91620aa166a21ab1620aa1620ab" + "166a21ac1620ab1620ac166a21ad1620ac1620ad166a21ae1620ad1620ae166a21af1620ae1620af166a21b01620af" + "1620b0166a21b11620b01620b1166a21b21620b11620b2166a21b31620b21620b3166a21b41620b31620b4166a21b5" + "1620b41620b5166a21b61620b51620b6166a21b71620b61620b7166a21b81620b71620b8166a21b91620b81620b916" + "6a21ba1620b91620ba166a21bb1620ba1620bb166a21bc1620bb1620bc166a21bd1620bc1620bd166a21be1620bd16" + "20be166a21bf1620be1620bf166a21c01620bf1620c0166a21c11620c01620c1166a21c21620c11620c2166a21c316" + "20c21620c3166a21c41620c31620c4166a21c51620c41620c5166a21c61620c51620c6166a21c71620c61620c7166a" + "21c81620c71620c8166a21c91620c81620c9166a21ca1620c91620ca166a21cb1620ca1620cb166a21cc1620cb1620" + "cc166a21cd1620cc1620cd166a21ce1620cd1620ce166a21cf1620ce1620cf166a21d01620cf1620d0166a21d11620" + "d01620d1166a21d21620d11620d2166a21d31620d21620d3166a21d41620d31620d4166a21d51620d41620d5166a21" + "d61620d51620d6166a21d71620d61620d7166a21d81620d71620d8166a21d91620d81620d9166a21da1620d91620da" + "166a21db1620da1620db166a21dc1620db1620dc166a21dd1620dc1620dd166a21de1620dd1620de166a21df1620de" + "1620df166a21e01620df1620e0166a21e11620e01620e1166a21e21620e11620e2166a21e31620e21620e3166a21e4" + "1620e31620e4166a21e51620e41620e5166a21e61620e51620e6166a21e71620e61620e7166a21e81620e71620e816" + "6a21e91620e81620e9166a21ea1620e91620ea166a21eb1620ea1620eb166a21ec1620eb1620ec166a21ed1620ec16" + "20ed166a21ee1620ed1620ee166a21ef1620ee1620ef166a21f01620ef1620f0166a21f11620f01620f1166a21f216" + "20f11620f2166a21f31620f21620f3166a21f41620f31620f4166a21f51620f41620f5166a21f61620f51620f6166a" + "21f71620f61620f7166a21f81620f71620f8166a21f91620f81620f9166a21fa1620f91620fa166a21fb1620fa1620" + "fb166a21fc1620fb1620fc166a21fd1620fc1620fd166a21fe1620fd1620fe166a21ff1620fe1620ff166a21801720" + "ff162080176a2181172080172081176a2182172081172082176a2183172082172083176a2184172083172084176a21" + "85172084172085176a2186172085172086176a2187172086172087176a2188172087172088176a2189172088172089" + "176a218a17208917208a176a218b17208a17208b176a218c17208b17208c176a218d17208c17208d176a218e17208d" + "17208e176a218f17208e17208f176a219017208f172090176a2191172090172091176a2192172091172092176a2193" + "172092172093176a2194172093172094176a2195172094172095176a2196172095172096176a219717209617209717" + "6a2198172097172098176a2199172098172099176a219a17209917209a176a219b17209a17209b176a219c17209b17" + "209c176a219d17209c17209d176a219e17209d17209e176a219f17209e17209f176a21a017209f1720a0176a21a117" + "20a01720a1176a21a21720a11720a2176a21a31720a21720a3176a21a41720a31720a4176a21a51720a41720a5176a" + "21a61720a51720a6176a21a71720a61720a7176a21a81720a71720a8176a21a91720a81720a9176a21aa1720a91720" + "aa176a21ab1720aa1720ab176a21ac1720ab1720ac176a21ad1720ac1720ad176a21ae1720ad1720ae176a21af1720" + "ae1720af176a21b01720af1720b0176a21b11720b01720b1176a21b21720b11720b2176a21b31720b21720b3176a21" + "b41720b31720b4176a21b51720b41720b5176a21b61720b51720b6176a21b71720b61720b7176a21b81720b71720b8" + "176a21b91720b81720b9176a21ba1720b91720ba176a21bb1720ba1720bb176a21bc1720bb1720bc176a21bd1720bc" + "1720bd176a21be1720bd1720be176a21bf1720be1720bf176a21c01720bf1720c0176a21c11720c01720c1176a21c2" + "1720c11720c2176a21c31720c21720c3176a21c41720c31720c4176a21c51720c41720c5176a21c61720c51720c617" + "6a21c71720c61720c7176a21c81720c71720c8176a21c91720c81720c9176a21ca1720c91720ca176a21cb1720ca17" + "20cb176a21cc1720cb1720cc176a21cd1720cc1720cd176a21ce1720cd1720ce176a21cf1720ce1720cf176a21d017" + "20cf1720d0176a21d11720d01720d1176a21d21720d11720d2176a21d31720d21720d3176a21d41720d31720d4176a" + "21d51720d41720d5176a21d61720d51720d6176a21d71720d61720d7176a21d81720d71720d8176a21d91720d81720" + "d9176a21da1720d91720da176a21db1720da1720db176a21dc1720db1720dc176a21dd1720dc1720dd176a21de1720" + "dd1720de176a21df1720de1720df176a21e01720df1720e0176a21e11720e01720e1176a21e21720e11720e2176a21" + "e31720e21720e3176a21e41720e31720e4176a21e51720e41720e5176a21e61720e51720e6176a21e71720e61720e7" + "176a21e81720e71720e8176a21e91720e81720e9176a21ea1720e91720ea176a21eb1720ea1720eb176a21ec1720eb" + "1720ec176a21ed1720ec1720ed176a21ee1720ed1720ee176a21ef1720ee1720ef176a21f01720ef1720f0176a21f1" + "1720f01720f1176a21f21720f11720f2176a21f31720f21720f3176a21f41720f31720f4176a21f51720f41720f517" + "6a21f61720f51720f6176a21f71720f61720f7176a21f81720f71720f8176a21f91720f81720f9176a21fa1720f917" + "20fa176a21fb1720fa1720fb176a21fc1720fb1720fc176a21fd1720fc1720fd176a21fe1720fd1720fe176a21ff17" + "20fe1720ff176a21801820ff172080186a2181182080182081186a2182182081182082186a2183182082182083186a" + "2184182083182084186a2185182084182085186a2186182085182086186a2187182086182087186a21881820871820" + "88186a2189182088182089186a218a18208918208a186a218b18208a18208b186a218c18208b18208c186a218d1820" + "8c18208d186a218e18208d18208e186a218f18208e18208f186a219018208f182090186a2191182090182091186a21" + "92182091182092186a2193182092182093186a2194182093182094186a2195182094182095186a2196182095182096" + "186a2197182096182097186a2198182097182098186a2199182098182099186a219a18209918209a186a219b18209a" + "18209b186a219c18209b18209c186a219d18209c18209d186a219e18209d18209e186a219f18209e18209f186a21a0" + "18209f1820a0186a21a11820a01820a1186a21a21820a11820a2186a21a31820a21820a3186a21a41820a31820a418" + "6a21a51820a41820a5186a21a61820a51820a6186a21a71820a61820a7186a21a81820a71820a8186a21a91820a818" + "20a9186a21aa1820a91820aa186a21ab1820aa1820ab186a21ac1820ab1820ac186a21ad1820ac1820ad186a21ae18" + "20ad1820ae186a21af1820ae1820af186a21b01820af1820b0186a21b11820b01820b1186a21b21820b11820b2186a" + "21b31820b21820b3186a21b41820b31820b4186a21b51820b41820b5186a21b61820b51820b6186a21b71820b61820" + "b7186a21b81820b71820b8186a21b91820b81820b9186a21ba1820b91820ba186a21bb1820ba1820bb186a21bc1820" + "bb1820bc186a21bd1820bc1820bd186a21be1820bd1820be186a21bf1820be1820bf186a21c01820bf1820c0186a21" + "c11820c01820c1186a21c21820c11820c2186a21c31820c21820c3186a21c41820c31820c4186a21c51820c41820c5" + "186a21c61820c51820c6186a21c71820c61820c7186a21c81820c71820c8186a21c91820c81820c9186a21ca1820c9" + "1820ca186a21cb1820ca1820cb186a21cc1820cb1820cc186a21cd1820cc1820cd186a21ce1820cd1820ce186a21cf" + "1820ce1820cf186a21d01820cf1820d0186a21d11820d01820d1186a21d21820d11820d2186a21d31820d21820d318" + "6a21d41820d31820d4186a21d51820d41820d5186a21d61820d51820d6186a21d71820d61820d7186a21d81820d718" + "20d8186a21d91820d81820d9186a21da1820d91820da186a21db1820da1820db186a21dc1820db1820dc186a21dd18" + "20dc1820dd186a21de1820dd1820de186a21df1820de1820df186a21e01820df1820e0186a21e11820e01820e1186a" + "21e21820e11820e2186a21e31820e21820e3186a21e41820e31820e4186a21e51820e41820e5186a21e61820e51820" + "e6186a21e71820e61820e7186a21e81820e71820e8186a21e91820e81820e9186a21ea1820e91820ea186a21eb1820" + "ea1820eb186a21ec1820eb1820ec186a21ed1820ec1820ed186a21ee1820ed1820ee186a21ef1820ee1820ef186a21" + "f01820ef1820f0186a21f11820f01820f1186a21f21820f11820f2186a21f31820f21820f3186a21f41820f31820f4" + "186a21f51820f41820f5186a21f61820f51820f6186a21f71820f61820f7186a21f81820f71820f8186a21f91820f8" + "1820f9186a21fa1820f91820fa186a21fb1820fa1820fb186a21fc1820fb1820fc186a21fd1820fc1820fd186a21fe" + "1820fd1820fe186a21ff1820fe1820ff186a21801920ff182080196a2181192080192081196a218219208119208219" + "6a2183192082192083196a2184192083192084196a2185192084192085196a2186192085192086196a218719208619" + "2087196a2188192087192088196a2189192088192089196a218a19208919208a196a218b19208a19208b196a218c19" + "208b19208c196a218d19208c19208d196a218e19208d19208e196a218f19208e19208f196a219019208f192090196a" + "2191192090192091196a2192192091192092196a2193192092192093196a2194192093192094196a21951920941920" + "95196a2196192095192096196a2197192096192097196a2198192097192098196a2199192098192099196a219a1920" + "9919209a196a219b19209a19209b196a219c19209b19209c196a219d19209c19209d196a219e19209d19209e196a21" + "9f19209e19209f196a21a019209f1920a0196a21a11920a01920a1196a21a21920a11920a2196a21a31920a21920a3" + "196a21a41920a31920a4196a21a51920a41920a5196a21a61920a51920a6196a21a71920a61920a7196a21a81920a7" + "1920a8196a21a91920a81920a9196a21aa1920a91920aa196a21ab1920aa1920ab196a21ac1920ab1920ac196a21ad" + "1920ac1920ad196a21ae1920ad1920ae196a21af1920ae1920af196a21b01920af1920b0196a21b11920b01920b119" + "6a21b21920b11920b2196a21b31920b21920b3196a21b41920b31920b4196a21b51920b41920b5196a21b61920b519" + "20b6196a21b71920b61920b7196a21b81920b71920b8196a21b91920b81920b9196a21ba1920b91920ba196a21bb19" + "20ba1920bb196a21bc1920bb1920bc196a21bd1920bc1920bd196a21be1920bd1920be196a21bf1920be1920bf196a" + "21c01920bf1920c0196a21c11920c01920c1196a21c21920c11920c2196a21c31920c21920c3196a21c41920c31920" + "c4196a21c51920c41920c5196a21c61920c51920c6196a21c71920c61920c7196a21c81920c71920c8196a21c91920" + "c81920c9196a21ca1920c91920ca196a21cb1920ca1920cb196a21cc1920cb1920cc196a21cd1920cc1920cd196a21" + "ce1920cd1920ce196a21cf1920ce1920cf196a21d01920cf1920d0196a21d11920d01920d1196a21d21920d11920d2" + "196a21d31920d21920d3196a21d41920d31920d4196a21d51920d41920d5196a21d61920d51920d6196a21d71920d6" + "1920d7196a21d81920d71920d8196a21d91920d81920d9196a21da1920d91920da196a21db1920da1920db196a21dc" + "1920db1920dc196a21dd1920dc1920dd196a21de1920dd1920de196a21df1920de1920df196a21e01920df1920e019" + "6a21e11920e01920e1196a21e21920e11920e2196a21e31920e21920e3196a21e41920e31920e4196a21e51920e419" + "20e5196a21e61920e51920e6196a21e71920e61920e7196a21e81920e71920e8196a21e91920e81920e9196a21ea19" + "20e91920ea196a21eb1920ea1920eb196a21ec1920eb1920ec196a21ed1920ec1920ed196a21ee1920ed1920ee196a" + "21ef1920ee1920ef196a21f01920ef1920f0196a21f11920f01920f1196a21f21920f11920f2196a21f31920f21920" + "f3196a21f41920f31920f4196a21f51920f41920f5196a21f61920f51920f6196a21f71920f61920f7196a21f81920" + "f71920f8196a21f91920f81920f9196a21fa1920f91920fa196a21fb1920fa1920fb196a21fc1920fb1920fc196a21" + "fd1920fc1920fd196a21fe1920fd1920fe196a21ff1920fe1920ff196a21801a20ff1920801a6a21811a20801a2081" + "1a6a21821a20811a20821a6a21831a20821a20831a6a21841a20831a20841a6a21851a20841a20851a6a21861a2085" + "1a20861a6a21871a20861a20871a6a21881a20871a20881a6a21891a20881a20891a6a218a1a20891a208a1a6a218b" + "1a208a1a208b1a6a218c1a208b1a208c1a6a218d1a208c1a208d1a6a218e1a208d1a208e1a6a218f1a208e1a208f1a" + "6a21901a208f1a20901a6a21911a20901a20911a6a21921a20911a20921a6a21931a20921a20931a6a21941a20931a" + "20941a6a21951a20941a20951a6a21961a20951a20961a6a21971a20961a20971a6a21981a20971a20981a6a21991a" + "20981a20991a6a219a1a20991a209a1a6a219b1a209a1a209b1a6a219c1a209b1a209c1a6a219d1a209c1a209d1a6a" + "219e1a209d1a209e1a6a219f1a209e1a209f1a6a21a01a209f1a20a01a6a21a11a20a01a20a11a6a21a21a20a11a20" + "a21a6a21a31a20a21a20a31a6a21a41a20a31a20a41a6a21a51a20a41a20a51a6a21a61a20a51a20a61a6a21a71a20" + "a61a20a71a6a21a81a20a71a20a81a6a21a91a20a81a20a91a6a21aa1a20a91a20aa1a6a21ab1a20aa1a20ab1a6a21" + "ac1a20ab1a20ac1a6a21ad1a20ac1a20ad1a6a21ae1a20ad1a20ae1a6a21af1a20ae1a20af1a6a21b01a20af1a20b0" + "1a6a21b11a20b01a20b11a6a21b21a20b11a20b21a6a21b31a20b21a20b31a6a21b41a20b31a20b41a6a21b51a20b4" + "1a20b51a6a21b61a20b51a20b61a6a21b71a20b61a20b71a6a21b81a20b71a20b81a6a21b91a20b81a20b91a6a21ba" + "1a20b91a20ba1a6a21bb1a20ba1a20bb1a6a21bc1a20bb1a20bc1a6a21bd1a20bc1a20bd1a6a21be1a20bd1a20be1a" + "6a21bf1a20be1a20bf1a6a21c01a20bf1a20c01a6a21c11a20c01a20c11a6a21c21a20c11a20c21a6a21c31a20c21a" + "20c31a6a21c41a20c31a20c41a6a21c51a20c41a20c51a6a21c61a20c51a20c61a6a21c71a20c61a20c71a6a21c81a" + "20c71a20c81a6a21c91a20c81a20c91a6a21ca1a20c91a20ca1a6a21cb1a20ca1a20cb1a6a21cc1a20cb1a20cc1a6a" + "21cd1a20cc1a20cd1a6a21ce1a20cd1a20ce1a6a21cf1a20ce1a20cf1a6a21d01a20cf1a20d01a6a21d11a20d01a20" + "d11a6a21d21a20d11a20d21a6a21d31a20d21a20d31a6a21d41a20d31a20d41a6a21d51a20d41a20d51a6a21d61a20" + "d51a20d61a6a21d71a20d61a20d71a6a21d81a20d71a20d81a6a21d91a20d81a20d91a6a21da1a20d91a20da1a6a21" + "db1a20da1a20db1a6a21dc1a20db1a20dc1a6a21dd1a20dc1a20dd1a6a21de1a20dd1a20de1a6a21df1a20de1a20df" + "1a6a21e01a20df1a20e01a6a21e11a20e01a20e11a6a21e21a20e11a20e21a6a21e31a20e21a20e31a6a21e41a20e3" + "1a20e41a6a21e51a20e41a20e51a6a21e61a20e51a20e61a6a21e71a20e61a20e71a6a21e81a20e71a20e81a6a21e9" + "1a20e81a20e91a6a21ea1a20e91a20ea1a6a21eb1a20ea1a20eb1a6a21ec1a20eb1a20ec1a6a21ed1a20ec1a20ed1a" + "6a21ee1a20ed1a20ee1a6a21ef1a20ee1a20ef1a6a21f01a20ef1a20f01a6a21f11a20f01a20f11a6a21f21a20f11a" + "20f21a6a21f31a20f21a20f31a6a21f41a20f31a20f41a6a21f51a20f41a20f51a6a21f61a20f51a20f61a6a21f71a" + "20f61a20f71a6a21f81a20f71a20f81a6a21f91a20f81a20f91a6a21fa1a20f91a20fa1a6a21fb1a20fa1a20fb1a6a" + "21fc1a20fb1a20fc1a6a21fd1a20fc1a20fd1a6a21fe1a20fd1a20fe1a6a21ff1a20fe1a20ff1a6a21801b20ff1a20" + "801b6a21811b20801b20811b6a21821b20811b20821b6a21831b20821b20831b6a21841b20831b20841b6a21851b20" + "841b20851b6a21861b20851b20861b6a21871b20861b20871b6a21881b20871b20881b6a21891b20881b20891b6a21" + "8a1b20891b208a1b6a218b1b208a1b208b1b6a218c1b208b1b208c1b6a218d1b208c1b208d1b6a218e1b208d1b208e" + "1b6a218f1b208e1b208f1b6a21901b208f1b20901b6a21911b20901b20911b6a21921b20911b20921b6a21931b2092" + "1b20931b6a21941b20931b20941b6a21951b20941b20951b6a21961b20951b20961b6a21971b20961b20971b6a2198" + "1b20971b20981b6a21991b20981b20991b6a219a1b20991b209a1b6a219b1b209a1b209b1b6a219c1b209b1b209c1b" + "6a219d1b209c1b209d1b6a219e1b209d1b209e1b6a219f1b209e1b209f1b6a21a01b209f1b20a01b6a21a11b20a01b" + "20a11b6a21a21b20a11b20a21b6a21a31b20a21b20a31b6a21a41b20a31b20a41b6a21a51b20a41b20a51b6a21a61b" + "20a51b20a61b6a21a71b20a61b20a71b6a21a81b20a71b20a81b6a21a91b20a81b20a91b6a21aa1b20a91b20aa1b6a" + "21ab1b20aa1b20ab1b6a21ac1b20ab1b20ac1b6a21ad1b20ac1b20ad1b6a21ae1b20ad1b20ae1b6a21af1b20ae1b20" + "af1b6a21b01b20af1b20b01b6a21b11b20b01b20b11b6a21b21b20b11b20b21b6a21b31b20b21b20b31b6a21b41b20" + "b31b20b41b6a21b51b20b41b20b51b6a21b61b20b51b20b61b6a21b71b20b61b20b71b6a21b81b20b71b20b81b6a21" + "b91b20b81b20b91b6a21ba1b20b91b20ba1b6a21bb1b20ba1b20bb1b6a21bc1b20bb1b20bc1b6a21bd1b20bc1b20bd" + "1b6a21be1b20bd1b20be1b6a21bf1b20be1b20bf1b6a21c01b20bf1b20c01b6a21c11b20c01b20c11b6a21c21b20c1" + "1b20c21b6a21c31b20c21b20c31b6a21c41b20c31b20c41b6a21c51b20c41b20c51b6a21c61b20c51b20c61b6a21c7" + "1b20c61b20c71b6a21c81b20c71b20c81b6a21c91b20c81b20c91b6a21ca1b20c91b20ca1b6a21cb1b20ca1b20cb1b" + "6a21cc1b20cb1b20cc1b6a21cd1b20cc1b20cd1b6a21ce1b20cd1b20ce1b6a21cf1b20ce1b20cf1b6a21d01b20cf1b" + "20d01b6a21d11b20d01b20d11b6a21d21b20d11b20d21b6a21d31b20d21b20d31b6a21d41b20d31b20d41b6a21d51b" + "20d41b20d51b6a21d61b20d51b20d61b6a21d71b20d61b20d71b6a21d81b20d71b20d81b6a21d91b20d81b20d91b6a" + "21da1b20d91b20da1b6a21db1b20da1b20db1b6a21dc1b20db1b20dc1b6a21dd1b20dc1b20dd1b6a21de1b20dd1b20" + "de1b6a21df1b20de1b20df1b6a21e01b20df1b20e01b6a21e11b20e01b20e11b6a21e21b20e11b20e21b6a21e31b20" + "e21b20e31b6a21e41b20e31b20e41b6a21e51b20e41b20e51b6a21e61b20e51b20e61b6a21e71b20e61b20e71b6a21" + "e81b20e71b20e81b6a21e91b20e81b20e91b6a21ea1b20e91b20ea1b6a21eb1b20ea1b20eb1b6a21ec1b20eb1b20ec" + "1b6a21ed1b20ec1b20ed1b6a21ee1b20ed1b20ee1b6a21ef1b20ee1b20ef1b6a21f01b20ef1b20f01b6a21f11b20f0" + "1b20f11b6a21f21b20f11b20f21b6a21f31b20f21b20f31b6a21f41b20f31b20f41b6a21f51b20f41b20f51b6a21f6" + "1b20f51b20f61b6a21f71b20f61b20f71b6a21f81b20f71b20f81b6a21f91b20f81b20f91b6a21fa1b20f91b20fa1b" + "6a21fb1b20fa1b20fb1b6a21fc1b20fb1b20fc1b6a21fd1b20fc1b20fd1b6a21fe1b20fd1b20fe1b6a21ff1b20fe1b" + "20ff1b6a21801c20ff1b20801c6a21811c20801c20811c6a21821c20811c20821c6a21831c20821c20831c6a21841c" + "20831c20841c6a21851c20841c20851c6a21861c20851c20861c6a21871c20861c20871c6a21881c20871c20881c6a" + "21891c20881c20891c6a218a1c20891c208a1c6a218b1c208a1c208b1c6a218c1c208b1c208c1c6a218d1c208c1c20" + "8d1c6a218e1c208d1c208e1c6a218f1c208e1c208f1c6a21901c208f1c20901c6a21911c20901c20911c6a21921c20" + "911c20921c6a21931c20921c20931c6a21941c20931c20941c6a21951c20941c20951c6a21961c20951c20961c6a21" + "971c20961c20971c6a21981c20971c20981c6a21991c20981c20991c6a219a1c20991c209a1c6a219b1c209a1c209b" + "1c6a219c1c209b1c209c1c6a219d1c209c1c209d1c6a219e1c209d1c209e1c6a219f1c209e1c209f1c6a21a01c209f" + "1c20a01c6a21a11c20a01c20a11c6a21a21c20a11c20a21c6a21a31c20a21c20a31c6a21a41c20a31c20a41c6a21a5" + "1c20a41c20a51c6a21a61c20a51c20a61c6a21a71c20a61c20a71c6a21a81c20a71c20a81c6a21a91c20a81c20a91c" + "6a21aa1c20a91c20aa1c6a21ab1c20aa1c20ab1c6a21ac1c20ab1c20ac1c6a21ad1c20ac1c20ad1c6a21ae1c20ad1c" + "20ae1c6a21af1c20ae1c20af1c6a21b01c20af1c20b01c6a21b11c20b01c20b11c6a21b21c20b11c20b21c6a21b31c" + "20b21c20b31c6a21b41c20b31c20b41c6a21b51c20b41c20b51c6a21b61c20b51c20b61c6a21b71c20b61c20b71c6a" + "21b81c20b71c20b81c6a21b91c20b81c20b91c6a21ba1c20b91c20ba1c6a21bb1c20ba1c20bb1c6a21bc1c20bb1c20" + "bc1c6a21bd1c20bc1c20bd1c6a21be1c20bd1c20be1c6a21bf1c20be1c20bf1c6a21c01c20bf1c20c01c6a21c11c20" + "c01c20c11c6a21c21c20c11c20c21c6a21c31c20c21c20c31c6a21c41c20c31c20c41c6a21c51c20c41c20c51c6a21" + "c61c20c51c20c61c6a21c71c20c61c20c71c6a21c81c20c71c20c81c6a21c91c20c81c20c91c6a21ca1c20c91c20ca" + "1c6a21cb1c20ca1c20cb1c6a21cc1c20cb1c20cc1c6a21cd1c20cc1c20cd1c6a21ce1c20cd1c20ce1c6a21cf1c20ce" + "1c20cf1c6a21d01c20cf1c20d01c6a21d11c20d01c20d11c6a21d21c20d11c20d21c6a21d31c20d21c20d31c6a21d4" + "1c20d31c20d41c6a21d51c20d41c20d51c6a21d61c20d51c20d61c6a21d71c20d61c20d71c6a21d81c20d71c20d81c" + "6a21d91c20d81c20d91c6a21da1c20d91c20da1c6a21db1c20da1c20db1c6a21dc1c20db1c20dc1c6a21dd1c20dc1c" + "20dd1c6a21de1c20dd1c20de1c6a21df1c20de1c20df1c6a21e01c20df1c20e01c6a21e11c20e01c20e11c6a21e21c" + "20e11c20e21c6a21e31c20e21c20e31c6a21e41c20e31c20e41c6a21e51c20e41c20e51c6a21e61c20e51c20e61c6a" + "21e71c20e61c20e71c6a21e81c20e71c20e81c6a21e91c20e81c20e91c6a21ea1c20e91c20ea1c6a21eb1c20ea1c20" + "eb1c6a21ec1c20eb1c20ec1c6a21ed1c20ec1c20ed1c6a21ee1c20ed1c20ee1c6a21ef1c20ee1c20ef1c6a21f01c20" + "ef1c20f01c6a21f11c20f01c20f11c6a21f21c20f11c20f21c6a21f31c20f21c20f31c6a21f41c20f31c20f41c6a21" + "f51c20f41c20f51c6a21f61c20f51c20f61c6a21f71c20f61c20f71c6a21f81c20f71c20f81c6a21f91c20f81c20f9" + "1c6a21fa1c20f91c20fa1c6a21fb1c20fa1c20fb1c6a21fc1c20fb1c20fc1c6a21fd1c20fc1c20fd1c6a21fe1c20fd" + "1c20fe1c6a21ff1c20fe1c20ff1c6a21801d20ff1c20801d6a21811d20801d20811d6a21821d20811d20821d6a2183" + "1d20821d20831d6a21841d20831d20841d6a21851d20841d20851d6a21861d20851d20861d6a21871d20861d20871d" + "6a21881d20871d20881d6a21891d20881d20891d6a218a1d20891d208a1d6a218b1d208a1d208b1d6a218c1d208b1d" + "208c1d6a218d1d208c1d208d1d6a218e1d208d1d208e1d6a218f1d208e1d208f1d6a21901d208f1d20901d6a21911d" + "20901d20911d6a21921d20911d20921d6a21931d20921d20931d6a21941d20931d20941d6a21951d20941d20951d6a" + "21961d20951d20961d6a21971d20961d20971d6a21981d20971d20981d6a21991d20981d20991d6a219a1d20991d20" + "9a1d6a219b1d209a1d209b1d6a219c1d209b1d209c1d6a219d1d209c1d209d1d6a219e1d209d1d209e1d6a219f1d20" + "9e1d209f1d6a21a01d209f1d20a01d6a21a11d20a01d20a11d6a21a21d20a11d20a21d6a21a31d20a21d20a31d6a21" + "a41d20a31d20a41d6a21a51d20a41d20a51d6a21a61d20a51d20a61d6a21a71d20a61d20a71d6a21a81d20a71d20a8" + "1d6a21a91d20a81d20a91d6a21aa1d20a91d20aa1d6a21ab1d20aa1d20ab1d6a21ac1d20ab1d20ac1d6a21ad1d20ac" + "1d20ad1d6a21ae1d20ad1d20ae1d6a21af1d20ae1d20af1d6a21b01d20af1d20b01d6a21b11d20b01d20b11d6a21b2" + "1d20b11d20b21d6a21b31d20b21d20b31d6a21b41d20b31d20b41d6a21b51d20b41d20b51d6a21b61d20b51d20b61d" + "6a21b71d20b61d20b71d6a21b81d20b71d20b81d6a21b91d20b81d20b91d6a21ba1d20b91d20ba1d6a21bb1d20ba1d" + "20bb1d6a21bc1d20bb1d20bc1d6a21bd1d20bc1d20bd1d6a21be1d20bd1d20be1d6a21bf1d20be1d20bf1d6a21c01d" + "20bf1d20c01d6a21c11d20c01d20c11d6a21c21d20c11d20c21d6a21c31d20c21d20c31d6a21c41d20c31d20c41d6a" + "21c51d20c41d20c51d6a21c61d20c51d20c61d6a21c71d20c61d20c71d6a21c81d20c71d20c81d6a21c91d20c81d20" + "c91d6a21ca1d20c91d20ca1d6a21cb1d20ca1d20cb1d6a21cc1d20cb1d20cc1d6a21cd1d20cc1d20cd1d6a21ce1d20" + "cd1d20ce1d6a21cf1d20ce1d20cf1d6a21d01d20cf1d20d01d6a21d11d20d01d20d11d6a21d21d20d11d20d21d6a21" + "d31d20d21d20d31d6a21d41d20d31d20d41d6a21d51d20d41d20d51d6a21d61d20d51d20d61d6a21d71d20d61d20d7" + "1d6a21d81d20d71d20d81d6a21d91d20d81d20d91d6a21da1d20d91d20da1d6a21db1d20da1d20db1d6a21dc1d20db" + "1d20dc1d6a21dd1d20dc1d20dd1d6a21de1d20dd1d20de1d6a21df1d20de1d20df1d6a21e01d20df1d20e01d6a21e1" + "1d20e01d20e11d6a21e21d20e11d20e21d6a21e31d20e21d20e31d6a21e41d20e31d20e41d6a21e51d20e41d20e51d" + "6a21e61d20e51d20e61d6a21e71d20e61d20e71d6a21e81d20e71d20e81d6a21e91d20e81d20e91d6a21ea1d20e91d" + "20ea1d6a21eb1d20ea1d20eb1d6a21ec1d20eb1d20ec1d6a21ed1d20ec1d20ed1d6a21ee1d20ed1d20ee1d6a21ef1d" + "20ee1d20ef1d6a21f01d20ef1d20f01d6a21f11d20f01d20f11d6a21f21d20f11d20f21d6a21f31d20f21d20f31d6a" + "21f41d20f31d20f41d6a21f51d20f41d20f51d6a21f61d20f51d20f61d6a21f71d20f61d20f71d6a21f81d20f71d20" + "f81d6a21f91d20f81d20f91d6a21fa1d20f91d20fa1d6a21fb1d20fa1d20fb1d6a21fc1d20fb1d20fc1d6a21fd1d20" + "fc1d20fd1d6a21fe1d20fd1d20fe1d6a21ff1d20fe1d20ff1d6a21801e20ff1d20801e6a21811e20801e20811e6a21" + "821e20811e20821e6a21831e20821e20831e6a21841e20831e20841e6a21851e20841e20851e6a21861e20851e2086" + "1e6a21871e20861e20871e6a21881e20871e20881e6a21891e20881e20891e6a218a1e20891e208a1e6a218b1e208a" + "1e208b1e6a218c1e208b1e208c1e6a218d1e208c1e208d1e6a218e1e208d1e208e1e6a218f1e208e1e208f1e6a2190" + "1e208f1e20901e6a21911e20901e20911e6a21921e20911e20921e6a21931e20921e20931e6a21941e20931e20941e" + "6a21951e20941e20951e6a21961e20951e20961e6a21971e20961e20971e6a21981e20971e20981e6a21991e20981e" + "20991e6a219a1e20991e209a1e6a219b1e209a1e209b1e6a219c1e209b1e209c1e6a219d1e209c1e209d1e6a219e1e" + "209d1e209e1e6a219f1e209e1e209f1e6a21a01e209f1e20a01e6a21a11e20a01e20a11e6a21a21e20a11e20a21e6a" + "21a31e20a21e20a31e6a21a41e20a31e20a41e6a21a51e20a41e20a51e6a21a61e20a51e20a61e6a21a71e20a61e20" + "a71e6a21a81e20a71e20a81e6a21a91e20a81e20a91e6a21aa1e20a91e20aa1e6a21ab1e20aa1e20ab1e6a21ac1e20" + "ab1e20ac1e6a21ad1e20ac1e20ad1e6a21ae1e20ad1e20ae1e6a21af1e20ae1e20af1e6a21b01e20af1e20b01e6a21" + "b11e20b01e20b11e6a21b21e20b11e20b21e6a21b31e20b21e20b31e6a21b41e20b31e20b41e6a21b51e20b41e20b5" + "1e6a21b61e20b51e20b61e6a21b71e20b61e20b71e6a21b81e20b71e20b81e6a21b91e20b81e20b91e6a21ba1e20b9" + "1e20ba1e6a21bb1e20ba1e20bb1e6a21bc1e20bb1e20bc1e6a21bd1e20bc1e20bd1e6a21be1e20bd1e20be1e6a21bf" + "1e20be1e20bf1e6a21c01e20bf1e20c01e6a21c11e20c01e20c11e6a21c21e20c11e20c21e6a21c31e20c21e20c31e" + "6a21c41e20c31e20c41e6a21c51e20c41e20c51e6a21c61e20c51e20c61e6a21c71e20c61e20c71e6a21c81e20c71e" + "20c81e6a21c91e20c81e20c91e6a21ca1e20c91e20ca1e6a21cb1e20ca1e20cb1e6a21cc1e20cb1e20cc1e6a21cd1e" + "20cc1e20cd1e6a21ce1e20cd1e20ce1e6a21cf1e20ce1e20cf1e6a21d01e20cf1e20d01e6a21d11e20d01e20d11e6a" + "21d21e20d11e20d21e6a21d31e20d21e20d31e6a21d41e20d31e20d41e6a21d51e20d41e20d51e6a21d61e20d51e20" + "d61e6a21d71e20d61e20d71e6a21d81e20d71e20d81e6a21d91e20d81e20d91e6a21da1e20d91e20da1e6a21db1e20" + "da1e20db1e6a21dc1e20db1e20dc1e6a21dd1e20dc1e20dd1e6a21de1e20dd1e20de1e6a21df1e20de1e20df1e6a21" + "e01e20df1e20e01e6a21e11e20e01e20e11e6a21e21e20e11e20e21e6a21e31e20e21e20e31e6a21e41e20e31e20e4" + "1e6a21e51e20e41e20e51e6a21e61e20e51e20e61e6a21e71e20e61e20e71e6a21e81e20e71e20e81e6a21e91e20e8" + "1e20e91e6a21ea1e20e91e20ea1e6a21eb1e20ea1e20eb1e6a21ec1e20eb1e20ec1e6a21ed1e20ec1e20ed1e6a21ee" + "1e20ed1e20ee1e6a21ef1e20ee1e20ef1e6a21f01e20ef1e20f01e6a21f11e20f01e20f11e6a21f21e20f11e20f21e" + "6a21f31e20f21e20f31e6a21f41e20f31e20f41e6a21f51e20f41e20f51e6a21f61e20f51e20f61e6a21f71e20f61e" + "20f71e6a21f81e20f71e20f81e6a21f91e20f81e20f91e6a21fa1e20f91e20fa1e6a21fb1e20fa1e20fb1e6a21fc1e" + "20fb1e20fc1e6a21fd1e20fc1e20fd1e6a21fe1e20fd1e20fe1e6a21ff1e20fe1e20ff1e6a21801f20ff1e20801f6a" + "21811f20801f20811f6a21821f20811f20821f6a21831f20821f20831f6a21841f20831f20841f6a21851f20841f20" + "851f6a21861f20851f20861f6a21871f20861f20871f6a21881f20871f20881f6a21891f20881f20891f6a218a1f20" + "891f208a1f6a218b1f208a1f208b1f6a218c1f208b1f208c1f6a218d1f208c1f208d1f6a218e1f208d1f208e1f6a21" + "8f1f208e1f208f1f6a21901f208f1f20901f6a21911f20901f20911f6a21921f20911f20921f6a21931f20921f2093" + "1f6a21941f20931f20941f6a21951f20941f20951f6a21961f20951f20961f6a21971f20961f20971f6a21981f2097" + "1f20981f6a21991f20981f20991f6a219a1f20991f209a1f6a219b1f209a1f209b1f6a219c1f209b1f209c1f6a219d" + "1f209c1f209d1f6a219e1f209d1f209e1f6a219f1f209e1f209f1f6a21a01f209f1f20a01f6a21a11f20a01f20a11f" + "6a21a21f20a11f20a21f6a21a31f20a21f20a31f6a21a41f20a31f20a41f6a21a51f20a41f20a51f6a21a61f20a51f" + "20a61f6a21a71f20a61f20a71f6a21a81f20a71f20a81f6a21a91f20a81f20a91f6a21aa1f20a91f20aa1f6a21ab1f" + "20aa1f20ab1f6a21ac1f20ab1f20ac1f6a21ad1f20ac1f20ad1f6a21ae1f20ad1f20ae1f6a21af1f20ae1f20af1f6a" + "21b01f20af1f20b01f6a21b11f20b01f20b11f6a21b21f20b11f20b21f6a21b31f20b21f20b31f6a21b41f20b31f20" + "b41f6a21b51f20b41f20b51f6a21b61f20b51f20b61f6a21b71f20b61f20b71f6a21b81f20b71f20b81f6a21b91f20" + "b81f20b91f6a21ba1f20b91f20ba1f6a21bb1f20ba1f20bb1f6a21bc1f20bb1f20bc1f6a21bd1f20bc1f20bd1f6a21" + "be1f20bd1f20be1f6a21bf1f20be1f20bf1f6a21c01f20bf1f20c01f6a21c11f20c01f20c11f6a21c21f20c11f20c2" + "1f6a21c31f20c21f20c31f6a21c41f20c31f20c41f6a21c51f20c41f20c51f6a21c61f20c51f20c61f6a21c71f20c6" + "1f20c71f6a21c81f20c71f20c81f6a21c91f20c81f20c91f6a21ca1f20c91f20ca1f6a21cb1f20ca1f20cb1f6a21cc" + "1f20cb1f20cc1f6a21cd1f20cc1f20cd1f6a21ce1f20cd1f20ce1f6a21cf1f20ce1f20cf1f6a21d01f20cf1f20d01f" + "6a21d11f20d01f20d11f6a21d21f20d11f20d21f6a21d31f20d21f20d31f6a21d41f20d31f20d41f6a21d51f20d41f" + "20d51f6a21d61f20d51f20d61f6a21d71f20d61f20d71f6a21d81f20d71f20d81f6a21d91f20d81f20d91f6a21da1f" + "20d91f20da1f6a21db1f20da1f20db1f6a21dc1f20db1f20dc1f6a21dd1f20dc1f20dd1f6a21de1f20dd1f20de1f6a" + "21df1f20de1f20df1f6a21e01f20df1f20e01f6a21e11f20e01f20e11f6a21e21f20e11f20e21f6a21e31f20e21f20" + "e31f6a21e41f20e31f20e41f6a21e51f20e41f20e51f6a21e61f20e51f20e61f6a21e71f20e61f20e71f6a21e81f20" + "e71f20e81f6a21e91f20e81f20e91f6a21ea1f20e91f20ea1f6a21eb1f20ea1f20eb1f6a21ec1f20eb1f20ec1f6a21" + "ed1f20ec1f20ed1f6a21ee1f20ed1f20ee1f6a21ef1f20ee1f20ef1f6a21f01f20ef1f20f01f6a21f11f20f01f20f1" + "1f6a21f21f20f11f20f21f6a21f31f20f21f20f31f6a21f41f20f31f20f41f6a21f51f20f41f20f51f6a21f61f20f5" + "1f20f61f6a21f71f20f61f20f71f6a21f81f20f71f20f81f6a21f91f20f81f20f91f6a21fa1f20f91f20fa1f6a21fb" + "1f20fa1f20fb1f6a21fc1f20fb1f20fc1f6a21fd1f20fc1f20fd1f6a21fe1f20fd1f20fe1f6a21ff1f20fe1f20ff1f" + "6a21802020ff1f2080206a2181202080202081206a2182202081202082206a2183202082202083206a218420208320" + "2084206a2185202084202085206a2186202085202086206a2187202086202087206a2188202087202088206a218920" + "2088202089206a218a20208920208a206a218b20208a20208b206a218c20208b20208c206a218d20208c20208d206a" + "218e20208d20208e206a218f20208e20208f206a219020208f202090206a2191202090202091206a21922020912020" + "92206a2193202092202093206a2194202093202094206a2195202094202095206a2196202095202096206a21972020" + "96202097206a2198202097202098206a2199202098202099206a219a20209920209a206a219b20209a20209b206a21" + "9c20209b20209c206a219d20209c20209d206a219e20209d20209e206a219f20209e20209f206a21a020209f2020a0" + "206a21a12020a02020a1206a21a22020a12020a2206a21a32020a22020a3206a21a42020a32020a4206a21a52020a4" + "2020a5206a21a62020a52020a6206a21a72020a62020a7206a21a82020a72020a8206a21a92020a82020a9206a21aa" + "2020a92020aa206a21ab2020aa2020ab206a21ac2020ab2020ac206a21ad2020ac2020ad206a21ae2020ad2020ae20" + "6a21af2020ae2020af206a21b02020af2020b0206a21b12020b02020b1206a21b22020b12020b2206a21b32020b220" + "20b3206a21b42020b32020b4206a21b52020b42020b5206a21b62020b52020b6206a21b72020b62020b7206a21b820" + "20b72020b8206a21b92020b82020b9206a21ba2020b92020ba206a21bb2020ba2020bb206a21bc2020bb2020bc206a" + "21bd2020bc2020bd206a21be2020bd2020be206a21bf2020be2020bf206a21c02020bf2020c0206a21c12020c02020" + "c1206a21c22020c12020c2206a21c32020c22020c3206a21c42020c32020c4206a21c52020c42020c5206a21c62020" + "c52020c6206a21c72020c62020c7206a21c82020c72020c8206a21c92020c82020c9206a21ca2020c92020ca206a21" + "cb2020ca2020cb206a21cc2020cb2020cc206a21cd2020cc2020cd206a21ce2020cd2020ce206a21cf2020ce2020cf" + "206a21d02020cf2020d0206a21d12020d02020d1206a21d22020d12020d2206a21d32020d22020d3206a21d42020d3" + "2020d4206a21d52020d42020d5206a21d62020d52020d6206a21d72020d62020d7206a21d82020d72020d8206a21d9" + "2020d82020d9206a21da2020d92020da206a21db2020da2020db206a21dc2020db2020dc206a21dd2020dc2020dd20" + "6a21de2020dd2020de206a21df2020de2020df206a21e02020df2020e0206a21e12020e02020e1206a21e22020e120" + "20e2206a21e32020e22020e3206a21e42020e32020e4206a21e52020e42020e5206a21e62020e52020e6206a21e720" + "20e62020e7206a21e82020e72020e8206a21e92020e82020e9206a21ea2020e92020ea206a21eb2020ea2020eb206a" + "21ec2020eb2020ec206a21ed2020ec2020ed206a21ee2020ed2020ee206a21ef2020ee2020ef206a21f02020ef2020" + "f0206a21f12020f02020f1206a21f22020f12020f2206a21f32020f22020f3206a21f42020f32020f4206a21f52020" + "f42020f5206a21f62020f52020f6206a21f72020f62020f7206a21f82020f72020f8206a21f92020f82020f9206a21" + "fa2020f92020fa206a21fb2020fa2020fb206a21fc2020fb2020fc206a21fd2020fc2020fd206a21fe2020fd2020fe" + "206a21ff2020fe2020ff206a21802120ff202080216a2181212080212081216a2182212081212082216a2183212082" + "212083216a2184212083212084216a2185212084212085216a2186212085212086216a2187212086212087216a2188" + "212087212088216a2189212088212089216a218a21208921208a216a218b21208a21208b216a218c21208b21208c21" + "6a218d21208c21208d216a218e21208d21208e216a218f21208e21208f216a219021208f212090216a219121209021" + "2091216a2192212091212092216a2193212092212093216a2194212093212094216a2195212094212095216a219621" + "2095212096216a2197212096212097216a2198212097212098216a2199212098212099216a219a21209921209a216a" + "219b21209a21209b216a219c21209b21209c216a219d21209c21209d216a219e21209d21209e216a219f21209e2120" + "9f216a21a021209f2120a0216a21a12120a02120a1216a21a22120a12120a2216a21a32120a22120a3216a21a42120" + "a32120a4216a21a52120a42120a5216a21a62120a52120a6216a21a72120a62120a7216a21a82120a72120a8216a21" + "a92120a82120a9216a21aa2120a92120aa216a21ab2120aa2120ab216a21ac2120ab2120ac216a21ad2120ac2120ad" + "216a21ae2120ad2120ae216a21af2120ae2120af216a21b02120af2120b0216a21b12120b02120b1216a21b22120b1" + "2120b2216a21b32120b22120b3216a21b42120b32120b4216a21b52120b42120b5216a21b62120b52120b6216a21b7" + "2120b62120b7216a21b82120b72120b8216a21b92120b82120b9216a21ba2120b92120ba216a21bb2120ba2120bb21" + "6a21bc2120bb2120bc216a21bd2120bc2120bd216a21be2120bd2120be216a21bf2120be2120bf216a21c02120bf21" + "20c0216a21c12120c02120c1216a21c22120c12120c2216a21c32120c22120c3216a21c42120c32120c4216a21c521" + "20c42120c5216a21c62120c52120c6216a21c72120c62120c7216a21c82120c72120c8216a21c92120c82120c9216a" + "21ca2120c92120ca216a21cb2120ca2120cb216a21cc2120cb2120cc216a21cd2120cc2120cd216a21ce2120cd2120" + "ce216a21cf2120ce2120cf216a21d02120cf2120d0216a21d12120d02120d1216a21d22120d12120d2216a21d32120" + "d22120d3216a21d42120d32120d4216a21d52120d42120d5216a21d62120d52120d6216a21d72120d62120d7216a21" + "d82120d72120d8216a21d92120d82120d9216a21da2120d92120da216a21db2120da2120db216a21dc2120db2120dc" + "216a21dd2120dc2120dd216a21de2120dd2120de216a21df2120de2120df216a21e02120df2120e0216a21e12120e0" + "2120e1216a21e22120e12120e2216a21e32120e22120e3216a21e42120e32120e4216a21e52120e42120e5216a21e6" + "2120e52120e6216a21e72120e62120e7216a21e82120e72120e8216a21e92120e82120e9216a21ea2120e92120ea21" + "6a21eb2120ea2120eb216a21ec2120eb2120ec216a21ed2120ec2120ed216a21ee2120ed2120ee216a21ef2120ee21" + "20ef216a21f02120ef2120f0216a21f12120f02120f1216a21f22120f12120f2216a21f32120f22120f3216a21f421" + "20f32120f4216a21f52120f42120f5216a21f62120f52120f6216a21f72120f62120f7216a21f82120f72120f8216a" + "21f92120f82120f9216a21fa2120f92120fa216a21fb2120fa2120fb216a21fc2120fb2120fc216a21fd2120fc2120" + "fd216a21fe2120fd2120fe216a21ff2120fe2120ff216a21802220ff212080226a2181222080222081226a21822220" + "81222082226a2183222082222083226a2184222083222084226a2185222084222085226a2186222085222086226a21" + "87222086222087226a2188222087222088226a2189222088222089226a218a22208922208a226a218b22208a22208b" + "226a218c22208b22208c226a218d22208c22208d226a218e22208d22208e226a218f22208e22208f226a219022208f" + "222090226a2191222090222091226a2192222091222092226a2193222092222093226a2194222093222094226a2195" + "222094222095226a2196222095222096226a2197222096222097226a2198222097222098226a219922209822209922" + "6a219a22209922209a226a219b22209a22209b226a219c22209b22209c226a219d22209c22209d226a219e22209d22" + "209e226a219f22209e22209f226a21a022209f2220a0226a21a12220a02220a1226a21a22220a12220a2226a21a322" + "20a22220a3226a21a42220a32220a4226a21a52220a42220a5226a21a62220a52220a6226a21a72220a62220a7226a" + "21a82220a72220a8226a21a92220a82220a9226a21aa2220a92220aa226a21ab2220aa2220ab226a21ac2220ab2220" + "ac226a21ad2220ac2220ad226a21ae2220ad2220ae226a21af2220ae2220af226a21b02220af2220b0226a21b12220" + "b02220b1226a21b22220b12220b2226a21b32220b22220b3226a21b42220b32220b4226a21b52220b42220b5226a21" + "b62220b52220b6226a21b72220b62220b7226a21b82220b72220b8226a21b92220b82220b9226a21ba2220b92220ba" + "226a21bb2220ba2220bb226a21bc2220bb2220bc226a21bd2220bc2220bd226a21be2220bd2220be226a21bf2220be" + "2220bf226a21c02220bf2220c0226a21c12220c02220c1226a21c22220c12220c2226a21c32220c22220c3226a21c4" + "2220c32220c4226a21c52220c42220c5226a21c62220c52220c6226a21c72220c62220c7226a21c82220c72220c822" + "6a21c92220c82220c9226a21ca2220c92220ca226a21cb2220ca2220cb226a21cc2220cb2220cc226a21cd2220cc22" + "20cd226a21ce2220cd2220ce226a21cf2220ce2220cf226a21d02220cf2220d0226a21d12220d02220d1226a21d222" + "20d12220d2226a21d32220d22220d3226a21d42220d32220d4226a21d52220d42220d5226a21d62220d52220d6226a" + "21d72220d62220d7226a21d82220d72220d8226a21d92220d82220d9226a21da2220d92220da226a21db2220da2220" + "db226a21dc2220db2220dc226a21dd2220dc2220dd226a21de2220dd2220de226a21df2220de2220df226a21e02220" + "df2220e0226a21e12220e02220e1226a21e22220e12220e2226a21e32220e22220e3226a21e42220e32220e4226a21" + "e52220e42220e5226a21e62220e52220e6226a21e72220e62220e7226a21e82220e72220e8226a21e92220e82220e9" + "226a21ea2220e92220ea226a21eb2220ea2220eb226a21ec2220eb2220ec226a21ed2220ec2220ed226a21ee2220ed" + "2220ee226a21ef2220ee2220ef226a21f02220ef2220f0226a21f12220f02220f1226a21f22220f12220f2226a21f3" + "2220f22220f3226a21f42220f32220f4226a21f52220f42220f5226a21f62220f52220f6226a21f72220f62220f722" + "6a21f82220f72220f8226a21f92220f82220f9226a21fa2220f92220fa226a21fb2220fa2220fb226a21fc2220fb22" + "20fc226a21fd2220fc2220fd226a21fe2220fd2220fe226a21ff2220fe2220ff226a21802320ff222080236a218123" + "2080232081236a2182232081232082236a2183232082232083236a2184232083232084236a2185232084232085236a" + "2186232085232086236a2187232086232087236a2188232087232088236a2189232088232089236a218a2320892320" + "8a236a218b23208a23208b236a218c23208b23208c236a218d23208c23208d236a218e23208d23208e236a218f2320" + "8e23208f236a219023208f232090236a2191232090232091236a2192232091232092236a2193232092232093236a21" + "94232093232094236a2195232094232095236a2196232095232096236a2197232096232097236a2198232097232098" + "236a2199232098232099236a219a23209923209a236a219b23209a23209b236a219c23209b23209c236a219d23209c" + "23209d236a219e23209d23209e236a219f23209e23209f236a21a023209f2320a0236a21a12320a02320a1236a21a2" + "2320a12320a2236a21a32320a22320a3236a21a42320a32320a4236a21a52320a42320a5236a21a62320a52320a623" + "6a21a72320a62320a7236a21a82320a72320a8236a21a92320a82320a9236a21aa2320a92320aa236a21ab2320aa23" + "20ab236a21ac2320ab2320ac236a21ad2320ac2320ad236a21ae2320ad2320ae236a21af2320ae2320af236a21b023" + "20af2320b0236a21b12320b02320b1236a21b22320b12320b2236a21b32320b22320b3236a21b42320b32320b4236a" + "21b52320b42320b5236a21b62320b52320b6236a21b72320b62320b7236a21b82320b72320b8236a21b92320b82320" + "b9236a21ba2320b92320ba236a21bb2320ba2320bb236a21bc2320bb2320bc236a21bd2320bc2320bd236a21be2320" + "bd2320be236a21bf2320be2320bf236a21c02320bf2320c0236a21c12320c02320c1236a21c22320c12320c2236a21" + "c32320c22320c3236a21c42320c32320c4236a21c52320c42320c5236a21c62320c52320c6236a21c72320c62320c7" + "236a21c82320c72320c8236a21c92320c82320c9236a21ca2320c92320ca236a21cb2320ca2320cb236a21cc2320cb" + "2320cc236a21cd2320cc2320cd236a21ce2320cd2320ce236a21cf2320ce2320cf236a21d02320cf2320d0236a21d1" + "2320d02320d1236a21d22320d12320d2236a21d32320d22320d3236a21d42320d32320d4236a21d52320d42320d523" + "6a21d62320d52320d6236a21d72320d62320d7236a21d82320d72320d8236a21d92320d82320d9236a21da2320d923" + "20da236a21db2320da2320db236a21dc2320db2320dc236a21dd2320dc2320dd236a21de2320dd2320de236a21df23" + "20de2320df236a21e02320df2320e0236a21e12320e02320e1236a21e22320e12320e2236a21e32320e22320e3236a" + "21e42320e32320e4236a21e52320e42320e5236a21e62320e52320e6236a21e72320e62320e7236a21e82320e72320" + "e8236a21e92320e82320e9236a21ea2320e92320ea236a21eb2320ea2320eb236a21ec2320eb2320ec236a21ed2320" + "ec2320ed236a21ee2320ed2320ee236a21ef2320ee2320ef236a21f02320ef2320f0236a21f12320f02320f1236a21" + "f22320f12320f2236a21f32320f22320f3236a21f42320f32320f4236a21f52320f42320f5236a21f62320f52320f6" + "236a21f72320f62320f7236a21f82320f72320f8236a21f92320f82320f9236a21fa2320f92320fa236a21fb2320fa" + "2320fb236a21fc2320fb2320fc236a21fd2320fc2320fd236a21fe2320fd2320fe236a21ff2320fe2320ff236a2180" + "2420ff232080246a2181242080242081246a2182242081242082246a2183242082242083246a218424208324208424" + "6a2185242084242085246a2186242085242086246a2187242086242087246a2188242087242088246a218924208824" + "2089246a218a24208924208a246a218b24208a24208b246a218c24208b24208c246a218d24208c24208d246a218e24" + "208d24208e246a218f24208e24208f246a219024208f242090246a2191242090242091246a2192242091242092246a" + "2193242092242093246a2194242093242094246a2195242094242095246a2196242095242096246a21972420962420" + "97246a2198242097242098246a2199242098242099246a219a24209924209a246a219b24209a24209b246a219c2420" + "9b24209c246a219d24209c24209d246a219e24209d24209e246a219f24209e24209f246a21a024209f2420a0246a21" + "a12420a02420a1246a21a22420a12420a2246a21a32420a22420a3246a21a42420a32420a4246a21a52420a42420a5" + "246a21a62420a52420a6246a21a72420a62420a7246a21a82420a72420a8246a21a92420a82420a9246a21aa2420a9" + "2420aa246a21ab2420aa2420ab246a21ac2420ab2420ac246a21ad2420ac2420ad246a21ae2420ad2420ae246a21af" + "2420ae2420af246a21b02420af2420b0246a21b12420b02420b1246a21b22420b12420b2246a21b32420b22420b324" + "6a21b42420b32420b4246a21b52420b42420b5246a21b62420b52420b6246a21b72420b62420b7246a21b82420b724" + "20b8246a21b92420b82420b9246a21ba2420b92420ba246a21bb2420ba2420bb246a21bc2420bb2420bc246a21bd24" + "20bc2420bd246a21be2420bd2420be246a21bf2420be2420bf246a21c02420bf2420c0246a21c12420c02420c1246a" + "21c22420c12420c2246a21c32420c22420c3246a21c42420c32420c4246a21c52420c42420c5246a21c62420c52420" + "c6246a21c72420c62420c7246a21c82420c72420c8246a21c92420c82420c9246a21ca2420c92420ca246a21cb2420" + "ca2420cb246a21cc2420cb2420cc246a21cd2420cc2420cd246a21ce2420cd2420ce246a21cf2420ce2420cf246a21" + "d02420cf2420d0246a21d12420d02420d1246a21d22420d12420d2246a21d32420d22420d3246a21d42420d32420d4" + "246a21d52420d42420d5246a21d62420d52420d6246a21d72420d62420d7246a21d82420d72420d8246a21d92420d8" + "2420d9246a21da2420d92420da246a21db2420da2420db246a21dc2420db2420dc246a21dd2420dc2420dd246a21de" + "2420dd2420de246a21df2420de2420df246a21e02420df2420e0246a21e12420e02420e1246a21e22420e12420e224" + "6a21e32420e22420e3246a21e42420e32420e4246a21e52420e42420e5246a21e62420e52420e6246a21e72420e624" + "20e7246a21e82420e72420e8246a21e92420e82420e9246a21ea2420e92420ea246a21eb2420ea2420eb246a21ec24" + "20eb2420ec246a21ed2420ec2420ed246a21ee2420ed2420ee246a21ef2420ee2420ef246a21f02420ef2420f0246a" + "21f12420f02420f1246a21f22420f12420f2246a21f32420f22420f3246a21f42420f32420f4246a21f52420f42420" + "f5246a21f62420f52420f6246a21f72420f62420f7246a21f82420f72420f8246a21f92420f82420f9246a21fa2420" + "f92420fa246a21fb2420fa2420fb246a21fc2420fb2420fc246a21fd2420fc2420fd246a21fe2420fd2420fe246a21" + "ff2420fe2420ff246a21802520ff242080256a2181252080252081256a2182252081252082256a2183252082252083" + "256a2184252083252084256a2185252084252085256a2186252085252086256a2187252086252087256a2188252087" + "252088256a2189252088252089256a218a25208925208a256a218b25208a25208b256a218c25208b25208c256a218d" + "25208c25208d256a218e25208d25208e256a218f25208e25208f256a219025208f252090256a219125209025209125" + "6a2192252091252092256a2193252092252093256a2194252093252094256a2195252094252095256a219625209525" + "2096256a2197252096252097256a2198252097252098256a2199252098252099256a219a25209925209a256a219b25" + "209a25209b256a219c25209b25209c256a219d25209c25209d256a219e25209d25209e256a219f25209e25209f256a" + "21a025209f2520a0256a21a12520a02520a1256a21a22520a12520a2256a21a32520a22520a3256a21a42520a32520" + "a4256a21a52520a42520a5256a21a62520a52520a6256a21a72520a62520a7256a21a82520a72520a8256a21a92520" + "a82520a9256a21aa2520a92520aa256a21ab2520aa2520ab256a21ac2520ab2520ac256a21ad2520ac2520ad256a21" + "ae2520ad2520ae256a21af2520ae2520af256a21b02520af2520b0256a21b12520b02520b1256a21b22520b12520b2" + "256a21b32520b22520b3256a21b42520b32520b4256a21b52520b42520b5256a21b62520b52520b6256a21b72520b6" + "2520b7256a21b82520b72520b8256a21b92520b82520b9256a21ba2520b92520ba256a21bb2520ba2520bb256a21bc" + "2520bb2520bc256a21bd2520bc2520bd256a21be2520bd2520be256a21bf2520be2520bf256a21c02520bf2520c025" + "6a21c12520c02520c1256a21c22520c12520c2256a21c32520c22520c3256a21c42520c32520c4256a21c52520c425" + "20c5256a21c62520c52520c6256a21c72520c62520c7256a21c82520c72520c8256a21c92520c82520c9256a21ca25" + "20c92520ca256a21cb2520ca2520cb256a21cc2520cb2520cc256a21cd2520cc2520cd256a21ce2520cd2520ce256a" + "21cf2520ce2520cf256a21d02520cf2520d0256a21d12520d02520d1256a21d22520d12520d2256a21d32520d22520" + "d3256a21d42520d32520d4256a21d52520d42520d5256a21d62520d52520d6256a21d72520d62520d7256a21d82520" + "d72520d8256a21d92520d82520d9256a21da2520d92520da256a21db2520da2520db256a21dc2520db2520dc256a21" + "dd2520dc2520dd256a21de2520dd2520de256a21df2520de2520df256a21e02520df2520e0256a21e12520e02520e1" + "256a21e22520e12520e2256a21e32520e22520e3256a21e42520e32520e4256a21e52520e42520e5256a21e62520e5" + "2520e6256a21e72520e62520e7256a21e82520e72520e8256a21e92520e82520e9256a21ea2520e92520ea256a21eb" + "2520ea2520eb256a21ec2520eb2520ec256a21ed2520ec2520ed256a21ee2520ed2520ee256a21ef2520ee2520ef25" + "6a21f02520ef2520f0256a21f12520f02520f1256a21f22520f12520f2256a21f32520f22520f3256a21f42520f325" + "20f4256a21f52520f42520f5256a21f62520f52520f6256a21f72520f62520f7256a21f82520f72520f8256a21f925" + "20f82520f9256a21fa2520f92520fa256a21fb2520fa2520fb256a21fc2520fb2520fc256a21fd2520fc2520fd256a" + "21fe2520fd2520fe256a21ff2520fe2520ff256a21802620ff252080266a2181262080262081266a21822620812620" + "82266a2183262082262083266a2184262083262084266a2185262084262085266a2186262085262086266a21872620" + "86262087266a2188262087262088266a2189262088262089266a218a26208926208a266a218b26208a26208b266a21" + "8c26208b26208c266a218d26208c26208d266a218e26208d26208e266a218f26208e26208f266a219026208f262090" + "266a2191262090262091266a2192262091262092266a2193262092262093266a2194262093262094266a2195262094" + "262095266a2196262095262096266a2197262096262097266a2198262097262098266a2199262098262099266a219a" + "26209926209a266a219b26209a26209b266a219c26209b26209c266a219d26209c26209d266a219e26209d26209e26" + "6a219f26209e26209f266a21a026209f2620a0266a21a12620a02620a1266a21a22620a12620a2266a21a32620a226" + "20a3266a21a42620a32620a4266a21a52620a42620a5266a21a62620a52620a6266a21a72620a62620a7266a21a826" + "20a72620a8266a21a92620a82620a9266a21aa2620a92620aa266a21ab2620aa2620ab266a21ac2620ab2620ac266a" + "21ad2620ac2620ad266a21ae2620ad2620ae266a21af2620ae2620af266a21b02620af2620b0266a21b12620b02620" + "b1266a21b22620b12620b2266a21b32620b22620b3266a21b42620b32620b4266a21b52620b42620b5266a21b62620" + "b52620b6266a21b72620b62620b7266a21b82620b72620b8266a21b92620b82620b9266a21ba2620b92620ba266a21" + "bb2620ba2620bb266a21bc2620bb2620bc266a21bd2620bc2620bd266a21be2620bd2620be266a21bf2620be2620bf" + "266a21c02620bf2620c0266a21c12620c02620c1266a21c22620c12620c2266a21c32620c22620c3266a21c42620c3" + "2620c4266a21c52620c42620c5266a21c62620c52620c6266a21c72620c62620c7266a21c82620c72620c8266a21c9" + "2620c82620c9266a21ca2620c92620ca266a21cb2620ca2620cb266a21cc2620cb2620cc266a21cd2620cc2620cd26" + "6a21ce2620cd2620ce266a21cf2620ce2620cf266a21d02620cf2620d0266a21d12620d02620d1266a21d22620d126" + "20d2266a21d32620d22620d3266a21d42620d32620d4266a21d52620d42620d5266a21d62620d52620d6266a21d726" + "20d62620d7266a21d82620d72620d8266a21d92620d82620d9266a21da2620d92620da266a21db2620da2620db266a" + "21dc2620db2620dc266a21dd2620dc2620dd266a21de2620dd2620de266a21df2620de2620df266a21e02620df2620" + "e0266a21e12620e02620e1266a21e22620e12620e2266a21e32620e22620e3266a21e42620e32620e4266a21e52620" + "e42620e5266a21e62620e52620e6266a21e72620e62620e7266a21e82620e72620e8266a21e92620e82620e9266a21" + "ea2620e92620ea266a21eb2620ea2620eb266a21ec2620eb2620ec266a21ed2620ec2620ed266a21ee2620ed2620ee" + "266a21ef2620ee2620ef266a21f02620ef2620f0266a21f12620f02620f1266a21f22620f12620f2266a21f32620f2" + "2620f3266a21f42620f32620f4266a21f52620f42620f5266a21f62620f52620f6266a21f72620f62620f7266a21f8" + "2620f72620f8266a21f92620f82620f9266a21fa2620f92620fa266a21fb2620fa2620fb266a21fc2620fb2620fc26" + "6a21fd2620fc2620fd266a21fe2620fd2620fe266a21ff2620fe2620ff266a21802720ff262080276a218127208027" + "2081276a2182272081272082276a2183272082272083276a2184272083272084276a2185272084272085276a218627" + "2085272086276a2187272086272087276a2188272087272088276a2189272088272089276a218a27208927208a276a" + "218b27208a27208b276a218c27208b27208c276a218d27208c27208d276a218e27208d27208e276a218f27208e2720" + "8f276a219027208f272090276a2191272090272091276a2192272091272092276a2193272092272093276a21942720" + "93272094276a2195272094272095276a2196272095272096276a2197272096272097276a2198272097272098276a21" + "99272098272099276a219a27209927209a276a219b27209a27209b276a219c27209b27209c276a219d27209c27209d" + "276a219e27209d27209e276a219f27209e27209f276a21a027209f2720a0276a21a12720a02720a1276a21a22720a1" + "2720a2276a21a32720a22720a3276a21a42720a32720a4276a21a52720a42720a5276a21a62720a52720a6276a21a7" + "2720a62720a7276a21a82720a72720a8276a21a92720a82720a9276a21aa2720a92720aa276a21ab2720aa2720ab27" + "6a21ac2720ab2720ac276a21ad2720ac2720ad276a21ae2720ad2720ae276a21af2720ae2720af276a21b02720af27" + "20b0276a21b12720b02720b1276a21b22720b12720b2276a21b32720b22720b3276a21b42720b32720b4276a21b527" + "20b42720b5276a21b62720b52720b6276a21b72720b62720b7276a21b82720b72720b8276a21b92720b82720b9276a" + "21ba2720b92720ba276a21bb2720ba2720bb276a21bc2720bb2720bc276a21bd2720bc2720bd276a21be2720bd2720" + "be276a21bf2720be2720bf276a21c02720bf2720c0276a21c12720c02720c1276a21c22720c12720c2276a21c32720" + "c22720c3276a21c42720c32720c4276a21c52720c42720c5276a21c62720c52720c6276a21c72720c62720c7276a21" + "c82720c72720c8276a21c92720c82720c9276a21ca2720c92720ca276a21cb2720ca2720cb276a21cc2720cb2720cc" + "276a21cd2720cc2720cd276a21ce2720cd2720ce276a21cf2720ce2720cf276a21d02720cf2720d0276a21d12720d0" + "2720d1276a21d22720d12720d2276a21d32720d22720d3276a21d42720d32720d4276a21d52720d42720d5276a21d6" + "2720d52720d6276a21d72720d62720d7276a21d82720d72720d8276a21d92720d82720d9276a21da2720d92720da27" + "6a21db2720da2720db276a21dc2720db2720dc276a21dd2720dc2720dd276a21de2720dd2720de276a21df2720de27" + "20df276a21e02720df2720e0276a21e12720e02720e1276a21e22720e12720e2276a21e32720e22720e3276a21e427" + "20e32720e4276a21e52720e42720e5276a21e62720e52720e6276a21e72720e62720e7276a21e82720e72720e8276a" + "21e92720e82720e9276a21ea2720e92720ea276a21eb2720ea2720eb276a21ec2720eb2720ec276a21ed2720ec2720" + "ed276a21ee2720ed2720ee276a21ef2720ee2720ef276a21f02720ef2720f0276a21f12720f02720f1276a21f22720" + "f12720f2276a21f32720f22720f3276a21f42720f32720f4276a21f52720f42720f5276a21f62720f52720f6276a21" + "f72720f62720f7276a21f82720f72720f8276a21f92720f82720f9276a21fa2720f92720fa276a21fb2720fa2720fb" + "276a21fc2720fb2720fc276a21fd2720fc2720fd276a21fe2720fd2720fe276a21ff2720fe2720ff276a21802820ff" + "272080286a2181282080282081286a2182282081282082286a2183282082282083286a2184282083282084286a2185" + "282084282085286a2186282085282086286a2187282086282087286a2188282087282088286a218928208828208928" + "6a218a28208928208a286a218b28208a28208b286a218c28208b28208c286a218d28208c28208d286a218e28208d28" + "208e286a218f28208e28208f286a219028208f282090286a2191282090282091286a2192282091282092286a219328" + "2092282093286a2194282093282094286a2195282094282095286a2196282095282096286a2197282096282097286a" + "2198282097282098286a2199282098282099286a219a28209928209a286a219b28209a28209b286a219c28209b2820" + "9c286a219d28209c28209d286a219e28209d28209e286a219f28209e28209f286a21a028209f2820a0286a21a12820" + "a02820a1286a21a22820a12820a2286a21a32820a22820a3286a21a42820a32820a4286a21a52820a42820a5286a21" + "a62820a52820a6286a21a72820a62820a7286a21a82820a72820a8286a21a92820a82820a9286a21aa2820a92820aa" + "286a21ab2820aa2820ab286a21ac2820ab2820ac286a21ad2820ac2820ad286a21ae2820ad2820ae286a21af2820ae" + "2820af286a21b02820af2820b0286a21b12820b02820b1286a21b22820b12820b2286a21b32820b22820b3286a21b4" + "2820b32820b4286a21b52820b42820b5286a21b62820b52820b6286a21b72820b62820b7286a21b82820b72820b828" + "6a21b92820b82820b9286a21ba2820b92820ba286a21bb2820ba2820bb286a21bc2820bb2820bc286a21bd2820bc28" + "20bd286a21be2820bd2820be286a21bf2820be2820bf286a21c02820bf2820c0286a21c12820c02820c1286a21c228" + "20c12820c2286a21c32820c22820c3286a21c42820c32820c4286a21c52820c42820c5286a21c62820c52820c6286a" + "21c72820c62820c7286a21c82820c72820c8286a21c92820c82820c9286a21ca2820c92820ca286a21cb2820ca2820" + "cb286a21cc2820cb2820cc286a21cd2820cc2820cd286a21ce2820cd2820ce286a21cf2820ce2820cf286a21d02820" + "cf2820d0286a21d12820d02820d1286a21d22820d12820d2286a21d32820d22820d3286a21d42820d32820d4286a21" + "d52820d42820d5286a21d62820d52820d6286a21d72820d62820d7286a21d82820d72820d8286a21d92820d82820d9" + "286a21da2820d92820da286a21db2820da2820db286a21dc2820db2820dc286a21dd2820dc2820dd286a21de2820dd" + "2820de286a21df2820de2820df286a21e02820df2820e0286a21e12820e02820e1286a21e22820e12820e2286a21e3" + "2820e22820e3286a21e42820e32820e4286a21e52820e42820e5286a21e62820e52820e6286a21e72820e62820e728" + "6a21e82820e72820e8286a21e92820e82820e9286a21ea2820e92820ea286a21eb2820ea2820eb286a21ec2820eb28" + "20ec286a21ed2820ec2820ed286a21ee2820ed2820ee286a21ef2820ee2820ef286a21f02820ef2820f0286a21f128" + "20f02820f1286a21f22820f12820f2286a21f32820f22820f3286a21f42820f32820f4286a21f52820f42820f5286a" + "21f62820f52820f6286a21f72820f62820f7286a21f82820f72820f8286a21f92820f82820f9286a21fa2820f92820" + "fa286a21fb2820fa2820fb286a21fc2820fb2820fc286a21fd2820fc2820fd286a21fe2820fd2820fe286a21ff2820" + "fe2820ff286a21802920ff282080296a2181292080292081296a2182292081292082296a2183292082292083296a21" + "84292083292084296a2185292084292085296a2186292085292086296a2187292086292087296a2188292087292088" + "296a2189292088292089296a218a29208929208a296a218b29208a29208b296a218c29208b29208c296a218d29208c" + "29208d296a218e29208d29208e296a218f29208e29208f296a219029208f292090296a2191292090292091296a2192" + "292091292092296a2193292092292093296a2194292093292094296a2195292094292095296a219629209529209629" + "6a2197292096292097296a2198292097292098296a2199292098292099296a219a29209929209a296a219b29209a29" + "209b296a219c29209b29209c296a219d29209c29209d296a219e29209d29209e296a219f29209e29209f296a21a029" + "209f2920a0296a21a12920a02920a1296a21a22920a12920a2296a21a32920a22920a3296a21a42920a32920a4296a" + "21a52920a42920a5296a21a62920a52920a6296a21a72920a62920a7296a21a82920a72920a8296a21a92920a82920" + "a9296a21aa2920a92920aa296a21ab2920aa2920ab296a21ac2920ab2920ac296a21ad2920ac2920ad296a21ae2920" + "ad2920ae296a21af2920ae2920af296a21b02920af2920b0296a21b12920b02920b1296a21b22920b12920b2296a21" + "b32920b22920b3296a21b42920b32920b4296a21b52920b42920b5296a21b62920b52920b6296a21b72920b62920b7" + "296a21b82920b72920b8296a21b92920b82920b9296a21ba2920b92920ba296a21bb2920ba2920bb296a21bc2920bb" + "2920bc296a21bd2920bc2920bd296a21be2920bd2920be296a21bf2920be2920bf296a21c02920bf2920c0296a21c1" + "2920c02920c1296a21c22920c12920c2296a21c32920c22920c3296a21c42920c32920c4296a21c52920c42920c529" + "6a21c62920c52920c6296a21c72920c62920c7296a21c82920c72920c8296a21c92920c82920c9296a21ca2920c929" + "20ca296a21cb2920ca2920cb296a21cc2920cb2920cc296a21cd2920cc2920cd296a21ce2920cd2920ce296a21cf29" + "20ce2920cf296a21d02920cf2920d0296a21d12920d02920d1296a21d22920d12920d2296a21d32920d22920d3296a" + "21d42920d32920d4296a21d52920d42920d5296a21d62920d52920d6296a21d72920d62920d7296a21d82920d72920" + "d8296a21d92920d82920d9296a21da2920d92920da296a21db2920da2920db296a21dc2920db2920dc296a21dd2920" + "dc2920dd296a21de2920dd2920de296a21df2920de2920df296a21e02920df2920e0296a21e12920e02920e1296a21" + "e22920e12920e2296a21e32920e22920e3296a21e42920e32920e4296a21e52920e42920e5296a21e62920e52920e6" + "296a21e72920e62920e7296a21e82920e72920e8296a21e92920e82920e9296a21ea2920e92920ea296a21eb2920ea" + "2920eb296a21ec2920eb2920ec296a21ed2920ec2920ed296a21ee2920ed2920ee296a21ef2920ee2920ef296a21f0" + "2920ef2920f0296a21f12920f02920f1296a21f22920f12920f2296a21f32920f22920f3296a21f42920f32920f429" + "6a21f52920f42920f5296a21f62920f52920f6296a21f72920f62920f7296a21f82920f72920f8296a21f92920f829" + "20f9296a21fa2920f92920fa296a21fb2920fa2920fb296a21fc2920fb2920fc296a21fd2920fc2920fd296a21fe29" + "20fd2920fe296a21ff2920fe2920ff296a21802a20ff2920802a6a21812a20802a20812a6a21822a20812a20822a6a" + "21832a20822a20832a6a21842a20832a20842a6a21852a20842a20852a6a21862a20852a20862a6a21872a20862a20" + "872a6a21882a20872a20882a6a21892a20882a20892a6a218a2a20892a208a2a6a218b2a208a2a208b2a6a218c2a20" + "8b2a208c2a6a218d2a208c2a208d2a6a218e2a208d2a208e2a6a218f2a208e2a208f2a6a21902a208f2a20902a6a21" + "912a20902a20912a6a21922a20912a20922a6a21932a20922a20932a6a21942a20932a20942a6a21952a20942a2095" + "2a6a21962a20952a20962a6a21972a20962a20972a6a21982a20972a20982a6a21992a20982a20992a6a219a2a2099" + "2a209a2a6a219b2a209a2a209b2a6a219c2a209b2a209c2a6a219d2a209c2a209d2a6a219e2a209d2a209e2a6a219f" + "2a209e2a209f2a6a21a02a209f2a20a02a6a21a12a20a02a20a12a6a21a22a20a12a20a22a6a21a32a20a22a20a32a" + "6a21a42a20a32a20a42a6a21a52a20a42a20a52a6a21a62a20a52a20a62a6a21a72a20a62a20a72a6a21a82a20a72a" + "20a82a6a21a92a20a82a20a92a6a21aa2a20a92a20aa2a6a21ab2a20aa2a20ab2a6a21ac2a20ab2a20ac2a6a21ad2a" + "20ac2a20ad2a6a21ae2a20ad2a20ae2a6a21af2a20ae2a20af2a6a21b02a20af2a20b02a6a21b12a20b02a20b12a6a" + "21b22a20b12a20b22a6a21b32a20b22a20b32a6a21b42a20b32a20b42a6a21b52a20b42a20b52a6a21b62a20b52a20" + "b62a6a21b72a20b62a20b72a6a21b82a20b72a20b82a6a21b92a20b82a20b92a6a21ba2a20b92a20ba2a6a21bb2a20" + "ba2a20bb2a6a21bc2a20bb2a20bc2a6a21bd2a20bc2a20bd2a6a21be2a20bd2a20be2a6a21bf2a20be2a20bf2a6a21" + "c02a20bf2a20c02a6a21c12a20c02a20c12a6a21c22a20c12a20c22a6a21c32a20c22a20c32a6a21c42a20c32a20c4" + "2a6a21c52a20c42a20c52a6a21c62a20c52a20c62a6a21c72a20c62a20c72a6a21c82a20c72a20c82a6a21c92a20c8" + "2a20c92a6a21ca2a20c92a20ca2a6a21cb2a20ca2a20cb2a6a21cc2a20cb2a20cc2a6a21cd2a20cc2a20cd2a6a21ce" + "2a20cd2a20ce2a6a21cf2a20ce2a20cf2a6a21d02a20cf2a20d02a6a21d12a20d02a20d12a6a21d22a20d12a20d22a" + "6a21d32a20d22a20d32a6a21d42a20d32a20d42a6a21d52a20d42a20d52a6a21d62a20d52a20d62a6a21d72a20d62a" + "20d72a6a21d82a20d72a20d82a6a21d92a20d82a20d92a6a21da2a20d92a20da2a6a21db2a20da2a20db2a6a21dc2a" + "20db2a20dc2a6a21dd2a20dc2a20dd2a6a21de2a20dd2a20de2a6a21df2a20de2a20df2a6a21e02a20df2a20e02a6a" + "21e12a20e02a20e12a6a21e22a20e12a20e22a6a21e32a20e22a20e32a6a21e42a20e32a20e42a6a21e52a20e42a20" + "e52a6a21e62a20e52a20e62a6a21e72a20e62a20e72a6a21e82a20e72a20e82a6a21e92a20e82a20e92a6a21ea2a20" + "e92a20ea2a6a21eb2a20ea2a20eb2a6a21ec2a20eb2a20ec2a6a21ed2a20ec2a20ed2a6a21ee2a20ed2a20ee2a6a21" + "ef2a20ee2a20ef2a6a21f02a20ef2a20f02a6a21f12a20f02a20f12a6a21f22a20f12a20f22a6a21f32a20f22a20f3" + "2a6a21f42a20f32a20f42a6a21f52a20f42a20f52a6a21f62a20f52a20f62a6a21f72a20f62a20f72a6a21f82a20f7" + "2a20f82a6a21f92a20f82a20f92a6a21fa2a20f92a20fa2a6a21fb2a20fa2a20fb2a6a21fc2a20fb2a20fc2a6a21fd" + "2a20fc2a20fd2a6a21fe2a20fd2a20fe2a6a21ff2a20fe2a20ff2a6a21802b20ff2a20802b6a21812b20802b20812b" + "6a21822b20812b20822b6a21832b20822b20832b6a21842b20832b20842b6a21852b20842b20852b6a21862b20852b" + "20862b6a21872b20862b20872b6a21882b20872b20882b6a21892b20882b20892b6a218a2b20892b208a2b6a218b2b" + "208a2b208b2b6a218c2b208b2b208c2b6a218d2b208c2b208d2b6a218e2b208d2b208e2b6a218f2b208e2b208f2b6a" + "21902b208f2b20902b6a21912b20902b20912b6a21922b20912b20922b6a21932b20922b20932b6a21942b20932b20" + "942b6a21952b20942b20952b6a21962b20952b20962b6a21972b20962b20972b6a21982b20972b20982b6a21992b20" + "982b20992b6a219a2b20992b209a2b6a219b2b209a2b209b2b6a219c2b209b2b209c2b6a219d2b209c2b209d2b6a21" + "9e2b209d2b209e2b6a219f2b209e2b209f2b6a21a02b209f2b20a02b6a21a12b20a02b20a12b6a21a22b20a12b20a2" + "2b6a21a32b20a22b20a32b6a21a42b20a32b20a42b6a21a52b20a42b20a52b6a21a62b20a52b20a62b6a21a72b20a6" + "2b20a72b6a21a82b20a72b20a82b6a21a92b20a82b20a92b6a21aa2b20a92b20aa2b6a21ab2b20aa2b20ab2b6a21ac" + "2b20ab2b20ac2b6a21ad2b20ac2b20ad2b6a21ae2b20ad2b20ae2b6a21af2b20ae2b20af2b6a21b02b20af2b20b02b" + "6a21b12b20b02b20b12b6a21b22b20b12b20b22b6a21b32b20b22b20b32b6a21b42b20b32b20b42b6a21b52b20b42b" + "20b52b6a21b62b20b52b20b62b6a21b72b20b62b20b72b6a21b82b20b72b20b82b6a21b92b20b82b20b92b6a21ba2b" + "20b92b20ba2b6a21bb2b20ba2b20bb2b6a21bc2b20bb2b20bc2b6a21bd2b20bc2b20bd2b6a21be2b20bd2b20be2b6a" + "21bf2b20be2b20bf2b6a21c02b20bf2b20c02b6a21c12b20c02b20c12b6a21c22b20c12b20c22b6a21c32b20c22b20" + "c32b6a21c42b20c32b20c42b6a21c52b20c42b20c52b6a21c62b20c52b20c62b6a21c72b20c62b20c72b6a21c82b20" + "c72b20c82b6a21c92b20c82b20c92b6a21ca2b20c92b20ca2b6a21cb2b20ca2b20cb2b6a21cc2b20cb2b20cc2b6a21" + "cd2b20cc2b20cd2b6a21ce2b20cd2b20ce2b6a21cf2b20ce2b20cf2b6a21d02b20cf2b20d02b6a21d12b20d02b20d1" + "2b6a21d22b20d12b20d22b6a21d32b20d22b20d32b6a21d42b20d32b20d42b6a21d52b20d42b20d52b6a21d62b20d5" + "2b20d62b6a21d72b20d62b20d72b6a21d82b20d72b20d82b6a21d92b20d82b20d92b6a21da2b20d92b20da2b6a21db" + "2b20da2b20db2b6a21dc2b20db2b20dc2b6a21dd2b20dc2b20dd2b6a21de2b20dd2b20de2b6a21df2b20de2b20df2b" + "6a21e02b20df2b20e02b6a21e12b20e02b20e12b6a21e22b20e12b20e22b6a21e32b20e22b20e32b6a21e42b20e32b" + "20e42b6a21e52b20e42b20e52b6a21e62b20e52b20e62b6a21e72b20e62b20e72b6a21e82b20e72b20e82b6a21e92b" + "20e82b20e92b6a21ea2b20e92b20ea2b6a21eb2b20ea2b20eb2b6a21ec2b20eb2b20ec2b6a21ed2b20ec2b20ed2b6a" + "21ee2b20ed2b20ee2b6a21ef2b20ee2b20ef2b6a21f02b20ef2b20f02b6a21f12b20f02b20f12b6a21f22b20f12b20" + "f22b6a21f32b20f22b20f32b6a21f42b20f32b20f42b6a21f52b20f42b20f52b6a21f62b20f52b20f62b6a21f72b20" + "f62b20f72b6a21f82b20f72b20f82b6a21f92b20f82b20f92b6a21fa2b20f92b20fa2b6a21fb2b20fa2b20fb2b6a21" + "fc2b20fb2b20fc2b6a21fd2b20fc2b20fd2b6a21fe2b20fd2b20fe2b6a21ff2b20fe2b20ff2b6a21802c20ff2b2080" + "2c6a21812c20802c20812c6a21822c20812c20822c6a21832c20822c20832c6a21842c20832c20842c6a21852c2084" + "2c20852c6a21862c20852c20862c6a21872c20862c20872c6a21882c20872c20882c6a21892c20882c20892c6a218a" + "2c20892c208a2c6a218b2c208a2c208b2c6a218c2c208b2c208c2c6a218d2c208c2c208d2c6a218e2c208d2c208e2c" + "6a218f2c208e2c208f2c6a21902c208f2c20902c6a21912c20902c20912c6a21922c20912c20922c6a21932c20922c" + "20932c6a21942c20932c20942c6a21952c20942c20952c6a21962c20952c20962c6a21972c20962c20972c6a21982c" + "20972c20982c6a21992c20982c20992c6a219a2c20992c209a2c6a219b2c209a2c209b2c6a219c2c209b2c209c2c6a" + "219d2c209c2c209d2c6a219e2c209d2c209e2c6a219f2c209e2c209f2c6a21a02c209f2c20a02c6a21a12c20a02c20" + "a12c6a21a22c20a12c20a22c6a21a32c20a22c20a32c6a21a42c20a32c20a42c6a21a52c20a42c20a52c6a21a62c20" + "a52c20a62c6a21a72c20a62c20a72c6a21a82c20a72c20a82c6a21a92c20a82c20a92c6a21aa2c20a92c20aa2c6a21" + "ab2c20aa2c20ab2c6a21ac2c20ab2c20ac2c6a21ad2c20ac2c20ad2c6a21ae2c20ad2c20ae2c6a21af2c20ae2c20af" + "2c6a21b02c20af2c20b02c6a21b12c20b02c20b12c6a21b22c20b12c20b22c6a21b32c20b22c20b32c6a21b42c20b3" + "2c20b42c6a21b52c20b42c20b52c6a21b62c20b52c20b62c6a21b72c20b62c20b72c6a21b82c20b72c20b82c6a21b9" + "2c20b82c20b92c6a21ba2c20b92c20ba2c6a21bb2c20ba2c20bb2c6a21bc2c20bb2c20bc2c6a21bd2c20bc2c20bd2c" + "6a21be2c20bd2c20be2c6a21bf2c20be2c20bf2c6a21c02c20bf2c20c02c6a21c12c20c02c20c12c6a21c22c20c12c" + "20c22c6a21c32c20c22c20c32c6a21c42c20c32c20c42c6a21c52c20c42c20c52c6a21c62c20c52c20c62c6a21c72c" + "20c62c20c72c6a21c82c20c72c20c82c6a21c92c20c82c20c92c6a21ca2c20c92c20ca2c6a21cb2c20ca2c20cb2c6a" + "21cc2c20cb2c20cc2c6a21cd2c20cc2c20cd2c6a21ce2c20cd2c20ce2c6a21cf2c20ce2c20cf2c6a21d02c20cf2c20" + "d02c6a21d12c20d02c20d12c6a21d22c20d12c20d22c6a21d32c20d22c20d32c6a21d42c20d32c20d42c6a21d52c20" + "d42c20d52c6a21d62c20d52c20d62c6a21d72c20d62c20d72c6a21d82c20d72c20d82c6a21d92c20d82c20d92c6a21" + "da2c20d92c20da2c6a21db2c20da2c20db2c6a21dc2c20db2c20dc2c6a21dd2c20dc2c20dd2c6a21de2c20dd2c20de" + "2c6a21df2c20de2c20df2c6a21e02c20df2c20e02c6a21e12c20e02c20e12c6a21e22c20e12c20e22c6a21e32c20e2" + "2c20e32c6a21e42c20e32c20e42c6a21e52c20e42c20e52c6a21e62c20e52c20e62c6a21e72c20e62c20e72c6a21e8" + "2c20e72c20e82c6a21e92c20e82c20e92c6a21ea2c20e92c20ea2c6a21eb2c20ea2c20eb2c6a21ec2c20eb2c20ec2c" + "6a21ed2c20ec2c20ed2c6a21ee2c20ed2c20ee2c6a21ef2c20ee2c20ef2c6a21f02c20ef2c20f02c6a21f12c20f02c" + "20f12c6a21f22c20f12c20f22c6a21f32c20f22c20f32c6a21f42c20f32c20f42c6a21f52c20f42c20f52c6a21f62c" + "20f52c20f62c6a21f72c20f62c20f72c6a21f82c20f72c20f82c6a21f92c20f82c20f92c6a21fa2c20f92c20fa2c6a" + "21fb2c20fa2c20fb2c6a21fc2c20fb2c20fc2c6a21fd2c20fc2c20fd2c6a21fe2c20fd2c20fe2c6a21ff2c20fe2c20" + "ff2c6a21802d20ff2c20802d6a21812d20802d20812d6a21822d20812d20822d6a21832d20822d20832d6a21842d20" + "832d20842d6a21852d20842d20852d6a21862d20852d20862d6a21872d20862d20872d6a21882d20872d20882d6a21" + "892d20882d20892d6a218a2d20892d208a2d6a218b2d208a2d208b2d6a218c2d208b2d208c2d6a218d2d208c2d208d" + "2d6a218e2d208d2d208e2d6a218f2d208e2d208f2d6a21902d208f2d20902d6a21912d20902d20912d6a21922d2091" + "2d20922d6a21932d20922d20932d6a21942d20932d20942d6a21952d20942d20952d6a21962d20952d20962d6a2197" + "2d20962d20972d6a21982d20972d20982d6a21992d20982d20992d6a219a2d20992d209a2d6a219b2d209a2d209b2d" + "6a219c2d209b2d209c2d6a219d2d209c2d209d2d6a219e2d209d2d209e2d6a219f2d209e2d209f2d6a21a02d209f2d" + "20a02d6a21a12d20a02d20a12d6a21a22d20a12d20a22d6a21a32d20a22d20a32d6a21a42d20a32d20a42d6a21a52d" + "20a42d20a52d6a21a62d20a52d20a62d6a21a72d20a62d20a72d6a21a82d20a72d20a82d6a21a92d20a82d20a92d6a" + "21aa2d20a92d20aa2d6a21ab2d20aa2d20ab2d6a21ac2d20ab2d20ac2d6a21ad2d20ac2d20ad2d6a21ae2d20ad2d20" + "ae2d6a21af2d20ae2d20af2d6a21b02d20af2d20b02d6a21b12d20b02d20b12d6a21b22d20b12d20b22d6a21b32d20" + "b22d20b32d6a21b42d20b32d20b42d6a21b52d20b42d20b52d6a21b62d20b52d20b62d6a21b72d20b62d20b72d6a21" + "b82d20b72d20b82d6a21b92d20b82d20b92d6a21ba2d20b92d20ba2d6a21bb2d20ba2d20bb2d6a21bc2d20bb2d20bc" + "2d6a21bd2d20bc2d20bd2d6a21be2d20bd2d20be2d6a21bf2d20be2d20bf2d6a21c02d20bf2d20c02d6a21c12d20c0" + "2d20c12d6a21c22d20c12d20c22d6a21c32d20c22d20c32d6a21c42d20c32d20c42d6a21c52d20c42d20c52d6a21c6" + "2d20c52d20c62d6a21c72d20c62d20c72d6a21c82d20c72d20c82d6a21c92d20c82d20c92d6a21ca2d20c92d20ca2d" + "6a21cb2d20ca2d20cb2d6a21cc2d20cb2d20cc2d6a21cd2d20cc2d20cd2d6a21ce2d20cd2d20ce2d6a21cf2d20ce2d" + "20cf2d6a21d02d20cf2d20d02d6a21d12d20d02d20d12d6a21d22d20d12d20d22d6a21d32d20d22d20d32d6a21d42d" + "20d32d20d42d6a21d52d20d42d20d52d6a21d62d20d52d20d62d6a21d72d20d62d20d72d6a21d82d20d72d20d82d6a" + "21d92d20d82d20d92d6a21da2d20d92d20da2d6a21db2d20da2d20db2d6a21dc2d20db2d20dc2d6a21dd2d20dc2d20" + "dd2d6a21de2d20dd2d20de2d6a21df2d20de2d20df2d6a21e02d20df2d20e02d6a21e12d20e02d20e12d6a21e22d20" + "e12d20e22d6a21e32d20e22d20e32d6a21e42d20e32d20e42d6a21e52d20e42d20e52d6a21e62d20e52d20e62d6a21" + "e72d20e62d20e72d6a21e82d20e72d20e82d6a21e92d20e82d20e92d6a21ea2d20e92d20ea2d6a21eb2d20ea2d20eb" + "2d6a21ec2d20eb2d20ec2d6a21ed2d20ec2d20ed2d6a21ee2d20ed2d20ee2d6a21ef2d20ee2d20ef2d6a21f02d20ef" + "2d20f02d6a21f12d20f02d20f12d6a21f22d20f12d20f22d6a21f32d20f22d20f32d6a21f42d20f32d20f42d6a21f5" + "2d20f42d20f52d6a21f62d20f52d20f62d6a21f72d20f62d20f72d6a21f82d20f72d20f82d6a21f92d20f82d20f92d" + "6a21fa2d20f92d20fa2d6a21fb2d20fa2d20fb2d6a21fc2d20fb2d20fc2d6a21fd2d20fc2d20fd2d6a21fe2d20fd2d" + "20fe2d6a21ff2d20fe2d20ff2d6a21802e20ff2d20802e6a21812e20802e20812e6a21822e20812e20822e6a21832e" + "20822e20832e6a21842e20832e20842e6a21852e20842e20852e6a21862e20852e20862e6a21872e20862e20872e6a" + "21882e20872e20882e6a21892e20882e20892e6a218a2e20892e208a2e6a218b2e208a2e208b2e6a218c2e208b2e20" + "8c2e6a218d2e208c2e208d2e6a218e2e208d2e208e2e6a218f2e208e2e208f2e6a21902e208f2e20902e6a21912e20" + "902e20912e6a21922e20912e20922e6a21932e20922e20932e6a21942e20932e20942e6a21952e20942e20952e6a21" + "962e20952e20962e6a21972e20962e20972e6a21982e20972e20982e6a21992e20982e20992e6a219a2e20992e209a" + "2e6a219b2e209a2e209b2e6a219c2e209b2e209c2e6a219d2e209c2e209d2e6a219e2e209d2e209e2e6a219f2e209e" + "2e209f2e6a21a02e209f2e20a02e6a21a12e20a02e20a12e6a21a22e20a12e20a22e6a21a32e20a22e20a32e6a21a4" + "2e20a32e20a42e6a21a52e20a42e20a52e6a21a62e20a52e20a62e6a21a72e20a62e20a72e6a21a82e20a72e20a82e" + "6a21a92e20a82e20a92e6a21aa2e20a92e20aa2e6a21ab2e20aa2e20ab2e6a21ac2e20ab2e20ac2e6a21ad2e20ac2e" + "20ad2e6a21ae2e20ad2e20ae2e6a21af2e20ae2e20af2e6a21b02e20af2e20b02e6a21b12e20b02e20b12e6a21b22e" + "20b12e20b22e6a21b32e20b22e20b32e6a21b42e20b32e20b42e6a21b52e20b42e20b52e6a21b62e20b52e20b62e6a" + "21b72e20b62e20b72e6a21b82e20b72e20b82e6a21b92e20b82e20b92e6a21ba2e20b92e20ba2e6a21bb2e20ba2e20" + "bb2e6a21bc2e20bb2e20bc2e6a21bd2e20bc2e20bd2e6a21be2e20bd2e20be2e6a21bf2e20be2e20bf2e6a21c02e20" + "bf2e20c02e6a21c12e20c02e20c12e6a21c22e20c12e20c22e6a21c32e20c22e20c32e6a21c42e20c32e20c42e6a21" + "c52e20c42e20c52e6a21c62e20c52e20c62e6a21c72e20c62e20c72e6a21c82e20c72e20c82e6a21c92e20c82e20c9" + "2e6a21ca2e20c92e20ca2e6a21cb2e20ca2e20cb2e6a21cc2e20cb2e20cc2e6a21cd2e20cc2e20cd2e6a21ce2e20cd" + "2e20ce2e6a21cf2e20ce2e20cf2e6a21d02e20cf2e20d02e6a21d12e20d02e20d12e6a21d22e20d12e20d22e6a21d3" + "2e20d22e20d32e6a21d42e20d32e20d42e6a21d52e20d42e20d52e6a21d62e20d52e20d62e6a21d72e20d62e20d72e" + "6a21d82e20d72e20d82e6a21d92e20d82e20d92e6a21da2e20d92e20da2e6a21db2e20da2e20db2e6a21dc2e20db2e" + "20dc2e6a21dd2e20dc2e20dd2e6a21de2e20dd2e20de2e6a21df2e20de2e20df2e6a21e02e20df2e20e02e6a21e12e" + "20e02e20e12e6a21e22e20e12e20e22e6a21e32e20e22e20e32e6a21e42e20e32e20e42e6a21e52e20e42e20e52e6a" + "21e62e20e52e20e62e6a21e72e20e62e20e72e6a21e82e20e72e20e82e6a21e92e20e82e20e92e6a21ea2e20e92e20" + "ea2e6a21eb2e20ea2e20eb2e6a21ec2e20eb2e20ec2e6a21ed2e20ec2e20ed2e6a21ee2e20ed2e20ee2e6a21ef2e20" + "ee2e20ef2e6a21f02e20ef2e20f02e6a21f12e20f02e20f12e6a21f22e20f12e20f22e6a21f32e20f22e20f32e6a21" + "f42e20f32e20f42e6a21f52e20f42e20f52e6a21f62e20f52e20f62e6a21f72e20f62e20f72e6a21f82e20f72e20f8" + "2e6a21f92e20f82e20f92e6a21fa2e20f92e20fa2e6a21fb2e20fa2e20fb2e6a21fc2e20fb2e20fc2e6a21fd2e20fc" + "2e20fd2e6a21fe2e20fd2e20fe2e6a21ff2e20fe2e20ff2e6a21802f20ff2e20802f6a21812f20802f20812f6a2182" + "2f20812f20822f6a21832f20822f20832f6a21842f20832f20842f6a21852f20842f20852f6a21862f20852f20862f" + "6a21872f20862f20872f6a21882f20872f20882f6a21892f20882f20892f6a218a2f20892f208a2f6a218b2f208a2f" + "208b2f6a218c2f208b2f208c2f6a218d2f208c2f208d2f6a218e2f208d2f208e2f6a218f2f208e2f208f2f6a21902f" + "208f2f20902f6a21912f20902f20912f6a21922f20912f20922f6a21932f20922f20932f6a21942f20932f20942f6a" + "21952f20942f20952f6a21962f20952f20962f6a21972f20962f20972f6a21982f20972f20982f6a21992f20982f20" + "992f6a219a2f20992f209a2f6a219b2f209a2f209b2f6a219c2f209b2f209c2f6a219d2f209c2f209d2f6a219e2f20" + "9d2f209e2f6a219f2f209e2f209f2f6a21a02f209f2f20a02f6a21a12f20a02f20a12f6a21a22f20a12f20a22f6a21" + "a32f20a22f20a32f6a21a42f20a32f20a42f6a21a52f20a42f20a52f6a21a62f20a52f20a62f6a21a72f20a62f20a7" + "2f6a21a82f20a72f20a82f6a21a92f20a82f20a92f6a21aa2f20a92f20aa2f6a21ab2f20aa2f20ab2f6a21ac2f20ab" + "2f20ac2f6a21ad2f20ac2f20ad2f6a21ae2f20ad2f20ae2f6a21af2f20ae2f20af2f6a21b02f20af2f20b02f6a21b1" + "2f20b02f20b12f6a21b22f20b12f20b22f6a21b32f20b22f20b32f6a21b42f20b32f20b42f6a21b52f20b42f20b52f" + "6a21b62f20b52f20b62f6a21b72f20b62f20b72f6a21b82f20b72f20b82f6a21b92f20b82f20b92f6a21ba2f20b92f" + "20ba2f6a21bb2f20ba2f20bb2f6a21bc2f20bb2f20bc2f6a21bd2f20bc2f20bd2f6a21be2f20bd2f20be2f6a21bf2f" + "20be2f20bf2f6a21c02f20bf2f20c02f6a21c12f20c02f20c12f6a21c22f20c12f20c22f6a21c32f20c22f20c32f6a" + "21c42f20c32f20c42f6a21c52f20c42f20c52f6a21c62f20c52f20c62f6a21c72f20c62f20c72f6a21c82f20c72f20" + "c82f6a21c92f20c82f20c92f6a21ca2f20c92f20ca2f6a21cb2f20ca2f20cb2f6a21cc2f20cb2f20cc2f6a21cd2f20" + "cc2f20cd2f6a21ce2f20cd2f20ce2f6a21cf2f20ce2f20cf2f6a21d02f20cf2f20d02f6a21d12f20d02f20d12f6a21" + "d22f20d12f20d22f6a21d32f20d22f20d32f6a21d42f20d32f20d42f6a21d52f20d42f20d52f6a21d62f20d52f20d6" + "2f6a21d72f20d62f20d72f6a21d82f20d72f20d82f6a21d92f20d82f20d92f6a21da2f20d92f20da2f6a21db2f20da" + "2f20db2f6a21dc2f20db2f20dc2f6a21dd2f20dc2f20dd2f6a21de2f20dd2f20de2f6a21df2f20de2f20df2f6a21e0" + "2f20df2f20e02f6a21e12f20e02f20e12f6a21e22f20e12f20e22f6a21e32f20e22f20e32f6a21e42f20e32f20e42f" + "6a21e52f20e42f20e52f6a21e62f20e52f20e62f6a21e72f20e62f20e72f6a21e82f20e72f20e82f6a21e92f20e82f" + "20e92f6a21ea2f20e92f20ea2f6a21eb2f20ea2f20eb2f6a21ec2f20eb2f20ec2f6a21ed2f20ec2f20ed2f6a21ee2f" + "20ed2f20ee2f6a21ef2f20ee2f20ef2f6a21f02f20ef2f20f02f6a21f12f20f02f20f12f6a21f22f20f12f20f22f6a" + "21f32f20f22f20f32f6a21f42f20f32f20f42f6a21f52f20f42f20f52f6a21f62f20f52f20f62f6a21f72f20f62f20" + "f72f6a21f82f20f72f20f82f6a21f92f20f82f20f92f6a21fa2f20f92f20fa2f6a21fb2f20fa2f20fb2f6a21fc2f20" + "fb2f20fc2f6a21fd2f20fc2f20fd2f6a21fe2f20fd2f20fe2f6a21ff2f20fe2f20ff2f6a21803020ff2f2080306a21" + "81302080302081306a2182302081302082306a2183302082302083306a2184302083302084306a2185302084302085" + "306a2186302085302086306a2187302086302087306a2188302087302088306a2189302088302089306a218a302089" + "30208a306a218b30208a30208b306a218c30208b30208c306a218d30208c30208d306a218e30208d30208e306a218f" + "30208e30208f306a219030208f302090306a2191302090302091306a2192302091302092306a219330209230209330" + "6a2194302093302094306a2195302094302095306a2196302095302096306a2197302096302097306a219830209730" + "2098306a2199302098302099306a219a30209930209a306a219b30209a30209b306a219c30209b30209c306a219d30" + "209c30209d306a219e30209d30209e306a219f30209e30209f306a21a030209f3020a0306a21a13020a03020a1306a" + "21a23020a13020a2306a21a33020a23020a3306a21a43020a33020a4306a21a53020a43020a5306a21a63020a53020" + "a6306a21a73020a63020a7306a21a83020a73020a8306a21a93020a83020a9306a21aa3020a93020aa306a21ab3020" + "aa3020ab306a21ac3020ab3020ac306a21ad3020ac3020ad306a21ae3020ad3020ae306a21af3020ae3020af306a21" + "b03020af3020b0306a21b13020b03020b1306a21b23020b13020b2306a21b33020b23020b3306a21b43020b33020b4" + "306a21b53020b43020b5306a21b63020b53020b6306a21b73020b63020b7306a21b83020b73020b8306a21b93020b8" + "3020b9306a21ba3020b93020ba306a21bb3020ba3020bb306a21bc3020bb3020bc306a21bd3020bc3020bd306a21be" + "3020bd3020be306a21bf3020be3020bf306a21c03020bf3020c0306a21c13020c03020c1306a21c23020c13020c230" + "6a21c33020c23020c3306a21c43020c33020c4306a21c53020c43020c5306a21c63020c53020c6306a21c73020c630" + "20c7306a21c83020c73020c8306a21c93020c83020c9306a21ca3020c93020ca306a21cb3020ca3020cb306a21cc30" + "20cb3020cc306a21cd3020cc3020cd306a21ce3020cd3020ce306a21cf3020ce3020cf306a21d03020cf3020d0306a" + "21d13020d03020d1306a21d23020d13020d2306a21d33020d23020d3306a21d43020d33020d4306a21d53020d43020" + "d5306a21d63020d53020d6306a21d73020d63020d7306a21d83020d73020d8306a21d93020d83020d9306a21da3020" + "d93020da306a21db3020da3020db306a21dc3020db3020dc306a21dd3020dc3020dd306a21de3020dd3020de306a21" + "df3020de3020df306a21e03020df3020e0306a21e13020e03020e1306a21e23020e13020e2306a21e33020e23020e3" + "306a21e43020e33020e4306a21e53020e43020e5306a21e63020e53020e6306a21e73020e63020e7306a21e83020e7" + "3020e8306a21e93020e83020e9306a21ea3020e93020ea306a21eb3020ea3020eb306a21ec3020eb3020ec306a21ed" + "3020ec3020ed306a21ee3020ed3020ee306a21ef3020ee3020ef306a21f03020ef3020f0306a21f13020f03020f130" + "6a21f23020f13020f2306a21f33020f23020f3306a21f43020f33020f4306a21f53020f43020f5306a21f63020f530" + "20f6306a21f73020f63020f7306a21f83020f73020f8306a21f93020f83020f9306a21fa3020f93020fa306a21fb30" + "20fa3020fb306a21fc3020fb3020fc306a21fd3020fc3020fd306a21fe3020fd3020fe306a21ff3020fe3020ff306a" + "21803120ff302080316a2181312080312081316a2182312081312082316a2183312082312083316a21843120833120" + "84316a2185312084312085316a2186312085312086316a2187312086312087316a2188312087312088316a21893120" + "88312089316a218a31208931208a316a218b31208a31208b316a218c31208b31208c316a218d31208c31208d316a21" + "8e31208d31208e316a218f31208e31208f316a219031208f312090316a2191312090312091316a2192312091312092" + "316a2193312092312093316a2194312093312094316a2195312094312095316a2196312095312096316a2197312096" + "312097316a2198312097312098316a2199312098312099316a219a31209931209a316a219b31209a31209b316a219c" + "31209b31209c316a219d31209c31209d316a219e31209d31209e316a219f31209e31209f316a21a031209f3120a031" + "6a21a13120a03120a1316a21a23120a13120a2316a21a33120a23120a3316a21a43120a33120a4316a21a53120a431" + "20a5316a21a63120a53120a6316a21a73120a63120a7316a21a83120a73120a8316a21a93120a83120a9316a21aa31" + "20a93120aa316a21ab3120aa3120ab316a21ac3120ab3120ac316a21ad3120ac3120ad316a21ae3120ad3120ae316a" + "21af3120ae3120af316a21b03120af3120b0316a21b13120b03120b1316a21b23120b13120b2316a21b33120b23120" + "b3316a21b43120b33120b4316a21b53120b43120b5316a21b63120b53120b6316a21b73120b63120b7316a21b83120" + "b73120b8316a21b93120b83120b9316a21ba3120b93120ba316a21bb3120ba3120bb316a21bc3120bb3120bc316a21" + "bd3120bc3120bd316a21be3120bd3120be316a21bf3120be3120bf316a21c03120bf3120c0316a21c13120c03120c1" + "316a21c23120c13120c2316a21c33120c23120c3316a21c43120c33120c4316a21c53120c43120c5316a21c63120c5" + "3120c6316a21c73120c63120c7316a21c83120c73120c8316a21c93120c83120c9316a21ca3120c93120ca316a21cb" + "3120ca3120cb316a21cc3120cb3120cc316a21cd3120cc3120cd316a21ce3120cd3120ce316a21cf3120ce3120cf31" + "6a21d03120cf3120d0316a21d13120d03120d1316a21d23120d13120d2316a21d33120d23120d3316a21d43120d331" + "20d4316a21d53120d43120d5316a21d63120d53120d6316a21d73120d63120d7316a21d83120d73120d8316a21d931" + "20d83120d9316a21da3120d93120da316a21db3120da3120db316a21dc3120db3120dc316a21dd3120dc3120dd316a" + "21de3120dd3120de316a21df3120de3120df316a21e03120df3120e0316a21e13120e03120e1316a21e23120e13120" + "e2316a21e33120e23120e3316a21e43120e33120e4316a21e53120e43120e5316a21e63120e53120e6316a21e73120" + "e63120e7316a21e83120e73120e8316a21e93120e83120e9316a21ea3120e93120ea316a21eb3120ea3120eb316a21" + "ec3120eb3120ec316a21ed3120ec3120ed316a21ee3120ed3120ee316a21ef3120ee3120ef316a21f03120ef3120f0" + "316a21f13120f03120f1316a21f23120f13120f2316a21f33120f23120f3316a21f43120f33120f4316a21f53120f4" + "3120f5316a21f63120f53120f6316a21f73120f63120f7316a21f83120f73120f8316a21f93120f83120f9316a21fa" + "3120f93120fa316a21fb3120fa3120fb316a21fc3120fb3120fc316a21fd3120fc3120fd316a21fe3120fd3120fe31" + "6a21ff3120fe3120ff316a21803220ff312080326a2181322080322081326a2182322081322082326a218332208232" + "2083326a2184322083322084326a2185322084322085326a2186322085322086326a2187322086322087326a218832" + "2087322088326a2189322088322089326a218a32208932208a326a218b32208a32208b326a218c32208b32208c326a" + "218d32208c32208d326a218e32208d32208e326a218f32208e32208f326a219032208f322090326a21913220903220" + "91326a2192322091322092326a2193322092322093326a2194322093322094326a2195322094322095326a21963220" + "95322096326a2197322096322097326a2198322097322098326a2199322098322099326a219a32209932209a326a21" + "9b32209a32209b326a219c32209b32209c326a219d32209c32209d326a219e32209d32209e326a219f32209e32209f" + "326a21a032209f3220a0326a21a13220a03220a1326a21a23220a13220a2326a21a33220a23220a3326a21a43220a3" + "3220a4326a21a53220a43220a5326a21a63220a53220a6326a21a73220a63220a7326a21a83220a73220a8326a21a9" + "3220a83220a9326a21aa3220a93220aa326a21ab3220aa3220ab326a21ac3220ab3220ac326a21ad3220ac3220ad32" + "6a21ae3220ad3220ae326a21af3220ae3220af326a21b03220af3220b0326a21b13220b03220b1326a21b23220b132" + "20b2326a21b33220b23220b3326a21b43220b33220b4326a21b53220b43220b5326a21b63220b53220b6326a21b732" + "20b63220b7326a21b83220b73220b8326a21b93220b83220b9326a21ba3220b93220ba326a21bb3220ba3220bb326a" + "21bc3220bb3220bc326a21bd3220bc3220bd326a21be3220bd3220be326a21bf3220be3220bf326a21c03220bf3220" + "c0326a21c13220c03220c1326a21c23220c13220c2326a21c33220c23220c3326a21c43220c33220c4326a21c53220" + "c43220c5326a21c63220c53220c6326a21c73220c63220c7326a21c83220c73220c8326a21c93220c83220c9326a21" + "ca3220c93220ca326a21cb3220ca3220cb326a21cc3220cb3220cc326a21cd3220cc3220cd326a21ce3220cd3220ce" + "326a21cf3220ce3220cf326a21d03220cf3220d0326a21d13220d03220d1326a21d23220d13220d2326a21d33220d2" + "3220d3326a21d43220d33220d4326a21d53220d43220d5326a21d63220d53220d6326a21d73220d63220d7326a21d8" + "3220d73220d8326a21d93220d83220d9326a21da3220d93220da326a21db3220da3220db326a21dc3220db3220dc32" + "6a21dd3220dc3220dd326a21de3220dd3220de326a21df3220de3220df326a21e03220df3220e0326a21e13220e032" + "20e1326a21e23220e13220e2326a21e33220e23220e3326a21e43220e33220e4326a21e53220e43220e5326a21e632" + "20e53220e6326a21e73220e63220e7326a21e83220e73220e8326a21e93220e83220e9326a21ea3220e93220ea326a" + "21eb3220ea3220eb326a21ec3220eb3220ec326a21ed3220ec3220ed326a21ee3220ed3220ee326a21ef3220ee3220" + "ef326a21f03220ef3220f0326a21f13220f03220f1326a21f23220f13220f2326a21f33220f23220f3326a21f43220" + "f33220f4326a21f53220f43220f5326a21f63220f53220f6326a21f73220f63220f7326a21f83220f73220f8326a21" + "f93220f83220f9326a21fa3220f93220fa326a21fb3220fa3220fb326a21fc3220fb3220fc326a21fd3220fc3220fd" + "326a21fe3220fd3220fe326a21ff3220fe3220ff326a21803320ff322080336a2181332080332081336a2182332081" + "332082336a2183332082332083336a2184332083332084336a2185332084332085336a2186332085332086336a2187" + "332086332087336a2188332087332088336a2189332088332089336a218a33208933208a336a218b33208a33208b33" + "6a218c33208b33208c336a218d33208c33208d336a218e33208d33208e336a218f33208e33208f336a219033208f33" + "2090336a2191332090332091336a2192332091332092336a2193332092332093336a2194332093332094336a219533" + "2094332095336a2196332095332096336a2197332096332097336a2198332097332098336a2199332098332099336a" + "219a33209933209a336a219b33209a33209b336a219c33209b33209c336a219d33209c33209d336a219e33209d3320" + "9e336a219f33209e33209f336a21a033209f3320a0336a21a13320a03320a1336a21a23320a13320a2336a21a33320" + "a23320a3336a21a43320a33320a4336a21a53320a43320a5336a21a63320a53320a6336a21a73320a63320a7336a21" + "a83320a73320a8336a21a93320a83320a9336a21aa3320a93320aa336a21ab3320aa3320ab336a21ac3320ab3320ac" + "336a21ad3320ac3320ad336a21ae3320ad3320ae336a21af3320ae3320af336a21b03320af3320b0336a21b13320b0" + "3320b1336a21b23320b13320b2336a21b33320b23320b3336a21b43320b33320b4336a21b53320b43320b5336a21b6" + "3320b53320b6336a21b73320b63320b7336a21b83320b73320b8336a21b93320b83320b9336a21ba3320b93320ba33" + "6a21bb3320ba3320bb336a21bc3320bb3320bc336a21bd3320bc3320bd336a21be3320bd3320be336a21bf3320be33" + "20bf336a21c03320bf3320c0336a21c13320c03320c1336a21c23320c13320c2336a21c33320c23320c3336a21c433" + "20c33320c4336a21c53320c43320c5336a21c63320c53320c6336a21c73320c63320c7336a21c83320c73320c8336a" + "21c93320c83320c9336a21ca3320c93320ca336a21cb3320ca3320cb336a21cc3320cb3320cc336a21cd3320cc3320" + "cd336a21ce3320cd3320ce336a21cf3320ce3320cf336a21d03320cf3320d0336a21d13320d03320d1336a21d23320" + "d13320d2336a21d33320d23320d3336a21d43320d33320d4336a21d53320d43320d5336a21d63320d53320d6336a21" + "d73320d63320d7336a21d83320d73320d8336a21d93320d83320d9336a21da3320d93320da336a21db3320da3320db" + "336a21dc3320db3320dc336a21dd3320dc3320dd336a21de3320dd3320de336a21df3320de3320df336a21e03320df" + "3320e0336a21e13320e03320e1336a21e23320e13320e2336a21e33320e23320e3336a21e43320e33320e4336a21e5" + "3320e43320e5336a21e63320e53320e6336a21e73320e63320e7336a21e83320e73320e8336a21e93320e83320e933" + "6a21ea3320e93320ea336a21eb3320ea3320eb336a21ec3320eb3320ec336a21ed3320ec3320ed336a21ee3320ed33" + "20ee336a21ef3320ee3320ef336a21f03320ef3320f0336a21f13320f03320f1336a21f23320f13320f2336a21f333" + "20f23320f3336a21f43320f33320f4336a21f53320f43320f5336a21f63320f53320f6336a21f73320f63320f7336a" + "21f83320f73320f8336a21f93320f83320f9336a21fa3320f93320fa336a21fb3320fa3320fb336a21fc3320fb3320" + "fc336a21fd3320fc3320fd336a21fe3320fd3320fe336a21ff3320fe3320ff336a21803420ff332080346a21813420" + "80342081346a2182342081342082346a2183342082342083346a2184342083342084346a2185342084342085346a21" + "86342085342086346a2187342086342087346a2188342087342088346a2189342088342089346a218a34208934208a" + "346a218b34208a34208b346a218c34208b34208c346a218d34208c34208d346a218e34208d34208e346a218f34208e" + "34208f346a219034208f342090346a2191342090342091346a2192342091342092346a2193342092342093346a2194" + "342093342094346a2195342094342095346a2196342095342096346a2197342096342097346a219834209734209834" + "6a2199342098342099346a219a34209934209a346a219b34209a34209b346a219c34209b34209c346a219d34209c34" + "209d346a219e34209d34209e346a219f34209e34209f346a21a034209f3420a0346a21a13420a03420a1346a21a234" + "20a13420a2346a21a33420a23420a3346a21a43420a33420a4346a21a53420a43420a5346a21a63420a53420a6346a" + "21a73420a63420a7346a21a83420a73420a8346a21a93420a83420a9346a21aa3420a93420aa346a21ab3420aa3420" + "ab346a21ac3420ab3420ac346a21ad3420ac3420ad346a21ae3420ad3420ae346a21af3420ae3420af346a21b03420" + "af3420b0346a21b13420b03420b1346a21b23420b13420b2346a21b33420b23420b3346a21b43420b33420b4346a21" + "b53420b43420b5346a21b63420b53420b6346a21b73420b63420b7346a21b83420b73420b8346a21b93420b83420b9" + "346a21ba3420b93420ba346a21bb3420ba3420bb346a21bc3420bb3420bc346a21bd3420bc3420bd346a21be3420bd" + "3420be346a21bf3420be3420bf346a21c03420bf3420c0346a21c13420c03420c1346a21c23420c13420c2346a21c3" + "3420c23420c3346a21c43420c33420c4346a21c53420c43420c5346a21c63420c53420c6346a21c73420c63420c734" + "6a21c83420c73420c8346a21c93420c83420c9346a21ca3420c93420ca346a21cb3420ca3420cb346a21cc3420cb34" + "20cc346a21cd3420cc3420cd346a21ce3420cd3420ce346a21cf3420ce3420cf346a21d03420cf3420d0346a21d134" + "20d03420d1346a21d23420d13420d2346a21d33420d23420d3346a21d43420d33420d4346a21d53420d43420d5346a" + "21d63420d53420d6346a21d73420d63420d7346a21d83420d73420d8346a21d93420d83420d9346a21da3420d93420" + "da346a21db3420da3420db346a21dc3420db3420dc346a21dd3420dc3420dd346a21de3420dd3420de346a21df3420" + "de3420df346a21e03420df3420e0346a21e13420e03420e1346a21e23420e13420e2346a21e33420e23420e3346a21" + "e43420e33420e4346a21e53420e43420e5346a21e63420e53420e6346a21e73420e63420e7346a21e83420e73420e8" + "346a21e93420e83420e9346a21ea3420e93420ea346a21eb3420ea3420eb346a21ec3420eb3420ec346a21ed3420ec" + "3420ed346a21ee3420ed3420ee346a21ef3420ee3420ef346a21f03420ef3420f0346a21f13420f03420f1346a21f2" + "3420f13420f2346a21f33420f23420f3346a21f43420f33420f4346a21f53420f43420f5346a21f63420f53420f634" + "6a21f73420f63420f7346a21f83420f73420f8346a21f93420f83420f9346a21fa3420f93420fa346a21fb3420fa34" + "20fb346a21fc3420fb3420fc346a21fd3420fc3420fd346a21fe3420fd3420fe346a21ff3420fe3420ff346a218035" + "20ff342080356a2181352080352081356a2182352081352082356a2183352082352083356a2184352083352084356a" + "2185352084352085356a2186352085352086356a2187352086352087356a2188352087352088356a21893520883520" + "89356a218a35208935208a356a218b35208a35208b356a218c35208b35208c356a218d35208c35208d356a218e3520" + "8d35208e356a218f35208e35208f356a219035208f352090356a2191352090352091356a2192352091352092356a21" + "93352092352093356a2194352093352094356a2195352094352095356a2196352095352096356a2197352096352097" + "356a2198352097352098356a2199352098352099356a219a35209935209a356a219b35209a35209b356a219c35209b" + "35209c356a219d35209c35209d356a219e35209d35209e356a219f35209e35209f356a21a035209f3520a0356a21a1" + "3520a03520a1356a21a23520a13520a2356a21a33520a23520a3356a21a43520a33520a4356a21a53520a43520a535" + "6a21a63520a53520a6356a21a73520a63520a7356a21a83520a73520a8356a21a93520a83520a9356a21aa3520a935" + "20aa356a21ab3520aa3520ab356a21ac3520ab3520ac356a21ad3520ac3520ad356a21ae3520ad3520ae356a21af35" + "20ae3520af356a21b03520af3520b0356a21b13520b03520b1356a21b23520b13520b2356a21b33520b23520b3356a" + "21b43520b33520b4356a21b53520b43520b5356a21b63520b53520b6356a21b73520b63520b7356a21b83520b73520" + "b8356a21b93520b83520b9356a21ba3520b93520ba356a21bb3520ba3520bb356a21bc3520bb3520bc356a21bd3520" + "bc3520bd356a21be3520bd3520be356a21bf3520be3520bf356a21c03520bf3520c0356a21c13520c03520c1356a21" + "c23520c13520c2356a21c33520c23520c3356a21c43520c33520c4356a21c53520c43520c5356a21c63520c53520c6" + "356a21c73520c63520c7356a21c83520c73520c8356a21c93520c83520c9356a21ca3520c93520ca356a21cb3520ca" + "3520cb356a21cc3520cb3520cc356a21cd3520cc3520cd356a21ce3520cd3520ce356a21cf3520ce3520cf356a21d0" + "3520cf3520d0356a21d13520d03520d1356a21d23520d13520d2356a21d33520d23520d3356a21d43520d33520d435" + "6a21d53520d43520d5356a21d63520d53520d6356a21d73520d63520d7356a21d83520d73520d8356a21d93520d835" + "20d9356a21da3520d93520da356a21db3520da3520db356a21dc3520db3520dc356a21dd3520dc3520dd356a21de35" + "20dd3520de356a21df3520de3520df356a21e03520df3520e0356a21e13520e03520e1356a21e23520e13520e2356a" + "21e33520e23520e3356a21e43520e33520e4356a21e53520e43520e5356a21e63520e53520e6356a21e73520e63520" + "e7356a21e83520e73520e8356a21e93520e83520e9356a21ea3520e93520ea356a21eb3520ea3520eb356a21ec3520" + "eb3520ec356a21ed3520ec3520ed356a21ee3520ed3520ee356a21ef3520ee3520ef356a21f03520ef3520f0356a21" + "f13520f03520f1356a21f23520f13520f2356a21f33520f23520f3356a21f43520f33520f4356a21f53520f43520f5" + "356a21f63520f53520f6356a21f73520f63520f7356a21f83520f73520f8356a21f93520f83520f9356a21fa3520f9" + "3520fa356a21fb3520fa3520fb356a21fc3520fb3520fc356a21fd3520fc3520fd356a21fe3520fd3520fe356a21ff" + "3520fe3520ff356a21803620ff352080366a2181362080362081366a2182362081362082366a218336208236208336" + "6a2184362083362084366a2185362084362085366a2186362085362086366a2187362086362087366a218836208736" + "2088366a2189362088362089366a218a36208936208a366a218b36208a36208b366a218c36208b36208c366a218d36" + "208c36208d366a218e36208d36208e366a218f36208e36208f366a219036208f362090366a2191362090362091366a" + "2192362091362092366a2193362092362093366a2194362093362094366a2195362094362095366a21963620953620" + "96366a2197362096362097366a2198362097362098366a2199362098362099366a219a36209936209a366a219b3620" + "9a36209b366a219c36209b36209c366a219d36209c36209d366a219e36209d36209e366a219f36209e36209f366a21" + "a036209f3620a0366a21a13620a03620a1366a21a23620a13620a2366a21a33620a23620a3366a21a43620a33620a4" + "366a21a53620a43620a5366a21a63620a53620a6366a21a73620a63620a7366a21a83620a73620a8366a21a93620a8" + "3620a9366a21aa3620a93620aa366a21ab3620aa3620ab366a21ac3620ab3620ac366a21ad3620ac3620ad366a21ae" + "3620ad3620ae366a21af3620ae3620af366a21b03620af3620b0366a21b13620b03620b1366a21b23620b13620b236" + "6a21b33620b23620b3366a21b43620b33620b4366a21b53620b43620b5366a21b63620b53620b6366a21b73620b636" + "20b7366a21b83620b73620b8366a21b93620b83620b9366a21ba3620b93620ba366a21bb3620ba3620bb366a21bc36" + "20bb3620bc366a21bd3620bc3620bd366a21be3620bd3620be366a21bf3620be3620bf366a21c03620bf3620c0366a" + "21c13620c03620c1366a21c23620c13620c2366a21c33620c23620c3366a21c43620c33620c4366a21c53620c43620" + "c5366a21c63620c53620c6366a21c73620c63620c7366a21c83620c73620c8366a21c93620c83620c9366a21ca3620" + "c93620ca366a21cb3620ca3620cb366a21cc3620cb3620cc366a21cd3620cc3620cd366a21ce3620cd3620ce366a21" + "cf3620ce3620cf366a21d03620cf3620d0366a21d13620d03620d1366a21d23620d13620d2366a21d33620d23620d3" + "366a21d43620d33620d4366a21d53620d43620d5366a21d63620d53620d6366a21d73620d63620d7366a21d83620d7" + "3620d8366a21d93620d83620d9366a21da3620d93620da366a21db3620da3620db366a21dc3620db3620dc366a21dd" + "3620dc3620dd366a21de3620dd3620de366a21df3620de3620df366a21e03620df3620e0366a21e13620e03620e136" + "6a21e23620e13620e2366a21e33620e23620e3366a21e43620e33620e4366a21e53620e43620e5366a21e63620e536" + "20e6366a21e73620e63620e7366a21e83620e73620e8366a21e93620e83620e9366a21ea3620e93620ea366a21eb36" + "20ea3620eb366a21ec3620eb3620ec366a21ed3620ec3620ed366a21ee3620ed3620ee366a21ef3620ee3620ef366a" + "21f03620ef3620f0366a21f13620f03620f1366a21f23620f13620f2366a21f33620f23620f3366a21f43620f33620" + "f4366a21f53620f43620f5366a21f63620f53620f6366a21f73620f63620f7366a21f83620f73620f8366a21f93620" + "f83620f9366a21fa3620f93620fa366a21fb3620fa3620fb366a21fc3620fb3620fc366a21fd3620fc3620fd366a21" + "fe3620fd3620fe366a21ff3620fe3620ff366a21803720ff362080376a2181372080372081376a2182372081372082" + "376a2183372082372083376a2184372083372084376a2185372084372085376a2186372085372086376a2187372086" + "372087376a2188372087372088376a2189372088372089376a218a37208937208a376a218b37208a37208b376a218c" + "37208b37208c376a218d37208c37208d376a218e37208d37208e376a218f37208e37208f376a219037208f37209037" + "6a2191372090372091376a2192372091372092376a2193372092372093376a2194372093372094376a219537209437" + "2095376a2196372095372096376a2197372096372097376a2198372097372098376a2199372098372099376a219a37" + "209937209a376a219b37209a37209b376a219c37209b37209c376a219d37209c37209d376a219e37209d37209e376a" + "219f37209e37209f376a21a037209f3720a0376a21a13720a03720a1376a21a23720a13720a2376a21a33720a23720" + "a3376a21a43720a33720a4376a21a53720a43720a5376a21a63720a53720a6376a21a73720a63720a7376a21a83720" + "a73720a8376a21a93720a83720a9376a21aa3720a93720aa376a21ab3720aa3720ab376a21ac3720ab3720ac376a21" + "ad3720ac3720ad376a21ae3720ad3720ae376a21af3720ae3720af376a21b03720af3720b0376a21b13720b03720b1" + "376a21b23720b13720b2376a21b33720b23720b3376a21b43720b33720b4376a21b53720b43720b5376a21b63720b5" + "3720b6376a21b73720b63720b7376a21b83720b73720b8376a21b93720b83720b9376a21ba3720b93720ba376a21bb" + "3720ba3720bb376a21bc3720bb3720bc376a21bd3720bc3720bd376a21be3720bd3720be376a21bf3720be3720bf37" + "6a21c03720bf3720c0376a21c13720c03720c1376a21c23720c13720c2376a21c33720c23720c3376a21c43720c337" + "20c4376a21c53720c43720c5376a21c63720c53720c6376a21c73720c63720c7376a21c83720c73720c8376a21c937" + "20c83720c9376a21ca3720c93720ca376a21cb3720ca3720cb376a21cc3720cb3720cc376a21cd3720cc3720cd376a" + "21ce3720cd3720ce376a21cf3720ce3720cf376a21d03720cf3720d0376a21d13720d03720d1376a21d23720d13720" + "d2376a21d33720d23720d3376a21d43720d33720d4376a21d53720d43720d5376a21d63720d53720d6376a21d73720" + "d63720d7376a21d83720d73720d8376a21d93720d83720d9376a21da3720d93720da376a21db3720da3720db376a21" + "dc3720db3720dc376a21dd3720dc3720dd376a21de3720dd3720de376a21df3720de3720df376a21e03720df3720e0" + "376a21e13720e03720e1376a21e23720e13720e2376a21e33720e23720e3376a21e43720e33720e4376a21e53720e4" + "3720e5376a21e63720e53720e6376a21e73720e63720e7376a21e83720e73720e8376a21e93720e83720e9376a21ea" + "3720e93720ea376a21eb3720ea3720eb376a21ec3720eb3720ec376a21ed3720ec3720ed376a21ee3720ed3720ee37" + "6a21ef3720ee3720ef376a21f03720ef3720f0376a21f13720f03720f1376a21f23720f13720f2376a21f33720f237" + "20f3376a21f43720f33720f4376a21f53720f43720f5376a21f63720f53720f6376a21f73720f63720f7376a21f837" + "20f73720f8376a21f93720f83720f9376a21fa3720f93720fa376a21fb3720fa3720fb376a21fc3720fb3720fc376a" + "21fd3720fc3720fd376a21fe3720fd3720fe376a21ff3720fe3720ff376a21803820ff372080386a21813820803820" + "81386a2182382081382082386a2183382082382083386a2184382083382084386a2185382084382085386a21863820" + "85382086386a2187382086382087386a2188382087382088386a2189382088382089386a218a38208938208a386a21" + "8b38208a38208b386a218c38208b38208c386a218d38208c38208d386a218e38208d38208e386a218f38208e38208f" + "386a219038208f382090386a2191382090382091386a2192382091382092386a2193382092382093386a2194382093" + "382094386a2195382094382095386a2196382095382096386a2197382096382097386a2198382097382098386a2199" + "382098382099386a219a38209938209a386a219b38209a38209b386a219c38209b38209c386a219d38209c38209d38" + "6a219e38209d38209e386a219f38209e38209f386a21a038209f3820a0386a21a13820a03820a1386a21a23820a138" + "20a2386a21a33820a23820a3386a21a43820a33820a4386a21a53820a43820a5386a21a63820a53820a6386a21a738" + "20a63820a7386a21a83820a73820a8386a21a93820a83820a9386a21aa3820a93820aa386a21ab3820aa3820ab386a" + "21ac3820ab3820ac386a21ad3820ac3820ad386a21ae3820ad3820ae386a21af3820ae3820af386a21b03820af3820" + "b0386a21b13820b03820b1386a21b23820b13820b2386a21b33820b23820b3386a21b43820b33820b4386a21b53820" + "b43820b5386a21b63820b53820b6386a21b73820b63820b7386a21b83820b73820b8386a21b93820b83820b9386a21" + "ba3820b93820ba386a21bb3820ba3820bb386a21bc3820bb3820bc386a21bd3820bc3820bd386a21be3820bd3820be" + "386a21bf3820be3820bf386a21c03820bf3820c0386a21c13820c03820c1386a21c23820c13820c2386a21c33820c2" + "3820c3386a21c43820c33820c4386a21c53820c43820c5386a21c63820c53820c6386a21c73820c63820c7386a21c8" + "3820c73820c8386a21c93820c83820c9386a21ca3820c93820ca386a21cb3820ca3820cb386a21cc3820cb3820cc38" + "6a21cd3820cc3820cd386a21ce3820cd3820ce386a21cf3820ce3820cf386a21d03820cf3820d0386a21d13820d038" + "20d1386a21d23820d13820d2386a21d33820d23820d3386a21d43820d33820d4386a21d53820d43820d5386a21d638" + "20d53820d6386a21d73820d63820d7386a21d83820d73820d8386a21d93820d83820d9386a21da3820d93820da386a" + "21db3820da3820db386a21dc3820db3820dc386a21dd3820dc3820dd386a21de3820dd3820de386a21df3820de3820" + "df386a21e03820df3820e0386a21e13820e03820e1386a21e23820e13820e2386a21e33820e23820e3386a21e43820" + "e33820e4386a21e53820e43820e5386a21e63820e53820e6386a21e73820e63820e7386a21e83820e73820e8386a21" + "e93820e83820e9386a21ea3820e93820ea386a21eb3820ea3820eb386a21ec3820eb3820ec386a21ed3820ec3820ed" + "386a21ee3820ed3820ee386a21ef3820ee3820ef386a21f03820ef3820f0386a21f13820f03820f1386a21f23820f1" + "3820f2386a21f33820f23820f3386a21f43820f33820f4386a21f53820f43820f5386a21f63820f53820f6386a21f7" + "3820f63820f7386a21f83820f73820f8386a21f93820f83820f9386a21fa3820f93820fa386a21fb3820fa3820fb38" + "6a21fc3820fb3820fc386a21fd3820fc3820fd386a21fe3820fd3820fe386a21ff3820fe3820ff386a21803920ff38" + "2080396a2181392080392081396a2182392081392082396a2183392082392083396a2184392083392084396a218539" + "2084392085396a2186392085392086396a2187392086392087396a2188392087392088396a2189392088392089396a" + "218a39208939208a396a218b39208a39208b396a218c39208b39208c396a218d39208c39208d396a218e39208d3920" + "8e396a218f39208e39208f396a219039208f392090396a2191392090392091396a2192392091392092396a21933920" + "92392093396a2194392093392094396a2195392094392095396a2196392095392096396a2197392096392097396a21" + "98392097392098396a2199392098392099396a219a39209939209a396a219b39209a39209b396a219c39209b39209c" + "396a219d39209c39209d396a219e39209d39209e396a219f39209e39209f396a21a039209f3920a0396a21a13920a0" + "3920a1396a21a23920a13920a2396a21a33920a23920a3396a21a43920a33920a4396a21a53920a43920a5396a21a6" + "3920a53920a6396a21a73920a63920a7396a21a83920a73920a8396a21a93920a83920a9396a21aa3920a93920aa39" + "6a21ab3920aa3920ab396a21ac3920ab3920ac396a21ad3920ac3920ad396a21ae3920ad3920ae396a21af3920ae39" + "20af396a21b03920af3920b0396a21b13920b03920b1396a21b23920b13920b2396a21b33920b23920b3396a21b439" + "20b33920b4396a21b53920b43920b5396a21b63920b53920b6396a21b73920b63920b7396a21b83920b73920b8396a" + "21b93920b83920b9396a21ba3920b93920ba396a21bb3920ba3920bb396a21bc3920bb3920bc396a21bd3920bc3920" + "bd396a21be3920bd3920be396a21bf3920be3920bf396a21c03920bf3920c0396a21c13920c03920c1396a21c23920" + "c13920c2396a21c33920c23920c3396a21c43920c33920c4396a21c53920c43920c5396a21c63920c53920c6396a21" + "c73920c63920c7396a21c83920c73920c8396a21c93920c83920c9396a21ca3920c93920ca396a21cb3920ca3920cb" + "396a21cc3920cb3920cc396a21cd3920cc3920cd396a21ce3920cd3920ce396a21cf3920ce3920cf396a21d03920cf" + "3920d0396a21d13920d03920d1396a21d23920d13920d2396a21d33920d23920d3396a21d43920d33920d4396a21d5" + "3920d43920d5396a21d63920d53920d6396a21d73920d63920d7396a21d83920d73920d8396a21d93920d83920d939" + "6a21da3920d93920da396a21db3920da3920db396a21dc3920db3920dc396a21dd3920dc3920dd396a21de3920dd39" + "20de396a21df3920de3920df396a21e03920df3920e0396a21e13920e03920e1396a21e23920e13920e2396a21e339" + "20e23920e3396a21e43920e33920e4396a21e53920e43920e5396a21e63920e53920e6396a21e73920e63920e7396a" + "21e83920e73920e8396a21e93920e83920e9396a21ea3920e93920ea396a21eb3920ea3920eb396a21ec3920eb3920" + "ec396a21ed3920ec3920ed396a21ee3920ed3920ee396a21ef3920ee3920ef396a21f03920ef3920f0396a21f13920" + "f03920f1396a21f23920f13920f2396a21f33920f23920f3396a21f43920f33920f4396a21f53920f43920f5396a21" + "f63920f53920f6396a21f73920f63920f7396a21f83920f73920f8396a21f93920f83920f9396a21fa3920f93920fa" + "396a21fb3920fa3920fb396a21fc3920fb3920fc396a21fd3920fc3920fd396a21fe3920fd3920fe396a21ff3920fe" + "3920ff396a21803a20ff3920803a6a21813a20803a20813a6a21823a20813a20823a6a21833a20823a20833a6a2184" + "3a20833a20843a6a21853a20843a20853a6a21863a20853a20863a6a21873a20863a20873a6a21883a20873a20883a" + "6a21893a20883a20893a6a218a3a20893a208a3a6a218b3a208a3a208b3a6a218c3a208b3a208c3a6a218d3a208c3a" + "208d3a6a218e3a208d3a208e3a6a218f3a208e3a208f3a6a21903a208f3a20903a6a21913a20903a20913a6a21923a" + "20913a20923a6a21933a20923a20933a6a21943a20933a20943a6a21953a20943a20953a6a21963a20953a20963a6a" + "21973a20963a20973a6a21983a20973a20983a6a21993a20983a20993a6a219a3a20993a209a3a6a219b3a209a3a20" + "9b3a6a219c3a209b3a209c3a6a219d3a209c3a209d3a6a219e3a209d3a209e3a6a219f3a209e3a209f3a6a21a03a20" + "9f3a20a03a6a21a13a20a03a20a13a6a21a23a20a13a20a23a6a21a33a20a23a20a33a6a21a43a20a33a20a43a6a21" + "a53a20a43a20a53a6a21a63a20a53a20a63a6a21a73a20a63a20a73a6a21a83a20a73a20a83a6a21a93a20a83a20a9" + "3a6a21aa3a20a93a20aa3a6a21ab3a20aa3a20ab3a6a21ac3a20ab3a20ac3a6a21ad3a20ac3a20ad3a6a21ae3a20ad" + "3a20ae3a6a21af3a20ae3a20af3a6a21b03a20af3a20b03a6a21b13a20b03a20b13a6a21b23a20b13a20b23a6a21b3" + "3a20b23a20b33a6a21b43a20b33a20b43a6a21b53a20b43a20b53a6a21b63a20b53a20b63a6a21b73a20b63a20b73a" + "6a21b83a20b73a20b83a6a21b93a20b83a20b93a6a21ba3a20b93a20ba3a6a21bb3a20ba3a20bb3a6a21bc3a20bb3a" + "20bc3a6a21bd3a20bc3a20bd3a6a21be3a20bd3a20be3a6a21bf3a20be3a20bf3a6a21c03a20bf3a20c03a6a21c13a" + "20c03a20c13a6a21c23a20c13a20c23a6a21c33a20c23a20c33a6a21c43a20c33a20c43a6a21c53a20c43a20c53a6a" + "21c63a20c53a20c63a6a21c73a20c63a20c73a6a21c83a20c73a20c83a6a21c93a20c83a20c93a6a21ca3a20c93a20" + "ca3a6a21cb3a20ca3a20cb3a6a21cc3a20cb3a20cc3a6a21cd3a20cc3a20cd3a6a21ce3a20cd3a20ce3a6a21cf3a20" + "ce3a20cf3a6a21d03a20cf3a20d03a6a21d13a20d03a20d13a6a21d23a20d13a20d23a6a21d33a20d23a20d33a6a21" + "d43a20d33a20d43a6a21d53a20d43a20d53a6a21d63a20d53a20d63a6a21d73a20d63a20d73a6a21d83a20d73a20d8" + "3a6a21d93a20d83a20d93a6a21da3a20d93a20da3a6a21db3a20da3a20db3a6a21dc3a20db3a20dc3a6a21dd3a20dc" + "3a20dd3a6a21de3a20dd3a20de3a6a21df3a20de3a20df3a6a21e03a20df3a20e03a6a21e13a20e03a20e13a6a21e2" + "3a20e13a20e23a6a21e33a20e23a20e33a6a21e43a20e33a20e43a6a21e53a20e43a20e53a6a21e63a20e53a20e63a" + "6a21e73a20e63a20e73a6a21e83a20e73a20e83a6a21e93a20e83a20e93a6a21ea3a20e93a20ea3a6a21eb3a20ea3a" + "20eb3a6a21ec3a20eb3a20ec3a6a21ed3a20ec3a20ed3a6a21ee3a20ed3a20ee3a6a21ef3a20ee3a20ef3a6a21f03a" + "20ef3a20f03a6a21f13a20f03a20f13a6a21f23a20f13a20f23a6a21f33a20f23a20f33a6a21f43a20f33a20f43a6a" + "21f53a20f43a20f53a6a21f63a20f53a20f63a6a21f73a20f63a20f73a6a21f83a20f73a20f83a6a21f93a20f83a20" + "f93a6a21fa3a20f93a20fa3a6a21fb3a20fa3a20fb3a6a21fc3a20fb3a20fc3a6a21fd3a20fc3a20fd3a6a21fe3a20" + "fd3a20fe3a6a21ff3a20fe3a20ff3a6a21803b20ff3a20803b6a21813b20803b20813b6a21823b20813b20823b6a21" + "833b20823b20833b6a21843b20833b20843b6a21853b20843b20853b6a21863b20853b20863b6a21873b20863b2087" + "3b6a21883b20873b20883b6a21893b20883b20893b6a218a3b20893b208a3b6a218b3b208a3b208b3b6a218c3b208b" + "3b208c3b6a218d3b208c3b208d3b6a218e3b208d3b208e3b6a218f3b208e3b208f3b6a21903b208f3b20903b6a2191" + "3b20903b20913b6a21923b20913b20923b6a21933b20923b20933b6a21943b20933b20943b6a21953b20943b20953b" + "6a21963b20953b20963b6a21973b20963b20973b6a21983b20973b20983b6a21993b20983b20993b6a219a3b20993b" + "209a3b6a219b3b209a3b209b3b6a219c3b209b3b209c3b6a219d3b209c3b209d3b6a219e3b209d3b209e3b6a219f3b" + "209e3b209f3b6a21a03b209f3b20a03b6a21a13b20a03b20a13b6a21a23b20a13b20a23b6a21a33b20a23b20a33b6a" + "21a43b20a33b20a43b6a21a53b20a43b20a53b6a21a63b20a53b20a63b6a21a73b20a63b20a73b6a21a83b20a73b20" + "a83b6a21a93b20a83b20a93b6a21aa3b20a93b20aa3b6a21ab3b20aa3b20ab3b6a21ac3b20ab3b20ac3b6a21ad3b20" + "ac3b20ad3b6a21ae3b20ad3b20ae3b6a21af3b20ae3b20af3b6a21b03b20af3b20b03b6a21b13b20b03b20b13b6a21" + "b23b20b13b20b23b6a21b33b20b23b20b33b6a21b43b20b33b20b43b6a21b53b20b43b20b53b6a21b63b20b53b20b6" + "3b6a21b73b20b63b20b73b6a21b83b20b73b20b83b6a21b93b20b83b20b93b6a21ba3b20b93b20ba3b6a21bb3b20ba" + "3b20bb3b6a21bc3b20bb3b20bc3b6a21bd3b20bc3b20bd3b6a21be3b20bd3b20be3b6a21bf3b20be3b20bf3b6a21c0" + "3b20bf3b20c03b6a21c13b20c03b20c13b6a21c23b20c13b20c23b6a21c33b20c23b20c33b6a21c43b20c33b20c43b" + "6a21c53b20c43b20c53b6a21c63b20c53b20c63b6a21c73b20c63b20c73b6a21c83b20c73b20c83b6a21c93b20c83b" + "20c93b6a21ca3b20c93b20ca3b6a21cb3b20ca3b20cb3b6a21cc3b20cb3b20cc3b6a21cd3b20cc3b20cd3b6a21ce3b" + "20cd3b20ce3b6a21cf3b20ce3b20cf3b6a21d03b20cf3b20d03b6a21d13b20d03b20d13b6a21d23b20d13b20d23b6a" + "21d33b20d23b20d33b6a21d43b20d33b20d43b6a21d53b20d43b20d53b6a21d63b20d53b20d63b6a21d73b20d63b20" + "d73b6a21d83b20d73b20d83b6a21d93b20d83b20d93b6a21da3b20d93b20da3b6a21db3b20da3b20db3b6a21dc3b20" + "db3b20dc3b6a21dd3b20dc3b20dd3b6a21de3b20dd3b20de3b6a21df3b20de3b20df3b6a21e03b20df3b20e03b6a21" + "e13b20e03b20e13b6a21e23b20e13b20e23b6a21e33b20e23b20e33b6a21e43b20e33b20e43b6a21e53b20e43b20e5" + "3b6a21e63b20e53b20e63b6a21e73b20e63b20e73b6a21e83b20e73b20e83b6a21e93b20e83b20e93b6a21ea3b20e9" + "3b20ea3b6a21eb3b20ea3b20eb3b6a21ec3b20eb3b20ec3b6a21ed3b20ec3b20ed3b6a21ee3b20ed3b20ee3b6a21ef" + "3b20ee3b20ef3b6a21f03b20ef3b20f03b6a21f13b20f03b20f13b6a21f23b20f13b20f23b6a21f33b20f23b20f33b" + "6a21f43b20f33b20f43b6a21f53b20f43b20f53b6a21f63b20f53b20f63b6a21f73b20f63b20f73b6a21f83b20f73b" + "20f83b6a21f93b20f83b20f93b6a21fa3b20f93b20fa3b6a21fb3b20fa3b20fb3b6a21fc3b20fb3b20fc3b6a21fd3b" + "20fc3b20fd3b6a21fe3b20fd3b20fe3b6a21ff3b20fe3b20ff3b6a21803c20ff3b20803c6a21813c20803c20813c6a" + "21823c20813c20823c6a21833c20823c20833c6a21843c20833c20843c6a21853c20843c20853c6a21863c20853c20" + "863c6a21873c20863c20873c6a21883c20873c20883c6a21893c20883c20893c6a218a3c20893c208a3c6a218b3c20" + "8a3c208b3c6a218c3c208b3c208c3c6a218d3c208c3c208d3c6a218e3c208d3c208e3c6a218f3c208e3c208f3c6a21" + "903c208f3c20903c6a21913c20903c20913c6a21923c20913c20923c6a21933c20923c20933c6a21943c20933c2094" + "3c6a21953c20943c20953c6a21963c20953c20963c6a21973c20963c20973c6a21983c20973c20983c6a21993c2098" + "3c20993c6a219a3c20993c209a3c6a219b3c209a3c209b3c6a219c3c209b3c209c3c6a219d3c209c3c209d3c6a219e" + "3c209d3c209e3c6a219f3c209e3c209f3c6a21a03c209f3c20a03c6a21a13c20a03c20a13c6a21a23c20a13c20a23c" + "6a21a33c20a23c20a33c6a21a43c20a33c20a43c6a21a53c20a43c20a53c6a21a63c20a53c20a63c6a21a73c20a63c" + "20a73c6a21a83c20a73c20a83c6a21a93c20a83c20a93c6a21aa3c20a93c20aa3c6a21ab3c20aa3c20ab3c6a21ac3c" + "20ab3c20ac3c6a21ad3c20ac3c20ad3c6a21ae3c20ad3c20ae3c6a21af3c20ae3c20af3c6a21b03c20af3c20b03c6a" + "21b13c20b03c20b13c6a21b23c20b13c20b23c6a21b33c20b23c20b33c6a21b43c20b33c20b43c6a21b53c20b43c20" + "b53c6a21b63c20b53c20b63c6a21b73c20b63c20b73c6a21b83c20b73c20b83c6a21b93c20b83c20b93c6a21ba3c20" + "b93c20ba3c6a21bb3c20ba3c20bb3c6a21bc3c20bb3c20bc3c6a21bd3c20bc3c20bd3c6a21be3c20bd3c20be3c6a21" + "bf3c20be3c20bf3c6a21c03c20bf3c20c03c6a21c13c20c03c20c13c6a21c23c20c13c20c23c6a21c33c20c23c20c3" + "3c6a21c43c20c33c20c43c6a21c53c20c43c20c53c6a21c63c20c53c20c63c6a21c73c20c63c20c73c6a21c83c20c7" + "3c20c83c6a21c93c20c83c20c93c6a21ca3c20c93c20ca3c6a21cb3c20ca3c20cb3c6a21cc3c20cb3c20cc3c6a21cd" + "3c20cc3c20cd3c6a21ce3c20cd3c20ce3c6a21cf3c20ce3c20cf3c6a21d03c20cf3c20d03c6a21d13c20d03c20d13c" + "6a21d23c20d13c20d23c6a21d33c20d23c20d33c6a21d43c20d33c20d43c6a21d53c20d43c20d53c6a21d63c20d53c" + "20d63c6a21d73c20d63c20d73c6a21d83c20d73c20d83c6a21d93c20d83c20d93c6a21da3c20d93c20da3c6a21db3c" + "20da3c20db3c6a21dc3c20db3c20dc3c6a21dd3c20dc3c20dd3c6a21de3c20dd3c20de3c6a21df3c20de3c20df3c6a" + "21e03c20df3c20e03c6a21e13c20e03c20e13c6a21e23c20e13c20e23c6a21e33c20e23c20e33c6a21e43c20e33c20" + "e43c6a21e53c20e43c20e53c6a21e63c20e53c20e63c6a21e73c20e63c20e73c6a21e83c20e73c20e83c6a21e93c20" + "e83c20e93c6a21ea3c20e93c20ea3c6a21eb3c20ea3c20eb3c6a21ec3c20eb3c20ec3c6a21ed3c20ec3c20ed3c6a21" + "ee3c20ed3c20ee3c6a21ef3c20ee3c20ef3c6a21f03c20ef3c20f03c6a21f13c20f03c20f13c6a21f23c20f13c20f2" + "3c6a21f33c20f23c20f33c6a21f43c20f33c20f43c6a21f53c20f43c20f53c6a21f63c20f53c20f63c6a21f73c20f6" + "3c20f73c6a21f83c20f73c20f83c6a21f93c20f83c20f93c6a21fa3c20f93c20fa3c6a21fb3c20fa3c20fb3c6a21fc" + "3c20fb3c20fc3c6a21fd3c20fc3c20fd3c6a21fe3c20fd3c20fe3c6a21ff3c20fe3c20ff3c6a21803d20ff3c20803d" + "6a21813d20803d20813d6a21823d20813d20823d6a21833d20823d20833d6a21843d20833d20843d6a21853d20843d" + "20853d6a21863d20853d20863d6a21873d20863d20873d6a21883d20873d20883d6a21893d20883d20893d6a218a3d" + "20893d208a3d6a218b3d208a3d208b3d6a218c3d208b3d208c3d6a218d3d208c3d208d3d6a218e3d208d3d208e3d6a" + "218f3d208e3d208f3d6a21903d208f3d20903d6a21913d20903d20913d6a21923d20913d20923d6a21933d20923d20" + "933d6a21943d20933d20943d6a21953d20943d20953d6a21963d20953d20963d6a21973d20963d20973d6a21983d20" + "973d20983d6a21993d20983d20993d6a219a3d20993d209a3d6a219b3d209a3d209b3d6a219c3d209b3d209c3d6a21" + "9d3d209c3d209d3d6a219e3d209d3d209e3d6a219f3d209e3d209f3d6a21a03d209f3d20a03d6a21a13d20a03d20a1" + "3d6a21a23d20a13d20a23d6a21a33d20a23d20a33d6a21a43d20a33d20a43d6a21a53d20a43d20a53d6a21a63d20a5" + "3d20a63d6a21a73d20a63d20a73d6a21a83d20a73d20a83d6a21a93d20a83d20a93d6a21aa3d20a93d20aa3d6a21ab" + "3d20aa3d20ab3d6a21ac3d20ab3d20ac3d6a21ad3d20ac3d20ad3d6a21ae3d20ad3d20ae3d6a21af3d20ae3d20af3d" + "6a21b03d20af3d20b03d6a21b13d20b03d20b13d6a21b23d20b13d20b23d6a21b33d20b23d20b33d6a21b43d20b33d" + "20b43d6a21b53d20b43d20b53d6a21b63d20b53d20b63d6a21b73d20b63d20b73d6a21b83d20b73d20b83d6a21b93d" + "20b83d20b93d6a21ba3d20b93d20ba3d6a21bb3d20ba3d20bb3d6a21bc3d20bb3d20bc3d6a21bd3d20bc3d20bd3d6a" + "21be3d20bd3d20be3d6a21bf3d20be3d20bf3d6a21c03d20bf3d20c03d6a21c13d20c03d20c13d6a21c23d20c13d20" + "c23d6a21c33d20c23d20c33d6a21c43d20c33d20c43d6a21c53d20c43d20c53d6a21c63d20c53d20c63d6a21c73d20" + "c63d20c73d6a21c83d20c73d20c83d6a21c93d20c83d20c93d6a21ca3d20c93d20ca3d6a21cb3d20ca3d20cb3d6a21" + "cc3d20cb3d20cc3d6a21cd3d20cc3d20cd3d6a21ce3d20cd3d20ce3d6a21cf3d20ce3d20cf3d6a21d03d20cf3d20d0" + "3d6a21d13d20d03d20d13d6a21d23d20d13d20d23d6a21d33d20d23d20d33d6a21d43d20d33d20d43d6a21d53d20d4" + "3d20d53d6a21d63d20d53d20d63d6a21d73d20d63d20d73d6a21d83d20d73d20d83d6a21d93d20d83d20d93d6a21da" + "3d20d93d20da3d6a21db3d20da3d20db3d6a21dc3d20db3d20dc3d6a21dd3d20dc3d20dd3d6a21de3d20dd3d20de3d" + "6a21df3d20de3d20df3d6a21e03d20df3d20e03d6a21e13d20e03d20e13d6a21e23d20e13d20e23d6a21e33d20e23d" + "20e33d6a21e43d20e33d20e43d6a21e53d20e43d20e53d6a21e63d20e53d20e63d6a21e73d20e63d20e73d6a21e83d" + "20e73d20e83d6a21e93d20e83d20e93d6a21ea3d20e93d20ea3d6a21eb3d20ea3d20eb3d6a21ec3d20eb3d20ec3d6a" + "21ed3d20ec3d20ed3d6a21ee3d20ed3d20ee3d6a21ef3d20ee3d20ef3d6a21f03d20ef3d20f03d6a21f13d20f03d20" + "f13d6a21f23d20f13d20f23d6a21f33d20f23d20f33d6a21f43d20f33d20f43d6a21f53d20f43d20f53d6a21f63d20" + "f53d20f63d6a21f73d20f63d20f73d6a21f83d20f73d20f83d6a21f93d20f83d20f93d6a21fa3d20f93d20fa3d6a21" + "fb3d20fa3d20fb3d6a21fc3d20fb3d20fc3d6a21fd3d20fc3d20fd3d6a21fe3d20fd3d20fe3d6a21ff3d20fe3d20ff" + "3d6a21803e20ff3d20803e6a21813e20803e20813e6a21823e20813e20823e6a21833e20823e20833e6a21843e2083" + "3e20843e6a21853e20843e20853e6a21863e20853e20863e6a21873e20863e20873e6a21883e20873e20883e6a2189" + "3e20883e20893e6a218a3e20893e208a3e6a218b3e208a3e208b3e6a218c3e208b3e208c3e6a218d3e208c3e208d3e" + "6a218e3e208d3e208e3e6a218f3e208e3e208f3e6a21903e208f3e20903e6a21913e20903e20913e6a21923e20913e" + "20923e6a21933e20923e20933e6a21943e20933e20943e6a21953e20943e20953e6a21963e20953e20963e6a21973e" + "20963e20973e6a21983e20973e20983e6a21993e20983e20993e6a219a3e20993e209a3e6a219b3e209a3e209b3e6a" + "219c3e209b3e209c3e6a219d3e209c3e209d3e6a219e3e209d3e209e3e6a219f3e209e3e209f3e6a21a03e209f3e20" + "a03e6a21a13e20a03e20a13e6a21a23e20a13e20a23e6a21a33e20a23e20a33e6a21a43e20a33e20a43e6a21a53e20" + "a43e20a53e6a21a63e20a53e20a63e6a21a73e20a63e20a73e6a21a83e20a73e20a83e6a21a93e20a83e20a93e6a21" + "aa3e20a93e20aa3e6a21ab3e20aa3e20ab3e6a21ac3e20ab3e20ac3e6a21ad3e20ac3e20ad3e6a21ae3e20ad3e20ae" + "3e6a21af3e20ae3e20af3e6a21b03e20af3e20b03e6a21b13e20b03e20b13e6a21b23e20b13e20b23e6a21b33e20b2" + "3e20b33e6a21b43e20b33e20b43e6a21b53e20b43e20b53e6a21b63e20b53e20b63e6a21b73e20b63e20b73e6a21b8" + "3e20b73e20b83e6a21b93e20b83e20b93e6a21ba3e20b93e20ba3e6a21bb3e20ba3e20bb3e6a21bc3e20bb3e20bc3e" + "6a21bd3e20bc3e20bd3e6a21be3e20bd3e20be3e6a21bf3e20be3e20bf3e6a21c03e20bf3e20c03e6a21c13e20c03e" + "20c13e6a21c23e20c13e20c23e6a21c33e20c23e20c33e6a21c43e20c33e20c43e6a21c53e20c43e20c53e6a21c63e" + "20c53e20c63e6a21c73e20c63e20c73e6a21c83e20c73e20c83e6a21c93e20c83e20c93e6a21ca3e20c93e20ca3e6a" + "21cb3e20ca3e20cb3e6a21cc3e20cb3e20cc3e6a21cd3e20cc3e20cd3e6a21ce3e20cd3e20ce3e6a21cf3e20ce3e20" + "cf3e6a21d03e20cf3e20d03e6a21d13e20d03e20d13e6a21d23e20d13e20d23e6a21d33e20d23e20d33e6a21d43e20" + "d33e20d43e6a21d53e20d43e20d53e6a21d63e20d53e20d63e6a21d73e20d63e20d73e6a21d83e20d73e20d83e6a21" + "d93e20d83e20d93e6a21da3e20d93e20da3e6a21db3e20da3e20db3e6a21dc3e20db3e20dc3e6a21dd3e20dc3e20dd" + "3e6a21de3e20dd3e20de3e6a21df3e20de3e20df3e6a21e03e20df3e20e03e6a21e13e20e03e20e13e6a21e23e20e1" + "3e20e23e6a21e33e20e23e20e33e6a21e43e20e33e20e43e6a21e53e20e43e20e53e6a21e63e20e53e20e63e6a21e7" + "3e20e63e20e73e6a21e83e20e73e20e83e6a21e93e20e83e20e93e6a21ea3e20e93e20ea3e6a21eb3e20ea3e20eb3e" + "6a21ec3e20eb3e20ec3e6a21ed3e20ec3e20ed3e6a21ee3e20ed3e20ee3e6a21ef3e20ee3e20ef3e6a21f03e20ef3e" + "20f03e6a21f13e20f03e20f13e6a21f23e20f13e20f23e6a21f33e20f23e20f33e6a21f43e20f33e20f43e6a21f53e" + "20f43e20f53e6a21f63e20f53e20f63e6a21f73e20f63e20f73e6a21f83e20f73e20f83e6a21f93e20f83e20f93e6a" + "21fa3e20f93e20fa3e6a21fb3e20fa3e20fb3e6a21fc3e20fb3e20fc3e6a21fd3e20fc3e20fd3e6a21fe3e20fd3e20" + "fe3e6a21ff3e20fe3e20ff3e6a21803f20ff3e20803f6a21813f20803f20813f6a21823f20813f20823f6a21833f20" + "823f20833f6a21843f20833f20843f6a21853f20843f20853f6a21863f20853f20863f6a21873f20863f20873f6a21" + "883f20873f20883f6a21893f20883f20893f6a218a3f20893f208a3f6a218b3f208a3f208b3f6a218c3f208b3f208c" + "3f6a218d3f208c3f208d3f6a218e3f208d3f208e3f6a218f3f208e3f208f3f6a21903f208f3f20903f6a21913f2090" + "3f20913f6a21923f20913f20923f6a21933f20923f20933f6a21943f20933f20943f6a21953f20943f20953f6a2196" + "3f20953f20963f6a21973f20963f20973f6a21983f20973f20983f6a21993f20983f20993f6a219a3f20993f209a3f" + "6a219b3f209a3f209b3f6a219c3f209b3f209c3f6a219d3f209c3f209d3f6a219e3f209d3f209e3f6a219f3f209e3f" + "209f3f6a21a03f209f3f20a03f6a21a13f20a03f20a13f6a21a23f20a13f20a23f6a21a33f20a23f20a33f6a21a43f" + "20a33f20a43f6a21a53f20a43f20a53f6a21a63f20a53f20a63f6a21a73f20a63f20a73f6a21a83f20a73f20a83f6a" + "21a93f20a83f20a93f6a21aa3f20a93f20aa3f6a21ab3f20aa3f20ab3f6a21ac3f20ab3f20ac3f6a21ad3f20ac3f20" + "ad3f6a21ae3f20ad3f20ae3f6a21af3f20ae3f20af3f6a21b03f20af3f20b03f6a21b13f20b03f20b13f6a21b23f20" + "b13f20b23f6a21b33f20b23f20b33f6a21b43f20b33f20b43f6a21b53f20b43f20b53f6a21b63f20b53f20b63f6a21" + "b73f20b63f20b73f6a21b83f20b73f20b83f6a21b93f20b83f20b93f6a21ba3f20b93f20ba3f6a21bb3f20ba3f20bb" + "3f6a21bc3f20bb3f20bc3f6a21bd3f20bc3f20bd3f6a21be3f20bd3f20be3f6a21bf3f20be3f20bf3f6a21c03f20bf" + "3f20c03f6a21c13f20c03f20c13f6a21c23f20c13f20c23f6a21c33f20c23f20c33f6a21c43f20c33f20c43f6a21c5" + "3f20c43f20c53f6a21c63f20c53f20c63f6a21c73f20c63f20c73f6a21c83f20c73f20c83f6a21c93f20c83f20c93f" + "6a21ca3f20c93f20ca3f6a21cb3f20ca3f20cb3f6a21cc3f20cb3f20cc3f6a21cd3f20cc3f20cd3f6a21ce3f20cd3f" + "20ce3f6a21cf3f20ce3f20cf3f6a21d03f20cf3f20d03f6a21d13f20d03f20d13f6a21d23f20d13f20d23f6a21d33f" + "20d23f20d33f6a21d43f20d33f20d43f6a21d53f20d43f20d53f6a21d63f20d53f20d63f6a21d73f20d63f20d73f6a" + "21d83f20d73f20d83f6a21d93f20d83f20d93f6a21da3f20d93f20da3f6a21db3f20da3f20db3f6a21dc3f20db3f20" + "dc3f6a21dd3f20dc3f20dd3f6a21de3f20dd3f20de3f6a21df3f20de3f20df3f6a21e03f20df3f20e03f6a21e13f20" + "e03f20e13f6a21e23f20e13f20e23f6a21e33f20e23f20e33f6a21e43f20e33f20e43f6a21e53f20e43f20e53f6a21" + "e63f20e53f20e63f6a21e73f20e63f20e73f6a21e83f20e73f20e83f6a21e93f20e83f20e93f6a21ea3f20e93f20ea" + "3f6a21eb3f20ea3f20eb3f6a21ec3f20eb3f20ec3f6a21ed3f20ec3f20ed3f6a21ee3f20ed3f20ee3f6a21ef3f20ee" + "3f20ef3f6a21f03f20ef3f20f03f6a21f13f20f03f20f13f6a21f23f20f13f20f23f6a21f33f20f23f20f33f6a21f4" + "3f20f33f20f43f6a21f53f20f43f20f53f6a21f63f20f53f20f63f6a21f73f20f63f20f73f6a21f83f20f73f20f83f" + "6a21f93f20f83f20f93f6a21fa3f20f93f20fa3f6a21fb3f20fa3f20fb3f6a21fc3f20fb3f20fc3f6a21fd3f20fc3f" + "20fd3f6a21fe3f20fd3f20fe3f6a21ff3f20fe3f20ff3f6a21804020ff3f2080406a2181402080402081406a218240" + "2081402082406a2183402082402083406a2184402083402084406a2185402084402085406a2186402085402086406a" + "2187402086402087406a2188402087402088406a2189402088402089406a218a40208940208a406a218b40208a4020" + "8b406a218c40208b40208c406a218d40208c40208d406a218e40208d40208e406a218f40208e40208f406a21904020" + "8f402090406a2191402090402091406a2192402091402092406a2193402092402093406a2194402093402094406a21" + "95402094402095406a2196402095402096406a2197402096402097406a2198402097402098406a2199402098402099" + "406a219a40209940209a406a219b40209a40209b406a219c40209b40209c406a219d40209c40209d406a219e40209d" + "40209e406a219f40209e40209f406a21a040209f4020a0406a21a14020a04020a1406a21a24020a14020a2406a21a3" + "4020a24020a3406a21a44020a34020a4406a21a54020a44020a5406a21a64020a54020a6406a21a74020a64020a740" + "6a21a84020a74020a8406a21a94020a84020a9406a21aa4020a94020aa406a21ab4020aa4020ab406a21ac4020ab40" + "20ac406a21ad4020ac4020ad406a21ae4020ad4020ae406a21af4020ae4020af406a21b04020af4020b0406a21b140" + "20b04020b1406a21b24020b14020b2406a21b34020b24020b3406a21b44020b34020b4406a21b54020b44020b5406a" + "21b64020b54020b6406a21b74020b64020b7406a21b84020b74020b8406a21b94020b84020b9406a21ba4020b94020" + "ba406a21bb4020ba4020bb406a21bc4020bb4020bc406a21bd4020bc4020bd406a21be4020bd4020be406a21bf4020" + "be4020bf406a21c04020bf4020c0406a21c14020c04020c1406a21c24020c14020c2406a21c34020c24020c3406a21" + "c44020c34020c4406a21c54020c44020c5406a21c64020c54020c6406a21c74020c64020c7406a21c84020c74020c8" + "406a21c94020c84020c9406a21ca4020c94020ca406a21cb4020ca4020cb406a21cc4020cb4020cc406a21cd4020cc" + "4020cd406a21ce4020cd4020ce406a21cf4020ce4020cf406a21d04020cf4020d0406a21d14020d04020d1406a21d2" + "4020d14020d2406a21d34020d24020d3406a21d44020d34020d4406a21d54020d44020d5406a21d64020d54020d640" + "6a21d74020d64020d7406a21d84020d74020d8406a21d94020d84020d9406a21da4020d94020da406a21db4020da40" + "20db406a21dc4020db4020dc406a21dd4020dc4020dd406a21de4020dd4020de406a21df4020de4020df406a21e040" + "20df4020e0406a21e14020e04020e1406a21e24020e14020e2406a21e34020e24020e3406a21e44020e34020e4406a" + "21e54020e44020e5406a21e64020e54020e6406a21e74020e64020e7406a21e84020e74020e8406a21e94020e84020" + "e9406a21ea4020e94020ea406a21eb4020ea4020eb406a21ec4020eb4020ec406a21ed4020ec4020ed406a21ee4020" + "ed4020ee406a21ef4020ee4020ef406a21f04020ef4020f0406a21f14020f04020f1406a21f24020f14020f2406a21" + "f34020f24020f3406a21f44020f34020f4406a21f54020f44020f5406a21f64020f54020f6406a21f74020f64020f7" + "406a21f84020f74020f8406a21f94020f84020f9406a21fa4020f94020fa406a21fb4020fa4020fb406a21fc4020fb" + "4020fc406a21fd4020fc4020fd406a21fe4020fd4020fe406a21ff4020fe4020ff406a21804120ff402080416a2181" + "412080412081416a2182412081412082416a2183412082412083416a2184412083412084416a218541208441208541" + "6a2186412085412086416a2187412086412087416a2188412087412088416a2189412088412089416a218a41208941" + "208a416a218b41208a41208b416a218c41208b41208c416a218d41208c41208d416a218e41208d41208e416a218f41" + "208e41208f416a219041208f412090416a2191412090412091416a2192412091412092416a2193412092412093416a" + "2194412093412094416a2195412094412095416a2196412095412096416a2197412096412097416a21984120974120" + "98416a2199412098412099416a219a41209941209a416a219b41209a41209b416a219c41209b41209c416a219d4120" + "9c41209d416a219e41209d41209e416a219f41209e41209f416a21a041209f4120a0416a21a14120a04120a1416a21" + "a24120a14120a2416a21a34120a24120a3416a21a44120a34120a4416a21a54120a44120a5416a21a64120a54120a6" + "416a21a74120a64120a7416a21a84120a74120a8416a21a94120a84120a9416a21aa4120a94120aa416a21ab4120aa" + "4120ab416a21ac4120ab4120ac416a21ad4120ac4120ad416a21ae4120ad4120ae416a21af4120ae4120af416a21b0" + "4120af4120b0416a21b14120b04120b1416a21b24120b14120b2416a21b34120b24120b3416a21b44120b34120b441" + "6a21b54120b44120b5416a21b64120b54120b6416a21b74120b64120b7416a21b84120b74120b8416a21b94120b841" + "20b9416a21ba4120b94120ba416a21bb4120ba4120bb416a21bc4120bb4120bc416a21bd4120bc4120bd416a21be41" + "20bd4120be416a21bf4120be4120bf416a21c04120bf4120c0416a21c14120c04120c1416a21c24120c14120c2416a" + "21c34120c24120c3416a21c44120c34120c4416a21c54120c44120c5416a21c64120c54120c6416a21c74120c64120" + "c7416a21c84120c74120c8416a21c94120c84120c9416a21ca4120c94120ca416a21cb4120ca4120cb416a21cc4120" + "cb4120cc416a21cd4120cc4120cd416a21ce4120cd4120ce416a21cf4120ce4120cf416a21d04120cf4120d0416a21" + "d14120d04120d1416a21d24120d14120d2416a21d34120d24120d3416a21d44120d34120d4416a21d54120d44120d5" + "416a21d64120d54120d6416a21d74120d64120d7416a21d84120d74120d8416a21d94120d84120d9416a21da4120d9" + "4120da416a21db4120da4120db416a21dc4120db4120dc416a21dd4120dc4120dd416a21de4120dd4120de416a21df" + "4120de4120df416a21e04120df4120e0416a21e14120e04120e1416a21e24120e14120e2416a21e34120e24120e341" + "6a21e44120e34120e4416a21e54120e44120e5416a21e64120e54120e6416a21e74120e64120e7416a21e84120e741" + "20e8416a21e94120e84120e9416a21ea4120e94120ea416a21eb4120ea4120eb416a21ec4120eb4120ec416a21ed41" + "20ec4120ed416a21ee4120ed4120ee416a21ef4120ee4120ef416a21f04120ef4120f0416a21f14120f04120f1416a" + "21f24120f14120f2416a21f34120f24120f3416a21f44120f34120f4416a21f54120f44120f5416a21f64120f54120" + "f6416a21f74120f64120f7416a21f84120f74120f8416a21f94120f84120f9416a21fa4120f94120fa416a21fb4120" + "fa4120fb416a21fc4120fb4120fc416a21fd4120fc4120fd416a21fe4120fd4120fe416a21ff4120fe4120ff416a21" + "804220ff412080426a2181422080422081426a2182422081422082426a2183422082422083426a2184422083422084" + "426a2185422084422085426a2186422085422086426a2187422086422087426a2188422087422088426a2189422088" + "422089426a218a42208942208a426a218b42208a42208b426a218c42208b42208c426a218d42208c42208d426a218e" + "42208d42208e426a218f42208e42208f426a219042208f422090426a2191422090422091426a219242209142209242" + "6a2193422092422093426a2194422093422094426a2195422094422095426a2196422095422096426a219742209642" + "2097426a2198422097422098426a2199422098422099426a219a42209942209a426a219b42209a42209b426a219c42" + "209b42209c426a219d42209c42209d426a219e42209d42209e426a219f42209e42209f426a21a042209f4220a0426a" + "21a14220a04220a1426a21a24220a14220a2426a21a34220a24220a3426a21a44220a34220a4426a21a54220a44220" + "a5426a21a64220a54220a6426a21a74220a64220a7426a21a84220a74220a8426a21a94220a84220a9426a21aa4220" + "a94220aa426a21ab4220aa4220ab426a21ac4220ab4220ac426a21ad4220ac4220ad426a21ae4220ad4220ae426a21" + "af4220ae4220af426a21b04220af4220b0426a21b14220b04220b1426a21b24220b14220b2426a21b34220b24220b3" + "426a21b44220b34220b4426a21b54220b44220b5426a21b64220b54220b6426a21b74220b64220b7426a21b84220b7" + "4220b8426a21b94220b84220b9426a21ba4220b94220ba426a21bb4220ba4220bb426a21bc4220bb4220bc426a21bd" + "4220bc4220bd426a21be4220bd4220be426a21bf4220be4220bf426a21c04220bf4220c0426a21c14220c04220c142" + "6a21c24220c14220c2426a21c34220c24220c3426a21c44220c34220c4426a21c54220c44220c5426a21c64220c542" + "20c6426a21c74220c64220c7426a21c84220c74220c8426a21c94220c84220c9426a21ca4220c94220ca426a21cb42" + "20ca4220cb426a21cc4220cb4220cc426a21cd4220cc4220cd426a21ce4220cd4220ce426a21cf4220ce4220cf426a" + "21d04220cf4220d0426a21d14220d04220d1426a21d24220d14220d2426a21d34220d24220d3426a21d44220d34220" + "d4426a21d54220d44220d5426a21d64220d54220d6426a21d74220d64220d7426a21d84220d74220d8426a21d94220" + "d84220d9426a21da4220d94220da426a21db4220da4220db426a21dc4220db4220dc426a21dd4220dc4220dd426a21" + "de4220dd4220de426a21df4220de4220df426a21e04220df4220e0426a21e14220e04220e1426a21e24220e14220e2" + "426a21e34220e24220e3426a21e44220e34220e4426a21e54220e44220e5426a21e64220e54220e6426a21e74220e6" + "4220e7426a21e84220e74220e8426a21e94220e84220e9426a21ea4220e94220ea426a21eb4220ea4220eb426a21ec" + "4220eb4220ec426a21ed4220ec4220ed426a21ee4220ed4220ee426a21ef4220ee4220ef426a21f04220ef4220f042" + "6a21f14220f04220f1426a21f24220f14220f2426a21f34220f24220f3426a21f44220f34220f4426a21f54220f442" + "20f5426a21f64220f54220f6426a21f74220f64220f7426a21f84220f74220f8426a21f94220f84220f9426a21fa42" + "20f94220fa426a21fb4220fa4220fb426a21fc4220fb4220fc426a21fd4220fc4220fd426a21fe4220fd4220fe426a" + "21ff4220fe4220ff426a21804320ff422080436a2181432080432081436a2182432081432082436a21834320824320" + "83436a2184432083432084436a2185432084432085436a2186432085432086436a2187432086432087436a21884320" + "87432088436a2189432088432089436a218a43208943208a436a218b43208a43208b436a218c43208b43208c436a21" + "8d43208c43208d436a218e43208d43208e436a218f43208e43208f436a219043208f432090436a2191432090432091" + "436a2192432091432092436a2193432092432093436a2194432093432094436a2195432094432095436a2196432095" + "432096436a2197432096432097436a2198432097432098436a2199432098432099436a219a43209943209a436a219b" + "43209a43209b436a219c43209b43209c436a219d43209c43209d436a219e43209d43209e436a219f43209e43209f43" + "6a21a043209f4320a0436a21a14320a04320a1436a21a24320a14320a2436a21a34320a24320a3436a21a44320a343" + "20a4436a21a54320a44320a5436a21a64320a54320a6436a21a74320a64320a7436a21a84320a74320a8436a21a943" + "20a84320a9436a21aa4320a94320aa436a21ab4320aa4320ab436a21ac4320ab4320ac436a21ad4320ac4320ad436a" + "21ae4320ad4320ae436a21af4320ae4320af436a21b04320af4320b0436a21b14320b04320b1436a21b24320b14320" + "b2436a21b34320b24320b3436a21b44320b34320b4436a21b54320b44320b5436a21b64320b54320b6436a21b74320" + "b64320b7436a21b84320b74320b8436a21b94320b84320b9436a21ba4320b94320ba436a21bb4320ba4320bb436a21" + "bc4320bb4320bc436a21bd4320bc4320bd436a21be4320bd4320be436a21bf4320be4320bf436a21c04320bf4320c0" + "436a21c14320c04320c1436a21c24320c14320c2436a21c34320c24320c3436a21c44320c34320c4436a21c54320c4" + "4320c5436a21c64320c54320c6436a21c74320c64320c7436a21c84320c74320c8436a21c94320c84320c9436a21ca" + "4320c94320ca436a21cb4320ca4320cb436a21cc4320cb4320cc436a21cd4320cc4320cd436a21ce4320cd4320ce43" + "6a21cf4320ce4320cf436a21d04320cf4320d0436a21d14320d04320d1436a21d24320d14320d2436a21d34320d243" + "20d3436a21d44320d34320d4436a21d54320d44320d5436a21d64320d54320d6436a21d74320d64320d7436a21d843" + "20d74320d8436a21d94320d84320d9436a21da4320d94320da436a21db4320da4320db436a21dc4320db4320dc436a" + "21dd4320dc4320dd436a21de4320dd4320de436a21df4320de4320df436a21e04320df4320e0436a21e14320e04320" + "e1436a21e24320e14320e2436a21e34320e24320e3436a21e44320e34320e4436a21e54320e44320e5436a21e64320" + "e54320e6436a21e74320e64320e7436a21e84320e74320e8436a21e94320e84320e9436a21ea4320e94320ea436a21" + "eb4320ea4320eb436a21ec4320eb4320ec436a21ed4320ec4320ed436a21ee4320ed4320ee436a21ef4320ee4320ef" + "436a21f04320ef4320f0436a21f14320f04320f1436a21f24320f14320f2436a21f34320f24320f3436a21f44320f3" + "4320f4436a21f54320f44320f5436a21f64320f54320f6436a21f74320f64320f7436a21f84320f74320f8436a21f9" + "4320f84320f9436a21fa4320f94320fa436a21fb4320fa4320fb436a21fc4320fb4320fc436a21fd4320fc4320fd43" + "6a21fe4320fd4320fe436a21ff4320fe4320ff436a21804420ff432080446a2181442080442081446a218244208144" + "2082446a2183442082442083446a2184442083442084446a2185442084442085446a2186442085442086446a218744" + "2086442087446a2188442087442088446a2189442088442089446a218a44208944208a446a218b44208a44208b446a" + "218c44208b44208c446a218d44208c44208d446a218e44208d44208e446a218f44208e44208f446a219044208f4420" + "90446a2191442090442091446a2192442091442092446a2193442092442093446a2194442093442094446a21954420" + "94442095446a2196442095442096446a2197442096442097446a2198442097442098446a2199442098442099446a21" + "9a44209944209a446a219b44209a44209b446a219c44209b44209c446a219d44209c44209d446a219e44209d44209e" + "446a219f44209e44209f446a21a044209f4420a0446a21a14420a04420a1446a21a24420a14420a2446a21a34420a2" + "4420a3446a21a44420a34420a4446a21a54420a44420a5446a21a64420a54420a6446a21a74420a64420a7446a21a8" + "4420a74420a8446a21a94420a84420a9446a21aa4420a94420aa446a21ab4420aa4420ab446a21ac4420ab4420ac44" + "6a21ad4420ac4420ad446a21ae4420ad4420ae446a21af4420ae4420af446a21b04420af4420b0446a21b14420b044" + "20b1446a21b24420b14420b2446a21b34420b24420b3446a21b44420b34420b4446a21b54420b44420b5446a21b644" + "20b54420b6446a21b74420b64420b7446a21b84420b74420b8446a21b94420b84420b9446a21ba4420b94420ba446a" + "21bb4420ba4420bb446a21bc4420bb4420bc446a21bd4420bc4420bd446a21be4420bd4420be446a21bf4420be4420" + "bf446a21c04420bf4420c0446a21c14420c04420c1446a21c24420c14420c2446a21c34420c24420c3446a21c44420" + "c34420c4446a21c54420c44420c5446a21c64420c54420c6446a21c74420c64420c7446a21c84420c74420c8446a21" + "c94420c84420c9446a21ca4420c94420ca446a21cb4420ca4420cb446a21cc4420cb4420cc446a21cd4420cc4420cd" + "446a21ce4420cd4420ce446a21cf4420ce4420cf446a21d04420cf4420d0446a21d14420d04420d1446a21d24420d1" + "4420d2446a21d34420d24420d3446a21d44420d34420d4446a21d54420d44420d5446a21d64420d54420d6446a21d7" + "4420d64420d7446a21d84420d74420d8446a21d94420d84420d9446a21da4420d94420da446a21db4420da4420db44" + "6a21dc4420db4420dc446a21dd4420dc4420dd446a21de4420dd4420de446a21df4420de4420df446a21e04420df44" + "20e0446a21e14420e04420e1446a21e24420e14420e2446a21e34420e24420e3446a21e44420e34420e4446a21e544" + "20e44420e5446a21e64420e54420e6446a21e74420e64420e7446a21e84420e74420e8446a21e94420e84420e9446a" + "21ea4420e94420ea446a21eb4420ea4420eb446a21ec4420eb4420ec446a21ed4420ec4420ed446a21ee4420ed4420" + "ee446a21ef4420ee4420ef446a21f04420ef4420f0446a21f14420f04420f1446a21f24420f14420f2446a21f34420" + "f24420f3446a21f44420f34420f4446a21f54420f44420f5446a21f64420f54420f6446a21f74420f64420f7446a21" + "f84420f74420f8446a21f94420f84420f9446a21fa4420f94420fa446a21fb4420fa4420fb446a21fc4420fb4420fc" + "446a21fd4420fc4420fd446a21fe4420fd4420fe446a21ff4420fe4420ff446a21804520ff442080456a2181452080" + "452081456a2182452081452082456a2183452082452083456a2184452083452084456a2185452084452085456a2186" + "452085452086456a2187452086452087456a2188452087452088456a2189452088452089456a218a45208945208a45" + "6a218b45208a45208b456a218c45208b45208c456a218d45208c45208d456a218e45208d45208e456a218f45208e45" + "208f456a219045208f452090456a2191452090452091456a2192452091452092456a2193452092452093456a219445" + "2093452094456a2195452094452095456a2196452095452096456a2197452096452097456a2198452097452098456a" + "2199452098452099456a219a45209945209a456a219b45209a45209b456a219c45209b45209c456a219d45209c4520" + "9d456a219e45209d45209e456a219f45209e45209f456a21a045209f4520a0456a21a14520a04520a1456a21a24520" + "a14520a2456a21a34520a24520a3456a21a44520a34520a4456a21a54520a44520a5456a21a64520a54520a6456a21" + "a74520a64520a7456a21a84520a74520a8456a21a94520a84520a9456a21aa4520a94520aa456a21ab4520aa4520ab" + "456a21ac4520ab4520ac456a21ad4520ac4520ad456a21ae4520ad4520ae456a21af4520ae4520af456a21b04520af" + "4520b0456a21b14520b04520b1456a21b24520b14520b2456a21b34520b24520b3456a21b44520b34520b4456a21b5" + "4520b44520b5456a21b64520b54520b6456a21b74520b64520b7456a21b84520b74520b8456a21b94520b84520b945" + "6a21ba4520b94520ba456a21bb4520ba4520bb456a21bc4520bb4520bc456a21bd4520bc4520bd456a21be4520bd45" + "20be456a21bf4520be4520bf456a21c04520bf4520c0456a21c14520c04520c1456a21c24520c14520c2456a21c345" + "20c24520c3456a21c44520c34520c4456a21c54520c44520c5456a21c64520c54520c6456a21c74520c64520c7456a" + "21c84520c74520c8456a21c94520c84520c9456a21ca4520c94520ca456a21cb4520ca4520cb456a21cc4520cb4520" + "cc456a21cd4520cc4520cd456a21ce4520cd4520ce456a21cf4520ce4520cf456a21d04520cf4520d0456a21d14520" + "d04520d1456a21d24520d14520d2456a21d34520d24520d3456a21d44520d34520d4456a21d54520d44520d5456a21" + "d64520d54520d6456a21d74520d64520d7456a21d84520d74520d8456a21d94520d84520d9456a21da4520d94520da" + "456a21db4520da4520db456a21dc4520db4520dc456a21dd4520dc4520dd456a21de4520dd4520de456a21df4520de" + "4520df456a21e04520df4520e0456a21e14520e04520e1456a21e24520e14520e2456a21e34520e24520e3456a21e4" + "4520e34520e4456a21e54520e44520e5456a21e64520e54520e6456a21e74520e64520e7456a21e84520e74520e845" + "6a21e94520e84520e9456a21ea4520e94520ea456a21eb4520ea4520eb456a21ec4520eb4520ec456a21ed4520ec45" + "20ed456a21ee4520ed4520ee456a21ef4520ee4520ef456a21f04520ef4520f0456a21f14520f04520f1456a21f245" + "20f14520f2456a21f34520f24520f3456a21f44520f34520f4456a21f54520f44520f5456a21f64520f54520f6456a" + "21f74520f64520f7456a21f84520f74520f8456a21f94520f84520f9456a21fa4520f94520fa456a21fb4520fa4520" + "fb456a21fc4520fb4520fc456a21fd4520fc4520fd456a21fe4520fd4520fe456a21ff4520fe4520ff456a21804620" + "ff452080466a2181462080462081466a2182462081462082466a2183462082462083466a2184462083462084466a21" + "85462084462085466a2186462085462086466a2187462086462087466a2188462087462088466a2189462088462089" + "466a218a46208946208a466a218b46208a46208b466a218c46208b46208c466a218d46208c46208d466a218e46208d" + "46208e466a218f46208e46208f466a219046208f462090466a2191462090462091466a2192462091462092466a2193" + "462092462093466a2194462093462094466a2195462094462095466a2196462095462096466a219746209646209746" + "6a2198462097462098466a2199462098462099466a219a46209946209a466a219b46209a46209b466a219c46209b46" + "209c466a219d46209c46209d466a219e46209d46209e466a219f46209e46209f466a21a046209f4620a0466a21a146" + "20a04620a1466a21a24620a14620a2466a21a34620a24620a3466a21a44620a34620a4466a21a54620a44620a5466a" + "21a64620a54620a6466a21a74620a64620a7466a21a84620a74620a8466a21a94620a84620a9466a21aa4620a94620" + "aa466a21ab4620aa4620ab466a21ac4620ab4620ac466a21ad4620ac4620ad466a21ae4620ad4620ae466a21af4620" + "ae4620af466a21b04620af4620b0466a21b14620b04620b1466a21b24620b14620b2466a21b34620b24620b3466a21" + "b44620b34620b4466a21b54620b44620b5466a21b64620b54620b6466a21b74620b64620b7466a21b84620b74620b8" + "466a21b94620b84620b9466a21ba4620b94620ba466a21bb4620ba4620bb466a21bc4620bb4620bc466a21bd4620bc" + "4620bd466a21be4620bd4620be466a21bf4620be4620bf466a21c04620bf4620c0466a21c14620c04620c1466a21c2" + "4620c14620c2466a21c34620c24620c3466a21c44620c34620c4466a21c54620c44620c5466a21c64620c54620c646" + "6a21c74620c64620c7466a21c84620c74620c8466a21c94620c84620c9466a21ca4620c94620ca466a21cb4620ca46" + "20cb466a21cc4620cb4620cc466a21cd4620cc4620cd466a21ce4620cd4620ce466a21cf4620ce4620cf466a21d046" + "20cf4620d0466a21d14620d04620d1466a21d24620d14620d2466a21d34620d24620d3466a21d44620d34620d4466a" + "21d54620d44620d5466a21d64620d54620d6466a21d74620d64620d7466a21d84620d74620d8466a21d94620d84620" + "d9466a21da4620d94620da466a21db4620da4620db466a21dc4620db4620dc466a21dd4620dc4620dd466a21de4620" + "dd4620de466a21df4620de4620df466a21e04620df4620e0466a21e14620e04620e1466a21e24620e14620e2466a21" + "e34620e24620e3466a21e44620e34620e4466a21e54620e44620e5466a21e64620e54620e6466a21e74620e64620e7" + "466a21e84620e74620e8466a21e94620e84620e9466a21ea4620e94620ea466a21eb4620ea4620eb466a21ec4620eb" + "4620ec466a21ed4620ec4620ed466a21ee4620ed4620ee466a21ef4620ee4620ef466a21f04620ef4620f0466a21f1" + "4620f04620f1466a21f24620f14620f2466a21f34620f24620f3466a21f44620f34620f4466a21f54620f44620f546" + "6a21f64620f54620f6466a21f74620f64620f7466a21f84620f74620f8466a21f94620f84620f9466a21fa4620f946" + "20fa466a21fb4620fa4620fb466a21fc4620fb4620fc466a21fd4620fc4620fd466a21fe4620fd4620fe466a21ff46" + "20fe4620ff466a21804720ff462080476a2181472080472081476a2182472081472082476a2183472082472083476a" + "2184472083472084476a2185472084472085476a2186472085472086476a2187472086472087476a21884720874720" + "88476a2189472088472089476a218a47208947208a476a218b47208a47208b476a218c47208b47208c476a218d4720" + "8c47208d476a218e47208d47208e476a218f47208e47208f476a219047208f472090476a2191472090472091476a21" + "92472091472092476a2193472092472093476a2194472093472094476a2195472094472095476a2196472095472096" + "476a2197472096472097476a2198472097472098476a2199472098472099476a219a47209947209a476a219b47209a" + "47209b476a219c47209b47209c476a219d47209c47209d476a219e47209d47209e476a219f47209e47209f476a21a0" + "47209f4720a0476a21a14720a04720a1476a21a24720a14720a2476a21a34720a24720a3476a21a44720a34720a447" + "6a21a54720a44720a5476a21a64720a54720a6476a21a74720a64720a7476a21a84720a74720a8476a21a94720a847" + "20a9476a21aa4720a94720aa476a21ab4720aa4720ab476a21ac4720ab4720ac476a21ad4720ac4720ad476a21ae47" + "20ad4720ae476a21af4720ae4720af476a21b04720af4720b0476a21b14720b04720b1476a21b24720b14720b2476a" + "21b34720b24720b3476a21b44720b34720b4476a21b54720b44720b5476a21b64720b54720b6476a21b74720b64720" + "b7476a21b84720b74720b8476a21b94720b84720b9476a21ba4720b94720ba476a21bb4720ba4720bb476a21bc4720" + "bb4720bc476a21bd4720bc4720bd476a21be4720bd4720be476a21bf4720be4720bf476a21c04720bf4720c0476a21" + "c14720c04720c1476a21c24720c14720c2476a21c34720c24720c3476a21c44720c34720c4476a21c54720c44720c5" + "476a21c64720c54720c6476a21c74720c64720c7476a21c84720c74720c8476a21c94720c84720c9476a21ca4720c9" + "4720ca476a21cb4720ca4720cb476a21cc4720cb4720cc476a21cd4720cc4720cd476a21ce4720cd4720ce476a21cf" + "4720ce4720cf476a21d04720cf4720d0476a21d14720d04720d1476a21d24720d14720d2476a21d34720d24720d347" + "6a21d44720d34720d4476a21d54720d44720d5476a21d64720d54720d6476a21d74720d64720d7476a21d84720d747" + "20d8476a21d94720d84720d9476a21da4720d94720da476a21db4720da4720db476a21dc4720db4720dc476a21dd47" + "20dc4720dd476a21de4720dd4720de476a21df4720de4720df476a21e04720df4720e0476a21e14720e04720e1476a" + "21e24720e14720e2476a21e34720e24720e3476a21e44720e34720e4476a21e54720e44720e5476a21e64720e54720" + "e6476a21e74720e64720e7476a21e84720e74720e8476a21e94720e84720e9476a21ea4720e94720ea476a21eb4720" + "ea4720eb476a21ec4720eb4720ec476a21ed4720ec4720ed476a21ee4720ed4720ee476a21ef4720ee4720ef476a21" + "f04720ef4720f0476a21f14720f04720f1476a21f24720f14720f2476a21f34720f24720f3476a21f44720f34720f4" + "476a21f54720f44720f5476a21f64720f54720f6476a21f74720f64720f7476a21f84720f74720f8476a21f94720f8" + "4720f9476a21fa4720f94720fa476a21fb4720fa4720fb476a21fc4720fb4720fc476a21fd4720fc4720fd476a21fe" + "4720fd4720fe476a21ff4720fe4720ff476a21804820ff472080486a2181482080482081486a218248208148208248" + "6a2183482082482083486a2184482083482084486a2185482084482085486a2186482085482086486a218748208648" + "2087486a2188482087482088486a2189482088482089486a218a48208948208a486a218b48208a48208b486a218c48" + "208b48208c486a218d48208c48208d486a218e48208d48208e486a218f48208e48208f486a219048208f482090486a" + "2191482090482091486a2192482091482092486a2193482092482093486a2194482093482094486a21954820944820" + "95486a2196482095482096486a2197482096482097486a2198482097482098486a2199482098482099486a219a4820" + "9948209a486a219b48209a48209b486a219c48209b48209c486a219d48209c48209d486a219e48209d48209e486a21" + "9f48209e48209f486a21a048209f4820a0486a21a14820a04820a1486a21a24820a14820a2486a21a34820a24820a3" + "486a21a44820a34820a4486a21a54820a44820a5486a21a64820a54820a6486a21a74820a64820a7486a21a84820a7" + "4820a8486a21a94820a84820a9486a21aa4820a94820aa486a21ab4820aa4820ab486a21ac4820ab4820ac486a21ad" + "4820ac4820ad486a21ae4820ad4820ae486a21af4820ae4820af486a21b04820af4820b0486a21b14820b04820b148" + "6a21b24820b14820b2486a21b34820b24820b3486a21b44820b34820b4486a21b54820b44820b5486a21b64820b548" + "20b6486a21b74820b64820b7486a21b84820b74820b8486a21b94820b84820b9486a21ba4820b94820ba486a21bb48" + "20ba4820bb486a21bc4820bb4820bc486a21bd4820bc4820bd486a21be4820bd4820be486a21bf4820be4820bf486a" + "21c04820bf4820c0486a21c14820c04820c1486a21c24820c14820c2486a21c34820c24820c3486a21c44820c34820" + "c4486a21c54820c44820c5486a21c64820c54820c6486a21c74820c64820c7486a21c84820c74820c8486a21c94820" + "c84820c9486a21ca4820c94820ca486a21cb4820ca4820cb486a21cc4820cb4820cc486a21cd4820cc4820cd486a21" + "ce4820cd4820ce486a21cf4820ce4820cf486a21d04820cf4820d0486a21d14820d04820d1486a21d24820d14820d2" + "486a21d34820d24820d3486a21d44820d34820d4486a21d54820d44820d5486a21d64820d54820d6486a21d74820d6" + "4820d7486a21d84820d74820d8486a21d94820d84820d9486a21da4820d94820da486a21db4820da4820db486a21dc" + "4820db4820dc486a21dd4820dc4820dd486a21de4820dd4820de486a21df4820de4820df486a21e04820df4820e048" + "6a21e14820e04820e1486a21e24820e14820e2486a21e34820e24820e3486a21e44820e34820e4486a21e54820e448" + "20e5486a21e64820e54820e6486a21e74820e64820e7486a21e84820e74820e8486a21e94820e84820e9486a21ea48" + "20e94820ea486a21eb4820ea4820eb486a21ec4820eb4820ec486a21ed4820ec4820ed486a21ee4820ed4820ee486a" + "21ef4820ee4820ef486a21f04820ef4820f0486a21f14820f04820f1486a21f24820f14820f2486a21f34820f24820" + "f3486a21f44820f34820f4486a21f54820f44820f5486a21f64820f54820f6486a21f74820f64820f7486a21f84820" + "f74820f8486a21f94820f84820f9486a21fa4820f94820fa486a21fb4820fa4820fb486a21fc4820fb4820fc486a21" + "fd4820fc4820fd486a21fe4820fd4820fe486a21ff4820fe4820ff486a21804920ff482080496a2181492080492081" + "496a2182492081492082496a2183492082492083496a2184492083492084496a2185492084492085496a2186492085" + "492086496a2187492086492087496a2188492087492088496a2189492088492089496a218a49208949208a496a218b" + "49208a49208b496a218c49208b49208c496a218d49208c49208d496a218e49208d49208e496a218f49208e49208f49" + "6a219049208f492090496a2191492090492091496a2192492091492092496a2193492092492093496a219449209349" + "2094496a2195492094492095496a2196492095492096496a2197492096492097496a2198492097492098496a219949" + "2098492099496a219a49209949209a496a219b49209a49209b496a219c49209b49209c496a219d49209c49209d496a" + "219e49209d49209e496a219f49209e49209f496a21a049209f4920a0496a21a14920a04920a1496a21a24920a14920" + "a2496a21a34920a24920a3496a21a44920a34920a4496a21a54920a44920a5496a21a64920a54920a6496a21a74920" + "a64920a7496a21a84920a74920a8496a21a94920a84920a9496a21aa4920a94920aa496a21ab4920aa4920ab496a21" + "ac4920ab4920ac496a21ad4920ac4920ad496a21ae4920ad4920ae496a21af4920ae4920af496a21b04920af4920b0" + "496a21b14920b04920b1496a21b24920b14920b2496a21b34920b24920b3496a21b44920b34920b4496a21b54920b4" + "4920b5496a21b64920b54920b6496a21b74920b64920b7496a21b84920b74920b8496a21b94920b84920b9496a21ba" + "4920b94920ba496a21bb4920ba4920bb496a21bc4920bb4920bc496a21bd4920bc4920bd496a21be4920bd4920be49" + "6a21bf4920be4920bf496a21c04920bf4920c0496a21c14920c04920c1496a21c24920c14920c2496a21c34920c249" + "20c3496a21c44920c34920c4496a21c54920c44920c5496a21c64920c54920c6496a21c74920c64920c7496a21c849" + "20c74920c8496a21c94920c84920c9496a21ca4920c94920ca496a21cb4920ca4920cb496a21cc4920cb4920cc496a" + "21cd4920cc4920cd496a21ce4920cd4920ce496a21cf4920ce4920cf496a21d04920cf4920d0496a21d14920d04920" + "d1496a21d24920d14920d2496a21d34920d24920d3496a21d44920d34920d4496a21d54920d44920d5496a21d64920" + "d54920d6496a21d74920d64920d7496a21d84920d74920d8496a21d94920d84920d9496a21da4920d94920da496a21" + "db4920da4920db496a21dc4920db4920dc496a21dd4920dc4920dd496a21de4920dd4920de496a21df4920de4920df" + "496a21e04920df4920e0496a21e14920e04920e1496a21e24920e14920e2496a21e34920e24920e3496a21e44920e3" + "4920e4496a21e54920e44920e5496a21e64920e54920e6496a21e74920e64920e7496a21e84920e74920e8496a21e9" + "4920e84920e9496a21ea4920e94920ea496a21eb4920ea4920eb496a21ec4920eb4920ec496a21ed4920ec4920ed49" + "6a21ee4920ed4920ee496a21ef4920ee4920ef496a21f04920ef4920f0496a21f14920f04920f1496a21f24920f149" + "20f2496a21f34920f24920f3496a21f44920f34920f4496a21f54920f44920f5496a21f64920f54920f6496a21f749" + "20f64920f7496a21f84920f74920f8496a21f94920f84920f9496a21fa4920f94920fa496a21fb4920fa4920fb496a" + "21fc4920fb4920fc496a21fd4920fc4920fd496a21fe4920fd4920fe496a21ff4920fe4920ff496a21804a20ff4920" + "804a6a21814a20804a20814a6a21824a20814a20824a6a21834a20824a20834a6a21844a20834a20844a6a21854a20" + "844a20854a6a21864a20854a20864a6a21874a20864a20874a6a21884a20874a20884a6a21894a20884a20894a6a21" + "8a4a20894a208a4a6a218b4a208a4a208b4a6a218c4a208b4a208c4a6a218d4a208c4a208d4a6a218e4a208d4a208e" + "4a6a218f4a208e4a208f4a6a21904a208f4a20904a6a21914a20904a20914a6a21924a20914a20924a6a21934a2092" + "4a20934a6a21944a20934a20944a6a21954a20944a20954a6a21964a20954a20964a6a21974a20964a20974a6a2198" + "4a20974a20984a6a21994a20984a20994a6a219a4a20994a209a4a6a219b4a209a4a209b4a6a219c4a209b4a209c4a" + "6a219d4a209c4a209d4a6a219e4a209d4a209e4a6a219f4a209e4a209f4a6a21a04a209f4a20a04a6a21a14a20a04a" + "20a14a6a21a24a20a14a20a24a6a21a34a20a24a20a34a6a21a44a20a34a20a44a6a21a54a20a44a20a54a6a21a64a" + "20a54a20a64a6a21a74a20a64a20a74a6a21a84a20a74a20a84a6a21a94a20a84a20a94a6a21aa4a20a94a20aa4a6a" + "21ab4a20aa4a20ab4a6a21ac4a20ab4a20ac4a6a21ad4a20ac4a20ad4a6a21ae4a20ad4a20ae4a6a21af4a20ae4a20" + "af4a6a21b04a20af4a20b04a6a21b14a20b04a20b14a6a21b24a20b14a20b24a6a21b34a20b24a20b34a6a21b44a20" + "b34a20b44a6a21b54a20b44a20b54a6a21b64a20b54a20b64a6a21b74a20b64a20b74a6a21b84a20b74a20b84a6a21" + "b94a20b84a20b94a6a21ba4a20b94a20ba4a6a21bb4a20ba4a20bb4a6a21bc4a20bb4a20bc4a6a21bd4a20bc4a20bd" + "4a6a21be4a20bd4a20be4a6a21bf4a20be4a20bf4a6a21c04a20bf4a20c04a6a21c14a20c04a20c14a6a21c24a20c1" + "4a20c24a6a21c34a20c24a20c34a6a21c44a20c34a20c44a6a21c54a20c44a20c54a6a21c64a20c54a20c64a6a21c7" + "4a20c64a20c74a6a21c84a20c74a20c84a6a21c94a20c84a20c94a6a21ca4a20c94a20ca4a6a21cb4a20ca4a20cb4a" + "6a21cc4a20cb4a20cc4a6a21cd4a20cc4a20cd4a6a21ce4a20cd4a20ce4a6a21cf4a20ce4a20cf4a6a21d04a20cf4a" + "20d04a6a21d14a20d04a20d14a6a21d24a20d14a20d24a6a21d34a20d24a20d34a6a21d44a20d34a20d44a6a21d54a" + "20d44a20d54a6a21d64a20d54a20d64a6a21d74a20d64a20d74a6a21d84a20d74a20d84a6a21d94a20d84a20d94a6a" + "21da4a20d94a20da4a6a21db4a20da4a20db4a6a21dc4a20db4a20dc4a6a21dd4a20dc4a20dd4a6a21de4a20dd4a20" + "de4a6a21df4a20de4a20df4a6a21e04a20df4a20e04a6a21e14a20e04a20e14a6a21e24a20e14a20e24a6a21e34a20" + "e24a20e34a6a21e44a20e34a20e44a6a21e54a20e44a20e54a6a21e64a20e54a20e64a6a21e74a20e64a20e74a6a21" + "e84a20e74a20e84a6a21e94a20e84a20e94a6a21ea4a20e94a20ea4a6a21eb4a20ea4a20eb4a6a21ec4a20eb4a20ec" + "4a6a21ed4a20ec4a20ed4a6a21ee4a20ed4a20ee4a6a21ef4a20ee4a20ef4a6a21f04a20ef4a20f04a6a21f14a20f0" + "4a20f14a6a21f24a20f14a20f24a6a21f34a20f24a20f34a6a21f44a20f34a20f44a6a21f54a20f44a20f54a6a21f6" + "4a20f54a20f64a6a21f74a20f64a20f74a6a21f84a20f74a20f84a6a21f94a20f84a20f94a6a21fa4a20f94a20fa4a" + "6a21fb4a20fa4a20fb4a6a21fc4a20fb4a20fc4a6a21fd4a20fc4a20fd4a6a21fe4a20fd4a20fe4a6a21ff4a20fe4a" + "20ff4a6a21804b20ff4a20804b6a21814b20804b20814b6a21824b20814b20824b6a21834b20824b20834b6a21844b" + "20834b20844b6a21854b20844b20854b6a21864b20854b20864b6a21874b20864b20874b6a21884b20874b20884b6a" + "21894b20884b20894b6a218a4b20894b208a4b6a218b4b208a4b208b4b6a218c4b208b4b208c4b6a218d4b208c4b20" + "8d4b6a218e4b208d4b208e4b6a218f4b208e4b208f4b6a21904b208f4b20904b6a21914b20904b20914b6a21924b20" + "914b20924b6a21934b20924b20934b6a21944b20934b20944b6a21954b20944b20954b6a21964b20954b20964b6a21" + "974b20964b20974b6a21984b20974b20984b6a21994b20984b20994b6a219a4b20994b209a4b6a219b4b209a4b209b" + "4b6a219c4b209b4b209c4b6a219d4b209c4b209d4b6a219e4b209d4b209e4b6a219f4b209e4b209f4b6a21a04b209f" + "4b20a04b6a21a14b20a04b20a14b6a21a24b20a14b20a24b6a21a34b20a24b20a34b6a21a44b20a34b20a44b6a21a5" + "4b20a44b20a54b6a21a64b20a54b20a64b6a21a74b20a64b20a74b6a21a84b20a74b20a84b6a21a94b20a84b20a94b" + "6a21aa4b20a94b20aa4b6a21ab4b20aa4b20ab4b6a21ac4b20ab4b20ac4b6a21ad4b20ac4b20ad4b6a21ae4b20ad4b" + "20ae4b6a21af4b20ae4b20af4b6a21b04b20af4b20b04b6a21b14b20b04b20b14b6a21b24b20b14b20b24b6a21b34b" + "20b24b20b34b6a21b44b20b34b20b44b6a21b54b20b44b20b54b6a21b64b20b54b20b64b6a21b74b20b64b20b74b6a" + "21b84b20b74b20b84b6a21b94b20b84b20b94b6a21ba4b20b94b20ba4b6a21bb4b20ba4b20bb4b6a21bc4b20bb4b20" + "bc4b6a21bd4b20bc4b20bd4b6a21be4b20bd4b20be4b6a21bf4b20be4b20bf4b6a21c04b20bf4b20c04b6a21c14b20" + "c04b20c14b6a21c24b20c14b20c24b6a21c34b20c24b20c34b6a21c44b20c34b20c44b6a21c54b20c44b20c54b6a21" + "c64b20c54b20c64b6a21c74b20c64b20c74b6a21c84b20c74b20c84b6a21c94b20c84b20c94b6a21ca4b20c94b20ca" + "4b6a21cb4b20ca4b20cb4b6a21cc4b20cb4b20cc4b6a21cd4b20cc4b20cd4b6a21ce4b20cd4b20ce4b6a21cf4b20ce" + "4b20cf4b6a21d04b20cf4b20d04b6a21d14b20d04b20d14b6a21d24b20d14b20d24b6a21d34b20d24b20d34b6a21d4" + "4b20d34b20d44b6a21d54b20d44b20d54b6a21d64b20d54b20d64b6a21d74b20d64b20d74b6a21d84b20d74b20d84b" + "6a21d94b20d84b20d94b6a21da4b20d94b20da4b6a21db4b20da4b20db4b6a21dc4b20db4b20dc4b6a21dd4b20dc4b" + "20dd4b6a21de4b20dd4b20de4b6a21df4b20de4b20df4b6a21e04b20df4b20e04b6a21e14b20e04b20e14b6a21e24b" + "20e14b20e24b6a21e34b20e24b20e34b6a21e44b20e34b20e44b6a21e54b20e44b20e54b6a21e64b20e54b20e64b6a" + "21e74b20e64b20e74b6a21e84b20e74b20e84b6a21e94b20e84b20e94b6a21ea4b20e94b20ea4b6a21eb4b20ea4b20" + "eb4b6a21ec4b20eb4b20ec4b6a21ed4b20ec4b20ed4b6a21ee4b20ed4b20ee4b6a21ef4b20ee4b20ef4b6a21f04b20" + "ef4b20f04b6a21f14b20f04b20f14b6a21f24b20f14b20f24b6a21f34b20f24b20f34b6a21f44b20f34b20f44b6a21" + "f54b20f44b20f54b6a21f64b20f54b20f64b6a21f74b20f64b20f74b6a21f84b20f74b20f84b6a21f94b20f84b20f9" + "4b6a21fa4b20f94b20fa4b6a21fb4b20fa4b20fb4b6a21fc4b20fb4b20fc4b6a21fd4b20fc4b20fd4b6a21fe4b20fd" + "4b20fe4b6a21ff4b20fe4b20ff4b6a21804c20ff4b20804c6a21814c20804c20814c6a21824c20814c20824c6a2183" + "4c20824c20834c6a21844c20834c20844c6a21854c20844c20854c6a21864c20854c20864c6a21874c20864c20874c" + "6a21884c20874c20884c6a21894c20884c20894c6a218a4c20894c208a4c6a218b4c208a4c208b4c6a218c4c208b4c" + "208c4c6a218d4c208c4c208d4c6a218e4c208d4c208e4c6a218f4c208e4c208f4c6a21904c208f4c20904c6a21914c" + "20904c20914c6a21924c20914c20924c6a21934c20924c20934c6a21944c20934c20944c6a21954c20944c20954c6a" + "21964c20954c20964c6a21974c20964c20974c6a21984c20974c20984c6a21994c20984c20994c6a219a4c20994c20" + "9a4c6a219b4c209a4c209b4c6a219c4c209b4c209c4c6a219d4c209c4c209d4c6a219e4c209d4c209e4c6a219f4c20" + "9e4c209f4c6a21a04c209f4c20a04c6a21a14c20a04c20a14c6a21a24c20a14c20a24c6a21a34c20a24c20a34c6a21" + "a44c20a34c20a44c6a21a54c20a44c20a54c6a21a64c20a54c20a64c6a21a74c20a64c20a74c6a21a84c20a74c20a8" + "4c6a21a94c20a84c20a94c6a21aa4c20a94c20aa4c6a21ab4c20aa4c20ab4c6a21ac4c20ab4c20ac4c6a21ad4c20ac" + "4c20ad4c6a21ae4c20ad4c20ae4c6a21af4c20ae4c20af4c6a21b04c20af4c20b04c6a21b14c20b04c20b14c6a21b2" + "4c20b14c20b24c6a21b34c20b24c20b34c6a21b44c20b34c20b44c6a21b54c20b44c20b54c6a21b64c20b54c20b64c" + "6a21b74c20b64c20b74c6a21b84c20b74c20b84c6a21b94c20b84c20b94c6a21ba4c20b94c20ba4c6a21bb4c20ba4c" + "20bb4c6a21bc4c20bb4c20bc4c6a21bd4c20bc4c20bd4c6a21be4c20bd4c20be4c6a21bf4c20be4c20bf4c6a21c04c" + "20bf4c20c04c6a21c14c20c04c20c14c6a21c24c20c14c20c24c6a21c34c20c24c20c34c6a21c44c20c34c20c44c6a" + "21c54c20c44c20c54c6a21c64c20c54c20c64c6a21c74c20c64c20c74c6a21c84c20c74c20c84c6a21c94c20c84c20" + "c94c6a21ca4c20c94c20ca4c6a21cb4c20ca4c20cb4c6a21cc4c20cb4c20cc4c6a21cd4c20cc4c20cd4c6a21ce4c20" + "cd4c20ce4c6a21cf4c20ce4c20cf4c6a21d04c20cf4c20d04c6a21d14c20d04c20d14c6a21d24c20d14c20d24c6a21" + "d34c20d24c20d34c6a21d44c20d34c20d44c6a21d54c20d44c20d54c6a21d64c20d54c20d64c6a21d74c20d64c20d7" + "4c6a21d84c20d74c20d84c6a21d94c20d84c20d94c6a21da4c20d94c20da4c6a21db4c20da4c20db4c6a21dc4c20db" + "4c20dc4c6a21dd4c20dc4c20dd4c6a21de4c20dd4c20de4c6a21df4c20de4c20df4c6a21e04c20df4c20e04c6a21e1" + "4c20e04c20e14c6a21e24c20e14c20e24c6a21e34c20e24c20e34c6a21e44c20e34c20e44c6a21e54c20e44c20e54c" + "6a21e64c20e54c20e64c6a21e74c20e64c20e74c6a21e84c20e74c20e84c6a21e94c20e84c20e94c6a21ea4c20e94c" + "20ea4c6a21eb4c20ea4c20eb4c6a21ec4c20eb4c20ec4c6a21ed4c20ec4c20ed4c6a21ee4c20ed4c20ee4c6a21ef4c" + "20ee4c20ef4c6a21f04c20ef4c20f04c6a21f14c20f04c20f14c6a21f24c20f14c20f24c6a21f34c20f24c20f34c6a" + "21f44c20f34c20f44c6a21f54c20f44c20f54c6a21f64c20f54c20f64c6a21f74c20f64c20f74c6a21f84c20f74c20" + "f84c6a21f94c20f84c20f94c6a21fa4c20f94c20fa4c6a21fb4c20fa4c20fb4c6a21fc4c20fb4c20fc4c6a21fd4c20" + "fc4c20fd4c6a21fe4c20fd4c20fe4c6a21ff4c20fe4c20ff4c6a21804d20ff4c20804d6a21814d20804d20814d6a21" + "824d20814d20824d6a21834d20824d20834d6a21844d20834d20844d6a21854d20844d20854d6a21864d20854d2086" + "4d6a21874d20864d20874d6a21884d20874d20884d6a21894d20884d20894d6a218a4d20894d208a4d6a218b4d208a" + "4d208b4d6a218c4d208b4d208c4d6a218d4d208c4d208d4d6a218e4d208d4d208e4d6a218f4d208e4d208f4d6a2190" + "4d208f4d20904d6a21914d20904d20914d6a21924d20914d20924d6a21934d20924d20934d6a21944d20934d20944d" + "6a21954d20944d20954d6a21964d20954d20964d6a21974d20964d20974d6a21984d20974d20984d6a21994d20984d" + "20994d6a219a4d20994d209a4d6a219b4d209a4d209b4d6a219c4d209b4d209c4d6a219d4d209c4d209d4d6a219e4d" + "209d4d209e4d6a219f4d209e4d209f4d6a21a04d209f4d20a04d6a21a14d20a04d20a14d6a21a24d20a14d20a24d6a" + "21a34d20a24d20a34d6a21a44d20a34d20a44d6a21a54d20a44d20a54d6a21a64d20a54d20a64d6a21a74d20a64d20" + "a74d6a21a84d20a74d20a84d6a21a94d20a84d20a94d6a21aa4d20a94d20aa4d6a21ab4d20aa4d20ab4d6a21ac4d20" + "ab4d20ac4d6a21ad4d20ac4d20ad4d6a21ae4d20ad4d20ae4d6a21af4d20ae4d20af4d6a21b04d20af4d20b04d6a21" + "b14d20b04d20b14d6a21b24d20b14d20b24d6a21b34d20b24d20b34d6a21b44d20b34d20b44d6a21b54d20b44d20b5" + "4d6a21b64d20b54d20b64d6a21b74d20b64d20b74d6a21b84d20b74d20b84d6a21b94d20b84d20b94d6a21ba4d20b9" + "4d20ba4d6a21bb4d20ba4d20bb4d6a21bc4d20bb4d20bc4d6a21bd4d20bc4d20bd4d6a21be4d20bd4d20be4d6a21bf" + "4d20be4d20bf4d6a21c04d20bf4d20c04d6a21c14d20c04d20c14d6a21c24d20c14d20c24d6a21c34d20c24d20c34d" + "6a21c44d20c34d20c44d6a21c54d20c44d20c54d6a21c64d20c54d20c64d6a21c74d20c64d20c74d6a21c84d20c74d" + "20c84d6a21c94d20c84d20c94d6a21ca4d20c94d20ca4d6a21cb4d20ca4d20cb4d6a21cc4d20cb4d20cc4d6a21cd4d" + "20cc4d20cd4d6a21ce4d20cd4d20ce4d6a21cf4d20ce4d20cf4d6a21d04d20cf4d20d04d6a21d14d20d04d20d14d6a" + "21d24d20d14d20d24d6a21d34d20d24d20d34d6a21d44d20d34d20d44d6a21d54d20d44d20d54d6a21d64d20d54d20" + "d64d6a21d74d20d64d20d74d6a21d84d20d74d20d84d6a21d94d20d84d20d94d6a21da4d20d94d20da4d6a21db4d20" + "da4d20db4d6a21dc4d20db4d20dc4d6a21dd4d20dc4d20dd4d6a21de4d20dd4d20de4d6a21df4d20de4d20df4d6a21" + "e04d20df4d20e04d6a21e14d20e04d20e14d6a21e24d20e14d20e24d6a21e34d20e24d20e34d6a21e44d20e34d20e4" + "4d6a21e54d20e44d20e54d6a21e64d20e54d20e64d6a21e74d20e64d20e74d6a21e84d20e74d20e84d6a21e94d20e8" + "4d20e94d6a21ea4d20e94d20ea4d6a21eb4d20ea4d20eb4d6a21ec4d20eb4d20ec4d6a21ed4d20ec4d20ed4d6a21ee" + "4d20ed4d20ee4d6a21ef4d20ee4d20ef4d6a21f04d20ef4d20f04d6a21f14d20f04d20f14d6a21f24d20f14d20f24d" + "6a21f34d20f24d20f34d6a21f44d20f34d20f44d6a21f54d20f44d20f54d6a21f64d20f54d20f64d6a21f74d20f64d" + "20f74d6a21f84d20f74d20f84d6a21f94d20f84d20f94d6a21fa4d20f94d20fa4d6a21fb4d20fa4d20fb4d6a21fc4d" + "20fb4d20fc4d6a21fd4d20fc4d20fd4d6a21fe4d20fd4d20fe4d6a21ff4d20fe4d20ff4d6a21804e20ff4d20804e6a" + "21814e20804e20814e6a21824e20814e20824e6a21834e20824e20834e6a21844e20834e20844e6a21854e20844e20" + "854e6a21864e20854e20864e6a21874e20864e20874e6a21884e20874e20884e6a21894e20884e20894e6a218a4e20" + "894e208a4e6a218b4e208a4e208b4e6a218c4e208b4e208c4e6a218d4e208c4e208d4e6a218e4e208d4e208e4e6a21" + "8f4e208f4e0b"; + +extern std::string const functions5kHex = + "0061736d0100000001070160027f7f017f038a27882700000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000007e2d303882708" + "7465737430303030000008746573743030303100010874657374303030320002087465737430303033000308746573" + "7430303034000408746573743030303500050874657374303030360006087465737430303037000708746573743030" + "303800080874657374303030390009087465737430303130000a087465737430303131000b08746573743030313200" + "0c087465737430303133000d087465737430303134000e087465737430303135000f08746573743030313600100874" + "6573743030313700110874657374303031380012087465737430303139001308746573743030323000140874657374" + "3030323100150874657374303032320016087465737430303233001708746573743030323400180874657374303032" + "350019087465737430303236001a087465737430303237001b087465737430303238001c087465737430303239001d" + "087465737430303330001e087465737430303331001f08746573743030333200200874657374303033330021087465" + "7374303033340022087465737430303335002308746573743030333600240874657374303033370025087465737430" + "3033380026087465737430303339002708746573743030343000280874657374303034310029087465737430303432" + "002a087465737430303433002b087465737430303434002c087465737430303435002d087465737430303436002e08" + "7465737430303437002f08746573743030343800300874657374303034390031087465737430303530003208746573" + "7430303531003308746573743030353200340874657374303035330035087465737430303534003608746573743030" + "3535003708746573743030353600380874657374303035370039087465737430303538003a08746573743030353900" + "3b087465737430303630003c087465737430303631003d087465737430303632003e087465737430303633003f0874" + "6573743030363400400874657374303036350041087465737430303636004208746573743030363700430874657374" + "3030363800440874657374303036390045087465737430303730004608746573743030373100470874657374303037" + "3200480874657374303037330049087465737430303734004a087465737430303735004b087465737430303736004c" + "087465737430303737004d087465737430303738004e087465737430303739004f0874657374303038300050087465" + "7374303038310051087465737430303832005208746573743030383300530874657374303038340054087465737430" + "3038350055087465737430303836005608746573743030383700570874657374303038380058087465737430303839" + "0059087465737430303930005a087465737430303931005b087465737430303932005c087465737430303933005d08" + "7465737430303934005e087465737430303935005f0874657374303039360060087465737430303937006108746573" + "7430303938006208746573743030393900630874657374303130300064087465737430313031006508746573743031" + "3032006608746573743031303300670874657374303130340068087465737430313035006908746573743031303600" + "6a087465737430313037006b087465737430313038006c087465737430313039006d087465737430313130006e0874" + "65737430313131006f0874657374303131320070087465737430313133007108746573743031313400720874657374" + "3031313500730874657374303131360074087465737430313137007508746573743031313800760874657374303131" + "39007708746573743031323000780874657374303132310079087465737430313232007a087465737430313233007b" + "087465737430313234007c087465737430313235007d087465737430313236007e087465737430313237007f087465" + "7374303132380080010874657374303132390081010874657374303133300082010874657374303133310083010874" + "6573743031333200840108746573743031333300850108746573743031333400860108746573743031333500870108" + "7465737430313336008801087465737430313337008901087465737430313338008a01087465737430313339008b01" + "087465737430313430008c01087465737430313431008d01087465737430313432008e01087465737430313433008f" + "0108746573743031343400900108746573743031343500910108746573743031343600920108746573743031343700" + "9301087465737430313438009401087465737430313439009501087465737430313530009601087465737430313531" + "009701087465737430313532009801087465737430313533009901087465737430313534009a010874657374303135" + "35009b01087465737430313536009c01087465737430313537009d01087465737430313538009e0108746573743031" + "3539009f0108746573743031363000a00108746573743031363100a10108746573743031363200a201087465737430" + "31363300a30108746573743031363400a40108746573743031363500a50108746573743031363600a6010874657374" + "3031363700a70108746573743031363800a80108746573743031363900a90108746573743031373000aa0108746573" + "743031373100ab0108746573743031373200ac0108746573743031373300ad0108746573743031373400ae01087465" + "73743031373500af0108746573743031373600b00108746573743031373700b10108746573743031373800b2010874" + "6573743031373900b30108746573743031383000b40108746573743031383100b50108746573743031383200b60108" + "746573743031383300b70108746573743031383400b80108746573743031383500b90108746573743031383600ba01" + "08746573743031383700bb0108746573743031383800bc0108746573743031383900bd0108746573743031393000be" + "0108746573743031393100bf0108746573743031393200c00108746573743031393300c10108746573743031393400" + "c20108746573743031393500c30108746573743031393600c40108746573743031393700c501087465737430313938" + "00c60108746573743031393900c70108746573743032303000c80108746573743032303100c9010874657374303230" + "3200ca0108746573743032303300cb0108746573743032303400cc0108746573743032303500cd0108746573743032" + "303600ce0108746573743032303700cf0108746573743032303800d00108746573743032303900d101087465737430" + "32313000d20108746573743032313100d30108746573743032313200d40108746573743032313300d5010874657374" + "3032313400d60108746573743032313500d70108746573743032313600d80108746573743032313700d90108746573" + "743032313800da0108746573743032313900db0108746573743032323000dc0108746573743032323100dd01087465" + "73743032323200de0108746573743032323300df0108746573743032323400e00108746573743032323500e1010874" + "6573743032323600e20108746573743032323700e30108746573743032323800e40108746573743032323900e50108" + "746573743032333000e60108746573743032333100e70108746573743032333200e80108746573743032333300e901" + "08746573743032333400ea0108746573743032333500eb0108746573743032333600ec0108746573743032333700ed" + "0108746573743032333800ee0108746573743032333900ef0108746573743032343000f00108746573743032343100" + "f10108746573743032343200f20108746573743032343300f30108746573743032343400f401087465737430323435" + "00f50108746573743032343600f60108746573743032343700f70108746573743032343800f8010874657374303234" + "3900f90108746573743032353000fa0108746573743032353100fb0108746573743032353200fc0108746573743032" + "353300fd0108746573743032353400fe0108746573743032353500ff01087465737430323536008002087465737430" + "3235370081020874657374303235380082020874657374303235390083020874657374303236300084020874657374" + "3032363100850208746573743032363200860208746573743032363300870208746573743032363400880208746573" + "7430323635008902087465737430323636008a02087465737430323637008b02087465737430323638008c02087465" + "737430323639008d02087465737430323730008e02087465737430323731008f020874657374303237320090020874" + "6573743032373300910208746573743032373400920208746573743032373500930208746573743032373600940208" + "7465737430323737009502087465737430323738009602087465737430323739009702087465737430323830009802" + "087465737430323831009902087465737430323832009a02087465737430323833009b02087465737430323834009c" + "02087465737430323835009d02087465737430323836009e02087465737430323837009f0208746573743032383800" + "a00208746573743032383900a10208746573743032393000a20208746573743032393100a302087465737430323932" + "00a40208746573743032393300a50208746573743032393400a60208746573743032393500a7020874657374303239" + "3600a80208746573743032393700a90208746573743032393800aa0208746573743032393900ab0208746573743033" + "303000ac0208746573743033303100ad0208746573743033303200ae0208746573743033303300af02087465737430" + "33303400b00208746573743033303500b10208746573743033303600b20208746573743033303700b3020874657374" + "3033303800b40208746573743033303900b50208746573743033313000b60208746573743033313100b70208746573" + "743033313200b80208746573743033313300b90208746573743033313400ba0208746573743033313500bb02087465" + "73743033313600bc0208746573743033313700bd0208746573743033313800be0208746573743033313900bf020874" + "6573743033323000c00208746573743033323100c10208746573743033323200c20208746573743033323300c30208" + "746573743033323400c40208746573743033323500c50208746573743033323600c60208746573743033323700c702" + "08746573743033323800c80208746573743033323900c90208746573743033333000ca0208746573743033333100cb" + "0208746573743033333200cc0208746573743033333300cd0208746573743033333400ce0208746573743033333500" + "cf0208746573743033333600d00208746573743033333700d10208746573743033333800d202087465737430333339" + "00d30208746573743033343000d40208746573743033343100d50208746573743033343200d6020874657374303334" + "3300d70208746573743033343400d80208746573743033343500d90208746573743033343600da0208746573743033" + "343700db0208746573743033343800dc0208746573743033343900dd0208746573743033353000de02087465737430" + "33353100df0208746573743033353200e00208746573743033353300e10208746573743033353400e2020874657374" + "3033353500e30208746573743033353600e40208746573743033353700e50208746573743033353800e60208746573" + "743033353900e70208746573743033363000e80208746573743033363100e90208746573743033363200ea02087465" + "73743033363300eb0208746573743033363400ec0208746573743033363500ed0208746573743033363600ee020874" + "6573743033363700ef0208746573743033363800f00208746573743033363900f10208746573743033373000f20208" + "746573743033373100f30208746573743033373200f40208746573743033373300f50208746573743033373400f602" + "08746573743033373500f70208746573743033373600f80208746573743033373700f90208746573743033373800fa" + "0208746573743033373900fb0208746573743033383000fc0208746573743033383100fd0208746573743033383200" + "fe0208746573743033383300ff02087465737430333834008003087465737430333835008103087465737430333836" + "0082030874657374303338370083030874657374303338380084030874657374303338390085030874657374303339" + "3000860308746573743033393100870308746573743033393200880308746573743033393300890308746573743033" + "3934008a03087465737430333935008b03087465737430333936008c03087465737430333937008d03087465737430" + "333938008e03087465737430333939008f030874657374303430300090030874657374303430310091030874657374" + "3034303200920308746573743034303300930308746573743034303400940308746573743034303500950308746573" + "7430343036009603087465737430343037009703087465737430343038009803087465737430343039009903087465" + "737430343130009a03087465737430343131009b03087465737430343132009c03087465737430343133009d030874" + "65737430343134009e03087465737430343135009f0308746573743034313600a00308746573743034313700a10308" + "746573743034313800a20308746573743034313900a30308746573743034323000a40308746573743034323100a503" + "08746573743034323200a60308746573743034323300a70308746573743034323400a80308746573743034323500a9" + "0308746573743034323600aa0308746573743034323700ab0308746573743034323800ac0308746573743034323900" + "ad0308746573743034333000ae0308746573743034333100af0308746573743034333200b003087465737430343333" + "00b10308746573743034333400b20308746573743034333500b30308746573743034333600b4030874657374303433" + "3700b50308746573743034333800b60308746573743034333900b70308746573743034343000b80308746573743034" + "343100b90308746573743034343200ba0308746573743034343300bb0308746573743034343400bc03087465737430" + "34343500bd0308746573743034343600be0308746573743034343700bf0308746573743034343800c0030874657374" + "3034343900c10308746573743034353000c20308746573743034353100c30308746573743034353200c40308746573" + "743034353300c50308746573743034353400c60308746573743034353500c70308746573743034353600c803087465" + "73743034353700c90308746573743034353800ca0308746573743034353900cb0308746573743034363000cc030874" + "6573743034363100cd0308746573743034363200ce0308746573743034363300cf0308746573743034363400d00308" + "746573743034363500d10308746573743034363600d20308746573743034363700d30308746573743034363800d403" + "08746573743034363900d50308746573743034373000d60308746573743034373100d70308746573743034373200d8" + "0308746573743034373300d90308746573743034373400da0308746573743034373500db0308746573743034373600" + "dc0308746573743034373700dd0308746573743034373800de0308746573743034373900df03087465737430343830" + "00e00308746573743034383100e10308746573743034383200e20308746573743034383300e3030874657374303438" + "3400e40308746573743034383500e50308746573743034383600e60308746573743034383700e70308746573743034" + "383800e80308746573743034383900e90308746573743034393000ea0308746573743034393100eb03087465737430" + "34393200ec0308746573743034393300ed0308746573743034393400ee0308746573743034393500ef030874657374" + "3034393600f00308746573743034393700f10308746573743034393800f20308746573743034393900f30308746573" + "743035303000f40308746573743035303100f50308746573743035303200f60308746573743035303300f703087465" + "73743035303400f80308746573743035303500f90308746573743035303600fa0308746573743035303700fb030874" + "6573743035303800fc0308746573743035303900fd0308746573743035313000fe0308746573743035313100ff0308" + "7465737430353132008004087465737430353133008104087465737430353134008204087465737430353135008304" + "0874657374303531360084040874657374303531370085040874657374303531380086040874657374303531390087" + "04087465737430353230008804087465737430353231008904087465737430353232008a0408746573743035323300" + "8b04087465737430353234008c04087465737430353235008d04087465737430353236008e04087465737430353237" + "008f040874657374303532380090040874657374303532390091040874657374303533300092040874657374303533" + "3100930408746573743035333200940408746573743035333300950408746573743035333400960408746573743035" + "3335009704087465737430353336009804087465737430353337009904087465737430353338009a04087465737430" + "353339009b04087465737430353430009c04087465737430353431009d04087465737430353432009e040874657374" + "30353433009f0408746573743035343400a00408746573743035343500a10408746573743035343600a20408746573" + "743035343700a30408746573743035343800a40408746573743035343900a50408746573743035353000a604087465" + "73743035353100a70408746573743035353200a80408746573743035353300a90408746573743035353400aa040874" + "6573743035353500ab0408746573743035353600ac0408746573743035353700ad0408746573743035353800ae0408" + "746573743035353900af0408746573743035363000b00408746573743035363100b10408746573743035363200b204" + "08746573743035363300b30408746573743035363400b40408746573743035363500b50408746573743035363600b6" + "0408746573743035363700b70408746573743035363800b80408746573743035363900b90408746573743035373000" + "ba0408746573743035373100bb0408746573743035373200bc0408746573743035373300bd04087465737430353734" + "00be0408746573743035373500bf0408746573743035373600c00408746573743035373700c1040874657374303537" + "3800c20408746573743035373900c30408746573743035383000c40408746573743035383100c50408746573743035" + "383200c60408746573743035383300c70408746573743035383400c80408746573743035383500c904087465737430" + "35383600ca0408746573743035383700cb0408746573743035383800cc0408746573743035383900cd040874657374" + "3035393000ce0408746573743035393100cf0408746573743035393200d00408746573743035393300d10408746573" + "743035393400d20408746573743035393500d30408746573743035393600d40408746573743035393700d504087465" + "73743035393800d60408746573743035393900d70408746573743036303000d80408746573743036303100d9040874" + "6573743036303200da0408746573743036303300db0408746573743036303400dc0408746573743036303500dd0408" + "746573743036303600de0408746573743036303700df0408746573743036303800e00408746573743036303900e104" + "08746573743036313000e20408746573743036313100e30408746573743036313200e40408746573743036313300e5" + "0408746573743036313400e60408746573743036313500e70408746573743036313600e80408746573743036313700" + "e90408746573743036313800ea0408746573743036313900eb0408746573743036323000ec04087465737430363231" + "00ed0408746573743036323200ee0408746573743036323300ef0408746573743036323400f0040874657374303632" + "3500f10408746573743036323600f20408746573743036323700f30408746573743036323800f40408746573743036" + "323900f50408746573743036333000f60408746573743036333100f70408746573743036333200f804087465737430" + "36333300f90408746573743036333400fa0408746573743036333500fb0408746573743036333600fc040874657374" + "3036333700fd0408746573743036333800fe0408746573743036333900ff0408746573743036343000800508746573" + "7430363431008105087465737430363432008205087465737430363433008305087465737430363434008405087465" + "7374303634350085050874657374303634360086050874657374303634370087050874657374303634380088050874" + "65737430363439008905087465737430363530008a05087465737430363531008b05087465737430363532008c0508" + "7465737430363533008d05087465737430363534008e05087465737430363535008f05087465737430363536009005" + "0874657374303635370091050874657374303635380092050874657374303635390093050874657374303636300094" + "0508746573743036363100950508746573743036363200960508746573743036363300970508746573743036363400" + "9805087465737430363635009905087465737430363636009a05087465737430363637009b05087465737430363638" + "009c05087465737430363639009d05087465737430363730009e05087465737430363731009f050874657374303637" + "3200a00508746573743036373300a10508746573743036373400a20508746573743036373500a30508746573743036" + "373600a40508746573743036373700a50508746573743036373800a60508746573743036373900a705087465737430" + "36383000a80508746573743036383100a90508746573743036383200aa0508746573743036383300ab050874657374" + "3036383400ac0508746573743036383500ad0508746573743036383600ae0508746573743036383700af0508746573" + "743036383800b00508746573743036383900b10508746573743036393000b20508746573743036393100b305087465" + "73743036393200b40508746573743036393300b50508746573743036393400b60508746573743036393500b7050874" + "6573743036393600b80508746573743036393700b90508746573743036393800ba0508746573743036393900bb0508" + "746573743037303000bc0508746573743037303100bd0508746573743037303200be0508746573743037303300bf05" + "08746573743037303400c00508746573743037303500c10508746573743037303600c20508746573743037303700c3" + "0508746573743037303800c40508746573743037303900c50508746573743037313000c60508746573743037313100" + "c70508746573743037313200c80508746573743037313300c90508746573743037313400ca05087465737430373135" + "00cb0508746573743037313600cc0508746573743037313700cd0508746573743037313800ce050874657374303731" + "3900cf0508746573743037323000d00508746573743037323100d10508746573743037323200d20508746573743037" + "323300d30508746573743037323400d40508746573743037323500d50508746573743037323600d605087465737430" + "37323700d70508746573743037323800d80508746573743037323900d90508746573743037333000da050874657374" + "3037333100db0508746573743037333200dc0508746573743037333300dd0508746573743037333400de0508746573" + "743037333500df0508746573743037333600e00508746573743037333700e10508746573743037333800e205087465" + "73743037333900e30508746573743037343000e40508746573743037343100e50508746573743037343200e6050874" + "6573743037343300e70508746573743037343400e80508746573743037343500e90508746573743037343600ea0508" + "746573743037343700eb0508746573743037343800ec0508746573743037343900ed0508746573743037353000ee05" + "08746573743037353100ef0508746573743037353200f00508746573743037353300f10508746573743037353400f2" + "0508746573743037353500f30508746573743037353600f40508746573743037353700f50508746573743037353800" + "f60508746573743037353900f70508746573743037363000f80508746573743037363100f905087465737430373632" + "00fa0508746573743037363300fb0508746573743037363400fc0508746573743037363500fd050874657374303736" + "3600fe0508746573743037363700ff0508746573743037363800800608746573743037363900810608746573743037" + "3730008206087465737430373731008306087465737430373732008406087465737430373733008506087465737430" + "3737340086060874657374303737350087060874657374303737360088060874657374303737370089060874657374" + "30373738008a06087465737430373739008b06087465737430373830008c06087465737430373831008d0608746573" + "7430373832008e06087465737430373833008f06087465737430373834009006087465737430373835009106087465" + "7374303738360092060874657374303738370093060874657374303738380094060874657374303738390095060874" + "6573743037393000960608746573743037393100970608746573743037393200980608746573743037393300990608" + "7465737430373934009a06087465737430373935009b06087465737430373936009c06087465737430373937009d06" + "087465737430373938009e06087465737430373939009f0608746573743038303000a00608746573743038303100a1" + "0608746573743038303200a20608746573743038303300a30608746573743038303400a40608746573743038303500" + "a50608746573743038303600a60608746573743038303700a70608746573743038303800a806087465737430383039" + "00a90608746573743038313000aa0608746573743038313100ab0608746573743038313200ac060874657374303831" + "3300ad0608746573743038313400ae0608746573743038313500af0608746573743038313600b00608746573743038" + "313700b10608746573743038313800b20608746573743038313900b30608746573743038323000b406087465737430" + "38323100b50608746573743038323200b60608746573743038323300b70608746573743038323400b8060874657374" + "3038323500b90608746573743038323600ba0608746573743038323700bb0608746573743038323800bc0608746573" + "743038323900bd0608746573743038333000be0608746573743038333100bf0608746573743038333200c006087465" + "73743038333300c10608746573743038333400c20608746573743038333500c30608746573743038333600c4060874" + "6573743038333700c50608746573743038333800c60608746573743038333900c70608746573743038343000c80608" + "746573743038343100c90608746573743038343200ca0608746573743038343300cb0608746573743038343400cc06" + "08746573743038343500cd0608746573743038343600ce0608746573743038343700cf0608746573743038343800d0" + "0608746573743038343900d10608746573743038353000d20608746573743038353100d30608746573743038353200" + "d40608746573743038353300d50608746573743038353400d60608746573743038353500d706087465737430383536" + "00d80608746573743038353700d90608746573743038353800da0608746573743038353900db060874657374303836" + "3000dc0608746573743038363100dd0608746573743038363200de0608746573743038363300df0608746573743038" + "363400e00608746573743038363500e10608746573743038363600e20608746573743038363700e306087465737430" + "38363800e40608746573743038363900e50608746573743038373000e60608746573743038373100e7060874657374" + "3038373200e80608746573743038373300e90608746573743038373400ea0608746573743038373500eb0608746573" + "743038373600ec0608746573743038373700ed0608746573743038373800ee0608746573743038373900ef06087465" + "73743038383000f00608746573743038383100f10608746573743038383200f20608746573743038383300f3060874" + "6573743038383400f40608746573743038383500f50608746573743038383600f60608746573743038383700f70608" + "746573743038383800f80608746573743038383900f90608746573743038393000fa0608746573743038393100fb06" + "08746573743038393200fc0608746573743038393300fd0608746573743038393400fe0608746573743038393500ff" + "0608746573743038393600800708746573743038393700810708746573743038393800820708746573743038393900" + "8307087465737430393030008407087465737430393031008507087465737430393032008607087465737430393033" + "008707087465737430393034008807087465737430393035008907087465737430393036008a070874657374303930" + "37008b07087465737430393038008c07087465737430393039008d07087465737430393130008e0708746573743039" + "3131008f07087465737430393132009007087465737430393133009107087465737430393134009207087465737430" + "3931350093070874657374303931360094070874657374303931370095070874657374303931380096070874657374" + "30393139009707087465737430393230009807087465737430393231009907087465737430393232009a0708746573" + "7430393233009b07087465737430393234009c07087465737430393235009d07087465737430393236009e07087465" + "737430393237009f0708746573743039323800a00708746573743039323900a10708746573743039333000a2070874" + "6573743039333100a30708746573743039333200a40708746573743039333300a50708746573743039333400a60708" + "746573743039333500a70708746573743039333600a80708746573743039333700a90708746573743039333800aa07" + "08746573743039333900ab0708746573743039343000ac0708746573743039343100ad0708746573743039343200ae" + "0708746573743039343300af0708746573743039343400b00708746573743039343500b10708746573743039343600" + "b20708746573743039343700b30708746573743039343800b40708746573743039343900b507087465737430393530" + "00b60708746573743039353100b70708746573743039353200b80708746573743039353300b9070874657374303935" + "3400ba0708746573743039353500bb0708746573743039353600bc0708746573743039353700bd0708746573743039" + "353800be0708746573743039353900bf0708746573743039363000c00708746573743039363100c107087465737430" + "39363200c20708746573743039363300c30708746573743039363400c40708746573743039363500c5070874657374" + "3039363600c60708746573743039363700c70708746573743039363800c80708746573743039363900c90708746573" + "743039373000ca0708746573743039373100cb0708746573743039373200cc0708746573743039373300cd07087465" + "73743039373400ce0708746573743039373500cf0708746573743039373600d00708746573743039373700d1070874" + "6573743039373800d20708746573743039373900d30708746573743039383000d40708746573743039383100d50708" + "746573743039383200d60708746573743039383300d70708746573743039383400d80708746573743039383500d907" + "08746573743039383600da0708746573743039383700db0708746573743039383800dc0708746573743039383900dd" + "0708746573743039393000de0708746573743039393100df0708746573743039393200e00708746573743039393300" + "e10708746573743039393400e20708746573743039393500e30708746573743039393600e407087465737430393937" + "00e50708746573743039393800e60708746573743039393900e70708746573743130303000e8070874657374313030" + "3100e90708746573743130303200ea0708746573743130303300eb0708746573743130303400ec0708746573743130" + "303500ed0708746573743130303600ee0708746573743130303700ef0708746573743130303800f007087465737431" + "30303900f10708746573743130313000f20708746573743130313100f30708746573743130313200f4070874657374" + "3130313300f50708746573743130313400f60708746573743130313500f70708746573743130313600f80708746573" + "743130313700f90708746573743130313800fa0708746573743130313900fb0708746573743130323000fc07087465" + "73743130323100fd0708746573743130323200fe0708746573743130323300ff070874657374313032340080080874" + "6573743130323500810808746573743130323600820808746573743130323700830808746573743130323800840808" + "7465737431303239008508087465737431303330008608087465737431303331008708087465737431303332008808" + "087465737431303333008908087465737431303334008a08087465737431303335008b08087465737431303336008c" + "08087465737431303337008d08087465737431303338008e08087465737431303339008f0808746573743130343000" + "9008087465737431303431009108087465737431303432009208087465737431303433009308087465737431303434" + "0094080874657374313034350095080874657374313034360096080874657374313034370097080874657374313034" + "38009808087465737431303439009908087465737431303530009a08087465737431303531009b0808746573743130" + "3532009c08087465737431303533009d08087465737431303534009e08087465737431303535009f08087465737431" + "30353600a00808746573743130353700a10808746573743130353800a20808746573743130353900a3080874657374" + "3130363000a40808746573743130363100a50808746573743130363200a60808746573743130363300a70808746573" + "743130363400a80808746573743130363500a90808746573743130363600aa0808746573743130363700ab08087465" + "73743130363800ac0808746573743130363900ad0808746573743130373000ae0808746573743130373100af080874" + "6573743130373200b00808746573743130373300b10808746573743130373400b20808746573743130373500b30808" + "746573743130373600b40808746573743130373700b50808746573743130373800b60808746573743130373900b708" + "08746573743130383000b80808746573743130383100b90808746573743130383200ba0808746573743130383300bb" + "0808746573743130383400bc0808746573743130383500bd0808746573743130383600be0808746573743130383700" + "bf0808746573743130383800c00808746573743130383900c10808746573743130393000c208087465737431303931" + "00c30808746573743130393200c40808746573743130393300c50808746573743130393400c6080874657374313039" + "3500c70808746573743130393600c80808746573743130393700c90808746573743130393800ca0808746573743130" + "393900cb0808746573743131303000cc0808746573743131303100cd0808746573743131303200ce08087465737431" + "31303300cf0808746573743131303400d00808746573743131303500d10808746573743131303600d2080874657374" + "3131303700d30808746573743131303800d40808746573743131303900d50808746573743131313000d60808746573" + "743131313100d70808746573743131313200d80808746573743131313300d90808746573743131313400da08087465" + "73743131313500db0808746573743131313600dc0808746573743131313700dd0808746573743131313800de080874" + "6573743131313900df0808746573743131323000e00808746573743131323100e10808746573743131323200e20808" + "746573743131323300e30808746573743131323400e40808746573743131323500e50808746573743131323600e608" + "08746573743131323700e70808746573743131323800e80808746573743131323900e90808746573743131333000ea" + "0808746573743131333100eb0808746573743131333200ec0808746573743131333300ed0808746573743131333400" + "ee0808746573743131333500ef0808746573743131333600f00808746573743131333700f108087465737431313338" + "00f20808746573743131333900f30808746573743131343000f40808746573743131343100f5080874657374313134" + "3200f60808746573743131343300f70808746573743131343400f80808746573743131343500f90808746573743131" + "343600fa0808746573743131343700fb0808746573743131343800fc0808746573743131343900fd08087465737431" + "31353000fe0808746573743131353100ff080874657374313135320080090874657374313135330081090874657374" + "3131353400820908746573743131353500830908746573743131353600840908746573743131353700850908746573" + "7431313538008609087465737431313539008709087465737431313630008809087465737431313631008909087465" + "737431313632008a09087465737431313633008b09087465737431313634008c09087465737431313635008d090874" + "65737431313636008e09087465737431313637008f0908746573743131363800900908746573743131363900910908" + "7465737431313730009209087465737431313731009309087465737431313732009409087465737431313733009509" + "0874657374313137340096090874657374313137350097090874657374313137360098090874657374313137370099" + "09087465737431313738009a09087465737431313739009b09087465737431313830009c0908746573743131383100" + "9d09087465737431313832009e09087465737431313833009f0908746573743131383400a009087465737431313835" + "00a10908746573743131383600a20908746573743131383700a30908746573743131383800a4090874657374313138" + "3900a50908746573743131393000a60908746573743131393100a70908746573743131393200a80908746573743131" + "393300a90908746573743131393400aa0908746573743131393500ab0908746573743131393600ac09087465737431" + "31393700ad0908746573743131393800ae0908746573743131393900af0908746573743132303000b0090874657374" + "3132303100b10908746573743132303200b20908746573743132303300b30908746573743132303400b40908746573" + "743132303500b50908746573743132303600b60908746573743132303700b70908746573743132303800b809087465" + "73743132303900b90908746573743132313000ba0908746573743132313100bb0908746573743132313200bc090874" + "6573743132313300bd0908746573743132313400be0908746573743132313500bf0908746573743132313600c00908" + "746573743132313700c10908746573743132313800c20908746573743132313900c30908746573743132323000c409" + "08746573743132323100c50908746573743132323200c60908746573743132323300c70908746573743132323400c8" + "0908746573743132323500c90908746573743132323600ca0908746573743132323700cb0908746573743132323800" + "cc0908746573743132323900cd0908746573743132333000ce0908746573743132333100cf09087465737431323332" + "00d00908746573743132333300d10908746573743132333400d20908746573743132333500d3090874657374313233" + "3600d40908746573743132333700d50908746573743132333800d60908746573743132333900d70908746573743132" + "343000d80908746573743132343100d90908746573743132343200da0908746573743132343300db09087465737431" + "32343400dc0908746573743132343500dd0908746573743132343600de0908746573743132343700df090874657374" + "3132343800e00908746573743132343900e10908746573743132353000e20908746573743132353100e30908746573" + "743132353200e40908746573743132353300e50908746573743132353400e60908746573743132353500e709087465" + "73743132353600e80908746573743132353700e90908746573743132353800ea0908746573743132353900eb090874" + "6573743132363000ec0908746573743132363100ed0908746573743132363200ee0908746573743132363300ef0908" + "746573743132363400f00908746573743132363500f10908746573743132363600f20908746573743132363700f309" + "08746573743132363800f40908746573743132363900f50908746573743132373000f60908746573743132373100f7" + "0908746573743132373200f80908746573743132373300f90908746573743132373400fa0908746573743132373500" + "fb0908746573743132373600fc0908746573743132373700fd0908746573743132373800fe09087465737431323739" + "00ff0908746573743132383000800a08746573743132383100810a08746573743132383200820a0874657374313238" + "3300830a08746573743132383400840a08746573743132383500850a08746573743132383600860a08746573743132" + "383700870a08746573743132383800880a08746573743132383900890a087465737431323930008a0a087465737431" + "323931008b0a087465737431323932008c0a087465737431323933008d0a087465737431323934008e0a0874657374" + "31323935008f0a08746573743132393600900a08746573743132393700910a08746573743132393800920a08746573" + "743132393900930a08746573743133303000940a08746573743133303100950a08746573743133303200960a087465" + "73743133303300970a08746573743133303400980a08746573743133303500990a087465737431333036009a0a0874" + "65737431333037009b0a087465737431333038009c0a087465737431333039009d0a087465737431333130009e0a08" + "7465737431333131009f0a08746573743133313200a00a08746573743133313300a10a08746573743133313400a20a" + "08746573743133313500a30a08746573743133313600a40a08746573743133313700a50a08746573743133313800a6" + "0a08746573743133313900a70a08746573743133323000a80a08746573743133323100a90a08746573743133323200" + "aa0a08746573743133323300ab0a08746573743133323400ac0a08746573743133323500ad0a087465737431333236" + "00ae0a08746573743133323700af0a08746573743133323800b00a08746573743133323900b10a0874657374313333" + "3000b20a08746573743133333100b30a08746573743133333200b40a08746573743133333300b50a08746573743133" + "333400b60a08746573743133333500b70a08746573743133333600b80a08746573743133333700b90a087465737431" + "33333800ba0a08746573743133333900bb0a08746573743133343000bc0a08746573743133343100bd0a0874657374" + "3133343200be0a08746573743133343300bf0a08746573743133343400c00a08746573743133343500c10a08746573" + "743133343600c20a08746573743133343700c30a08746573743133343800c40a08746573743133343900c50a087465" + "73743133353000c60a08746573743133353100c70a08746573743133353200c80a08746573743133353300c90a0874" + "6573743133353400ca0a08746573743133353500cb0a08746573743133353600cc0a08746573743133353700cd0a08" + "746573743133353800ce0a08746573743133353900cf0a08746573743133363000d00a08746573743133363100d10a" + "08746573743133363200d20a08746573743133363300d30a08746573743133363400d40a08746573743133363500d5" + "0a08746573743133363600d60a08746573743133363700d70a08746573743133363800d80a08746573743133363900" + "d90a08746573743133373000da0a08746573743133373100db0a08746573743133373200dc0a087465737431333733" + "00dd0a08746573743133373400de0a08746573743133373500df0a08746573743133373600e00a0874657374313337" + "3700e10a08746573743133373800e20a08746573743133373900e30a08746573743133383000e40a08746573743133" + "383100e50a08746573743133383200e60a08746573743133383300e70a08746573743133383400e80a087465737431" + "33383500e90a08746573743133383600ea0a08746573743133383700eb0a08746573743133383800ec0a0874657374" + "3133383900ed0a08746573743133393000ee0a08746573743133393100ef0a08746573743133393200f00a08746573" + "743133393300f10a08746573743133393400f20a08746573743133393500f30a08746573743133393600f40a087465" + "73743133393700f50a08746573743133393800f60a08746573743133393900f70a08746573743134303000f80a0874" + "6573743134303100f90a08746573743134303200fa0a08746573743134303300fb0a08746573743134303400fc0a08" + "746573743134303500fd0a08746573743134303600fe0a08746573743134303700ff0a08746573743134303800800b" + "08746573743134303900810b08746573743134313000820b08746573743134313100830b0874657374313431320084" + "0b08746573743134313300850b08746573743134313400860b08746573743134313500870b08746573743134313600" + "880b08746573743134313700890b087465737431343138008a0b087465737431343139008b0b087465737431343230" + "008c0b087465737431343231008d0b087465737431343232008e0b087465737431343233008f0b0874657374313432" + "3400900b08746573743134323500910b08746573743134323600920b08746573743134323700930b08746573743134" + "323800940b08746573743134323900950b08746573743134333000960b08746573743134333100970b087465737431" + "34333200980b08746573743134333300990b087465737431343334009a0b087465737431343335009b0b0874657374" + "31343336009c0b087465737431343337009d0b087465737431343338009e0b087465737431343339009f0b08746573" + "743134343000a00b08746573743134343100a10b08746573743134343200a20b08746573743134343300a30b087465" + "73743134343400a40b08746573743134343500a50b08746573743134343600a60b08746573743134343700a70b0874" + "6573743134343800a80b08746573743134343900a90b08746573743134353000aa0b08746573743134353100ab0b08" + "746573743134353200ac0b08746573743134353300ad0b08746573743134353400ae0b08746573743134353500af0b" + "08746573743134353600b00b08746573743134353700b10b08746573743134353800b20b08746573743134353900b3" + "0b08746573743134363000b40b08746573743134363100b50b08746573743134363200b60b08746573743134363300" + "b70b08746573743134363400b80b08746573743134363500b90b08746573743134363600ba0b087465737431343637" + "00bb0b08746573743134363800bc0b08746573743134363900bd0b08746573743134373000be0b0874657374313437" + "3100bf0b08746573743134373200c00b08746573743134373300c10b08746573743134373400c20b08746573743134" + "373500c30b08746573743134373600c40b08746573743134373700c50b08746573743134373800c60b087465737431" + "34373900c70b08746573743134383000c80b08746573743134383100c90b08746573743134383200ca0b0874657374" + "3134383300cb0b08746573743134383400cc0b08746573743134383500cd0b08746573743134383600ce0b08746573" + "743134383700cf0b08746573743134383800d00b08746573743134383900d10b08746573743134393000d20b087465" + "73743134393100d30b08746573743134393200d40b08746573743134393300d50b08746573743134393400d60b0874" + "6573743134393500d70b08746573743134393600d80b08746573743134393700d90b08746573743134393800da0b08" + "746573743134393900db0b08746573743135303000dc0b08746573743135303100dd0b08746573743135303200de0b" + "08746573743135303300df0b08746573743135303400e00b08746573743135303500e10b08746573743135303600e2" + "0b08746573743135303700e30b08746573743135303800e40b08746573743135303900e50b08746573743135313000" + "e60b08746573743135313100e70b08746573743135313200e80b08746573743135313300e90b087465737431353134" + "00ea0b08746573743135313500eb0b08746573743135313600ec0b08746573743135313700ed0b0874657374313531" + "3800ee0b08746573743135313900ef0b08746573743135323000f00b08746573743135323100f10b08746573743135" + "323200f20b08746573743135323300f30b08746573743135323400f40b08746573743135323500f50b087465737431" + "35323600f60b08746573743135323700f70b08746573743135323800f80b08746573743135323900f90b0874657374" + "3135333000fa0b08746573743135333100fb0b08746573743135333200fc0b08746573743135333300fd0b08746573" + "743135333400fe0b08746573743135333500ff0b08746573743135333600800c08746573743135333700810c087465" + "73743135333800820c08746573743135333900830c08746573743135343000840c08746573743135343100850c0874" + "6573743135343200860c08746573743135343300870c08746573743135343400880c08746573743135343500890c08" + "7465737431353436008a0c087465737431353437008b0c087465737431353438008c0c087465737431353439008d0c" + "087465737431353530008e0c087465737431353531008f0c08746573743135353200900c0874657374313535330091" + "0c08746573743135353400920c08746573743135353500930c08746573743135353600940c08746573743135353700" + "950c08746573743135353800960c08746573743135353900970c08746573743135363000980c087465737431353631" + "00990c087465737431353632009a0c087465737431353633009b0c087465737431353634009c0c0874657374313536" + "35009d0c087465737431353636009e0c087465737431353637009f0c08746573743135363800a00c08746573743135" + "363900a10c08746573743135373000a20c08746573743135373100a30c08746573743135373200a40c087465737431" + "35373300a50c08746573743135373400a60c08746573743135373500a70c08746573743135373600a80c0874657374" + "3135373700a90c08746573743135373800aa0c08746573743135373900ab0c08746573743135383000ac0c08746573" + "743135383100ad0c08746573743135383200ae0c08746573743135383300af0c08746573743135383400b00c087465" + "73743135383500b10c08746573743135383600b20c08746573743135383700b30c08746573743135383800b40c0874" + "6573743135383900b50c08746573743135393000b60c08746573743135393100b70c08746573743135393200b80c08" + "746573743135393300b90c08746573743135393400ba0c08746573743135393500bb0c08746573743135393600bc0c" + "08746573743135393700bd0c08746573743135393800be0c08746573743135393900bf0c08746573743136303000c0" + "0c08746573743136303100c10c08746573743136303200c20c08746573743136303300c30c08746573743136303400" + "c40c08746573743136303500c50c08746573743136303600c60c08746573743136303700c70c087465737431363038" + "00c80c08746573743136303900c90c08746573743136313000ca0c08746573743136313100cb0c0874657374313631" + "3200cc0c08746573743136313300cd0c08746573743136313400ce0c08746573743136313500cf0c08746573743136" + "313600d00c08746573743136313700d10c08746573743136313800d20c08746573743136313900d30c087465737431" + "36323000d40c08746573743136323100d50c08746573743136323200d60c08746573743136323300d70c0874657374" + "3136323400d80c08746573743136323500d90c08746573743136323600da0c08746573743136323700db0c08746573" + "743136323800dc0c08746573743136323900dd0c08746573743136333000de0c08746573743136333100df0c087465" + "73743136333200e00c08746573743136333300e10c08746573743136333400e20c08746573743136333500e30c0874" + "6573743136333600e40c08746573743136333700e50c08746573743136333800e60c08746573743136333900e70c08" + "746573743136343000e80c08746573743136343100e90c08746573743136343200ea0c08746573743136343300eb0c" + "08746573743136343400ec0c08746573743136343500ed0c08746573743136343600ee0c08746573743136343700ef" + "0c08746573743136343800f00c08746573743136343900f10c08746573743136353000f20c08746573743136353100" + "f30c08746573743136353200f40c08746573743136353300f50c08746573743136353400f60c087465737431363535" + "00f70c08746573743136353600f80c08746573743136353700f90c08746573743136353800fa0c0874657374313635" + "3900fb0c08746573743136363000fc0c08746573743136363100fd0c08746573743136363200fe0c08746573743136" + "363300ff0c08746573743136363400800d08746573743136363500810d08746573743136363600820d087465737431" + "36363700830d08746573743136363800840d08746573743136363900850d08746573743136373000860d0874657374" + "3136373100870d08746573743136373200880d08746573743136373300890d087465737431363734008a0d08746573" + "7431363735008b0d087465737431363736008c0d087465737431363737008d0d087465737431363738008e0d087465" + "737431363739008f0d08746573743136383000900d08746573743136383100910d08746573743136383200920d0874" + "6573743136383300930d08746573743136383400940d08746573743136383500950d08746573743136383600960d08" + "746573743136383700970d08746573743136383800980d08746573743136383900990d087465737431363930009a0d" + "087465737431363931009b0d087465737431363932009c0d087465737431363933009d0d087465737431363934009e" + "0d087465737431363935009f0d08746573743136393600a00d08746573743136393700a10d08746573743136393800" + "a20d08746573743136393900a30d08746573743137303000a40d08746573743137303100a50d087465737431373032" + "00a60d08746573743137303300a70d08746573743137303400a80d08746573743137303500a90d0874657374313730" + "3600aa0d08746573743137303700ab0d08746573743137303800ac0d08746573743137303900ad0d08746573743137" + "313000ae0d08746573743137313100af0d08746573743137313200b00d08746573743137313300b10d087465737431" + "37313400b20d08746573743137313500b30d08746573743137313600b40d08746573743137313700b50d0874657374" + "3137313800b60d08746573743137313900b70d08746573743137323000b80d08746573743137323100b90d08746573" + "743137323200ba0d08746573743137323300bb0d08746573743137323400bc0d08746573743137323500bd0d087465" + "73743137323600be0d08746573743137323700bf0d08746573743137323800c00d08746573743137323900c10d0874" + "6573743137333000c20d08746573743137333100c30d08746573743137333200c40d08746573743137333300c50d08" + "746573743137333400c60d08746573743137333500c70d08746573743137333600c80d08746573743137333700c90d" + "08746573743137333800ca0d08746573743137333900cb0d08746573743137343000cc0d08746573743137343100cd" + "0d08746573743137343200ce0d08746573743137343300cf0d08746573743137343400d00d08746573743137343500" + "d10d08746573743137343600d20d08746573743137343700d30d08746573743137343800d40d087465737431373439" + "00d50d08746573743137353000d60d08746573743137353100d70d08746573743137353200d80d0874657374313735" + "3300d90d08746573743137353400da0d08746573743137353500db0d08746573743137353600dc0d08746573743137" + "353700dd0d08746573743137353800de0d08746573743137353900df0d08746573743137363000e00d087465737431" + "37363100e10d08746573743137363200e20d08746573743137363300e30d08746573743137363400e40d0874657374" + "3137363500e50d08746573743137363600e60d08746573743137363700e70d08746573743137363800e80d08746573" + "743137363900e90d08746573743137373000ea0d08746573743137373100eb0d08746573743137373200ec0d087465" + "73743137373300ed0d08746573743137373400ee0d08746573743137373500ef0d08746573743137373600f00d0874" + "6573743137373700f10d08746573743137373800f20d08746573743137373900f30d08746573743137383000f40d08" + "746573743137383100f50d08746573743137383200f60d08746573743137383300f70d08746573743137383400f80d" + "08746573743137383500f90d08746573743137383600fa0d08746573743137383700fb0d08746573743137383800fc" + "0d08746573743137383900fd0d08746573743137393000fe0d08746573743137393100ff0d08746573743137393200" + "800e08746573743137393300810e08746573743137393400820e08746573743137393500830e087465737431373936" + "00840e08746573743137393700850e08746573743137393800860e08746573743137393900870e0874657374313830" + "3000880e08746573743138303100890e087465737431383032008a0e087465737431383033008b0e08746573743138" + "3034008c0e087465737431383035008d0e087465737431383036008e0e087465737431383037008f0e087465737431" + "38303800900e08746573743138303900910e08746573743138313000920e08746573743138313100930e0874657374" + "3138313200940e08746573743138313300950e08746573743138313400960e08746573743138313500970e08746573" + "743138313600980e08746573743138313700990e087465737431383138009a0e087465737431383139009b0e087465" + "737431383230009c0e087465737431383231009d0e087465737431383232009e0e087465737431383233009f0e0874" + "6573743138323400a00e08746573743138323500a10e08746573743138323600a20e08746573743138323700a30e08" + "746573743138323800a40e08746573743138323900a50e08746573743138333000a60e08746573743138333100a70e" + "08746573743138333200a80e08746573743138333300a90e08746573743138333400aa0e08746573743138333500ab" + "0e08746573743138333600ac0e08746573743138333700ad0e08746573743138333800ae0e08746573743138333900" + "af0e08746573743138343000b00e08746573743138343100b10e08746573743138343200b20e087465737431383433" + "00b30e08746573743138343400b40e08746573743138343500b50e08746573743138343600b60e0874657374313834" + "3700b70e08746573743138343800b80e08746573743138343900b90e08746573743138353000ba0e08746573743138" + "353100bb0e08746573743138353200bc0e08746573743138353300bd0e08746573743138353400be0e087465737431" + "38353500bf0e08746573743138353600c00e08746573743138353700c10e08746573743138353800c20e0874657374" + "3138353900c30e08746573743138363000c40e08746573743138363100c50e08746573743138363200c60e08746573" + "743138363300c70e08746573743138363400c80e08746573743138363500c90e08746573743138363600ca0e087465" + "73743138363700cb0e08746573743138363800cc0e08746573743138363900cd0e08746573743138373000ce0e0874" + "6573743138373100cf0e08746573743138373200d00e08746573743138373300d10e08746573743138373400d20e08" + "746573743138373500d30e08746573743138373600d40e08746573743138373700d50e08746573743138373800d60e" + "08746573743138373900d70e08746573743138383000d80e08746573743138383100d90e08746573743138383200da" + "0e08746573743138383300db0e08746573743138383400dc0e08746573743138383500dd0e08746573743138383600" + "de0e08746573743138383700df0e08746573743138383800e00e08746573743138383900e10e087465737431383930" + "00e20e08746573743138393100e30e08746573743138393200e40e08746573743138393300e50e0874657374313839" + "3400e60e08746573743138393500e70e08746573743138393600e80e08746573743138393700e90e08746573743138" + "393800ea0e08746573743138393900eb0e08746573743139303000ec0e08746573743139303100ed0e087465737431" + "39303200ee0e08746573743139303300ef0e08746573743139303400f00e08746573743139303500f10e0874657374" + "3139303600f20e08746573743139303700f30e08746573743139303800f40e08746573743139303900f50e08746573" + "743139313000f60e08746573743139313100f70e08746573743139313200f80e08746573743139313300f90e087465" + "73743139313400fa0e08746573743139313500fb0e08746573743139313600fc0e08746573743139313700fd0e0874" + "6573743139313800fe0e08746573743139313900ff0e08746573743139323000800f08746573743139323100810f08" + "746573743139323200820f08746573743139323300830f08746573743139323400840f08746573743139323500850f" + "08746573743139323600860f08746573743139323700870f08746573743139323800880f0874657374313932390089" + "0f087465737431393330008a0f087465737431393331008b0f087465737431393332008c0f08746573743139333300" + "8d0f087465737431393334008e0f087465737431393335008f0f08746573743139333600900f087465737431393337" + "00910f08746573743139333800920f08746573743139333900930f08746573743139343000940f0874657374313934" + "3100950f08746573743139343200960f08746573743139343300970f08746573743139343400980f08746573743139" + "343500990f087465737431393436009a0f087465737431393437009b0f087465737431393438009c0f087465737431" + "393439009d0f087465737431393530009e0f087465737431393531009f0f08746573743139353200a00f0874657374" + "3139353300a10f08746573743139353400a20f08746573743139353500a30f08746573743139353600a40f08746573" + "743139353700a50f08746573743139353800a60f08746573743139353900a70f08746573743139363000a80f087465" + "73743139363100a90f08746573743139363200aa0f08746573743139363300ab0f08746573743139363400ac0f0874" + "6573743139363500ad0f08746573743139363600ae0f08746573743139363700af0f08746573743139363800b00f08" + "746573743139363900b10f08746573743139373000b20f08746573743139373100b30f08746573743139373200b40f" + "08746573743139373300b50f08746573743139373400b60f08746573743139373500b70f08746573743139373600b8" + "0f08746573743139373700b90f08746573743139373800ba0f08746573743139373900bb0f08746573743139383000" + "bc0f08746573743139383100bd0f08746573743139383200be0f08746573743139383300bf0f087465737431393834" + "00c00f08746573743139383500c10f08746573743139383600c20f08746573743139383700c30f0874657374313938" + "3800c40f08746573743139383900c50f08746573743139393000c60f08746573743139393100c70f08746573743139" + "393200c80f08746573743139393300c90f08746573743139393400ca0f08746573743139393500cb0f087465737431" + "39393600cc0f08746573743139393700cd0f08746573743139393800ce0f08746573743139393900cf0f0874657374" + "3230303000d00f08746573743230303100d10f08746573743230303200d20f08746573743230303300d30f08746573" + "743230303400d40f08746573743230303500d50f08746573743230303600d60f08746573743230303700d70f087465" + "73743230303800d80f08746573743230303900d90f08746573743230313000da0f08746573743230313100db0f0874" + "6573743230313200dc0f08746573743230313300dd0f08746573743230313400de0f08746573743230313500df0f08" + "746573743230313600e00f08746573743230313700e10f08746573743230313800e20f08746573743230313900e30f" + "08746573743230323000e40f08746573743230323100e50f08746573743230323200e60f08746573743230323300e7" + "0f08746573743230323400e80f08746573743230323500e90f08746573743230323600ea0f08746573743230323700" + "eb0f08746573743230323800ec0f08746573743230323900ed0f08746573743230333000ee0f087465737432303331" + "00ef0f08746573743230333200f00f08746573743230333300f10f08746573743230333400f20f0874657374323033" + "3500f30f08746573743230333600f40f08746573743230333700f50f08746573743230333800f60f08746573743230" + "333900f70f08746573743230343000f80f08746573743230343100f90f08746573743230343200fa0f087465737432" + "30343300fb0f08746573743230343400fc0f08746573743230343500fd0f08746573743230343600fe0f0874657374" + "3230343700ff0f08746573743230343800801008746573743230343900811008746573743230353000821008746573" + "7432303531008310087465737432303532008410087465737432303533008510087465737432303534008610087465" + "737432303535008710087465737432303536008810087465737432303537008910087465737432303538008a100874" + "65737432303539008b10087465737432303630008c10087465737432303631008d10087465737432303632008e1008" + "7465737432303633008f10087465737432303634009010087465737432303635009110087465737432303636009210" + "0874657374323036370093100874657374323036380094100874657374323036390095100874657374323037300096" + "1008746573743230373100971008746573743230373200981008746573743230373300991008746573743230373400" + "9a10087465737432303735009b10087465737432303736009c10087465737432303737009d10087465737432303738" + "009e10087465737432303739009f1008746573743230383000a01008746573743230383100a1100874657374323038" + "3200a21008746573743230383300a31008746573743230383400a41008746573743230383500a51008746573743230" + "383600a61008746573743230383700a71008746573743230383800a81008746573743230383900a910087465737432" + "30393000aa1008746573743230393100ab1008746573743230393200ac1008746573743230393300ad100874657374" + "3230393400ae1008746573743230393500af1008746573743230393600b01008746573743230393700b11008746573" + "743230393800b21008746573743230393900b31008746573743231303000b41008746573743231303100b510087465" + "73743231303200b61008746573743231303300b71008746573743231303400b81008746573743231303500b9100874" + "6573743231303600ba1008746573743231303700bb1008746573743231303800bc1008746573743231303900bd1008" + "746573743231313000be1008746573743231313100bf1008746573743231313200c01008746573743231313300c110" + "08746573743231313400c21008746573743231313500c31008746573743231313600c41008746573743231313700c5" + "1008746573743231313800c61008746573743231313900c71008746573743231323000c81008746573743231323100" + "c91008746573743231323200ca1008746573743231323300cb1008746573743231323400cc10087465737432313235" + "00cd1008746573743231323600ce1008746573743231323700cf1008746573743231323800d0100874657374323132" + "3900d11008746573743231333000d21008746573743231333100d31008746573743231333200d41008746573743231" + "333300d51008746573743231333400d61008746573743231333500d71008746573743231333600d810087465737432" + "31333700d91008746573743231333800da1008746573743231333900db1008746573743231343000dc100874657374" + "3231343100dd1008746573743231343200de1008746573743231343300df1008746573743231343400e01008746573" + "743231343500e11008746573743231343600e21008746573743231343700e31008746573743231343800e410087465" + "73743231343900e51008746573743231353000e61008746573743231353100e71008746573743231353200e8100874" + "6573743231353300e91008746573743231353400ea1008746573743231353500eb1008746573743231353600ec1008" + "746573743231353700ed1008746573743231353800ee1008746573743231353900ef1008746573743231363000f010" + "08746573743231363100f11008746573743231363200f21008746573743231363300f31008746573743231363400f4" + "1008746573743231363500f51008746573743231363600f61008746573743231363700f71008746573743231363800" + "f81008746573743231363900f91008746573743231373000fa1008746573743231373100fb10087465737432313732" + "00fc1008746573743231373300fd1008746573743231373400fe1008746573743231373500ff100874657374323137" + "3600801108746573743231373700811108746573743231373800821108746573743231373900831108746573743231" + "3830008411087465737432313831008511087465737432313832008611087465737432313833008711087465737432" + "313834008811087465737432313835008911087465737432313836008a11087465737432313837008b110874657374" + "32313838008c11087465737432313839008d11087465737432313930008e11087465737432313931008f1108746573" + "7432313932009011087465737432313933009111087465737432313934009211087465737432313935009311087465" + "7374323139360094110874657374323139370095110874657374323139380096110874657374323139390097110874" + "65737432323030009811087465737432323031009911087465737432323032009a11087465737432323033009b1108" + "7465737432323034009c11087465737432323035009d11087465737432323036009e11087465737432323037009f11" + "08746573743232303800a01108746573743232303900a11108746573743232313000a21108746573743232313100a3" + "1108746573743232313200a41108746573743232313300a51108746573743232313400a61108746573743232313500" + "a71108746573743232313600a81108746573743232313700a91108746573743232313800aa11087465737432323139" + "00ab1108746573743232323000ac1108746573743232323100ad1108746573743232323200ae110874657374323232" + "3300af1108746573743232323400b01108746573743232323500b11108746573743232323600b21108746573743232" + "323700b31108746573743232323800b41108746573743232323900b51108746573743232333000b611087465737432" + "32333100b71108746573743232333200b81108746573743232333300b91108746573743232333400ba110874657374" + "3232333500bb1108746573743232333600bc1108746573743232333700bd1108746573743232333800be1108746573" + "743232333900bf1108746573743232343000c01108746573743232343100c11108746573743232343200c211087465" + "73743232343300c31108746573743232343400c41108746573743232343500c51108746573743232343600c6110874" + "6573743232343700c71108746573743232343800c81108746573743232343900c91108746573743232353000ca1108" + "746573743232353100cb1108746573743232353200cc1108746573743232353300cd1108746573743232353400ce11" + "08746573743232353500cf1108746573743232353600d01108746573743232353700d11108746573743232353800d2" + "1108746573743232353900d31108746573743232363000d41108746573743232363100d51108746573743232363200" + "d61108746573743232363300d71108746573743232363400d81108746573743232363500d911087465737432323636" + "00da1108746573743232363700db1108746573743232363800dc1108746573743232363900dd110874657374323237" + "3000de1108746573743232373100df1108746573743232373200e01108746573743232373300e11108746573743232" + "373400e21108746573743232373500e31108746573743232373600e41108746573743232373700e511087465737432" + "32373800e61108746573743232373900e71108746573743232383000e81108746573743232383100e9110874657374" + "3232383200ea1108746573743232383300eb1108746573743232383400ec1108746573743232383500ed1108746573" + "743232383600ee1108746573743232383700ef1108746573743232383800f01108746573743232383900f111087465" + "73743232393000f21108746573743232393100f31108746573743232393200f41108746573743232393300f5110874" + "6573743232393400f61108746573743232393500f71108746573743232393600f81108746573743232393700f91108" + "746573743232393800fa1108746573743232393900fb1108746573743233303000fc1108746573743233303100fd11" + "08746573743233303200fe1108746573743233303300ff110874657374323330340080120874657374323330350081" + "1208746573743233303600821208746573743233303700831208746573743233303800841208746573743233303900" + "8512087465737432333130008612087465737432333131008712087465737432333132008812087465737432333133" + "008912087465737432333134008a12087465737432333135008b12087465737432333136008c120874657374323331" + "37008d12087465737432333138008e12087465737432333139008f1208746573743233323000901208746573743233" + "3231009112087465737432333232009212087465737432333233009312087465737432333234009412087465737432" + "3332350095120874657374323332360096120874657374323332370097120874657374323332380098120874657374" + "32333239009912087465737432333330009a12087465737432333331009b12087465737432333332009c1208746573" + "7432333333009d12087465737432333334009e12087465737432333335009f1208746573743233333600a012087465" + "73743233333700a11208746573743233333800a21208746573743233333900a31208746573743233343000a4120874" + "6573743233343100a51208746573743233343200a61208746573743233343300a71208746573743233343400a81208" + "746573743233343500a91208746573743233343600aa1208746573743233343700ab1208746573743233343800ac12" + "08746573743233343900ad1208746573743233353000ae1208746573743233353100af1208746573743233353200b0" + "1208746573743233353300b11208746573743233353400b21208746573743233353500b31208746573743233353600" + "b41208746573743233353700b51208746573743233353800b61208746573743233353900b712087465737432333630" + "00b81208746573743233363100b91208746573743233363200ba1208746573743233363300bb120874657374323336" + "3400bc1208746573743233363500bd1208746573743233363600be1208746573743233363700bf1208746573743233" + "363800c01208746573743233363900c11208746573743233373000c21208746573743233373100c312087465737432" + "33373200c41208746573743233373300c51208746573743233373400c61208746573743233373500c7120874657374" + "3233373600c81208746573743233373700c91208746573743233373800ca1208746573743233373900cb1208746573" + "743233383000cc1208746573743233383100cd1208746573743233383200ce1208746573743233383300cf12087465" + "73743233383400d01208746573743233383500d11208746573743233383600d21208746573743233383700d3120874" + "6573743233383800d41208746573743233383900d51208746573743233393000d61208746573743233393100d71208" + "746573743233393200d81208746573743233393300d91208746573743233393400da1208746573743233393500db12" + "08746573743233393600dc1208746573743233393700dd1208746573743233393800de1208746573743233393900df" + "1208746573743234303000e01208746573743234303100e11208746573743234303200e21208746573743234303300" + "e31208746573743234303400e41208746573743234303500e51208746573743234303600e612087465737432343037" + "00e71208746573743234303800e81208746573743234303900e91208746573743234313000ea120874657374323431" + "3100eb1208746573743234313200ec1208746573743234313300ed1208746573743234313400ee1208746573743234" + "313500ef1208746573743234313600f01208746573743234313700f11208746573743234313800f212087465737432" + "34313900f31208746573743234323000f41208746573743234323100f51208746573743234323200f6120874657374" + "3234323300f71208746573743234323400f81208746573743234323500f91208746573743234323600fa1208746573" + "743234323700fb1208746573743234323800fc1208746573743234323900fd1208746573743234333000fe12087465" + "73743234333100ff120874657374323433320080130874657374323433330081130874657374323433340082130874" + "6573743234333500831308746573743234333600841308746573743234333700851308746573743234333800861308" + "7465737432343339008713087465737432343430008813087465737432343431008913087465737432343432008a13" + "087465737432343433008b13087465737432343434008c13087465737432343435008d13087465737432343436008e" + "13087465737432343437008f1308746573743234343800901308746573743234343900911308746573743234353000" + "9213087465737432343531009313087465737432343532009413087465737432343533009513087465737432343534" + "0096130874657374323435350097130874657374323435360098130874657374323435370099130874657374323435" + "38009a13087465737432343539009b13087465737432343630009c13087465737432343631009d1308746573743234" + "3632009e13087465737432343633009f1308746573743234363400a01308746573743234363500a113087465737432" + "34363600a21308746573743234363700a31308746573743234363800a41308746573743234363900a5130874657374" + "3234373000a61308746573743234373100a71308746573743234373200a81308746573743234373300a91308746573" + "743234373400aa1308746573743234373500ab1308746573743234373600ac1308746573743234373700ad13087465" + "73743234373800ae1308746573743234373900af1308746573743234383000b01308746573743234383100b1130874" + "6573743234383200b21308746573743234383300b31308746573743234383400b41308746573743234383500b51308" + "746573743234383600b61308746573743234383700b71308746573743234383800b81308746573743234383900b913" + "08746573743234393000ba1308746573743234393100bb1308746573743234393200bc1308746573743234393300bd" + "1308746573743234393400be1308746573743234393500bf1308746573743234393600c01308746573743234393700" + "c11308746573743234393800c21308746573743234393900c31308746573743235303000c413087465737432353031" + "00c51308746573743235303200c61308746573743235303300c71308746573743235303400c8130874657374323530" + "3500c91308746573743235303600ca1308746573743235303700cb1308746573743235303800cc1308746573743235" + "303900cd1308746573743235313000ce1308746573743235313100cf1308746573743235313200d013087465737432" + "35313300d11308746573743235313400d21308746573743235313500d31308746573743235313600d4130874657374" + "3235313700d51308746573743235313800d61308746573743235313900d71308746573743235323000d81308746573" + "743235323100d91308746573743235323200da1308746573743235323300db1308746573743235323400dc13087465" + "73743235323500dd1308746573743235323600de1308746573743235323700df1308746573743235323800e0130874" + "6573743235323900e11308746573743235333000e21308746573743235333100e31308746573743235333200e41308" + "746573743235333300e51308746573743235333400e61308746573743235333500e71308746573743235333600e813" + "08746573743235333700e91308746573743235333800ea1308746573743235333900eb1308746573743235343000ec" + "1308746573743235343100ed1308746573743235343200ee1308746573743235343300ef1308746573743235343400" + "f01308746573743235343500f11308746573743235343600f21308746573743235343700f313087465737432353438" + "00f41308746573743235343900f51308746573743235353000f61308746573743235353100f7130874657374323535" + "3200f81308746573743235353300f91308746573743235353400fa1308746573743235353500fb1308746573743235" + "353600fc1308746573743235353700fd1308746573743235353800fe1308746573743235353900ff13087465737432" + "3536300080140874657374323536310081140874657374323536320082140874657374323536330083140874657374" + "3235363400841408746573743235363500851408746573743235363600861408746573743235363700871408746573" + "7432353638008814087465737432353639008914087465737432353730008a14087465737432353731008b14087465" + "737432353732008c14087465737432353733008d14087465737432353734008e14087465737432353735008f140874" + "6573743235373600901408746573743235373700911408746573743235373800921408746573743235373900931408" + "7465737432353830009414087465737432353831009514087465737432353832009614087465737432353833009714" + "087465737432353834009814087465737432353835009914087465737432353836009a14087465737432353837009b" + "14087465737432353838009c14087465737432353839009d14087465737432353930009e1408746573743235393100" + "9f1408746573743235393200a01408746573743235393300a11408746573743235393400a214087465737432353935" + "00a31408746573743235393600a41408746573743235393700a51408746573743235393800a6140874657374323539" + "3900a71408746573743236303000a81408746573743236303100a91408746573743236303200aa1408746573743236" + "303300ab1408746573743236303400ac1408746573743236303500ad1408746573743236303600ae14087465737432" + "36303700af1408746573743236303800b01408746573743236303900b11408746573743236313000b2140874657374" + "3236313100b31408746573743236313200b41408746573743236313300b51408746573743236313400b61408746573" + "743236313500b71408746573743236313600b81408746573743236313700b91408746573743236313800ba14087465" + "73743236313900bb1408746573743236323000bc1408746573743236323100bd1408746573743236323200be140874" + "6573743236323300bf1408746573743236323400c01408746573743236323500c11408746573743236323600c21408" + "746573743236323700c31408746573743236323800c41408746573743236323900c51408746573743236333000c614" + "08746573743236333100c71408746573743236333200c81408746573743236333300c91408746573743236333400ca" + "1408746573743236333500cb1408746573743236333600cc1408746573743236333700cd1408746573743236333800" + "ce1408746573743236333900cf1408746573743236343000d01408746573743236343100d114087465737432363432" + "00d21408746573743236343300d31408746573743236343400d41408746573743236343500d5140874657374323634" + "3600d61408746573743236343700d71408746573743236343800d81408746573743236343900d91408746573743236" + "353000da1408746573743236353100db1408746573743236353200dc1408746573743236353300dd14087465737432" + "36353400de1408746573743236353500df1408746573743236353600e01408746573743236353700e1140874657374" + "3236353800e21408746573743236353900e31408746573743236363000e41408746573743236363100e51408746573" + "743236363200e61408746573743236363300e71408746573743236363400e81408746573743236363500e914087465" + "73743236363600ea1408746573743236363700eb1408746573743236363800ec1408746573743236363900ed140874" + "6573743236373000ee1408746573743236373100ef1408746573743236373200f01408746573743236373300f11408" + "746573743236373400f21408746573743236373500f31408746573743236373600f41408746573743236373700f514" + "08746573743236373800f61408746573743236373900f71408746573743236383000f81408746573743236383100f9" + "1408746573743236383200fa1408746573743236383300fb1408746573743236383400fc1408746573743236383500" + "fd1408746573743236383600fe1408746573743236383700ff14087465737432363838008015087465737432363839" + "0081150874657374323639300082150874657374323639310083150874657374323639320084150874657374323639" + "3300851508746573743236393400861508746573743236393500871508746573743236393600881508746573743236" + "3937008915087465737432363938008a15087465737432363939008b15087465737432373030008c15087465737432" + "373031008d15087465737432373032008e15087465737432373033008f150874657374323730340090150874657374" + "3237303500911508746573743237303600921508746573743237303700931508746573743237303800941508746573" + "7432373039009515087465737432373130009615087465737432373131009715087465737432373132009815087465" + "737432373133009915087465737432373134009a15087465737432373135009b15087465737432373136009c150874" + "65737432373137009d15087465737432373138009e15087465737432373139009f1508746573743237323000a01508" + "746573743237323100a11508746573743237323200a21508746573743237323300a31508746573743237323400a415" + "08746573743237323500a51508746573743237323600a61508746573743237323700a71508746573743237323800a8" + "1508746573743237323900a91508746573743237333000aa1508746573743237333100ab1508746573743237333200" + "ac1508746573743237333300ad1508746573743237333400ae1508746573743237333500af15087465737432373336" + "00b01508746573743237333700b11508746573743237333800b21508746573743237333900b3150874657374323734" + "3000b41508746573743237343100b51508746573743237343200b61508746573743237343300b71508746573743237" + "343400b81508746573743237343500b91508746573743237343600ba1508746573743237343700bb15087465737432" + "37343800bc1508746573743237343900bd1508746573743237353000be1508746573743237353100bf150874657374" + "3237353200c01508746573743237353300c11508746573743237353400c21508746573743237353500c31508746573" + "743237353600c41508746573743237353700c51508746573743237353800c61508746573743237353900c715087465" + "73743237363000c81508746573743237363100c91508746573743237363200ca1508746573743237363300cb150874" + "6573743237363400cc1508746573743237363500cd1508746573743237363600ce1508746573743237363700cf1508" + "746573743237363800d01508746573743237363900d11508746573743237373000d21508746573743237373100d315" + "08746573743237373200d41508746573743237373300d51508746573743237373400d61508746573743237373500d7" + "1508746573743237373600d81508746573743237373700d91508746573743237373800da1508746573743237373900" + "db1508746573743237383000dc1508746573743237383100dd1508746573743237383200de15087465737432373833" + "00df1508746573743237383400e01508746573743237383500e11508746573743237383600e2150874657374323738" + "3700e31508746573743237383800e41508746573743237383900e51508746573743237393000e61508746573743237" + "393100e71508746573743237393200e81508746573743237393300e91508746573743237393400ea15087465737432" + "37393500eb1508746573743237393600ec1508746573743237393700ed1508746573743237393800ee150874657374" + "3237393900ef1508746573743238303000f01508746573743238303100f11508746573743238303200f21508746573" + "743238303300f31508746573743238303400f41508746573743238303500f51508746573743238303600f615087465" + "73743238303700f71508746573743238303800f81508746573743238303900f91508746573743238313000fa150874" + "6573743238313100fb1508746573743238313200fc1508746573743238313300fd1508746573743238313400fe1508" + "746573743238313500ff15087465737432383136008016087465737432383137008116087465737432383138008216" + "0874657374323831390083160874657374323832300084160874657374323832310085160874657374323832320086" + "1608746573743238323300871608746573743238323400881608746573743238323500891608746573743238323600" + "8a16087465737432383237008b16087465737432383238008c16087465737432383239008d16087465737432383330" + "008e16087465737432383331008f160874657374323833320090160874657374323833330091160874657374323833" + "3400921608746573743238333500931608746573743238333600941608746573743238333700951608746573743238" + "3338009616087465737432383339009716087465737432383430009816087465737432383431009916087465737432" + "383432009a16087465737432383433009b16087465737432383434009c16087465737432383435009d160874657374" + "32383436009e16087465737432383437009f1608746573743238343800a01608746573743238343900a11608746573" + "743238353000a21608746573743238353100a31608746573743238353200a41608746573743238353300a516087465" + "73743238353400a61608746573743238353500a71608746573743238353600a81608746573743238353700a9160874" + "6573743238353800aa1608746573743238353900ab1608746573743238363000ac1608746573743238363100ad1608" + "746573743238363200ae1608746573743238363300af1608746573743238363400b01608746573743238363500b116" + "08746573743238363600b21608746573743238363700b31608746573743238363800b41608746573743238363900b5" + "1608746573743238373000b61608746573743238373100b71608746573743238373200b81608746573743238373300" + "b91608746573743238373400ba1608746573743238373500bb1608746573743238373600bc16087465737432383737" + "00bd1608746573743238373800be1608746573743238373900bf1608746573743238383000c0160874657374323838" + "3100c11608746573743238383200c21608746573743238383300c31608746573743238383400c41608746573743238" + "383500c51608746573743238383600c61608746573743238383700c71608746573743238383800c816087465737432" + "38383900c91608746573743238393000ca1608746573743238393100cb1608746573743238393200cc160874657374" + "3238393300cd1608746573743238393400ce1608746573743238393500cf1608746573743238393600d01608746573" + "743238393700d11608746573743238393800d21608746573743238393900d31608746573743239303000d416087465" + "73743239303100d51608746573743239303200d61608746573743239303300d71608746573743239303400d8160874" + "6573743239303500d91608746573743239303600da1608746573743239303700db1608746573743239303800dc1608" + "746573743239303900dd1608746573743239313000de1608746573743239313100df1608746573743239313200e016" + "08746573743239313300e11608746573743239313400e21608746573743239313500e31608746573743239313600e4" + "1608746573743239313700e51608746573743239313800e61608746573743239313900e71608746573743239323000" + "e81608746573743239323100e91608746573743239323200ea1608746573743239323300eb16087465737432393234" + "00ec1608746573743239323500ed1608746573743239323600ee1608746573743239323700ef160874657374323932" + "3800f01608746573743239323900f11608746573743239333000f21608746573743239333100f31608746573743239" + "333200f41608746573743239333300f51608746573743239333400f61608746573743239333500f716087465737432" + "39333600f81608746573743239333700f91608746573743239333800fa1608746573743239333900fb160874657374" + "3239343000fc1608746573743239343100fd1608746573743239343200fe1608746573743239343300ff1608746573" + "7432393434008017087465737432393435008117087465737432393436008217087465737432393437008317087465" + "7374323934380084170874657374323934390085170874657374323935300086170874657374323935310087170874" + "65737432393532008817087465737432393533008917087465737432393534008a17087465737432393535008b1708" + "7465737432393536008c17087465737432393537008d17087465737432393538008e17087465737432393539008f17" + "0874657374323936300090170874657374323936310091170874657374323936320092170874657374323936330093" + "1708746573743239363400941708746573743239363500951708746573743239363600961708746573743239363700" + "9717087465737432393638009817087465737432393639009917087465737432393730009a17087465737432393731" + "009b17087465737432393732009c17087465737432393733009d17087465737432393734009e170874657374323937" + "35009f1708746573743239373600a01708746573743239373700a11708746573743239373800a21708746573743239" + "373900a31708746573743239383000a41708746573743239383100a51708746573743239383200a617087465737432" + "39383300a71708746573743239383400a81708746573743239383500a91708746573743239383600aa170874657374" + "3239383700ab1708746573743239383800ac1708746573743239383900ad1708746573743239393000ae1708746573" + "743239393100af1708746573743239393200b01708746573743239393300b11708746573743239393400b217087465" + "73743239393500b31708746573743239393600b41708746573743239393700b51708746573743239393800b6170874" + "6573743239393900b71708746573743330303000b81708746573743330303100b91708746573743330303200ba1708" + "746573743330303300bb1708746573743330303400bc1708746573743330303500bd1708746573743330303600be17" + "08746573743330303700bf1708746573743330303800c01708746573743330303900c11708746573743330313000c2" + "1708746573743330313100c31708746573743330313200c41708746573743330313300c51708746573743330313400" + "c61708746573743330313500c71708746573743330313600c81708746573743330313700c917087465737433303138" + "00ca1708746573743330313900cb1708746573743330323000cc1708746573743330323100cd170874657374333032" + "3200ce1708746573743330323300cf1708746573743330323400d01708746573743330323500d11708746573743330" + "323600d21708746573743330323700d31708746573743330323800d41708746573743330323900d517087465737433" + "30333000d61708746573743330333100d71708746573743330333200d81708746573743330333300d9170874657374" + "3330333400da1708746573743330333500db1708746573743330333600dc1708746573743330333700dd1708746573" + "743330333800de1708746573743330333900df1708746573743330343000e01708746573743330343100e117087465" + "73743330343200e21708746573743330343300e31708746573743330343400e41708746573743330343500e5170874" + "6573743330343600e61708746573743330343700e71708746573743330343800e81708746573743330343900e91708" + "746573743330353000ea1708746573743330353100eb1708746573743330353200ec1708746573743330353300ed17" + "08746573743330353400ee1708746573743330353500ef1708746573743330353600f01708746573743330353700f1" + "1708746573743330353800f21708746573743330353900f31708746573743330363000f41708746573743330363100" + "f51708746573743330363200f61708746573743330363300f71708746573743330363400f817087465737433303635" + "00f91708746573743330363600fa1708746573743330363700fb1708746573743330363800fc170874657374333036" + "3900fd1708746573743330373000fe1708746573743330373100ff1708746573743330373200801808746573743330" + "3733008118087465737433303734008218087465737433303735008318087465737433303736008418087465737433" + "3037370085180874657374333037380086180874657374333037390087180874657374333038300088180874657374" + "33303831008918087465737433303832008a18087465737433303833008b18087465737433303834008c1808746573" + "7433303835008d18087465737433303836008e18087465737433303837008f18087465737433303838009018087465" + "7374333038390091180874657374333039300092180874657374333039310093180874657374333039320094180874" + "6573743330393300951808746573743330393400961808746573743330393500971808746573743330393600981808" + "7465737433303937009918087465737433303938009a18087465737433303939009b18087465737433313030009c18" + "087465737433313031009d18087465737433313032009e18087465737433313033009f1808746573743331303400a0" + "1808746573743331303500a11808746573743331303600a21808746573743331303700a31808746573743331303800" + "a41808746573743331303900a51808746573743331313000a61808746573743331313100a718087465737433313132" + "00a81808746573743331313300a91808746573743331313400aa1808746573743331313500ab180874657374333131" + "3600ac1808746573743331313700ad1808746573743331313800ae1808746573743331313900af1808746573743331" + "323000b01808746573743331323100b11808746573743331323200b21808746573743331323300b318087465737433" + "31323400b41808746573743331323500b51808746573743331323600b61808746573743331323700b7180874657374" + "3331323800b81808746573743331323900b91808746573743331333000ba1808746573743331333100bb1808746573" + "743331333200bc1808746573743331333300bd1808746573743331333400be1808746573743331333500bf18087465" + "73743331333600c01808746573743331333700c11808746573743331333800c21808746573743331333900c3180874" + "6573743331343000c41808746573743331343100c51808746573743331343200c61808746573743331343300c71808" + "746573743331343400c81808746573743331343500c91808746573743331343600ca1808746573743331343700cb18" + "08746573743331343800cc1808746573743331343900cd1808746573743331353000ce1808746573743331353100cf" + "1808746573743331353200d01808746573743331353300d11808746573743331353400d21808746573743331353500" + "d31808746573743331353600d41808746573743331353700d51808746573743331353800d618087465737433313539" + "00d71808746573743331363000d81808746573743331363100d91808746573743331363200da180874657374333136" + "3300db1808746573743331363400dc1808746573743331363500dd1808746573743331363600de1808746573743331" + "363700df1808746573743331363800e01808746573743331363900e11808746573743331373000e218087465737433" + "31373100e31808746573743331373200e41808746573743331373300e51808746573743331373400e6180874657374" + "3331373500e71808746573743331373600e81808746573743331373700e91808746573743331373800ea1808746573" + "743331373900eb1808746573743331383000ec1808746573743331383100ed1808746573743331383200ee18087465" + "73743331383300ef1808746573743331383400f01808746573743331383500f11808746573743331383600f2180874" + "6573743331383700f31808746573743331383800f41808746573743331383900f51808746573743331393000f61808" + "746573743331393100f71808746573743331393200f81808746573743331393300f91808746573743331393400fa18" + "08746573743331393500fb1808746573743331393600fc1808746573743331393700fd1808746573743331393800fe" + "1808746573743331393900ff1808746573743332303000801908746573743332303100811908746573743332303200" + "8219087465737433323033008319087465737433323034008419087465737433323035008519087465737433323036" + "0086190874657374333230370087190874657374333230380088190874657374333230390089190874657374333231" + "30008a19087465737433323131008b19087465737433323132008c19087465737433323133008d1908746573743332" + "3134008e19087465737433323135008f19087465737433323136009019087465737433323137009119087465737433" + "3231380092190874657374333231390093190874657374333232300094190874657374333232310095190874657374" + "3332323200961908746573743332323300971908746573743332323400981908746573743332323500991908746573" + "7433323236009a19087465737433323237009b19087465737433323238009c19087465737433323239009d19087465" + "737433323330009e19087465737433323331009f1908746573743332333200a01908746573743332333300a1190874" + "6573743332333400a21908746573743332333500a31908746573743332333600a41908746573743332333700a51908" + "746573743332333800a61908746573743332333900a71908746573743332343000a81908746573743332343100a919" + "08746573743332343200aa1908746573743332343300ab1908746573743332343400ac1908746573743332343500ad" + "1908746573743332343600ae1908746573743332343700af1908746573743332343800b01908746573743332343900" + "b11908746573743332353000b21908746573743332353100b31908746573743332353200b419087465737433323533" + "00b51908746573743332353400b61908746573743332353500b71908746573743332353600b8190874657374333235" + "3700b91908746573743332353800ba1908746573743332353900bb1908746573743332363000bc1908746573743332" + "363100bd1908746573743332363200be1908746573743332363300bf1908746573743332363400c019087465737433" + "32363500c11908746573743332363600c21908746573743332363700c31908746573743332363800c4190874657374" + "3332363900c51908746573743332373000c61908746573743332373100c71908746573743332373200c81908746573" + "743332373300c91908746573743332373400ca1908746573743332373500cb1908746573743332373600cc19087465" + "73743332373700cd1908746573743332373800ce1908746573743332373900cf1908746573743332383000d0190874" + "6573743332383100d11908746573743332383200d21908746573743332383300d31908746573743332383400d41908" + "746573743332383500d51908746573743332383600d61908746573743332383700d71908746573743332383800d819" + "08746573743332383900d91908746573743332393000da1908746573743332393100db1908746573743332393200dc" + "1908746573743332393300dd1908746573743332393400de1908746573743332393500df1908746573743332393600" + "e01908746573743332393700e11908746573743332393800e21908746573743332393900e319087465737433333030" + "00e41908746573743333303100e51908746573743333303200e61908746573743333303300e7190874657374333330" + "3400e81908746573743333303500e91908746573743333303600ea1908746573743333303700eb1908746573743333" + "303800ec1908746573743333303900ed1908746573743333313000ee1908746573743333313100ef19087465737433" + "33313200f01908746573743333313300f11908746573743333313400f21908746573743333313500f3190874657374" + "3333313600f41908746573743333313700f51908746573743333313800f61908746573743333313900f71908746573" + "743333323000f81908746573743333323100f91908746573743333323200fa1908746573743333323300fb19087465" + "73743333323400fc1908746573743333323500fd1908746573743333323600fe1908746573743333323700ff190874" + "6573743333323800801a08746573743333323900811a08746573743333333000821a08746573743333333100831a08" + "746573743333333200841a08746573743333333300851a08746573743333333400861a08746573743333333500871a" + "08746573743333333600881a08746573743333333700891a087465737433333338008a1a087465737433333339008b" + "1a087465737433333430008c1a087465737433333431008d1a087465737433333432008e1a08746573743333343300" + "8f1a08746573743333343400901a08746573743333343500911a08746573743333343600921a087465737433333437" + "00931a08746573743333343800941a08746573743333343900951a08746573743333353000961a0874657374333335" + "3100971a08746573743333353200981a08746573743333353300991a087465737433333534009a1a08746573743333" + "3535009b1a087465737433333536009c1a087465737433333537009d1a087465737433333538009e1a087465737433" + "333539009f1a08746573743333363000a01a08746573743333363100a11a08746573743333363200a21a0874657374" + "3333363300a31a08746573743333363400a41a08746573743333363500a51a08746573743333363600a61a08746573" + "743333363700a71a08746573743333363800a81a08746573743333363900a91a08746573743333373000aa1a087465" + "73743333373100ab1a08746573743333373200ac1a08746573743333373300ad1a08746573743333373400ae1a0874" + "6573743333373500af1a08746573743333373600b01a08746573743333373700b11a08746573743333373800b21a08" + "746573743333373900b31a08746573743333383000b41a08746573743333383100b51a08746573743333383200b61a" + "08746573743333383300b71a08746573743333383400b81a08746573743333383500b91a08746573743333383600ba" + "1a08746573743333383700bb1a08746573743333383800bc1a08746573743333383900bd1a08746573743333393000" + "be1a08746573743333393100bf1a08746573743333393200c01a08746573743333393300c11a087465737433333934" + "00c21a08746573743333393500c31a08746573743333393600c41a08746573743333393700c51a0874657374333339" + "3800c61a08746573743333393900c71a08746573743334303000c81a08746573743334303100c91a08746573743334" + "303200ca1a08746573743334303300cb1a08746573743334303400cc1a08746573743334303500cd1a087465737433" + "34303600ce1a08746573743334303700cf1a08746573743334303800d01a08746573743334303900d11a0874657374" + "3334313000d21a08746573743334313100d31a08746573743334313200d41a08746573743334313300d51a08746573" + "743334313400d61a08746573743334313500d71a08746573743334313600d81a08746573743334313700d91a087465" + "73743334313800da1a08746573743334313900db1a08746573743334323000dc1a08746573743334323100dd1a0874" + "6573743334323200de1a08746573743334323300df1a08746573743334323400e01a08746573743334323500e11a08" + "746573743334323600e21a08746573743334323700e31a08746573743334323800e41a08746573743334323900e51a" + "08746573743334333000e61a08746573743334333100e71a08746573743334333200e81a08746573743334333300e9" + "1a08746573743334333400ea1a08746573743334333500eb1a08746573743334333600ec1a08746573743334333700" + "ed1a08746573743334333800ee1a08746573743334333900ef1a08746573743334343000f01a087465737433343431" + "00f11a08746573743334343200f21a08746573743334343300f31a08746573743334343400f41a0874657374333434" + "3500f51a08746573743334343600f61a08746573743334343700f71a08746573743334343800f81a08746573743334" + "343900f91a08746573743334353000fa1a08746573743334353100fb1a08746573743334353200fc1a087465737433" + "34353300fd1a08746573743334353400fe1a08746573743334353500ff1a08746573743334353600801b0874657374" + "3334353700811b08746573743334353800821b08746573743334353900831b08746573743334363000841b08746573" + "743334363100851b08746573743334363200861b08746573743334363300871b08746573743334363400881b087465" + "73743334363500891b087465737433343636008a1b087465737433343637008b1b087465737433343638008c1b0874" + "65737433343639008d1b087465737433343730008e1b087465737433343731008f1b08746573743334373200901b08" + "746573743334373300911b08746573743334373400921b08746573743334373500931b08746573743334373600941b" + "08746573743334373700951b08746573743334373800961b08746573743334373900971b0874657374333438300098" + "1b08746573743334383100991b087465737433343832009a1b087465737433343833009b1b08746573743334383400" + "9c1b087465737433343835009d1b087465737433343836009e1b087465737433343837009f1b087465737433343838" + "00a01b08746573743334383900a11b08746573743334393000a21b08746573743334393100a31b0874657374333439" + "3200a41b08746573743334393300a51b08746573743334393400a61b08746573743334393500a71b08746573743334" + "393600a81b08746573743334393700a91b08746573743334393800aa1b08746573743334393900ab1b087465737433" + "35303000ac1b08746573743335303100ad1b08746573743335303200ae1b08746573743335303300af1b0874657374" + "3335303400b01b08746573743335303500b11b08746573743335303600b21b08746573743335303700b31b08746573" + "743335303800b41b08746573743335303900b51b08746573743335313000b61b08746573743335313100b71b087465" + "73743335313200b81b08746573743335313300b91b08746573743335313400ba1b08746573743335313500bb1b0874" + "6573743335313600bc1b08746573743335313700bd1b08746573743335313800be1b08746573743335313900bf1b08" + "746573743335323000c01b08746573743335323100c11b08746573743335323200c21b08746573743335323300c31b" + "08746573743335323400c41b08746573743335323500c51b08746573743335323600c61b08746573743335323700c7" + "1b08746573743335323800c81b08746573743335323900c91b08746573743335333000ca1b08746573743335333100" + "cb1b08746573743335333200cc1b08746573743335333300cd1b08746573743335333400ce1b087465737433353335" + "00cf1b08746573743335333600d01b08746573743335333700d11b08746573743335333800d21b0874657374333533" + "3900d31b08746573743335343000d41b08746573743335343100d51b08746573743335343200d61b08746573743335" + "343300d71b08746573743335343400d81b08746573743335343500d91b08746573743335343600da1b087465737433" + "35343700db1b08746573743335343800dc1b08746573743335343900dd1b08746573743335353000de1b0874657374" + "3335353100df1b08746573743335353200e01b08746573743335353300e11b08746573743335353400e21b08746573" + "743335353500e31b08746573743335353600e41b08746573743335353700e51b08746573743335353800e61b087465" + "73743335353900e71b08746573743335363000e81b08746573743335363100e91b08746573743335363200ea1b0874" + "6573743335363300eb1b08746573743335363400ec1b08746573743335363500ed1b08746573743335363600ee1b08" + "746573743335363700ef1b08746573743335363800f01b08746573743335363900f11b08746573743335373000f21b" + "08746573743335373100f31b08746573743335373200f41b08746573743335373300f51b08746573743335373400f6" + "1b08746573743335373500f71b08746573743335373600f81b08746573743335373700f91b08746573743335373800" + "fa1b08746573743335373900fb1b08746573743335383000fc1b08746573743335383100fd1b087465737433353832" + "00fe1b08746573743335383300ff1b08746573743335383400801c08746573743335383500811c0874657374333538" + "3600821c08746573743335383700831c08746573743335383800841c08746573743335383900851c08746573743335" + "393000861c08746573743335393100871c08746573743335393200881c08746573743335393300891c087465737433" + "353934008a1c087465737433353935008b1c087465737433353936008c1c087465737433353937008d1c0874657374" + "33353938008e1c087465737433353939008f1c08746573743336303000901c08746573743336303100911c08746573" + "743336303200921c08746573743336303300931c08746573743336303400941c08746573743336303500951c087465" + "73743336303600961c08746573743336303700971c08746573743336303800981c08746573743336303900991c0874" + "65737433363130009a1c087465737433363131009b1c087465737433363132009c1c087465737433363133009d1c08" + "7465737433363134009e1c087465737433363135009f1c08746573743336313600a01c08746573743336313700a11c" + "08746573743336313800a21c08746573743336313900a31c08746573743336323000a41c08746573743336323100a5" + "1c08746573743336323200a61c08746573743336323300a71c08746573743336323400a81c08746573743336323500" + "a91c08746573743336323600aa1c08746573743336323700ab1c08746573743336323800ac1c087465737433363239" + "00ad1c08746573743336333000ae1c08746573743336333100af1c08746573743336333200b01c0874657374333633" + "3300b11c08746573743336333400b21c08746573743336333500b31c08746573743336333600b41c08746573743336" + "333700b51c08746573743336333800b61c08746573743336333900b71c08746573743336343000b81c087465737433" + "36343100b91c08746573743336343200ba1c08746573743336343300bb1c08746573743336343400bc1c0874657374" + "3336343500bd1c08746573743336343600be1c08746573743336343700bf1c08746573743336343800c01c08746573" + "743336343900c11c08746573743336353000c21c08746573743336353100c31c08746573743336353200c41c087465" + "73743336353300c51c08746573743336353400c61c08746573743336353500c71c08746573743336353600c81c0874" + "6573743336353700c91c08746573743336353800ca1c08746573743336353900cb1c08746573743336363000cc1c08" + "746573743336363100cd1c08746573743336363200ce1c08746573743336363300cf1c08746573743336363400d01c" + "08746573743336363500d11c08746573743336363600d21c08746573743336363700d31c08746573743336363800d4" + "1c08746573743336363900d51c08746573743336373000d61c08746573743336373100d71c08746573743336373200" + "d81c08746573743336373300d91c08746573743336373400da1c08746573743336373500db1c087465737433363736" + "00dc1c08746573743336373700dd1c08746573743336373800de1c08746573743336373900df1c0874657374333638" + "3000e01c08746573743336383100e11c08746573743336383200e21c08746573743336383300e31c08746573743336" + "383400e41c08746573743336383500e51c08746573743336383600e61c08746573743336383700e71c087465737433" + "36383800e81c08746573743336383900e91c08746573743336393000ea1c08746573743336393100eb1c0874657374" + "3336393200ec1c08746573743336393300ed1c08746573743336393400ee1c08746573743336393500ef1c08746573" + "743336393600f01c08746573743336393700f11c08746573743336393800f21c08746573743336393900f31c087465" + "73743337303000f41c08746573743337303100f51c08746573743337303200f61c08746573743337303300f71c0874" + "6573743337303400f81c08746573743337303500f91c08746573743337303600fa1c08746573743337303700fb1c08" + "746573743337303800fc1c08746573743337303900fd1c08746573743337313000fe1c08746573743337313100ff1c" + "08746573743337313200801d08746573743337313300811d08746573743337313400821d0874657374333731350083" + "1d08746573743337313600841d08746573743337313700851d08746573743337313800861d08746573743337313900" + "871d08746573743337323000881d08746573743337323100891d087465737433373232008a1d087465737433373233" + "008b1d087465737433373234008c1d087465737433373235008d1d087465737433373236008e1d0874657374333732" + "37008f1d08746573743337323800901d08746573743337323900911d08746573743337333000921d08746573743337" + "333100931d08746573743337333200941d08746573743337333300951d08746573743337333400961d087465737433" + "37333500971d08746573743337333600981d08746573743337333700991d087465737433373338009a1d0874657374" + "33373339009b1d087465737433373430009c1d087465737433373431009d1d087465737433373432009e1d08746573" + "7433373433009f1d08746573743337343400a01d08746573743337343500a11d08746573743337343600a21d087465" + "73743337343700a31d08746573743337343800a41d08746573743337343900a51d08746573743337353000a61d0874" + "6573743337353100a71d08746573743337353200a81d08746573743337353300a91d08746573743337353400aa1d08" + "746573743337353500ab1d08746573743337353600ac1d08746573743337353700ad1d08746573743337353800ae1d" + "08746573743337353900af1d08746573743337363000b01d08746573743337363100b11d08746573743337363200b2" + "1d08746573743337363300b31d08746573743337363400b41d08746573743337363500b51d08746573743337363600" + "b61d08746573743337363700b71d08746573743337363800b81d08746573743337363900b91d087465737433373730" + "00ba1d08746573743337373100bb1d08746573743337373200bc1d08746573743337373300bd1d0874657374333737" + "3400be1d08746573743337373500bf1d08746573743337373600c01d08746573743337373700c11d08746573743337" + "373800c21d08746573743337373900c31d08746573743337383000c41d08746573743337383100c51d087465737433" + "37383200c61d08746573743337383300c71d08746573743337383400c81d08746573743337383500c91d0874657374" + "3337383600ca1d08746573743337383700cb1d08746573743337383800cc1d08746573743337383900cd1d08746573" + "743337393000ce1d08746573743337393100cf1d08746573743337393200d01d08746573743337393300d11d087465" + "73743337393400d21d08746573743337393500d31d08746573743337393600d41d08746573743337393700d51d0874" + "6573743337393800d61d08746573743337393900d71d08746573743338303000d81d08746573743338303100d91d08" + "746573743338303200da1d08746573743338303300db1d08746573743338303400dc1d08746573743338303500dd1d" + "08746573743338303600de1d08746573743338303700df1d08746573743338303800e01d08746573743338303900e1" + "1d08746573743338313000e21d08746573743338313100e31d08746573743338313200e41d08746573743338313300" + "e51d08746573743338313400e61d08746573743338313500e71d08746573743338313600e81d087465737433383137" + "00e91d08746573743338313800ea1d08746573743338313900eb1d08746573743338323000ec1d0874657374333832" + "3100ed1d08746573743338323200ee1d08746573743338323300ef1d08746573743338323400f01d08746573743338" + "323500f11d08746573743338323600f21d08746573743338323700f31d08746573743338323800f41d087465737433" + "38323900f51d08746573743338333000f61d08746573743338333100f71d08746573743338333200f81d0874657374" + "3338333300f91d08746573743338333400fa1d08746573743338333500fb1d08746573743338333600fc1d08746573" + "743338333700fd1d08746573743338333800fe1d08746573743338333900ff1d08746573743338343000801e087465" + "73743338343100811e08746573743338343200821e08746573743338343300831e08746573743338343400841e0874" + "6573743338343500851e08746573743338343600861e08746573743338343700871e08746573743338343800881e08" + "746573743338343900891e087465737433383530008a1e087465737433383531008b1e087465737433383532008c1e" + "087465737433383533008d1e087465737433383534008e1e087465737433383535008f1e0874657374333835360090" + "1e08746573743338353700911e08746573743338353800921e08746573743338353900931e08746573743338363000" + "941e08746573743338363100951e08746573743338363200961e08746573743338363300971e087465737433383634" + "00981e08746573743338363500991e087465737433383636009a1e087465737433383637009b1e0874657374333836" + "38009c1e087465737433383639009d1e087465737433383730009e1e087465737433383731009f1e08746573743338" + "373200a01e08746573743338373300a11e08746573743338373400a21e08746573743338373500a31e087465737433" + "38373600a41e08746573743338373700a51e08746573743338373800a61e08746573743338373900a71e0874657374" + "3338383000a81e08746573743338383100a91e08746573743338383200aa1e08746573743338383300ab1e08746573" + "743338383400ac1e08746573743338383500ad1e08746573743338383600ae1e08746573743338383700af1e087465" + "73743338383800b01e08746573743338383900b11e08746573743338393000b21e08746573743338393100b31e0874" + "6573743338393200b41e08746573743338393300b51e08746573743338393400b61e08746573743338393500b71e08" + "746573743338393600b81e08746573743338393700b91e08746573743338393800ba1e08746573743338393900bb1e" + "08746573743339303000bc1e08746573743339303100bd1e08746573743339303200be1e08746573743339303300bf" + "1e08746573743339303400c01e08746573743339303500c11e08746573743339303600c21e08746573743339303700" + "c31e08746573743339303800c41e08746573743339303900c51e08746573743339313000c61e087465737433393131" + "00c71e08746573743339313200c81e08746573743339313300c91e08746573743339313400ca1e0874657374333931" + "3500cb1e08746573743339313600cc1e08746573743339313700cd1e08746573743339313800ce1e08746573743339" + "313900cf1e08746573743339323000d01e08746573743339323100d11e08746573743339323200d21e087465737433" + "39323300d31e08746573743339323400d41e08746573743339323500d51e08746573743339323600d61e0874657374" + "3339323700d71e08746573743339323800d81e08746573743339323900d91e08746573743339333000da1e08746573" + "743339333100db1e08746573743339333200dc1e08746573743339333300dd1e08746573743339333400de1e087465" + "73743339333500df1e08746573743339333600e01e08746573743339333700e11e08746573743339333800e21e0874" + "6573743339333900e31e08746573743339343000e41e08746573743339343100e51e08746573743339343200e61e08" + "746573743339343300e71e08746573743339343400e81e08746573743339343500e91e08746573743339343600ea1e" + "08746573743339343700eb1e08746573743339343800ec1e08746573743339343900ed1e08746573743339353000ee" + "1e08746573743339353100ef1e08746573743339353200f01e08746573743339353300f11e08746573743339353400" + "f21e08746573743339353500f31e08746573743339353600f41e08746573743339353700f51e087465737433393538" + "00f61e08746573743339353900f71e08746573743339363000f81e08746573743339363100f91e0874657374333936" + "3200fa1e08746573743339363300fb1e08746573743339363400fc1e08746573743339363500fd1e08746573743339" + "363600fe1e08746573743339363700ff1e08746573743339363800801f08746573743339363900811f087465737433" + "39373000821f08746573743339373100831f08746573743339373200841f08746573743339373300851f0874657374" + "3339373400861f08746573743339373500871f08746573743339373600881f08746573743339373700891f08746573" + "7433393738008a1f087465737433393739008b1f087465737433393830008c1f087465737433393831008d1f087465" + "737433393832008e1f087465737433393833008f1f08746573743339383400901f08746573743339383500911f0874" + "6573743339383600921f08746573743339383700931f08746573743339383800941f08746573743339383900951f08" + "746573743339393000961f08746573743339393100971f08746573743339393200981f08746573743339393300991f" + "087465737433393934009a1f087465737433393935009b1f087465737433393936009c1f087465737433393937009d" + "1f087465737433393938009e1f087465737433393939009f1f08746573743430303000a01f08746573743430303100" + "a11f08746573743430303200a21f08746573743430303300a31f08746573743430303400a41f087465737434303035" + "00a51f08746573743430303600a61f08746573743430303700a71f08746573743430303800a81f0874657374343030" + "3900a91f08746573743430313000aa1f08746573743430313100ab1f08746573743430313200ac1f08746573743430" + "313300ad1f08746573743430313400ae1f08746573743430313500af1f08746573743430313600b01f087465737434" + "30313700b11f08746573743430313800b21f08746573743430313900b31f08746573743430323000b41f0874657374" + "3430323100b51f08746573743430323200b61f08746573743430323300b71f08746573743430323400b81f08746573" + "743430323500b91f08746573743430323600ba1f08746573743430323700bb1f08746573743430323800bc1f087465" + "73743430323900bd1f08746573743430333000be1f08746573743430333100bf1f08746573743430333200c01f0874" + "6573743430333300c11f08746573743430333400c21f08746573743430333500c31f08746573743430333600c41f08" + "746573743430333700c51f08746573743430333800c61f08746573743430333900c71f08746573743430343000c81f" + "08746573743430343100c91f08746573743430343200ca1f08746573743430343300cb1f08746573743430343400cc" + "1f08746573743430343500cd1f08746573743430343600ce1f08746573743430343700cf1f08746573743430343800" + "d01f08746573743430343900d11f08746573743430353000d21f08746573743430353100d31f087465737434303532" + "00d41f08746573743430353300d51f08746573743430353400d61f08746573743430353500d71f0874657374343035" + "3600d81f08746573743430353700d91f08746573743430353800da1f08746573743430353900db1f08746573743430" + "363000dc1f08746573743430363100dd1f08746573743430363200de1f08746573743430363300df1f087465737434" + "30363400e01f08746573743430363500e11f08746573743430363600e21f08746573743430363700e31f0874657374" + "3430363800e41f08746573743430363900e51f08746573743430373000e61f08746573743430373100e71f08746573" + "743430373200e81f08746573743430373300e91f08746573743430373400ea1f08746573743430373500eb1f087465" + "73743430373600ec1f08746573743430373700ed1f08746573743430373800ee1f08746573743430373900ef1f0874" + "6573743430383000f01f08746573743430383100f11f08746573743430383200f21f08746573743430383300f31f08" + "746573743430383400f41f08746573743430383500f51f08746573743430383600f61f08746573743430383700f71f" + "08746573743430383800f81f08746573743430383900f91f08746573743430393000fa1f08746573743430393100fb" + "1f08746573743430393200fc1f08746573743430393300fd1f08746573743430393400fe1f08746573743430393500" + "ff1f087465737434303936008020087465737434303937008120087465737434303938008220087465737434303939" + "0083200874657374343130300084200874657374343130310085200874657374343130320086200874657374343130" + "33008720087465737434313034008820087465737434313035008920087465737434313036008a2008746573743431" + "3037008b20087465737434313038008c20087465737434313039008d20087465737434313130008e20087465737434" + "313131008f200874657374343131320090200874657374343131330091200874657374343131340092200874657374" + "3431313500932008746573743431313600942008746573743431313700952008746573743431313800962008746573" + "7434313139009720087465737434313230009820087465737434313231009920087465737434313232009a20087465" + "737434313233009b20087465737434313234009c20087465737434313235009d20087465737434313236009e200874" + "65737434313237009f2008746573743431323800a02008746573743431323900a12008746573743431333000a22008" + "746573743431333100a32008746573743431333200a42008746573743431333300a52008746573743431333400a620" + "08746573743431333500a72008746573743431333600a82008746573743431333700a92008746573743431333800aa" + "2008746573743431333900ab2008746573743431343000ac2008746573743431343100ad2008746573743431343200" + "ae2008746573743431343300af2008746573743431343400b02008746573743431343500b120087465737434313436" + "00b22008746573743431343700b32008746573743431343800b42008746573743431343900b5200874657374343135" + "3000b62008746573743431353100b72008746573743431353200b82008746573743431353300b92008746573743431" + "353400ba2008746573743431353500bb2008746573743431353600bc2008746573743431353700bd20087465737434" + "31353800be2008746573743431353900bf2008746573743431363000c02008746573743431363100c1200874657374" + "3431363200c22008746573743431363300c32008746573743431363400c42008746573743431363500c52008746573" + "743431363600c62008746573743431363700c72008746573743431363800c82008746573743431363900c920087465" + "73743431373000ca2008746573743431373100cb2008746573743431373200cc2008746573743431373300cd200874" + "6573743431373400ce2008746573743431373500cf2008746573743431373600d02008746573743431373700d12008" + "746573743431373800d22008746573743431373900d32008746573743431383000d42008746573743431383100d520" + "08746573743431383200d62008746573743431383300d72008746573743431383400d82008746573743431383500d9" + "2008746573743431383600da2008746573743431383700db2008746573743431383800dc2008746573743431383900" + "dd2008746573743431393000de2008746573743431393100df2008746573743431393200e020087465737434313933" + "00e12008746573743431393400e22008746573743431393500e32008746573743431393600e4200874657374343139" + "3700e52008746573743431393800e62008746573743431393900e72008746573743432303000e82008746573743432" + "303100e92008746573743432303200ea2008746573743432303300eb2008746573743432303400ec20087465737434" + "32303500ed2008746573743432303600ee2008746573743432303700ef2008746573743432303800f0200874657374" + "3432303900f12008746573743432313000f22008746573743432313100f32008746573743432313200f42008746573" + "743432313300f52008746573743432313400f62008746573743432313500f72008746573743432313600f820087465" + "73743432313700f92008746573743432313800fa2008746573743432313900fb2008746573743432323000fc200874" + "6573743432323100fd2008746573743432323200fe2008746573743432323300ff2008746573743432323400802108" + "7465737434323235008121087465737434323236008221087465737434323237008321087465737434323238008421" + "0874657374343232390085210874657374343233300086210874657374343233310087210874657374343233320088" + "21087465737434323333008921087465737434323334008a21087465737434323335008b2108746573743432333600" + "8c21087465737434323337008d21087465737434323338008e21087465737434323339008f21087465737434323430" + "0090210874657374343234310091210874657374343234320092210874657374343234330093210874657374343234" + "3400942108746573743432343500952108746573743432343600962108746573743432343700972108746573743432" + "3438009821087465737434323439009921087465737434323530009a21087465737434323531009b21087465737434" + "323532009c21087465737434323533009d21087465737434323534009e21087465737434323535009f210874657374" + "3432353600a02108746573743432353700a12108746573743432353800a22108746573743432353900a32108746573" + "743432363000a42108746573743432363100a52108746573743432363200a62108746573743432363300a721087465" + "73743432363400a82108746573743432363500a92108746573743432363600aa2108746573743432363700ab210874" + "6573743432363800ac2108746573743432363900ad2108746573743432373000ae2108746573743432373100af2108" + "746573743432373200b02108746573743432373300b12108746573743432373400b22108746573743432373500b321" + "08746573743432373600b42108746573743432373700b52108746573743432373800b62108746573743432373900b7" + "2108746573743432383000b82108746573743432383100b92108746573743432383200ba2108746573743432383300" + "bb2108746573743432383400bc2108746573743432383500bd2108746573743432383600be21087465737434323837" + "00bf2108746573743432383800c02108746573743432383900c12108746573743432393000c2210874657374343239" + "3100c32108746573743432393200c42108746573743432393300c52108746573743432393400c62108746573743432" + "393500c72108746573743432393600c82108746573743432393700c92108746573743432393800ca21087465737434" + "32393900cb2108746573743433303000cc2108746573743433303100cd2108746573743433303200ce210874657374" + "3433303300cf2108746573743433303400d02108746573743433303500d12108746573743433303600d22108746573" + "743433303700d32108746573743433303800d42108746573743433303900d52108746573743433313000d621087465" + "73743433313100d72108746573743433313200d82108746573743433313300d92108746573743433313400da210874" + "6573743433313500db2108746573743433313600dc2108746573743433313700dd2108746573743433313800de2108" + "746573743433313900df2108746573743433323000e02108746573743433323100e12108746573743433323200e221" + "08746573743433323300e32108746573743433323400e42108746573743433323500e52108746573743433323600e6" + "2108746573743433323700e72108746573743433323800e82108746573743433323900e92108746573743433333000" + "ea2108746573743433333100eb2108746573743433333200ec2108746573743433333300ed21087465737434333334" + "00ee2108746573743433333500ef2108746573743433333600f02108746573743433333700f1210874657374343333" + "3800f22108746573743433333900f32108746573743433343000f42108746573743433343100f52108746573743433" + "343200f62108746573743433343300f72108746573743433343400f82108746573743433343500f921087465737434" + "33343600fa2108746573743433343700fb2108746573743433343800fc2108746573743433343900fd210874657374" + "3433353000fe2108746573743433353100ff2108746573743433353200802208746573743433353300812208746573" + "7434333534008222087465737434333535008322087465737434333536008422087465737434333537008522087465" + "7374343335380086220874657374343335390087220874657374343336300088220874657374343336310089220874" + "65737434333632008a22087465737434333633008b22087465737434333634008c22087465737434333635008d2208" + "7465737434333636008e22087465737434333637008f22087465737434333638009022087465737434333639009122" + "0874657374343337300092220874657374343337310093220874657374343337320094220874657374343337330095" + "2208746573743433373400962208746573743433373500972208746573743433373600982208746573743433373700" + "9922087465737434333738009a22087465737434333739009b22087465737434333830009c22087465737434333831" + "009d22087465737434333832009e22087465737434333833009f2208746573743433383400a0220874657374343338" + "3500a12208746573743433383600a22208746573743433383700a32208746573743433383800a42208746573743433" + "383900a52208746573743433393000a62208746573743433393100a72208746573743433393200a822087465737434" + "33393300a92208746573743433393400aa2208746573743433393500ab2208746573743433393600ac220874657374" + "3433393700ad2208746573743433393800ae2208746573743433393900af2208746573743434303000b02208746573" + "743434303100b12208746573743434303200b22208746573743434303300b32208746573743434303400b422087465" + "73743434303500b52208746573743434303600b62208746573743434303700b72208746573743434303800b8220874" + "6573743434303900b92208746573743434313000ba2208746573743434313100bb2208746573743434313200bc2208" + "746573743434313300bd2208746573743434313400be2208746573743434313500bf2208746573743434313600c022" + "08746573743434313700c12208746573743434313800c22208746573743434313900c32208746573743434323000c4" + "2208746573743434323100c52208746573743434323200c62208746573743434323300c72208746573743434323400" + "c82208746573743434323500c92208746573743434323600ca2208746573743434323700cb22087465737434343238" + "00cc2208746573743434323900cd2208746573743434333000ce2208746573743434333100cf220874657374343433" + "3200d02208746573743434333300d12208746573743434333400d22208746573743434333500d32208746573743434" + "333600d42208746573743434333700d52208746573743434333800d62208746573743434333900d722087465737434" + "34343000d82208746573743434343100d92208746573743434343200da2208746573743434343300db220874657374" + "3434343400dc2208746573743434343500dd2208746573743434343600de2208746573743434343700df2208746573" + "743434343800e02208746573743434343900e12208746573743434353000e22208746573743434353100e322087465" + "73743434353200e42208746573743434353300e52208746573743434353400e62208746573743434353500e7220874" + "6573743434353600e82208746573743434353700e92208746573743434353800ea2208746573743434353900eb2208" + "746573743434363000ec2208746573743434363100ed2208746573743434363200ee2208746573743434363300ef22" + "08746573743434363400f02208746573743434363500f12208746573743434363600f22208746573743434363700f3" + "2208746573743434363800f42208746573743434363900f52208746573743434373000f62208746573743434373100" + "f72208746573743434373200f82208746573743434373300f92208746573743434373400fa22087465737434343735" + "00fb2208746573743434373600fc2208746573743434373700fd2208746573743434373800fe220874657374343437" + "3900ff2208746573743434383000802308746573743434383100812308746573743434383200822308746573743434" + "3833008323087465737434343834008423087465737434343835008523087465737434343836008623087465737434" + "343837008723087465737434343838008823087465737434343839008923087465737434343930008a230874657374" + "34343931008b23087465737434343932008c23087465737434343933008d23087465737434343934008e2308746573" + "7434343935008f23087465737434343936009023087465737434343937009123087465737434343938009223087465" + "7374343439390093230874657374343530300094230874657374343530310095230874657374343530320096230874" + "65737434353033009723087465737434353034009823087465737434353035009923087465737434353036009a2308" + "7465737434353037009b23087465737434353038009c23087465737434353039009d23087465737434353130009e23" + "087465737434353131009f2308746573743435313200a02308746573743435313300a12308746573743435313400a2" + "2308746573743435313500a32308746573743435313600a42308746573743435313700a52308746573743435313800" + "a62308746573743435313900a72308746573743435323000a82308746573743435323100a923087465737434353232" + "00aa2308746573743435323300ab2308746573743435323400ac2308746573743435323500ad230874657374343532" + "3600ae2308746573743435323700af2308746573743435323800b02308746573743435323900b12308746573743435" + "333000b22308746573743435333100b32308746573743435333200b42308746573743435333300b523087465737434" + "35333400b62308746573743435333500b72308746573743435333600b82308746573743435333700b9230874657374" + "3435333800ba2308746573743435333900bb2308746573743435343000bc2308746573743435343100bd2308746573" + "743435343200be2308746573743435343300bf2308746573743435343400c02308746573743435343500c123087465" + "73743435343600c22308746573743435343700c32308746573743435343800c42308746573743435343900c5230874" + "6573743435353000c62308746573743435353100c72308746573743435353200c82308746573743435353300c92308" + "746573743435353400ca2308746573743435353500cb2308746573743435353600cc2308746573743435353700cd23" + "08746573743435353800ce2308746573743435353900cf2308746573743435363000d02308746573743435363100d1" + "2308746573743435363200d22308746573743435363300d32308746573743435363400d42308746573743435363500" + "d52308746573743435363600d62308746573743435363700d72308746573743435363800d823087465737434353639" + "00d92308746573743435373000da2308746573743435373100db2308746573743435373200dc230874657374343537" + "3300dd2308746573743435373400de2308746573743435373500df2308746573743435373600e02308746573743435" + "373700e12308746573743435373800e22308746573743435373900e32308746573743435383000e423087465737434" + "35383100e52308746573743435383200e62308746573743435383300e72308746573743435383400e8230874657374" + "3435383500e92308746573743435383600ea2308746573743435383700eb2308746573743435383800ec2308746573" + "743435383900ed2308746573743435393000ee2308746573743435393100ef2308746573743435393200f023087465" + "73743435393300f12308746573743435393400f22308746573743435393500f32308746573743435393600f4230874" + "6573743435393700f52308746573743435393800f62308746573743435393900f72308746573743436303000f82308" + "746573743436303100f92308746573743436303200fa2308746573743436303300fb2308746573743436303400fc23" + "08746573743436303500fd2308746573743436303600fe2308746573743436303700ff230874657374343630380080" + "2408746573743436303900812408746573743436313000822408746573743436313100832408746573743436313200" + "8424087465737434363133008524087465737434363134008624087465737434363135008724087465737434363136" + "008824087465737434363137008924087465737434363138008a24087465737434363139008b240874657374343632" + "30008c24087465737434363231008d24087465737434363232008e24087465737434363233008f2408746573743436" + "3234009024087465737434363235009124087465737434363236009224087465737434363237009324087465737434" + "3632380094240874657374343632390095240874657374343633300096240874657374343633310097240874657374" + "34363332009824087465737434363333009924087465737434363334009a24087465737434363335009b2408746573" + "7434363336009c24087465737434363337009d24087465737434363338009e24087465737434363339009f24087465" + "73743436343000a02408746573743436343100a12408746573743436343200a22408746573743436343300a3240874" + "6573743436343400a42408746573743436343500a52408746573743436343600a62408746573743436343700a72408" + "746573743436343800a82408746573743436343900a92408746573743436353000aa2408746573743436353100ab24" + "08746573743436353200ac2408746573743436353300ad2408746573743436353400ae2408746573743436353500af" + "2408746573743436353600b02408746573743436353700b12408746573743436353800b22408746573743436353900" + "b32408746573743436363000b42408746573743436363100b52408746573743436363200b624087465737434363633" + "00b72408746573743436363400b82408746573743436363500b92408746573743436363600ba240874657374343636" + "3700bb2408746573743436363800bc2408746573743436363900bd2408746573743436373000be2408746573743436" + "373100bf2408746573743436373200c02408746573743436373300c12408746573743436373400c224087465737434" + "36373500c32408746573743436373600c42408746573743436373700c52408746573743436373800c6240874657374" + "3436373900c72408746573743436383000c82408746573743436383100c92408746573743436383200ca2408746573" + "743436383300cb2408746573743436383400cc2408746573743436383500cd2408746573743436383600ce24087465" + "73743436383700cf2408746573743436383800d02408746573743436383900d12408746573743436393000d2240874" + "6573743436393100d32408746573743436393200d42408746573743436393300d52408746573743436393400d62408" + "746573743436393500d72408746573743436393600d82408746573743436393700d92408746573743436393800da24" + "08746573743436393900db2408746573743437303000dc2408746573743437303100dd2408746573743437303200de" + "2408746573743437303300df2408746573743437303400e02408746573743437303500e12408746573743437303600" + "e22408746573743437303700e32408746573743437303800e42408746573743437303900e524087465737434373130" + "00e62408746573743437313100e72408746573743437313200e82408746573743437313300e9240874657374343731" + "3400ea2408746573743437313500eb2408746573743437313600ec2408746573743437313700ed2408746573743437" + "313800ee2408746573743437313900ef2408746573743437323000f02408746573743437323100f124087465737434" + "37323200f22408746573743437323300f32408746573743437323400f42408746573743437323500f5240874657374" + "3437323600f62408746573743437323700f72408746573743437323800f82408746573743437323900f92408746573" + "743437333000fa2408746573743437333100fb2408746573743437333200fc2408746573743437333300fd24087465" + "73743437333400fe2408746573743437333500ff240874657374343733360080250874657374343733370081250874" + "6573743437333800822508746573743437333900832508746573743437343000842508746573743437343100852508" + "7465737434373432008625087465737434373433008725087465737434373434008825087465737434373435008925" + "087465737434373436008a25087465737434373437008b25087465737434373438008c25087465737434373439008d" + "25087465737434373530008e25087465737434373531008f2508746573743437353200902508746573743437353300" + "9125087465737434373534009225087465737434373535009325087465737434373536009425087465737434373537" + "0095250874657374343735380096250874657374343735390097250874657374343736300098250874657374343736" + "31009925087465737434373632009a25087465737434373633009b25087465737434373634009c2508746573743437" + "3635009d25087465737434373636009e25087465737434373637009f2508746573743437363800a025087465737434" + "37363900a12508746573743437373000a22508746573743437373100a32508746573743437373200a4250874657374" + "3437373300a52508746573743437373400a62508746573743437373500a72508746573743437373600a82508746573" + "743437373700a92508746573743437373800aa2508746573743437373900ab2508746573743437383000ac25087465" + "73743437383100ad2508746573743437383200ae2508746573743437383300af2508746573743437383400b0250874" + "6573743437383500b12508746573743437383600b22508746573743437383700b32508746573743437383800b42508" + "746573743437383900b52508746573743437393000b62508746573743437393100b72508746573743437393200b825" + "08746573743437393300b92508746573743437393400ba2508746573743437393500bb2508746573743437393600bc" + "2508746573743437393700bd2508746573743437393800be2508746573743437393900bf2508746573743438303000" + "c02508746573743438303100c12508746573743438303200c22508746573743438303300c325087465737434383034" + "00c42508746573743438303500c52508746573743438303600c62508746573743438303700c7250874657374343830" + "3800c82508746573743438303900c92508746573743438313000ca2508746573743438313100cb2508746573743438" + "313200cc2508746573743438313300cd2508746573743438313400ce2508746573743438313500cf25087465737434" + "38313600d02508746573743438313700d12508746573743438313800d22508746573743438313900d3250874657374" + "3438323000d42508746573743438323100d52508746573743438323200d62508746573743438323300d72508746573" + "743438323400d82508746573743438323500d92508746573743438323600da2508746573743438323700db25087465" + "73743438323800dc2508746573743438323900dd2508746573743438333000de2508746573743438333100df250874" + "6573743438333200e02508746573743438333300e12508746573743438333400e22508746573743438333500e32508" + "746573743438333600e42508746573743438333700e52508746573743438333800e62508746573743438333900e725" + "08746573743438343000e82508746573743438343100e92508746573743438343200ea2508746573743438343300eb" + "2508746573743438343400ec2508746573743438343500ed2508746573743438343600ee2508746573743438343700" + "ef2508746573743438343800f02508746573743438343900f12508746573743438353000f225087465737434383531" + "00f32508746573743438353200f42508746573743438353300f52508746573743438353400f6250874657374343835" + "3500f72508746573743438353600f82508746573743438353700f92508746573743438353800fa2508746573743438" + "353900fb2508746573743438363000fc2508746573743438363100fd2508746573743438363200fe25087465737434" + "38363300ff250874657374343836340080260874657374343836350081260874657374343836360082260874657374" + "3438363700832608746573743438363800842608746573743438363900852608746573743438373000862608746573" + "7434383731008726087465737434383732008826087465737434383733008926087465737434383734008a26087465" + "737434383735008b26087465737434383736008c26087465737434383737008d26087465737434383738008e260874" + "65737434383739008f2608746573743438383000902608746573743438383100912608746573743438383200922608" + "7465737434383833009326087465737434383834009426087465737434383835009526087465737434383836009626" + "087465737434383837009726087465737434383838009826087465737434383839009926087465737434383930009a" + "26087465737434383931009b26087465737434383932009c26087465737434383933009d2608746573743438393400" + "9e26087465737434383935009f2608746573743438393600a02608746573743438393700a126087465737434383938" + "00a22608746573743438393900a32608746573743439303000a42608746573743439303100a5260874657374343930" + "3200a62608746573743439303300a72608746573743439303400a82608746573743439303500a92608746573743439" + "303600aa2608746573743439303700ab2608746573743439303800ac2608746573743439303900ad26087465737434" + "39313000ae2608746573743439313100af2608746573743439313200b02608746573743439313300b1260874657374" + "3439313400b22608746573743439313500b32608746573743439313600b42608746573743439313700b52608746573" + "743439313800b62608746573743439313900b72608746573743439323000b82608746573743439323100b926087465" + "73743439323200ba2608746573743439323300bb2608746573743439323400bc2608746573743439323500bd260874" + "6573743439323600be2608746573743439323700bf2608746573743439323800c02608746573743439323900c12608" + "746573743439333000c22608746573743439333100c32608746573743439333200c42608746573743439333300c526" + "08746573743439333400c62608746573743439333500c72608746573743439333600c82608746573743439333700c9" + "2608746573743439333800ca2608746573743439333900cb2608746573743439343000cc2608746573743439343100" + "cd2608746573743439343200ce2608746573743439343300cf2608746573743439343400d026087465737434393435" + "00d12608746573743439343600d22608746573743439343700d32608746573743439343800d4260874657374343934" + "3900d52608746573743439353000d62608746573743439353100d72608746573743439353200d82608746573743439" + "353300d92608746573743439353400da2608746573743439353500db2608746573743439353600dc26087465737434" + "39353700dd2608746573743439353800de2608746573743439353900df2608746573743439363000e0260874657374" + "3439363100e12608746573743439363200e22608746573743439363300e32608746573743439363400e42608746573" + "743439363500e52608746573743439363600e62608746573743439363700e72608746573743439363800e826087465" + "73743439363900e92608746573743439373000ea2608746573743439373100eb2608746573743439373200ec260874" + "6573743439373300ed2608746573743439373400ee2608746573743439373500ef2608746573743439373600f02608" + "746573743439373700f12608746573743439373800f22608746573743439373900f32608746573743439383000f426" + "08746573743439383100f52608746573743439383200f62608746573743439383300f72608746573743439383400f8" + "2608746573743439383500f92608746573743439383600fa2608746573743439383700fb2608746573743439383800" + "fc2608746573743439383900fd2608746573743439393000fe2608746573743439393100ff26087465737434393932" + "0080270874657374343939330081270874657374343939340082270874657374343939350083270874657374343939" + "360084270874657374343939370085270874657374343939380086270874657374343939390087270ac2b802882707" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b"; + +extern std::string const opcReservedHex = + "0061736d010000000105016000017f03030200000404017000010503010001060b027f0141000b7e0142000b071401" + "10616c6c5f696e737472756374696f6e7300010907010041000b01000a53020400412a0b4c02017f017e0101010101" + "0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + "01010101010101010101010101010101410b0b0b0a010041000b0474657374"; + +extern std::string const impExpHex = + "0061736d0100000001100360027f7f017f6000017f60017f017f02330203656e760e6765745f6c65646765725f7371" + "6e000003656e76166765745f706172656e745f6c65646765725f686173680000030403010201050301000107310406" + "6d656d6f72790200096578705f66756e63310002096578705f66756e633200030c746573745f696d706f7274730004" + "0a2b03040041010b0700200041026c0b1c01027f4120410410001a41202802002100410041201001210120000b"; extern std::string const updateDataWasmHex = "0061736d01000000010e0360027f7f017f6000006000017f02130103656e760b7570646174" diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index 50ab5d653e..96b22268f6 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -59,27 +59,14 @@ generateDataBlob(uint32_t data_size); } // namespace wasm_constants extern std::string const ledgerSqnWasmHex; - extern std::string const allHostFunctionsWasmHex; - -extern std::string const deepRecursionHex; +extern std::string const allKeyletsWasmHex; +extern std::string const codecovTestsWasmHex; extern std::string const fibWasmHex; -extern std::string const b58WasmHex; - -extern std::string const sha512PureWasmHex; - -extern std::string const hfPerfTest; - -extern std::string const allKeyletsWasmHex; - -extern std::string const codecovTestsWasmHex; - extern std::string const floatTestsWasmHex; - extern std::string const float0Hex; - extern std::string const disabledFloatHex; extern std::string const memoryPointerAtLimitHex; @@ -134,10 +121,18 @@ extern std::string const junkAfterSectionHex; extern std::string const invalidSectionIdHex; extern std::string const localVariableBombHex; +extern std::string const deepRecursionHex; extern std::string const infiniteLoopWasmHex; extern std::string const startLoopHex; -extern std::string const badAllocHex; extern std::string const badAlignWasmHex; +extern std::string const thousandParamsHex; +extern std::string const thousand1ParamsHex; +extern std::string const locals10kHex; +extern std::string const functions5kHex; + +extern std::string const opcReservedHex; + +extern std::string const impExpHex; extern std::string const updateDataWasmHex; diff --git a/src/test/app/wasm_fixtures/infiniteLoop.c b/src/test/app/wasm_fixtures/infiniteLoop.c index 037a843cac..ba84a92ac1 100644 --- a/src/test/app/wasm_fixtures/infiniteLoop.c +++ b/src/test/app/wasm_fixtures/infiniteLoop.c @@ -1,8 +1,7 @@ -int -loop() +int loop() { - int volatile x = 0; - while (1) - x++; - return x; + int volatile x = 0; + while (1) + x++; + return x; } diff --git a/src/test/app/wasm_fixtures/ledgerSqn.c b/src/test/app/wasm_fixtures/ledgerSqn.c index 020fafc78f..e4d57b8593 100644 --- a/src/test/app/wasm_fixtures/ledgerSqn.c +++ b/src/test/app/wasm_fixtures/ledgerSqn.c @@ -1,16 +1,14 @@ #include -int32_t -get_ledger_sqn(uint8_t*, int32_t); +int32_t get_ledger_sqn(uint8_t *, int32_t); -int -finish() +int finish() { - uint32_t sqn; - int32_t result = get_ledger_sqn((uint8_t*)&sqn, sizeof(sqn)); + uint32_t sqn; + int32_t result = get_ledger_sqn((uint8_t *)&sqn, sizeof(sqn)); - if (result < 0) - return result; + if (result < 0) + return result; - return sqn >= 5 ? 5 : 0; + return sqn >= 5 ? 5 : 0; } diff --git a/src/test/app/wasm_fixtures/sha512Pure.c b/src/test/app/wasm_fixtures/sha512Pure.c deleted file mode 100644 index 2c736bb218..0000000000 --- a/src/test/app/wasm_fixtures/sha512Pure.c +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include - -static uint64_t const K512[] = { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, - 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, - 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, - 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, - 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, - 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, - 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, - 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, - 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, - 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, - 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, - 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, - 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817}; - -#define ROTATE(x, y) (((x) >> (y)) | ((x) << (64 - (y)))) -#define Sigma0(x) (ROTATE((x), 28) ^ ROTATE((x), 34) ^ ROTATE((x), 39)) -#define Sigma1(x) (ROTATE((x), 14) ^ ROTATE((x), 18) ^ ROTATE((x), 41)) -#define sigma0(x) (ROTATE((x), 1) ^ ROTATE((x), 8) ^ ((x) >> 7)) -#define sigma1(x) (ROTATE((x), 19) ^ ROTATE((x), 61) ^ ((x) >> 6)) - -#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) -#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) - -static inline uint64_t -B2U64(uint8_t val, uint8_t sh) -{ - return ((uint64_t)val) << sh; -} - -void* -allocate(int sz) -{ - return malloc(sz); -} -void -deallocate(void* p) -{ - free(p); -} - -uint8_t e_data[32 * 1024]; - -uint8_t* -sha512_process(uint8_t const* data, int32_t length) -{ - static uint64_t state[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - uint64_t a, b, c, d, e, f, g, h, s0, s1, T1, T2; - uint64_t X[16]; - - uint64_t blocks = length / 128; - while (blocks--) - { - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - f = state[5]; - g = state[6]; - h = state[7]; - - unsigned i; - for (i = 0; i < 16; i++) - { - X[i] = B2U64(data[0], 56) | B2U64(data[1], 48) | B2U64(data[2], 40) | B2U64(data[3], 32) | - B2U64(data[4], 24) | B2U64(data[5], 16) | B2U64(data[6], 8) | B2U64(data[7], 0); - data += 8; - - T1 = h; - T1 += Sigma1(e); - T1 += Ch(e, f, g); - T1 += K512[i]; - T1 += X[i]; - - T2 = Sigma0(a); - T2 += Maj(a, b, c); - - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - } - - for (i = 16; i < 80; i++) - { - s0 = X[(i + 1) & 0x0f]; - s0 = sigma0(s0); - s1 = X[(i + 14) & 0x0f]; - s1 = sigma1(s1); - - T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf]; - T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i]; - T2 = Sigma0(a) + Maj(a, b, c); - - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - } - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - } - - return (uint8_t*)(state); -} - -// int main () -//{ -// return 0; -// } diff --git a/src/test/app/wasm_fixtures/thousand1_params.c b/src/test/app/wasm_fixtures/thousand1_params.c new file mode 100644 index 0000000000..1a281461c4 --- /dev/null +++ b/src/test/app/wasm_fixtures/thousand1_params.c @@ -0,0 +1,264 @@ +// clang-format off + +#include + +int32_t test( + int32_t p0, int32_t p1, int32_t p2, int32_t p3, int32_t p4, int32_t p5, int32_t p6, int32_t p7 +, int32_t p8, int32_t p9, int32_t p10, int32_t p11, int32_t p12, int32_t p13, int32_t p14, int32_t p15 +, int32_t p16, int32_t p17, int32_t p18, int32_t p19, int32_t p20, int32_t p21, int32_t p22, int32_t p23 +, int32_t p24, int32_t p25, int32_t p26, int32_t p27, int32_t p28, int32_t p29, int32_t p30, int32_t p31 +, int32_t p32, int32_t p33, int32_t p34, int32_t p35, int32_t p36, int32_t p37, int32_t p38, int32_t p39 +, int32_t p40, int32_t p41, int32_t p42, int32_t p43, int32_t p44, int32_t p45, int32_t p46, int32_t p47 +, int32_t p48, int32_t p49, int32_t p50, int32_t p51, int32_t p52, int32_t p53, int32_t p54, int32_t p55 +, int32_t p56, int32_t p57, int32_t p58, int32_t p59, int32_t p60, int32_t p61, int32_t p62, int32_t p63 +, int32_t p64, int32_t p65, int32_t p66, int32_t p67, int32_t p68, int32_t p69, int32_t p70, int32_t p71 +, int32_t p72, int32_t p73, int32_t p74, int32_t p75, int32_t p76, int32_t p77, int32_t p78, int32_t p79 +, int32_t p80, int32_t p81, int32_t p82, int32_t p83, int32_t p84, int32_t p85, int32_t p86, int32_t p87 +, int32_t p88, int32_t p89, int32_t p90, int32_t p91, int32_t p92, int32_t p93, int32_t p94, int32_t p95 +, int32_t p96, int32_t p97, int32_t p98, int32_t p99, int32_t p100, int32_t p101, int32_t p102, int32_t p103 +, int32_t p104, int32_t p105, int32_t p106, int32_t p107, int32_t p108, int32_t p109, int32_t p110, int32_t p111 +, int32_t p112, int32_t p113, int32_t p114, int32_t p115, int32_t p116, int32_t p117, int32_t p118, int32_t p119 +, int32_t p120, int32_t p121, int32_t p122, int32_t p123, int32_t p124, int32_t p125, int32_t p126, int32_t p127 +, int32_t p128, int32_t p129, int32_t p130, int32_t p131, int32_t p132, int32_t p133, int32_t p134, int32_t p135 +, int32_t p136, int32_t p137, int32_t p138, int32_t p139, int32_t p140, int32_t p141, int32_t p142, int32_t p143 +, int32_t p144, int32_t p145, int32_t p146, int32_t p147, int32_t p148, int32_t p149, int32_t p150, int32_t p151 +, int32_t p152, int32_t p153, int32_t p154, int32_t p155, int32_t p156, int32_t p157, int32_t p158, int32_t p159 +, int32_t p160, int32_t p161, int32_t p162, int32_t p163, int32_t p164, int32_t p165, int32_t p166, int32_t p167 +, int32_t p168, int32_t p169, int32_t p170, int32_t p171, int32_t p172, int32_t p173, int32_t p174, int32_t p175 +, int32_t p176, int32_t p177, int32_t p178, int32_t p179, int32_t p180, int32_t p181, int32_t p182, int32_t p183 +, int32_t p184, int32_t p185, int32_t p186, int32_t p187, int32_t p188, int32_t p189, int32_t p190, int32_t p191 +, int32_t p192, int32_t p193, int32_t p194, int32_t p195, int32_t p196, int32_t p197, int32_t p198, int32_t p199 +, int32_t p200, int32_t p201, int32_t p202, int32_t p203, int32_t p204, int32_t p205, int32_t p206, int32_t p207 +, int32_t p208, int32_t p209, int32_t p210, int32_t p211, int32_t p212, int32_t p213, int32_t p214, int32_t p215 +, int32_t p216, int32_t p217, int32_t p218, int32_t p219, int32_t p220, int32_t p221, int32_t p222, int32_t p223 +, int32_t p224, int32_t p225, int32_t p226, int32_t p227, int32_t p228, int32_t p229, int32_t p230, int32_t p231 +, int32_t p232, int32_t p233, int32_t p234, int32_t p235, int32_t p236, int32_t p237, int32_t p238, int32_t p239 +, int32_t p240, int32_t p241, int32_t p242, int32_t p243, int32_t p244, int32_t p245, int32_t p246, int32_t p247 +, int32_t p248, int32_t p249, int32_t p250, int32_t p251, int32_t p252, int32_t p253, int32_t p254, int32_t p255 +, int32_t p256, int32_t p257, int32_t p258, int32_t p259, int32_t p260, int32_t p261, int32_t p262, int32_t p263 +, int32_t p264, int32_t p265, int32_t p266, int32_t p267, int32_t p268, int32_t p269, int32_t p270, int32_t p271 +, int32_t p272, int32_t p273, int32_t p274, int32_t p275, int32_t p276, int32_t p277, int32_t p278, int32_t p279 +, int32_t p280, int32_t p281, int32_t p282, int32_t p283, int32_t p284, int32_t p285, int32_t p286, int32_t p287 +, int32_t p288, int32_t p289, int32_t p290, int32_t p291, int32_t p292, int32_t p293, int32_t p294, int32_t p295 +, int32_t p296, int32_t p297, int32_t p298, int32_t p299, int32_t p300, int32_t p301, int32_t p302, int32_t p303 +, int32_t p304, int32_t p305, int32_t p306, int32_t p307, int32_t p308, int32_t p309, int32_t p310, int32_t p311 +, int32_t p312, int32_t p313, int32_t p314, int32_t p315, int32_t p316, int32_t p317, int32_t p318, int32_t p319 +, int32_t p320, int32_t p321, int32_t p322, int32_t p323, int32_t p324, int32_t p325, int32_t p326, int32_t p327 +, int32_t p328, int32_t p329, int32_t p330, int32_t p331, int32_t p332, int32_t p333, int32_t p334, int32_t p335 +, int32_t p336, int32_t p337, int32_t p338, int32_t p339, int32_t p340, int32_t p341, int32_t p342, int32_t p343 +, int32_t p344, int32_t p345, int32_t p346, int32_t p347, int32_t p348, int32_t p349, int32_t p350, int32_t p351 +, int32_t p352, int32_t p353, int32_t p354, int32_t p355, int32_t p356, int32_t p357, int32_t p358, int32_t p359 +, int32_t p360, int32_t p361, int32_t p362, int32_t p363, int32_t p364, int32_t p365, int32_t p366, int32_t p367 +, int32_t p368, int32_t p369, int32_t p370, int32_t p371, int32_t p372, int32_t p373, int32_t p374, int32_t p375 +, int32_t p376, int32_t p377, int32_t p378, int32_t p379, int32_t p380, int32_t p381, int32_t p382, int32_t p383 +, int32_t p384, int32_t p385, int32_t p386, int32_t p387, int32_t p388, int32_t p389, int32_t p390, int32_t p391 +, int32_t p392, int32_t p393, int32_t p394, int32_t p395, int32_t p396, int32_t p397, int32_t p398, int32_t p399 +, int32_t p400, int32_t p401, int32_t p402, int32_t p403, int32_t p404, int32_t p405, int32_t p406, int32_t p407 +, int32_t p408, int32_t p409, int32_t p410, int32_t p411, int32_t p412, int32_t p413, int32_t p414, int32_t p415 +, int32_t p416, int32_t p417, int32_t p418, int32_t p419, int32_t p420, int32_t p421, int32_t p422, int32_t p423 +, int32_t p424, int32_t p425, int32_t p426, int32_t p427, int32_t p428, int32_t p429, int32_t p430, int32_t p431 +, int32_t p432, int32_t p433, int32_t p434, int32_t p435, int32_t p436, int32_t p437, int32_t p438, int32_t p439 +, int32_t p440, int32_t p441, int32_t p442, int32_t p443, int32_t p444, int32_t p445, int32_t p446, int32_t p447 +, int32_t p448, int32_t p449, int32_t p450, int32_t p451, int32_t p452, int32_t p453, int32_t p454, int32_t p455 +, int32_t p456, int32_t p457, int32_t p458, int32_t p459, int32_t p460, int32_t p461, int32_t p462, int32_t p463 +, int32_t p464, int32_t p465, int32_t p466, int32_t p467, int32_t p468, int32_t p469, int32_t p470, int32_t p471 +, int32_t p472, int32_t p473, int32_t p474, int32_t p475, int32_t p476, int32_t p477, int32_t p478, int32_t p479 +, int32_t p480, int32_t p481, int32_t p482, int32_t p483, int32_t p484, int32_t p485, int32_t p486, int32_t p487 +, int32_t p488, int32_t p489, int32_t p490, int32_t p491, int32_t p492, int32_t p493, int32_t p494, int32_t p495 +, int32_t p496, int32_t p497, int32_t p498, int32_t p499, int32_t p500, int32_t p501, int32_t p502, int32_t p503 +, int32_t p504, int32_t p505, int32_t p506, int32_t p507, int32_t p508, int32_t p509, int32_t p510, int32_t p511 +, int32_t p512, int32_t p513, int32_t p514, int32_t p515, int32_t p516, int32_t p517, int32_t p518, int32_t p519 +, int32_t p520, int32_t p521, int32_t p522, int32_t p523, int32_t p524, int32_t p525, int32_t p526, int32_t p527 +, int32_t p528, int32_t p529, int32_t p530, int32_t p531, int32_t p532, int32_t p533, int32_t p534, int32_t p535 +, int32_t p536, int32_t p537, int32_t p538, int32_t p539, int32_t p540, int32_t p541, int32_t p542, int32_t p543 +, int32_t p544, int32_t p545, int32_t p546, int32_t p547, int32_t p548, int32_t p549, int32_t p550, int32_t p551 +, int32_t p552, int32_t p553, int32_t p554, int32_t p555, int32_t p556, int32_t p557, int32_t p558, int32_t p559 +, int32_t p560, int32_t p561, int32_t p562, int32_t p563, int32_t p564, int32_t p565, int32_t p566, int32_t p567 +, int32_t p568, int32_t p569, int32_t p570, int32_t p571, int32_t p572, int32_t p573, int32_t p574, int32_t p575 +, int32_t p576, int32_t p577, int32_t p578, int32_t p579, int32_t p580, int32_t p581, int32_t p582, int32_t p583 +, int32_t p584, int32_t p585, int32_t p586, int32_t p587, int32_t p588, int32_t p589, int32_t p590, int32_t p591 +, int32_t p592, int32_t p593, int32_t p594, int32_t p595, int32_t p596, int32_t p597, int32_t p598, int32_t p599 +, int32_t p600, int32_t p601, int32_t p602, int32_t p603, int32_t p604, int32_t p605, int32_t p606, int32_t p607 +, int32_t p608, int32_t p609, int32_t p610, int32_t p611, int32_t p612, int32_t p613, int32_t p614, int32_t p615 +, int32_t p616, int32_t p617, int32_t p618, int32_t p619, int32_t p620, int32_t p621, int32_t p622, int32_t p623 +, int32_t p624, int32_t p625, int32_t p626, int32_t p627, int32_t p628, int32_t p629, int32_t p630, int32_t p631 +, int32_t p632, int32_t p633, int32_t p634, int32_t p635, int32_t p636, int32_t p637, int32_t p638, int32_t p639 +, int32_t p640, int32_t p641, int32_t p642, int32_t p643, int32_t p644, int32_t p645, int32_t p646, int32_t p647 +, int32_t p648, int32_t p649, int32_t p650, int32_t p651, int32_t p652, int32_t p653, int32_t p654, int32_t p655 +, int32_t p656, int32_t p657, int32_t p658, int32_t p659, int32_t p660, int32_t p661, int32_t p662, int32_t p663 +, int32_t p664, int32_t p665, int32_t p666, int32_t p667, int32_t p668, int32_t p669, int32_t p670, int32_t p671 +, int32_t p672, int32_t p673, int32_t p674, int32_t p675, int32_t p676, int32_t p677, int32_t p678, int32_t p679 +, int32_t p680, int32_t p681, int32_t p682, int32_t p683, int32_t p684, int32_t p685, int32_t p686, int32_t p687 +, int32_t p688, int32_t p689, int32_t p690, int32_t p691, int32_t p692, int32_t p693, int32_t p694, int32_t p695 +, int32_t p696, int32_t p697, int32_t p698, int32_t p699, int32_t p700, int32_t p701, int32_t p702, int32_t p703 +, int32_t p704, int32_t p705, int32_t p706, int32_t p707, int32_t p708, int32_t p709, int32_t p710, int32_t p711 +, int32_t p712, int32_t p713, int32_t p714, int32_t p715, int32_t p716, int32_t p717, int32_t p718, int32_t p719 +, int32_t p720, int32_t p721, int32_t p722, int32_t p723, int32_t p724, int32_t p725, int32_t p726, int32_t p727 +, int32_t p728, int32_t p729, int32_t p730, int32_t p731, int32_t p732, int32_t p733, int32_t p734, int32_t p735 +, int32_t p736, int32_t p737, int32_t p738, int32_t p739, int32_t p740, int32_t p741, int32_t p742, int32_t p743 +, int32_t p744, int32_t p745, int32_t p746, int32_t p747, int32_t p748, int32_t p749, int32_t p750, int32_t p751 +, int32_t p752, int32_t p753, int32_t p754, int32_t p755, int32_t p756, int32_t p757, int32_t p758, int32_t p759 +, int32_t p760, int32_t p761, int32_t p762, int32_t p763, int32_t p764, int32_t p765, int32_t p766, int32_t p767 +, int32_t p768, int32_t p769, int32_t p770, int32_t p771, int32_t p772, int32_t p773, int32_t p774, int32_t p775 +, int32_t p776, int32_t p777, int32_t p778, int32_t p779, int32_t p780, int32_t p781, int32_t p782, int32_t p783 +, int32_t p784, int32_t p785, int32_t p786, int32_t p787, int32_t p788, int32_t p789, int32_t p790, int32_t p791 +, int32_t p792, int32_t p793, int32_t p794, int32_t p795, int32_t p796, int32_t p797, int32_t p798, int32_t p799 +, int32_t p800, int32_t p801, int32_t p802, int32_t p803, int32_t p804, int32_t p805, int32_t p806, int32_t p807 +, int32_t p808, int32_t p809, int32_t p810, int32_t p811, int32_t p812, int32_t p813, int32_t p814, int32_t p815 +, int32_t p816, int32_t p817, int32_t p818, int32_t p819, int32_t p820, int32_t p821, int32_t p822, int32_t p823 +, int32_t p824, int32_t p825, int32_t p826, int32_t p827, int32_t p828, int32_t p829, int32_t p830, int32_t p831 +, int32_t p832, int32_t p833, int32_t p834, int32_t p835, int32_t p836, int32_t p837, int32_t p838, int32_t p839 +, int32_t p840, int32_t p841, int32_t p842, int32_t p843, int32_t p844, int32_t p845, int32_t p846, int32_t p847 +, int32_t p848, int32_t p849, int32_t p850, int32_t p851, int32_t p852, int32_t p853, int32_t p854, int32_t p855 +, int32_t p856, int32_t p857, int32_t p858, int32_t p859, int32_t p860, int32_t p861, int32_t p862, int32_t p863 +, int32_t p864, int32_t p865, int32_t p866, int32_t p867, int32_t p868, int32_t p869, int32_t p870, int32_t p871 +, int32_t p872, int32_t p873, int32_t p874, int32_t p875, int32_t p876, int32_t p877, int32_t p878, int32_t p879 +, int32_t p880, int32_t p881, int32_t p882, int32_t p883, int32_t p884, int32_t p885, int32_t p886, int32_t p887 +, int32_t p888, int32_t p889, int32_t p890, int32_t p891, int32_t p892, int32_t p893, int32_t p894, int32_t p895 +, int32_t p896, int32_t p897, int32_t p898, int32_t p899, int32_t p900, int32_t p901, int32_t p902, int32_t p903 +, int32_t p904, int32_t p905, int32_t p906, int32_t p907, int32_t p908, int32_t p909, int32_t p910, int32_t p911 +, int32_t p912, int32_t p913, int32_t p914, int32_t p915, int32_t p916, int32_t p917, int32_t p918, int32_t p919 +, int32_t p920, int32_t p921, int32_t p922, int32_t p923, int32_t p924, int32_t p925, int32_t p926, int32_t p927 +, int32_t p928, int32_t p929, int32_t p930, int32_t p931, int32_t p932, int32_t p933, int32_t p934, int32_t p935 +, int32_t p936, int32_t p937, int32_t p938, int32_t p939, int32_t p940, int32_t p941, int32_t p942, int32_t p943 +, int32_t p944, int32_t p945, int32_t p946, int32_t p947, int32_t p948, int32_t p949, int32_t p950, int32_t p951 +, int32_t p952, int32_t p953, int32_t p954, int32_t p955, int32_t p956, int32_t p957, int32_t p958, int32_t p959 +, int32_t p960, int32_t p961, int32_t p962, int32_t p963, int32_t p964, int32_t p965, int32_t p966, int32_t p967 +, int32_t p968, int32_t p969, int32_t p970, int32_t p971, int32_t p972, int32_t p973, int32_t p974, int32_t p975 +, int32_t p976, int32_t p977, int32_t p978, int32_t p979, int32_t p980, int32_t p981, int32_t p982, int32_t p983 +, int32_t p984, int32_t p985, int32_t p986, int32_t p987, int32_t p988, int32_t p989, int32_t p990, int32_t p991 +, int32_t p992, int32_t p993, int32_t p994, int32_t p995, int32_t p996, int32_t p997, int32_t p998, int32_t p999 +, int32_t p1000 +) +{ + int32_t x; + x = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + + p8 + p9 + p10 + p11 + p12 + p13 + p14 + p15 + + p16 + p17 + p18 + p19 + p20 + p21 + p22 + p23 + + p24 + p25 + p26 + p27 + p28 + p29 + p30 + p31 + + p32 + p33 + p34 + p35 + p36 + p37 + p38 + p39 + + p40 + p41 + p42 + p43 + p44 + p45 + p46 + p47 + + p48 + p49 + p50 + p51 + p52 + p53 + p54 + p55 + + p56 + p57 + p58 + p59 + p60 + p61 + p62 + p63 + + p64 + p65 + p66 + p67 + p68 + p69 + p70 + p71 + + p72 + p73 + p74 + p75 + p76 + p77 + p78 + p79 + + p80 + p81 + p82 + p83 + p84 + p85 + p86 + p87 + + p88 + p89 + p90 + p91 + p92 + p93 + p94 + p95 + + p96 + p97 + p98 + p99 + p100 + p101 + p102 + p103 + + p104 + p105 + p106 + p107 + p108 + p109 + p110 + p111 + + p112 + p113 + p114 + p115 + p116 + p117 + p118 + p119 + + p120 + p121 + p122 + p123 + p124 + p125 + p126 + p127 + + p128 + p129 + p130 + p131 + p132 + p133 + p134 + p135 + + p136 + p137 + p138 + p139 + p140 + p141 + p142 + p143 + + p144 + p145 + p146 + p147 + p148 + p149 + p150 + p151 + + p152 + p153 + p154 + p155 + p156 + p157 + p158 + p159 + + p160 + p161 + p162 + p163 + p164 + p165 + p166 + p167 + + p168 + p169 + p170 + p171 + p172 + p173 + p174 + p175 + + p176 + p177 + p178 + p179 + p180 + p181 + p182 + p183 + + p184 + p185 + p186 + p187 + p188 + p189 + p190 + p191 + + p192 + p193 + p194 + p195 + p196 + p197 + p198 + p199 + + p200 + p201 + p202 + p203 + p204 + p205 + p206 + p207 + + p208 + p209 + p210 + p211 + p212 + p213 + p214 + p215 + + p216 + p217 + p218 + p219 + p220 + p221 + p222 + p223 + + p224 + p225 + p226 + p227 + p228 + p229 + p230 + p231 + + p232 + p233 + p234 + p235 + p236 + p237 + p238 + p239 + + p240 + p241 + p242 + p243 + p244 + p245 + p246 + p247 + + p248 + p249 + p250 + p251 + p252 + p253 + p254 + p255 + + p256 + p257 + p258 + p259 + p260 + p261 + p262 + p263 + + p264 + p265 + p266 + p267 + p268 + p269 + p270 + p271 + + p272 + p273 + p274 + p275 + p276 + p277 + p278 + p279 + + p280 + p281 + p282 + p283 + p284 + p285 + p286 + p287 + + p288 + p289 + p290 + p291 + p292 + p293 + p294 + p295 + + p296 + p297 + p298 + p299 + p300 + p301 + p302 + p303 + + p304 + p305 + p306 + p307 + p308 + p309 + p310 + p311 + + p312 + p313 + p314 + p315 + p316 + p317 + p318 + p319 + + p320 + p321 + p322 + p323 + p324 + p325 + p326 + p327 + + p328 + p329 + p330 + p331 + p332 + p333 + p334 + p335 + + p336 + p337 + p338 + p339 + p340 + p341 + p342 + p343 + + p344 + p345 + p346 + p347 + p348 + p349 + p350 + p351 + + p352 + p353 + p354 + p355 + p356 + p357 + p358 + p359 + + p360 + p361 + p362 + p363 + p364 + p365 + p366 + p367 + + p368 + p369 + p370 + p371 + p372 + p373 + p374 + p375 + + p376 + p377 + p378 + p379 + p380 + p381 + p382 + p383 + + p384 + p385 + p386 + p387 + p388 + p389 + p390 + p391 + + p392 + p393 + p394 + p395 + p396 + p397 + p398 + p399 + + p400 + p401 + p402 + p403 + p404 + p405 + p406 + p407 + + p408 + p409 + p410 + p411 + p412 + p413 + p414 + p415 + + p416 + p417 + p418 + p419 + p420 + p421 + p422 + p423 + + p424 + p425 + p426 + p427 + p428 + p429 + p430 + p431 + + p432 + p433 + p434 + p435 + p436 + p437 + p438 + p439 + + p440 + p441 + p442 + p443 + p444 + p445 + p446 + p447 + + p448 + p449 + p450 + p451 + p452 + p453 + p454 + p455 + + p456 + p457 + p458 + p459 + p460 + p461 + p462 + p463 + + p464 + p465 + p466 + p467 + p468 + p469 + p470 + p471 + + p472 + p473 + p474 + p475 + p476 + p477 + p478 + p479 + + p480 + p481 + p482 + p483 + p484 + p485 + p486 + p487 + + p488 + p489 + p490 + p491 + p492 + p493 + p494 + p495 + + p496 + p497 + p498 + p499 + p500 + p501 + p502 + p503 + + p504 + p505 + p506 + p507 + p508 + p509 + p510 + p511 + + p512 + p513 + p514 + p515 + p516 + p517 + p518 + p519 + + p520 + p521 + p522 + p523 + p524 + p525 + p526 + p527 + + p528 + p529 + p530 + p531 + p532 + p533 + p534 + p535 + + p536 + p537 + p538 + p539 + p540 + p541 + p542 + p543 + + p544 + p545 + p546 + p547 + p548 + p549 + p550 + p551 + + p552 + p553 + p554 + p555 + p556 + p557 + p558 + p559 + + p560 + p561 + p562 + p563 + p564 + p565 + p566 + p567 + + p568 + p569 + p570 + p571 + p572 + p573 + p574 + p575 + + p576 + p577 + p578 + p579 + p580 + p581 + p582 + p583 + + p584 + p585 + p586 + p587 + p588 + p589 + p590 + p591 + + p592 + p593 + p594 + p595 + p596 + p597 + p598 + p599 + + p600 + p601 + p602 + p603 + p604 + p605 + p606 + p607 + + p608 + p609 + p610 + p611 + p612 + p613 + p614 + p615 + + p616 + p617 + p618 + p619 + p620 + p621 + p622 + p623 + + p624 + p625 + p626 + p627 + p628 + p629 + p630 + p631 + + p632 + p633 + p634 + p635 + p636 + p637 + p638 + p639 + + p640 + p641 + p642 + p643 + p644 + p645 + p646 + p647 + + p648 + p649 + p650 + p651 + p652 + p653 + p654 + p655 + + p656 + p657 + p658 + p659 + p660 + p661 + p662 + p663 + + p664 + p665 + p666 + p667 + p668 + p669 + p670 + p671 + + p672 + p673 + p674 + p675 + p676 + p677 + p678 + p679 + + p680 + p681 + p682 + p683 + p684 + p685 + p686 + p687 + + p688 + p689 + p690 + p691 + p692 + p693 + p694 + p695 + + p696 + p697 + p698 + p699 + p700 + p701 + p702 + p703 + + p704 + p705 + p706 + p707 + p708 + p709 + p710 + p711 + + p712 + p713 + p714 + p715 + p716 + p717 + p718 + p719 + + p720 + p721 + p722 + p723 + p724 + p725 + p726 + p727 + + p728 + p729 + p730 + p731 + p732 + p733 + p734 + p735 + + p736 + p737 + p738 + p739 + p740 + p741 + p742 + p743 + + p744 + p745 + p746 + p747 + p748 + p749 + p750 + p751 + + p752 + p753 + p754 + p755 + p756 + p757 + p758 + p759 + + p760 + p761 + p762 + p763 + p764 + p765 + p766 + p767 + + p768 + p769 + p770 + p771 + p772 + p773 + p774 + p775 + + p776 + p777 + p778 + p779 + p780 + p781 + p782 + p783 + + p784 + p785 + p786 + p787 + p788 + p789 + p790 + p791 + + p792 + p793 + p794 + p795 + p796 + p797 + p798 + p799 + + p800 + p801 + p802 + p803 + p804 + p805 + p806 + p807 + + p808 + p809 + p810 + p811 + p812 + p813 + p814 + p815 + + p816 + p817 + p818 + p819 + p820 + p821 + p822 + p823 + + p824 + p825 + p826 + p827 + p828 + p829 + p830 + p831 + + p832 + p833 + p834 + p835 + p836 + p837 + p838 + p839 + + p840 + p841 + p842 + p843 + p844 + p845 + p846 + p847 + + p848 + p849 + p850 + p851 + p852 + p853 + p854 + p855 + + p856 + p857 + p858 + p859 + p860 + p861 + p862 + p863 + + p864 + p865 + p866 + p867 + p868 + p869 + p870 + p871 + + p872 + p873 + p874 + p875 + p876 + p877 + p878 + p879 + + p880 + p881 + p882 + p883 + p884 + p885 + p886 + p887 + + p888 + p889 + p890 + p891 + p892 + p893 + p894 + p895 + + p896 + p897 + p898 + p899 + p900 + p901 + p902 + p903 + + p904 + p905 + p906 + p907 + p908 + p909 + p910 + p911 + + p912 + p913 + p914 + p915 + p916 + p917 + p918 + p919 + + p920 + p921 + p922 + p923 + p924 + p925 + p926 + p927 + + p928 + p929 + p930 + p931 + p932 + p933 + p934 + p935 + + p936 + p937 + p938 + p939 + p940 + p941 + p942 + p943 + + p944 + p945 + p946 + p947 + p948 + p949 + p950 + p951 + + p952 + p953 + p954 + p955 + p956 + p957 + p958 + p959 + + p960 + p961 + p962 + p963 + p964 + p965 + p966 + p967 + + p968 + p969 + p970 + p971 + p972 + p973 + p974 + p975 + + p976 + p977 + p978 + p979 + p980 + p981 + p982 + p983 + + p984 + p985 + p986 + p987 + p988 + p989 + p990 + p991 + + p992 + p993 + p994 + p995 + p996 + p997 + p998 + p999 + + p1000; + return x; +} + +// clang-format on diff --git a/src/test/app/wasm_fixtures/thousand_params.c b/src/test/app/wasm_fixtures/thousand_params.c new file mode 100644 index 0000000000..d934ca38c8 --- /dev/null +++ b/src/test/app/wasm_fixtures/thousand_params.c @@ -0,0 +1,262 @@ +// clang-format off + +#include + +int32_t test( + int32_t p0, int32_t p1, int32_t p2, int32_t p3, int32_t p4, int32_t p5, int32_t p6, int32_t p7 +, int32_t p8, int32_t p9, int32_t p10, int32_t p11, int32_t p12, int32_t p13, int32_t p14, int32_t p15 +, int32_t p16, int32_t p17, int32_t p18, int32_t p19, int32_t p20, int32_t p21, int32_t p22, int32_t p23 +, int32_t p24, int32_t p25, int32_t p26, int32_t p27, int32_t p28, int32_t p29, int32_t p30, int32_t p31 +, int32_t p32, int32_t p33, int32_t p34, int32_t p35, int32_t p36, int32_t p37, int32_t p38, int32_t p39 +, int32_t p40, int32_t p41, int32_t p42, int32_t p43, int32_t p44, int32_t p45, int32_t p46, int32_t p47 +, int32_t p48, int32_t p49, int32_t p50, int32_t p51, int32_t p52, int32_t p53, int32_t p54, int32_t p55 +, int32_t p56, int32_t p57, int32_t p58, int32_t p59, int32_t p60, int32_t p61, int32_t p62, int32_t p63 +, int32_t p64, int32_t p65, int32_t p66, int32_t p67, int32_t p68, int32_t p69, int32_t p70, int32_t p71 +, int32_t p72, int32_t p73, int32_t p74, int32_t p75, int32_t p76, int32_t p77, int32_t p78, int32_t p79 +, int32_t p80, int32_t p81, int32_t p82, int32_t p83, int32_t p84, int32_t p85, int32_t p86, int32_t p87 +, int32_t p88, int32_t p89, int32_t p90, int32_t p91, int32_t p92, int32_t p93, int32_t p94, int32_t p95 +, int32_t p96, int32_t p97, int32_t p98, int32_t p99, int32_t p100, int32_t p101, int32_t p102, int32_t p103 +, int32_t p104, int32_t p105, int32_t p106, int32_t p107, int32_t p108, int32_t p109, int32_t p110, int32_t p111 +, int32_t p112, int32_t p113, int32_t p114, int32_t p115, int32_t p116, int32_t p117, int32_t p118, int32_t p119 +, int32_t p120, int32_t p121, int32_t p122, int32_t p123, int32_t p124, int32_t p125, int32_t p126, int32_t p127 +, int32_t p128, int32_t p129, int32_t p130, int32_t p131, int32_t p132, int32_t p133, int32_t p134, int32_t p135 +, int32_t p136, int32_t p137, int32_t p138, int32_t p139, int32_t p140, int32_t p141, int32_t p142, int32_t p143 +, int32_t p144, int32_t p145, int32_t p146, int32_t p147, int32_t p148, int32_t p149, int32_t p150, int32_t p151 +, int32_t p152, int32_t p153, int32_t p154, int32_t p155, int32_t p156, int32_t p157, int32_t p158, int32_t p159 +, int32_t p160, int32_t p161, int32_t p162, int32_t p163, int32_t p164, int32_t p165, int32_t p166, int32_t p167 +, int32_t p168, int32_t p169, int32_t p170, int32_t p171, int32_t p172, int32_t p173, int32_t p174, int32_t p175 +, int32_t p176, int32_t p177, int32_t p178, int32_t p179, int32_t p180, int32_t p181, int32_t p182, int32_t p183 +, int32_t p184, int32_t p185, int32_t p186, int32_t p187, int32_t p188, int32_t p189, int32_t p190, int32_t p191 +, int32_t p192, int32_t p193, int32_t p194, int32_t p195, int32_t p196, int32_t p197, int32_t p198, int32_t p199 +, int32_t p200, int32_t p201, int32_t p202, int32_t p203, int32_t p204, int32_t p205, int32_t p206, int32_t p207 +, int32_t p208, int32_t p209, int32_t p210, int32_t p211, int32_t p212, int32_t p213, int32_t p214, int32_t p215 +, int32_t p216, int32_t p217, int32_t p218, int32_t p219, int32_t p220, int32_t p221, int32_t p222, int32_t p223 +, int32_t p224, int32_t p225, int32_t p226, int32_t p227, int32_t p228, int32_t p229, int32_t p230, int32_t p231 +, int32_t p232, int32_t p233, int32_t p234, int32_t p235, int32_t p236, int32_t p237, int32_t p238, int32_t p239 +, int32_t p240, int32_t p241, int32_t p242, int32_t p243, int32_t p244, int32_t p245, int32_t p246, int32_t p247 +, int32_t p248, int32_t p249, int32_t p250, int32_t p251, int32_t p252, int32_t p253, int32_t p254, int32_t p255 +, int32_t p256, int32_t p257, int32_t p258, int32_t p259, int32_t p260, int32_t p261, int32_t p262, int32_t p263 +, int32_t p264, int32_t p265, int32_t p266, int32_t p267, int32_t p268, int32_t p269, int32_t p270, int32_t p271 +, int32_t p272, int32_t p273, int32_t p274, int32_t p275, int32_t p276, int32_t p277, int32_t p278, int32_t p279 +, int32_t p280, int32_t p281, int32_t p282, int32_t p283, int32_t p284, int32_t p285, int32_t p286, int32_t p287 +, int32_t p288, int32_t p289, int32_t p290, int32_t p291, int32_t p292, int32_t p293, int32_t p294, int32_t p295 +, int32_t p296, int32_t p297, int32_t p298, int32_t p299, int32_t p300, int32_t p301, int32_t p302, int32_t p303 +, int32_t p304, int32_t p305, int32_t p306, int32_t p307, int32_t p308, int32_t p309, int32_t p310, int32_t p311 +, int32_t p312, int32_t p313, int32_t p314, int32_t p315, int32_t p316, int32_t p317, int32_t p318, int32_t p319 +, int32_t p320, int32_t p321, int32_t p322, int32_t p323, int32_t p324, int32_t p325, int32_t p326, int32_t p327 +, int32_t p328, int32_t p329, int32_t p330, int32_t p331, int32_t p332, int32_t p333, int32_t p334, int32_t p335 +, int32_t p336, int32_t p337, int32_t p338, int32_t p339, int32_t p340, int32_t p341, int32_t p342, int32_t p343 +, int32_t p344, int32_t p345, int32_t p346, int32_t p347, int32_t p348, int32_t p349, int32_t p350, int32_t p351 +, int32_t p352, int32_t p353, int32_t p354, int32_t p355, int32_t p356, int32_t p357, int32_t p358, int32_t p359 +, int32_t p360, int32_t p361, int32_t p362, int32_t p363, int32_t p364, int32_t p365, int32_t p366, int32_t p367 +, int32_t p368, int32_t p369, int32_t p370, int32_t p371, int32_t p372, int32_t p373, int32_t p374, int32_t p375 +, int32_t p376, int32_t p377, int32_t p378, int32_t p379, int32_t p380, int32_t p381, int32_t p382, int32_t p383 +, int32_t p384, int32_t p385, int32_t p386, int32_t p387, int32_t p388, int32_t p389, int32_t p390, int32_t p391 +, int32_t p392, int32_t p393, int32_t p394, int32_t p395, int32_t p396, int32_t p397, int32_t p398, int32_t p399 +, int32_t p400, int32_t p401, int32_t p402, int32_t p403, int32_t p404, int32_t p405, int32_t p406, int32_t p407 +, int32_t p408, int32_t p409, int32_t p410, int32_t p411, int32_t p412, int32_t p413, int32_t p414, int32_t p415 +, int32_t p416, int32_t p417, int32_t p418, int32_t p419, int32_t p420, int32_t p421, int32_t p422, int32_t p423 +, int32_t p424, int32_t p425, int32_t p426, int32_t p427, int32_t p428, int32_t p429, int32_t p430, int32_t p431 +, int32_t p432, int32_t p433, int32_t p434, int32_t p435, int32_t p436, int32_t p437, int32_t p438, int32_t p439 +, int32_t p440, int32_t p441, int32_t p442, int32_t p443, int32_t p444, int32_t p445, int32_t p446, int32_t p447 +, int32_t p448, int32_t p449, int32_t p450, int32_t p451, int32_t p452, int32_t p453, int32_t p454, int32_t p455 +, int32_t p456, int32_t p457, int32_t p458, int32_t p459, int32_t p460, int32_t p461, int32_t p462, int32_t p463 +, int32_t p464, int32_t p465, int32_t p466, int32_t p467, int32_t p468, int32_t p469, int32_t p470, int32_t p471 +, int32_t p472, int32_t p473, int32_t p474, int32_t p475, int32_t p476, int32_t p477, int32_t p478, int32_t p479 +, int32_t p480, int32_t p481, int32_t p482, int32_t p483, int32_t p484, int32_t p485, int32_t p486, int32_t p487 +, int32_t p488, int32_t p489, int32_t p490, int32_t p491, int32_t p492, int32_t p493, int32_t p494, int32_t p495 +, int32_t p496, int32_t p497, int32_t p498, int32_t p499, int32_t p500, int32_t p501, int32_t p502, int32_t p503 +, int32_t p504, int32_t p505, int32_t p506, int32_t p507, int32_t p508, int32_t p509, int32_t p510, int32_t p511 +, int32_t p512, int32_t p513, int32_t p514, int32_t p515, int32_t p516, int32_t p517, int32_t p518, int32_t p519 +, int32_t p520, int32_t p521, int32_t p522, int32_t p523, int32_t p524, int32_t p525, int32_t p526, int32_t p527 +, int32_t p528, int32_t p529, int32_t p530, int32_t p531, int32_t p532, int32_t p533, int32_t p534, int32_t p535 +, int32_t p536, int32_t p537, int32_t p538, int32_t p539, int32_t p540, int32_t p541, int32_t p542, int32_t p543 +, int32_t p544, int32_t p545, int32_t p546, int32_t p547, int32_t p548, int32_t p549, int32_t p550, int32_t p551 +, int32_t p552, int32_t p553, int32_t p554, int32_t p555, int32_t p556, int32_t p557, int32_t p558, int32_t p559 +, int32_t p560, int32_t p561, int32_t p562, int32_t p563, int32_t p564, int32_t p565, int32_t p566, int32_t p567 +, int32_t p568, int32_t p569, int32_t p570, int32_t p571, int32_t p572, int32_t p573, int32_t p574, int32_t p575 +, int32_t p576, int32_t p577, int32_t p578, int32_t p579, int32_t p580, int32_t p581, int32_t p582, int32_t p583 +, int32_t p584, int32_t p585, int32_t p586, int32_t p587, int32_t p588, int32_t p589, int32_t p590, int32_t p591 +, int32_t p592, int32_t p593, int32_t p594, int32_t p595, int32_t p596, int32_t p597, int32_t p598, int32_t p599 +, int32_t p600, int32_t p601, int32_t p602, int32_t p603, int32_t p604, int32_t p605, int32_t p606, int32_t p607 +, int32_t p608, int32_t p609, int32_t p610, int32_t p611, int32_t p612, int32_t p613, int32_t p614, int32_t p615 +, int32_t p616, int32_t p617, int32_t p618, int32_t p619, int32_t p620, int32_t p621, int32_t p622, int32_t p623 +, int32_t p624, int32_t p625, int32_t p626, int32_t p627, int32_t p628, int32_t p629, int32_t p630, int32_t p631 +, int32_t p632, int32_t p633, int32_t p634, int32_t p635, int32_t p636, int32_t p637, int32_t p638, int32_t p639 +, int32_t p640, int32_t p641, int32_t p642, int32_t p643, int32_t p644, int32_t p645, int32_t p646, int32_t p647 +, int32_t p648, int32_t p649, int32_t p650, int32_t p651, int32_t p652, int32_t p653, int32_t p654, int32_t p655 +, int32_t p656, int32_t p657, int32_t p658, int32_t p659, int32_t p660, int32_t p661, int32_t p662, int32_t p663 +, int32_t p664, int32_t p665, int32_t p666, int32_t p667, int32_t p668, int32_t p669, int32_t p670, int32_t p671 +, int32_t p672, int32_t p673, int32_t p674, int32_t p675, int32_t p676, int32_t p677, int32_t p678, int32_t p679 +, int32_t p680, int32_t p681, int32_t p682, int32_t p683, int32_t p684, int32_t p685, int32_t p686, int32_t p687 +, int32_t p688, int32_t p689, int32_t p690, int32_t p691, int32_t p692, int32_t p693, int32_t p694, int32_t p695 +, int32_t p696, int32_t p697, int32_t p698, int32_t p699, int32_t p700, int32_t p701, int32_t p702, int32_t p703 +, int32_t p704, int32_t p705, int32_t p706, int32_t p707, int32_t p708, int32_t p709, int32_t p710, int32_t p711 +, int32_t p712, int32_t p713, int32_t p714, int32_t p715, int32_t p716, int32_t p717, int32_t p718, int32_t p719 +, int32_t p720, int32_t p721, int32_t p722, int32_t p723, int32_t p724, int32_t p725, int32_t p726, int32_t p727 +, int32_t p728, int32_t p729, int32_t p730, int32_t p731, int32_t p732, int32_t p733, int32_t p734, int32_t p735 +, int32_t p736, int32_t p737, int32_t p738, int32_t p739, int32_t p740, int32_t p741, int32_t p742, int32_t p743 +, int32_t p744, int32_t p745, int32_t p746, int32_t p747, int32_t p748, int32_t p749, int32_t p750, int32_t p751 +, int32_t p752, int32_t p753, int32_t p754, int32_t p755, int32_t p756, int32_t p757, int32_t p758, int32_t p759 +, int32_t p760, int32_t p761, int32_t p762, int32_t p763, int32_t p764, int32_t p765, int32_t p766, int32_t p767 +, int32_t p768, int32_t p769, int32_t p770, int32_t p771, int32_t p772, int32_t p773, int32_t p774, int32_t p775 +, int32_t p776, int32_t p777, int32_t p778, int32_t p779, int32_t p780, int32_t p781, int32_t p782, int32_t p783 +, int32_t p784, int32_t p785, int32_t p786, int32_t p787, int32_t p788, int32_t p789, int32_t p790, int32_t p791 +, int32_t p792, int32_t p793, int32_t p794, int32_t p795, int32_t p796, int32_t p797, int32_t p798, int32_t p799 +, int32_t p800, int32_t p801, int32_t p802, int32_t p803, int32_t p804, int32_t p805, int32_t p806, int32_t p807 +, int32_t p808, int32_t p809, int32_t p810, int32_t p811, int32_t p812, int32_t p813, int32_t p814, int32_t p815 +, int32_t p816, int32_t p817, int32_t p818, int32_t p819, int32_t p820, int32_t p821, int32_t p822, int32_t p823 +, int32_t p824, int32_t p825, int32_t p826, int32_t p827, int32_t p828, int32_t p829, int32_t p830, int32_t p831 +, int32_t p832, int32_t p833, int32_t p834, int32_t p835, int32_t p836, int32_t p837, int32_t p838, int32_t p839 +, int32_t p840, int32_t p841, int32_t p842, int32_t p843, int32_t p844, int32_t p845, int32_t p846, int32_t p847 +, int32_t p848, int32_t p849, int32_t p850, int32_t p851, int32_t p852, int32_t p853, int32_t p854, int32_t p855 +, int32_t p856, int32_t p857, int32_t p858, int32_t p859, int32_t p860, int32_t p861, int32_t p862, int32_t p863 +, int32_t p864, int32_t p865, int32_t p866, int32_t p867, int32_t p868, int32_t p869, int32_t p870, int32_t p871 +, int32_t p872, int32_t p873, int32_t p874, int32_t p875, int32_t p876, int32_t p877, int32_t p878, int32_t p879 +, int32_t p880, int32_t p881, int32_t p882, int32_t p883, int32_t p884, int32_t p885, int32_t p886, int32_t p887 +, int32_t p888, int32_t p889, int32_t p890, int32_t p891, int32_t p892, int32_t p893, int32_t p894, int32_t p895 +, int32_t p896, int32_t p897, int32_t p898, int32_t p899, int32_t p900, int32_t p901, int32_t p902, int32_t p903 +, int32_t p904, int32_t p905, int32_t p906, int32_t p907, int32_t p908, int32_t p909, int32_t p910, int32_t p911 +, int32_t p912, int32_t p913, int32_t p914, int32_t p915, int32_t p916, int32_t p917, int32_t p918, int32_t p919 +, int32_t p920, int32_t p921, int32_t p922, int32_t p923, int32_t p924, int32_t p925, int32_t p926, int32_t p927 +, int32_t p928, int32_t p929, int32_t p930, int32_t p931, int32_t p932, int32_t p933, int32_t p934, int32_t p935 +, int32_t p936, int32_t p937, int32_t p938, int32_t p939, int32_t p940, int32_t p941, int32_t p942, int32_t p943 +, int32_t p944, int32_t p945, int32_t p946, int32_t p947, int32_t p948, int32_t p949, int32_t p950, int32_t p951 +, int32_t p952, int32_t p953, int32_t p954, int32_t p955, int32_t p956, int32_t p957, int32_t p958, int32_t p959 +, int32_t p960, int32_t p961, int32_t p962, int32_t p963, int32_t p964, int32_t p965, int32_t p966, int32_t p967 +, int32_t p968, int32_t p969, int32_t p970, int32_t p971, int32_t p972, int32_t p973, int32_t p974, int32_t p975 +, int32_t p976, int32_t p977, int32_t p978, int32_t p979, int32_t p980, int32_t p981, int32_t p982, int32_t p983 +, int32_t p984, int32_t p985, int32_t p986, int32_t p987, int32_t p988, int32_t p989, int32_t p990, int32_t p991 +, int32_t p992, int32_t p993, int32_t p994, int32_t p995, int32_t p996, int32_t p997, int32_t p998, int32_t p999 +) +{ + int32_t x; + x = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + + p8 + p9 + p10 + p11 + p12 + p13 + p14 + p15 + + p16 + p17 + p18 + p19 + p20 + p21 + p22 + p23 + + p24 + p25 + p26 + p27 + p28 + p29 + p30 + p31 + + p32 + p33 + p34 + p35 + p36 + p37 + p38 + p39 + + p40 + p41 + p42 + p43 + p44 + p45 + p46 + p47 + + p48 + p49 + p50 + p51 + p52 + p53 + p54 + p55 + + p56 + p57 + p58 + p59 + p60 + p61 + p62 + p63 + + p64 + p65 + p66 + p67 + p68 + p69 + p70 + p71 + + p72 + p73 + p74 + p75 + p76 + p77 + p78 + p79 + + p80 + p81 + p82 + p83 + p84 + p85 + p86 + p87 + + p88 + p89 + p90 + p91 + p92 + p93 + p94 + p95 + + p96 + p97 + p98 + p99 + p100 + p101 + p102 + p103 + + p104 + p105 + p106 + p107 + p108 + p109 + p110 + p111 + + p112 + p113 + p114 + p115 + p116 + p117 + p118 + p119 + + p120 + p121 + p122 + p123 + p124 + p125 + p126 + p127 + + p128 + p129 + p130 + p131 + p132 + p133 + p134 + p135 + + p136 + p137 + p138 + p139 + p140 + p141 + p142 + p143 + + p144 + p145 + p146 + p147 + p148 + p149 + p150 + p151 + + p152 + p153 + p154 + p155 + p156 + p157 + p158 + p159 + + p160 + p161 + p162 + p163 + p164 + p165 + p166 + p167 + + p168 + p169 + p170 + p171 + p172 + p173 + p174 + p175 + + p176 + p177 + p178 + p179 + p180 + p181 + p182 + p183 + + p184 + p185 + p186 + p187 + p188 + p189 + p190 + p191 + + p192 + p193 + p194 + p195 + p196 + p197 + p198 + p199 + + p200 + p201 + p202 + p203 + p204 + p205 + p206 + p207 + + p208 + p209 + p210 + p211 + p212 + p213 + p214 + p215 + + p216 + p217 + p218 + p219 + p220 + p221 + p222 + p223 + + p224 + p225 + p226 + p227 + p228 + p229 + p230 + p231 + + p232 + p233 + p234 + p235 + p236 + p237 + p238 + p239 + + p240 + p241 + p242 + p243 + p244 + p245 + p246 + p247 + + p248 + p249 + p250 + p251 + p252 + p253 + p254 + p255 + + p256 + p257 + p258 + p259 + p260 + p261 + p262 + p263 + + p264 + p265 + p266 + p267 + p268 + p269 + p270 + p271 + + p272 + p273 + p274 + p275 + p276 + p277 + p278 + p279 + + p280 + p281 + p282 + p283 + p284 + p285 + p286 + p287 + + p288 + p289 + p290 + p291 + p292 + p293 + p294 + p295 + + p296 + p297 + p298 + p299 + p300 + p301 + p302 + p303 + + p304 + p305 + p306 + p307 + p308 + p309 + p310 + p311 + + p312 + p313 + p314 + p315 + p316 + p317 + p318 + p319 + + p320 + p321 + p322 + p323 + p324 + p325 + p326 + p327 + + p328 + p329 + p330 + p331 + p332 + p333 + p334 + p335 + + p336 + p337 + p338 + p339 + p340 + p341 + p342 + p343 + + p344 + p345 + p346 + p347 + p348 + p349 + p350 + p351 + + p352 + p353 + p354 + p355 + p356 + p357 + p358 + p359 + + p360 + p361 + p362 + p363 + p364 + p365 + p366 + p367 + + p368 + p369 + p370 + p371 + p372 + p373 + p374 + p375 + + p376 + p377 + p378 + p379 + p380 + p381 + p382 + p383 + + p384 + p385 + p386 + p387 + p388 + p389 + p390 + p391 + + p392 + p393 + p394 + p395 + p396 + p397 + p398 + p399 + + p400 + p401 + p402 + p403 + p404 + p405 + p406 + p407 + + p408 + p409 + p410 + p411 + p412 + p413 + p414 + p415 + + p416 + p417 + p418 + p419 + p420 + p421 + p422 + p423 + + p424 + p425 + p426 + p427 + p428 + p429 + p430 + p431 + + p432 + p433 + p434 + p435 + p436 + p437 + p438 + p439 + + p440 + p441 + p442 + p443 + p444 + p445 + p446 + p447 + + p448 + p449 + p450 + p451 + p452 + p453 + p454 + p455 + + p456 + p457 + p458 + p459 + p460 + p461 + p462 + p463 + + p464 + p465 + p466 + p467 + p468 + p469 + p470 + p471 + + p472 + p473 + p474 + p475 + p476 + p477 + p478 + p479 + + p480 + p481 + p482 + p483 + p484 + p485 + p486 + p487 + + p488 + p489 + p490 + p491 + p492 + p493 + p494 + p495 + + p496 + p497 + p498 + p499 + p500 + p501 + p502 + p503 + + p504 + p505 + p506 + p507 + p508 + p509 + p510 + p511 + + p512 + p513 + p514 + p515 + p516 + p517 + p518 + p519 + + p520 + p521 + p522 + p523 + p524 + p525 + p526 + p527 + + p528 + p529 + p530 + p531 + p532 + p533 + p534 + p535 + + p536 + p537 + p538 + p539 + p540 + p541 + p542 + p543 + + p544 + p545 + p546 + p547 + p548 + p549 + p550 + p551 + + p552 + p553 + p554 + p555 + p556 + p557 + p558 + p559 + + p560 + p561 + p562 + p563 + p564 + p565 + p566 + p567 + + p568 + p569 + p570 + p571 + p572 + p573 + p574 + p575 + + p576 + p577 + p578 + p579 + p580 + p581 + p582 + p583 + + p584 + p585 + p586 + p587 + p588 + p589 + p590 + p591 + + p592 + p593 + p594 + p595 + p596 + p597 + p598 + p599 + + p600 + p601 + p602 + p603 + p604 + p605 + p606 + p607 + + p608 + p609 + p610 + p611 + p612 + p613 + p614 + p615 + + p616 + p617 + p618 + p619 + p620 + p621 + p622 + p623 + + p624 + p625 + p626 + p627 + p628 + p629 + p630 + p631 + + p632 + p633 + p634 + p635 + p636 + p637 + p638 + p639 + + p640 + p641 + p642 + p643 + p644 + p645 + p646 + p647 + + p648 + p649 + p650 + p651 + p652 + p653 + p654 + p655 + + p656 + p657 + p658 + p659 + p660 + p661 + p662 + p663 + + p664 + p665 + p666 + p667 + p668 + p669 + p670 + p671 + + p672 + p673 + p674 + p675 + p676 + p677 + p678 + p679 + + p680 + p681 + p682 + p683 + p684 + p685 + p686 + p687 + + p688 + p689 + p690 + p691 + p692 + p693 + p694 + p695 + + p696 + p697 + p698 + p699 + p700 + p701 + p702 + p703 + + p704 + p705 + p706 + p707 + p708 + p709 + p710 + p711 + + p712 + p713 + p714 + p715 + p716 + p717 + p718 + p719 + + p720 + p721 + p722 + p723 + p724 + p725 + p726 + p727 + + p728 + p729 + p730 + p731 + p732 + p733 + p734 + p735 + + p736 + p737 + p738 + p739 + p740 + p741 + p742 + p743 + + p744 + p745 + p746 + p747 + p748 + p749 + p750 + p751 + + p752 + p753 + p754 + p755 + p756 + p757 + p758 + p759 + + p760 + p761 + p762 + p763 + p764 + p765 + p766 + p767 + + p768 + p769 + p770 + p771 + p772 + p773 + p774 + p775 + + p776 + p777 + p778 + p779 + p780 + p781 + p782 + p783 + + p784 + p785 + p786 + p787 + p788 + p789 + p790 + p791 + + p792 + p793 + p794 + p795 + p796 + p797 + p798 + p799 + + p800 + p801 + p802 + p803 + p804 + p805 + p806 + p807 + + p808 + p809 + p810 + p811 + p812 + p813 + p814 + p815 + + p816 + p817 + p818 + p819 + p820 + p821 + p822 + p823 + + p824 + p825 + p826 + p827 + p828 + p829 + p830 + p831 + + p832 + p833 + p834 + p835 + p836 + p837 + p838 + p839 + + p840 + p841 + p842 + p843 + p844 + p845 + p846 + p847 + + p848 + p849 + p850 + p851 + p852 + p853 + p854 + p855 + + p856 + p857 + p858 + p859 + p860 + p861 + p862 + p863 + + p864 + p865 + p866 + p867 + p868 + p869 + p870 + p871 + + p872 + p873 + p874 + p875 + p876 + p877 + p878 + p879 + + p880 + p881 + p882 + p883 + p884 + p885 + p886 + p887 + + p888 + p889 + p890 + p891 + p892 + p893 + p894 + p895 + + p896 + p897 + p898 + p899 + p900 + p901 + p902 + p903 + + p904 + p905 + p906 + p907 + p908 + p909 + p910 + p911 + + p912 + p913 + p914 + p915 + p916 + p917 + p918 + p919 + + p920 + p921 + p922 + p923 + p924 + p925 + p926 + p927 + + p928 + p929 + p930 + p931 + p932 + p933 + p934 + p935 + + p936 + p937 + p938 + p939 + p940 + p941 + p942 + p943 + + p944 + p945 + p946 + p947 + p948 + p949 + p950 + p951 + + p952 + p953 + p954 + p955 + p956 + p957 + p958 + p959 + + p960 + p961 + p962 + p963 + p964 + p965 + p966 + p967 + + p968 + p969 + p970 + p971 + p972 + p973 + p974 + p975 + + p976 + p977 + p978 + p979 + p980 + p981 + p982 + p983 + + p984 + p985 + p986 + p987 + p988 + p989 + p990 + p991 + + p992 + p993 + p994 + p995 + p996 + p997 + p998 + p999; + return x; +} + +// clang-format on diff --git a/src/test/app/wasm_fixtures/updateData.c b/src/test/app/wasm_fixtures/updateData.c index 8436f1c390..0c50647492 100644 --- a/src/test/app/wasm_fixtures/updateData.c +++ b/src/test/app/wasm_fixtures/updateData.c @@ -1,13 +1,11 @@ #include -int32_t -update_data(uint8_t const*, int32_t); +int32_t update_data(uint8_t const *, int32_t); -int -finish() +int finish() { - uint8_t buf[] = "Data"; - update_data(buf, sizeof(buf) - 1); + uint8_t buf[] = "Data"; + update_data(buf, sizeof(buf) - 1); - return -256; + return -256; } diff --git a/src/test/app/wasm_fixtures/wat/functions_5k.wat b/src/test/app/wasm_fixtures/wat/functions_5k.wat new file mode 100644 index 0000000000..819472726d --- /dev/null +++ b/src/test/app/wasm_fixtures/wat/functions_5k.wat @@ -0,0 +1,45002 @@ +(module + (func $test0000 (export "test0000") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0001 (export "test0001") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0002 (export "test0002") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0003 (export "test0003") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0004 (export "test0004") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0005 (export "test0005") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0006 (export "test0006") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0007 (export "test0007") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0008 (export "test0008") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0009 (export "test0009") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0010 (export "test0010") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0011 (export "test0011") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0012 (export "test0012") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0013 (export "test0013") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0014 (export "test0014") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0015 (export "test0015") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0016 (export "test0016") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0017 (export "test0017") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0018 (export "test0018") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0019 (export "test0019") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0020 (export "test0020") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0021 (export "test0021") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0022 (export "test0022") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0023 (export "test0023") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0024 (export "test0024") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0025 (export "test0025") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0026 (export "test0026") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0027 (export "test0027") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0028 (export "test0028") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0029 (export "test0029") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0030 (export "test0030") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0031 (export "test0031") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0032 (export "test0032") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0033 (export "test0033") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0034 (export "test0034") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0035 (export "test0035") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0036 (export "test0036") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0037 (export "test0037") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0038 (export "test0038") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0039 (export "test0039") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0040 (export "test0040") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0041 (export "test0041") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0042 (export "test0042") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0043 (export "test0043") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0044 (export "test0044") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0045 (export "test0045") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0046 (export "test0046") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0047 (export "test0047") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0048 (export "test0048") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0049 (export "test0049") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0050 (export "test0050") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0051 (export "test0051") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0052 (export "test0052") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0053 (export "test0053") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0054 (export "test0054") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0055 (export "test0055") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0056 (export "test0056") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0057 (export "test0057") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0058 (export "test0058") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0059 (export "test0059") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0060 (export "test0060") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0061 (export "test0061") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0062 (export "test0062") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0063 (export "test0063") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0064 (export "test0064") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0065 (export "test0065") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0066 (export "test0066") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0067 (export "test0067") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0068 (export "test0068") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0069 (export "test0069") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0070 (export "test0070") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0071 (export "test0071") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0072 (export "test0072") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0073 (export "test0073") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0074 (export "test0074") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0075 (export "test0075") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0076 (export "test0076") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0077 (export "test0077") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0078 (export "test0078") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0079 (export "test0079") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0080 (export "test0080") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0081 (export "test0081") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0082 (export "test0082") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0083 (export "test0083") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0084 (export "test0084") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0085 (export "test0085") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0086 (export "test0086") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0087 (export "test0087") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0088 (export "test0088") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0089 (export "test0089") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0090 (export "test0090") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0091 (export "test0091") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0092 (export "test0092") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0093 (export "test0093") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0094 (export "test0094") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0095 (export "test0095") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0096 (export "test0096") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0097 (export "test0097") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0098 (export "test0098") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0099 (export "test0099") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0100 (export "test0100") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0101 (export "test0101") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0102 (export "test0102") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0103 (export "test0103") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0104 (export "test0104") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0105 (export "test0105") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0106 (export "test0106") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0107 (export "test0107") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0108 (export "test0108") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0109 (export "test0109") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0110 (export "test0110") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0111 (export "test0111") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0112 (export "test0112") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0113 (export "test0113") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0114 (export "test0114") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0115 (export "test0115") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0116 (export "test0116") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0117 (export "test0117") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0118 (export "test0118") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0119 (export "test0119") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0120 (export "test0120") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0121 (export "test0121") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0122 (export "test0122") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0123 (export "test0123") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0124 (export "test0124") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0125 (export "test0125") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0126 (export "test0126") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0127 (export "test0127") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0128 (export "test0128") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0129 (export "test0129") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0130 (export "test0130") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0131 (export "test0131") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0132 (export "test0132") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0133 (export "test0133") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0134 (export "test0134") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0135 (export "test0135") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0136 (export "test0136") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0137 (export "test0137") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0138 (export "test0138") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0139 (export "test0139") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0140 (export "test0140") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0141 (export "test0141") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0142 (export "test0142") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0143 (export "test0143") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0144 (export "test0144") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0145 (export "test0145") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0146 (export "test0146") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0147 (export "test0147") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0148 (export "test0148") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0149 (export "test0149") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0150 (export "test0150") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0151 (export "test0151") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0152 (export "test0152") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0153 (export "test0153") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0154 (export "test0154") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0155 (export "test0155") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0156 (export "test0156") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0157 (export "test0157") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0158 (export "test0158") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0159 (export "test0159") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0160 (export "test0160") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0161 (export "test0161") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0162 (export "test0162") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0163 (export "test0163") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0164 (export "test0164") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0165 (export "test0165") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0166 (export "test0166") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0167 (export "test0167") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0168 (export "test0168") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0169 (export "test0169") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0170 (export "test0170") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0171 (export "test0171") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0172 (export "test0172") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0173 (export "test0173") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0174 (export "test0174") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0175 (export "test0175") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0176 (export "test0176") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0177 (export "test0177") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0178 (export "test0178") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0179 (export "test0179") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0180 (export "test0180") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0181 (export "test0181") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0182 (export "test0182") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0183 (export "test0183") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0184 (export "test0184") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0185 (export "test0185") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0186 (export "test0186") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0187 (export "test0187") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0188 (export "test0188") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0189 (export "test0189") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0190 (export "test0190") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0191 (export "test0191") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0192 (export "test0192") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0193 (export "test0193") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0194 (export "test0194") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0195 (export "test0195") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0196 (export "test0196") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0197 (export "test0197") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0198 (export "test0198") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0199 (export "test0199") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0200 (export "test0200") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0201 (export "test0201") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0202 (export "test0202") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0203 (export "test0203") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0204 (export "test0204") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0205 (export "test0205") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0206 (export "test0206") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0207 (export "test0207") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0208 (export "test0208") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0209 (export "test0209") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0210 (export "test0210") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0211 (export "test0211") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0212 (export "test0212") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0213 (export "test0213") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0214 (export "test0214") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0215 (export "test0215") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0216 (export "test0216") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0217 (export "test0217") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0218 (export "test0218") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0219 (export "test0219") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0220 (export "test0220") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0221 (export "test0221") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0222 (export "test0222") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0223 (export "test0223") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0224 (export "test0224") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0225 (export "test0225") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0226 (export "test0226") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0227 (export "test0227") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0228 (export "test0228") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0229 (export "test0229") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0230 (export "test0230") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0231 (export "test0231") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0232 (export "test0232") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0233 (export "test0233") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0234 (export "test0234") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0235 (export "test0235") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0236 (export "test0236") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0237 (export "test0237") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0238 (export "test0238") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0239 (export "test0239") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0240 (export "test0240") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0241 (export "test0241") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0242 (export "test0242") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0243 (export "test0243") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0244 (export "test0244") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0245 (export "test0245") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0246 (export "test0246") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0247 (export "test0247") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0248 (export "test0248") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0249 (export "test0249") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0250 (export "test0250") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0251 (export "test0251") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0252 (export "test0252") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0253 (export "test0253") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0254 (export "test0254") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0255 (export "test0255") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0256 (export "test0256") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0257 (export "test0257") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0258 (export "test0258") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0259 (export "test0259") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0260 (export "test0260") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0261 (export "test0261") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0262 (export "test0262") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0263 (export "test0263") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0264 (export "test0264") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0265 (export "test0265") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0266 (export "test0266") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0267 (export "test0267") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0268 (export "test0268") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0269 (export "test0269") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0270 (export "test0270") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0271 (export "test0271") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0272 (export "test0272") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0273 (export "test0273") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0274 (export "test0274") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0275 (export "test0275") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0276 (export "test0276") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0277 (export "test0277") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0278 (export "test0278") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0279 (export "test0279") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0280 (export "test0280") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0281 (export "test0281") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0282 (export "test0282") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0283 (export "test0283") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0284 (export "test0284") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0285 (export "test0285") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0286 (export "test0286") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0287 (export "test0287") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0288 (export "test0288") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0289 (export "test0289") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0290 (export "test0290") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0291 (export "test0291") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0292 (export "test0292") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0293 (export "test0293") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0294 (export "test0294") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0295 (export "test0295") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0296 (export "test0296") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0297 (export "test0297") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0298 (export "test0298") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0299 (export "test0299") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0300 (export "test0300") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0301 (export "test0301") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0302 (export "test0302") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0303 (export "test0303") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0304 (export "test0304") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0305 (export "test0305") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0306 (export "test0306") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0307 (export "test0307") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0308 (export "test0308") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0309 (export "test0309") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0310 (export "test0310") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0311 (export "test0311") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0312 (export "test0312") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0313 (export "test0313") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0314 (export "test0314") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0315 (export "test0315") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0316 (export "test0316") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0317 (export "test0317") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0318 (export "test0318") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0319 (export "test0319") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0320 (export "test0320") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0321 (export "test0321") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0322 (export "test0322") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0323 (export "test0323") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0324 (export "test0324") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0325 (export "test0325") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0326 (export "test0326") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0327 (export "test0327") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0328 (export "test0328") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0329 (export "test0329") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0330 (export "test0330") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0331 (export "test0331") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0332 (export "test0332") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0333 (export "test0333") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0334 (export "test0334") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0335 (export "test0335") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0336 (export "test0336") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0337 (export "test0337") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0338 (export "test0338") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0339 (export "test0339") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0340 (export "test0340") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0341 (export "test0341") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0342 (export "test0342") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0343 (export "test0343") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0344 (export "test0344") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0345 (export "test0345") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0346 (export "test0346") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0347 (export "test0347") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0348 (export "test0348") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0349 (export "test0349") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0350 (export "test0350") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0351 (export "test0351") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0352 (export "test0352") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0353 (export "test0353") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0354 (export "test0354") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0355 (export "test0355") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0356 (export "test0356") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0357 (export "test0357") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0358 (export "test0358") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0359 (export "test0359") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0360 (export "test0360") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0361 (export "test0361") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0362 (export "test0362") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0363 (export "test0363") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0364 (export "test0364") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0365 (export "test0365") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0366 (export "test0366") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0367 (export "test0367") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0368 (export "test0368") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0369 (export "test0369") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0370 (export "test0370") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0371 (export "test0371") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0372 (export "test0372") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0373 (export "test0373") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0374 (export "test0374") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0375 (export "test0375") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0376 (export "test0376") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0377 (export "test0377") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0378 (export "test0378") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0379 (export "test0379") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0380 (export "test0380") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0381 (export "test0381") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0382 (export "test0382") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0383 (export "test0383") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0384 (export "test0384") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0385 (export "test0385") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0386 (export "test0386") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0387 (export "test0387") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0388 (export "test0388") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0389 (export "test0389") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0390 (export "test0390") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0391 (export "test0391") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0392 (export "test0392") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0393 (export "test0393") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0394 (export "test0394") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0395 (export "test0395") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0396 (export "test0396") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0397 (export "test0397") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0398 (export "test0398") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0399 (export "test0399") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0400 (export "test0400") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0401 (export "test0401") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0402 (export "test0402") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0403 (export "test0403") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0404 (export "test0404") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0405 (export "test0405") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0406 (export "test0406") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0407 (export "test0407") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0408 (export "test0408") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0409 (export "test0409") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0410 (export "test0410") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0411 (export "test0411") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0412 (export "test0412") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0413 (export "test0413") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0414 (export "test0414") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0415 (export "test0415") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0416 (export "test0416") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0417 (export "test0417") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0418 (export "test0418") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0419 (export "test0419") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0420 (export "test0420") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0421 (export "test0421") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0422 (export "test0422") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0423 (export "test0423") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0424 (export "test0424") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0425 (export "test0425") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0426 (export "test0426") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0427 (export "test0427") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0428 (export "test0428") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0429 (export "test0429") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0430 (export "test0430") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0431 (export "test0431") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0432 (export "test0432") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0433 (export "test0433") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0434 (export "test0434") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0435 (export "test0435") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0436 (export "test0436") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0437 (export "test0437") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0438 (export "test0438") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0439 (export "test0439") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0440 (export "test0440") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0441 (export "test0441") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0442 (export "test0442") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0443 (export "test0443") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0444 (export "test0444") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0445 (export "test0445") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0446 (export "test0446") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0447 (export "test0447") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0448 (export "test0448") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0449 (export "test0449") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0450 (export "test0450") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0451 (export "test0451") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0452 (export "test0452") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0453 (export "test0453") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0454 (export "test0454") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0455 (export "test0455") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0456 (export "test0456") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0457 (export "test0457") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0458 (export "test0458") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0459 (export "test0459") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0460 (export "test0460") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0461 (export "test0461") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0462 (export "test0462") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0463 (export "test0463") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0464 (export "test0464") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0465 (export "test0465") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0466 (export "test0466") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0467 (export "test0467") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0468 (export "test0468") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0469 (export "test0469") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0470 (export "test0470") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0471 (export "test0471") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0472 (export "test0472") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0473 (export "test0473") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0474 (export "test0474") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0475 (export "test0475") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0476 (export "test0476") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0477 (export "test0477") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0478 (export "test0478") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0479 (export "test0479") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0480 (export "test0480") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0481 (export "test0481") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0482 (export "test0482") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0483 (export "test0483") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0484 (export "test0484") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0485 (export "test0485") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0486 (export "test0486") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0487 (export "test0487") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0488 (export "test0488") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0489 (export "test0489") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0490 (export "test0490") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0491 (export "test0491") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0492 (export "test0492") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0493 (export "test0493") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0494 (export "test0494") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0495 (export "test0495") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0496 (export "test0496") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0497 (export "test0497") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0498 (export "test0498") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0499 (export "test0499") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0500 (export "test0500") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0501 (export "test0501") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0502 (export "test0502") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0503 (export "test0503") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0504 (export "test0504") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0505 (export "test0505") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0506 (export "test0506") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0507 (export "test0507") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0508 (export "test0508") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0509 (export "test0509") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0510 (export "test0510") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0511 (export "test0511") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0512 (export "test0512") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0513 (export "test0513") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0514 (export "test0514") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0515 (export "test0515") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0516 (export "test0516") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0517 (export "test0517") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0518 (export "test0518") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0519 (export "test0519") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0520 (export "test0520") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0521 (export "test0521") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0522 (export "test0522") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0523 (export "test0523") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0524 (export "test0524") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0525 (export "test0525") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0526 (export "test0526") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0527 (export "test0527") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0528 (export "test0528") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0529 (export "test0529") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0530 (export "test0530") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0531 (export "test0531") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0532 (export "test0532") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0533 (export "test0533") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0534 (export "test0534") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0535 (export "test0535") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0536 (export "test0536") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0537 (export "test0537") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0538 (export "test0538") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0539 (export "test0539") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0540 (export "test0540") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0541 (export "test0541") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0542 (export "test0542") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0543 (export "test0543") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0544 (export "test0544") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0545 (export "test0545") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0546 (export "test0546") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0547 (export "test0547") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0548 (export "test0548") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0549 (export "test0549") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0550 (export "test0550") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0551 (export "test0551") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0552 (export "test0552") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0553 (export "test0553") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0554 (export "test0554") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0555 (export "test0555") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0556 (export "test0556") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0557 (export "test0557") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0558 (export "test0558") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0559 (export "test0559") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0560 (export "test0560") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0561 (export "test0561") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0562 (export "test0562") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0563 (export "test0563") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0564 (export "test0564") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0565 (export "test0565") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0566 (export "test0566") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0567 (export "test0567") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0568 (export "test0568") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0569 (export "test0569") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0570 (export "test0570") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0571 (export "test0571") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0572 (export "test0572") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0573 (export "test0573") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0574 (export "test0574") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0575 (export "test0575") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0576 (export "test0576") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0577 (export "test0577") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0578 (export "test0578") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0579 (export "test0579") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0580 (export "test0580") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0581 (export "test0581") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0582 (export "test0582") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0583 (export "test0583") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0584 (export "test0584") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0585 (export "test0585") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0586 (export "test0586") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0587 (export "test0587") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0588 (export "test0588") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0589 (export "test0589") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0590 (export "test0590") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0591 (export "test0591") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0592 (export "test0592") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0593 (export "test0593") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0594 (export "test0594") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0595 (export "test0595") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0596 (export "test0596") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0597 (export "test0597") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0598 (export "test0598") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0599 (export "test0599") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0600 (export "test0600") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0601 (export "test0601") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0602 (export "test0602") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0603 (export "test0603") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0604 (export "test0604") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0605 (export "test0605") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0606 (export "test0606") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0607 (export "test0607") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0608 (export "test0608") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0609 (export "test0609") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0610 (export "test0610") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0611 (export "test0611") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0612 (export "test0612") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0613 (export "test0613") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0614 (export "test0614") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0615 (export "test0615") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0616 (export "test0616") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0617 (export "test0617") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0618 (export "test0618") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0619 (export "test0619") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0620 (export "test0620") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0621 (export "test0621") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0622 (export "test0622") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0623 (export "test0623") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0624 (export "test0624") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0625 (export "test0625") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0626 (export "test0626") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0627 (export "test0627") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0628 (export "test0628") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0629 (export "test0629") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0630 (export "test0630") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0631 (export "test0631") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0632 (export "test0632") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0633 (export "test0633") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0634 (export "test0634") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0635 (export "test0635") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0636 (export "test0636") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0637 (export "test0637") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0638 (export "test0638") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0639 (export "test0639") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0640 (export "test0640") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0641 (export "test0641") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0642 (export "test0642") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0643 (export "test0643") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0644 (export "test0644") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0645 (export "test0645") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0646 (export "test0646") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0647 (export "test0647") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0648 (export "test0648") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0649 (export "test0649") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0650 (export "test0650") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0651 (export "test0651") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0652 (export "test0652") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0653 (export "test0653") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0654 (export "test0654") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0655 (export "test0655") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0656 (export "test0656") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0657 (export "test0657") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0658 (export "test0658") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0659 (export "test0659") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0660 (export "test0660") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0661 (export "test0661") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0662 (export "test0662") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0663 (export "test0663") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0664 (export "test0664") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0665 (export "test0665") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0666 (export "test0666") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0667 (export "test0667") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0668 (export "test0668") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0669 (export "test0669") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0670 (export "test0670") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0671 (export "test0671") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0672 (export "test0672") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0673 (export "test0673") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0674 (export "test0674") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0675 (export "test0675") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0676 (export "test0676") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0677 (export "test0677") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0678 (export "test0678") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0679 (export "test0679") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0680 (export "test0680") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0681 (export "test0681") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0682 (export "test0682") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0683 (export "test0683") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0684 (export "test0684") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0685 (export "test0685") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0686 (export "test0686") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0687 (export "test0687") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0688 (export "test0688") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0689 (export "test0689") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0690 (export "test0690") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0691 (export "test0691") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0692 (export "test0692") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0693 (export "test0693") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0694 (export "test0694") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0695 (export "test0695") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0696 (export "test0696") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0697 (export "test0697") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0698 (export "test0698") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0699 (export "test0699") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0700 (export "test0700") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0701 (export "test0701") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0702 (export "test0702") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0703 (export "test0703") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0704 (export "test0704") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0705 (export "test0705") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0706 (export "test0706") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0707 (export "test0707") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0708 (export "test0708") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0709 (export "test0709") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0710 (export "test0710") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0711 (export "test0711") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0712 (export "test0712") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0713 (export "test0713") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0714 (export "test0714") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0715 (export "test0715") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0716 (export "test0716") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0717 (export "test0717") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0718 (export "test0718") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0719 (export "test0719") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0720 (export "test0720") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0721 (export "test0721") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0722 (export "test0722") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0723 (export "test0723") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0724 (export "test0724") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0725 (export "test0725") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0726 (export "test0726") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0727 (export "test0727") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0728 (export "test0728") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0729 (export "test0729") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0730 (export "test0730") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0731 (export "test0731") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0732 (export "test0732") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0733 (export "test0733") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0734 (export "test0734") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0735 (export "test0735") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0736 (export "test0736") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0737 (export "test0737") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0738 (export "test0738") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0739 (export "test0739") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0740 (export "test0740") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0741 (export "test0741") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0742 (export "test0742") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0743 (export "test0743") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0744 (export "test0744") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0745 (export "test0745") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0746 (export "test0746") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0747 (export "test0747") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0748 (export "test0748") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0749 (export "test0749") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0750 (export "test0750") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0751 (export "test0751") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0752 (export "test0752") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0753 (export "test0753") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0754 (export "test0754") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0755 (export "test0755") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0756 (export "test0756") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0757 (export "test0757") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0758 (export "test0758") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0759 (export "test0759") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0760 (export "test0760") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0761 (export "test0761") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0762 (export "test0762") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0763 (export "test0763") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0764 (export "test0764") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0765 (export "test0765") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0766 (export "test0766") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0767 (export "test0767") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0768 (export "test0768") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0769 (export "test0769") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0770 (export "test0770") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0771 (export "test0771") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0772 (export "test0772") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0773 (export "test0773") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0774 (export "test0774") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0775 (export "test0775") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0776 (export "test0776") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0777 (export "test0777") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0778 (export "test0778") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0779 (export "test0779") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0780 (export "test0780") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0781 (export "test0781") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0782 (export "test0782") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0783 (export "test0783") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0784 (export "test0784") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0785 (export "test0785") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0786 (export "test0786") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0787 (export "test0787") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0788 (export "test0788") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0789 (export "test0789") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0790 (export "test0790") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0791 (export "test0791") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0792 (export "test0792") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0793 (export "test0793") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0794 (export "test0794") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0795 (export "test0795") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0796 (export "test0796") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0797 (export "test0797") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0798 (export "test0798") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0799 (export "test0799") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0800 (export "test0800") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0801 (export "test0801") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0802 (export "test0802") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0803 (export "test0803") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0804 (export "test0804") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0805 (export "test0805") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0806 (export "test0806") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0807 (export "test0807") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0808 (export "test0808") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0809 (export "test0809") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0810 (export "test0810") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0811 (export "test0811") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0812 (export "test0812") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0813 (export "test0813") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0814 (export "test0814") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0815 (export "test0815") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0816 (export "test0816") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0817 (export "test0817") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0818 (export "test0818") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0819 (export "test0819") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0820 (export "test0820") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0821 (export "test0821") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0822 (export "test0822") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0823 (export "test0823") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0824 (export "test0824") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0825 (export "test0825") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0826 (export "test0826") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0827 (export "test0827") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0828 (export "test0828") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0829 (export "test0829") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0830 (export "test0830") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0831 (export "test0831") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0832 (export "test0832") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0833 (export "test0833") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0834 (export "test0834") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0835 (export "test0835") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0836 (export "test0836") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0837 (export "test0837") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0838 (export "test0838") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0839 (export "test0839") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0840 (export "test0840") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0841 (export "test0841") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0842 (export "test0842") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0843 (export "test0843") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0844 (export "test0844") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0845 (export "test0845") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0846 (export "test0846") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0847 (export "test0847") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0848 (export "test0848") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0849 (export "test0849") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0850 (export "test0850") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0851 (export "test0851") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0852 (export "test0852") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0853 (export "test0853") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0854 (export "test0854") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0855 (export "test0855") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0856 (export "test0856") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0857 (export "test0857") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0858 (export "test0858") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0859 (export "test0859") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0860 (export "test0860") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0861 (export "test0861") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0862 (export "test0862") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0863 (export "test0863") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0864 (export "test0864") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0865 (export "test0865") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0866 (export "test0866") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0867 (export "test0867") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0868 (export "test0868") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0869 (export "test0869") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0870 (export "test0870") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0871 (export "test0871") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0872 (export "test0872") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0873 (export "test0873") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0874 (export "test0874") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0875 (export "test0875") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0876 (export "test0876") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0877 (export "test0877") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0878 (export "test0878") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0879 (export "test0879") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0880 (export "test0880") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0881 (export "test0881") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0882 (export "test0882") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0883 (export "test0883") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0884 (export "test0884") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0885 (export "test0885") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0886 (export "test0886") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0887 (export "test0887") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0888 (export "test0888") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0889 (export "test0889") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0890 (export "test0890") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0891 (export "test0891") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0892 (export "test0892") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0893 (export "test0893") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0894 (export "test0894") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0895 (export "test0895") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0896 (export "test0896") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0897 (export "test0897") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0898 (export "test0898") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0899 (export "test0899") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0900 (export "test0900") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0901 (export "test0901") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0902 (export "test0902") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0903 (export "test0903") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0904 (export "test0904") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0905 (export "test0905") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0906 (export "test0906") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0907 (export "test0907") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0908 (export "test0908") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0909 (export "test0909") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0910 (export "test0910") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0911 (export "test0911") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0912 (export "test0912") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0913 (export "test0913") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0914 (export "test0914") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0915 (export "test0915") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0916 (export "test0916") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0917 (export "test0917") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0918 (export "test0918") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0919 (export "test0919") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0920 (export "test0920") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0921 (export "test0921") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0922 (export "test0922") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0923 (export "test0923") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0924 (export "test0924") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0925 (export "test0925") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0926 (export "test0926") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0927 (export "test0927") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0928 (export "test0928") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0929 (export "test0929") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0930 (export "test0930") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0931 (export "test0931") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0932 (export "test0932") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0933 (export "test0933") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0934 (export "test0934") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0935 (export "test0935") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0936 (export "test0936") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0937 (export "test0937") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0938 (export "test0938") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0939 (export "test0939") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0940 (export "test0940") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0941 (export "test0941") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0942 (export "test0942") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0943 (export "test0943") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0944 (export "test0944") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0945 (export "test0945") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0946 (export "test0946") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0947 (export "test0947") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0948 (export "test0948") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0949 (export "test0949") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0950 (export "test0950") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0951 (export "test0951") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0952 (export "test0952") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0953 (export "test0953") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0954 (export "test0954") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0955 (export "test0955") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0956 (export "test0956") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0957 (export "test0957") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0958 (export "test0958") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0959 (export "test0959") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0960 (export "test0960") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0961 (export "test0961") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0962 (export "test0962") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0963 (export "test0963") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0964 (export "test0964") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0965 (export "test0965") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0966 (export "test0966") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0967 (export "test0967") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0968 (export "test0968") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0969 (export "test0969") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0970 (export "test0970") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0971 (export "test0971") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0972 (export "test0972") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0973 (export "test0973") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0974 (export "test0974") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0975 (export "test0975") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0976 (export "test0976") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0977 (export "test0977") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0978 (export "test0978") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0979 (export "test0979") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0980 (export "test0980") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0981 (export "test0981") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0982 (export "test0982") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0983 (export "test0983") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0984 (export "test0984") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0985 (export "test0985") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0986 (export "test0986") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0987 (export "test0987") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0988 (export "test0988") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0989 (export "test0989") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0990 (export "test0990") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0991 (export "test0991") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0992 (export "test0992") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0993 (export "test0993") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0994 (export "test0994") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0995 (export "test0995") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0996 (export "test0996") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0997 (export "test0997") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0998 (export "test0998") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test0999 (export "test0999") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1000 (export "test1000") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1001 (export "test1001") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1002 (export "test1002") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1003 (export "test1003") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1004 (export "test1004") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1005 (export "test1005") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1006 (export "test1006") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1007 (export "test1007") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1008 (export "test1008") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1009 (export "test1009") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1010 (export "test1010") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1011 (export "test1011") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1012 (export "test1012") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1013 (export "test1013") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1014 (export "test1014") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1015 (export "test1015") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1016 (export "test1016") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1017 (export "test1017") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1018 (export "test1018") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1019 (export "test1019") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1020 (export "test1020") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1021 (export "test1021") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1022 (export "test1022") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1023 (export "test1023") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1024 (export "test1024") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1025 (export "test1025") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1026 (export "test1026") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1027 (export "test1027") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1028 (export "test1028") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1029 (export "test1029") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1030 (export "test1030") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1031 (export "test1031") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1032 (export "test1032") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1033 (export "test1033") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1034 (export "test1034") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1035 (export "test1035") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1036 (export "test1036") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1037 (export "test1037") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1038 (export "test1038") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1039 (export "test1039") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1040 (export "test1040") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1041 (export "test1041") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1042 (export "test1042") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1043 (export "test1043") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1044 (export "test1044") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1045 (export "test1045") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1046 (export "test1046") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1047 (export "test1047") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1048 (export "test1048") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1049 (export "test1049") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1050 (export "test1050") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1051 (export "test1051") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1052 (export "test1052") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1053 (export "test1053") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1054 (export "test1054") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1055 (export "test1055") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1056 (export "test1056") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1057 (export "test1057") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1058 (export "test1058") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1059 (export "test1059") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1060 (export "test1060") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1061 (export "test1061") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1062 (export "test1062") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1063 (export "test1063") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1064 (export "test1064") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1065 (export "test1065") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1066 (export "test1066") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1067 (export "test1067") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1068 (export "test1068") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1069 (export "test1069") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1070 (export "test1070") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1071 (export "test1071") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1072 (export "test1072") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1073 (export "test1073") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1074 (export "test1074") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1075 (export "test1075") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1076 (export "test1076") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1077 (export "test1077") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1078 (export "test1078") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1079 (export "test1079") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1080 (export "test1080") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1081 (export "test1081") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1082 (export "test1082") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1083 (export "test1083") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1084 (export "test1084") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1085 (export "test1085") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1086 (export "test1086") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1087 (export "test1087") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1088 (export "test1088") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1089 (export "test1089") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1090 (export "test1090") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1091 (export "test1091") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1092 (export "test1092") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1093 (export "test1093") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1094 (export "test1094") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1095 (export "test1095") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1096 (export "test1096") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1097 (export "test1097") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1098 (export "test1098") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1099 (export "test1099") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1100 (export "test1100") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1101 (export "test1101") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1102 (export "test1102") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1103 (export "test1103") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1104 (export "test1104") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1105 (export "test1105") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1106 (export "test1106") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1107 (export "test1107") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1108 (export "test1108") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1109 (export "test1109") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1110 (export "test1110") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1111 (export "test1111") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1112 (export "test1112") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1113 (export "test1113") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1114 (export "test1114") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1115 (export "test1115") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1116 (export "test1116") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1117 (export "test1117") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1118 (export "test1118") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1119 (export "test1119") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1120 (export "test1120") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1121 (export "test1121") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1122 (export "test1122") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1123 (export "test1123") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1124 (export "test1124") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1125 (export "test1125") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1126 (export "test1126") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1127 (export "test1127") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1128 (export "test1128") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1129 (export "test1129") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1130 (export "test1130") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1131 (export "test1131") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1132 (export "test1132") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1133 (export "test1133") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1134 (export "test1134") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1135 (export "test1135") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1136 (export "test1136") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1137 (export "test1137") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1138 (export "test1138") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1139 (export "test1139") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1140 (export "test1140") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1141 (export "test1141") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1142 (export "test1142") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1143 (export "test1143") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1144 (export "test1144") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1145 (export "test1145") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1146 (export "test1146") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1147 (export "test1147") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1148 (export "test1148") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1149 (export "test1149") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1150 (export "test1150") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1151 (export "test1151") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1152 (export "test1152") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1153 (export "test1153") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1154 (export "test1154") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1155 (export "test1155") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1156 (export "test1156") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1157 (export "test1157") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1158 (export "test1158") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1159 (export "test1159") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1160 (export "test1160") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1161 (export "test1161") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1162 (export "test1162") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1163 (export "test1163") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1164 (export "test1164") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1165 (export "test1165") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1166 (export "test1166") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1167 (export "test1167") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1168 (export "test1168") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1169 (export "test1169") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1170 (export "test1170") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1171 (export "test1171") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1172 (export "test1172") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1173 (export "test1173") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1174 (export "test1174") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1175 (export "test1175") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1176 (export "test1176") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1177 (export "test1177") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1178 (export "test1178") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1179 (export "test1179") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1180 (export "test1180") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1181 (export "test1181") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1182 (export "test1182") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1183 (export "test1183") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1184 (export "test1184") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1185 (export "test1185") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1186 (export "test1186") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1187 (export "test1187") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1188 (export "test1188") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1189 (export "test1189") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1190 (export "test1190") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1191 (export "test1191") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1192 (export "test1192") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1193 (export "test1193") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1194 (export "test1194") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1195 (export "test1195") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1196 (export "test1196") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1197 (export "test1197") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1198 (export "test1198") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1199 (export "test1199") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1200 (export "test1200") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1201 (export "test1201") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1202 (export "test1202") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1203 (export "test1203") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1204 (export "test1204") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1205 (export "test1205") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1206 (export "test1206") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1207 (export "test1207") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1208 (export "test1208") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1209 (export "test1209") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1210 (export "test1210") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1211 (export "test1211") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1212 (export "test1212") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1213 (export "test1213") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1214 (export "test1214") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1215 (export "test1215") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1216 (export "test1216") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1217 (export "test1217") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1218 (export "test1218") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1219 (export "test1219") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1220 (export "test1220") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1221 (export "test1221") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1222 (export "test1222") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1223 (export "test1223") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1224 (export "test1224") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1225 (export "test1225") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1226 (export "test1226") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1227 (export "test1227") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1228 (export "test1228") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1229 (export "test1229") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1230 (export "test1230") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1231 (export "test1231") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1232 (export "test1232") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1233 (export "test1233") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1234 (export "test1234") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1235 (export "test1235") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1236 (export "test1236") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1237 (export "test1237") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1238 (export "test1238") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1239 (export "test1239") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1240 (export "test1240") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1241 (export "test1241") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1242 (export "test1242") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1243 (export "test1243") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1244 (export "test1244") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1245 (export "test1245") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1246 (export "test1246") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1247 (export "test1247") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1248 (export "test1248") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1249 (export "test1249") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1250 (export "test1250") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1251 (export "test1251") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1252 (export "test1252") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1253 (export "test1253") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1254 (export "test1254") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1255 (export "test1255") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1256 (export "test1256") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1257 (export "test1257") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1258 (export "test1258") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1259 (export "test1259") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1260 (export "test1260") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1261 (export "test1261") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1262 (export "test1262") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1263 (export "test1263") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1264 (export "test1264") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1265 (export "test1265") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1266 (export "test1266") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1267 (export "test1267") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1268 (export "test1268") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1269 (export "test1269") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1270 (export "test1270") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1271 (export "test1271") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1272 (export "test1272") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1273 (export "test1273") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1274 (export "test1274") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1275 (export "test1275") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1276 (export "test1276") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1277 (export "test1277") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1278 (export "test1278") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1279 (export "test1279") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1280 (export "test1280") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1281 (export "test1281") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1282 (export "test1282") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1283 (export "test1283") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1284 (export "test1284") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1285 (export "test1285") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1286 (export "test1286") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1287 (export "test1287") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1288 (export "test1288") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1289 (export "test1289") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1290 (export "test1290") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1291 (export "test1291") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1292 (export "test1292") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1293 (export "test1293") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1294 (export "test1294") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1295 (export "test1295") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1296 (export "test1296") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1297 (export "test1297") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1298 (export "test1298") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1299 (export "test1299") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1300 (export "test1300") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1301 (export "test1301") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1302 (export "test1302") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1303 (export "test1303") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1304 (export "test1304") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1305 (export "test1305") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1306 (export "test1306") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1307 (export "test1307") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1308 (export "test1308") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1309 (export "test1309") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1310 (export "test1310") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1311 (export "test1311") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1312 (export "test1312") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1313 (export "test1313") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1314 (export "test1314") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1315 (export "test1315") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1316 (export "test1316") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1317 (export "test1317") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1318 (export "test1318") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1319 (export "test1319") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1320 (export "test1320") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1321 (export "test1321") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1322 (export "test1322") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1323 (export "test1323") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1324 (export "test1324") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1325 (export "test1325") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1326 (export "test1326") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1327 (export "test1327") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1328 (export "test1328") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1329 (export "test1329") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1330 (export "test1330") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1331 (export "test1331") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1332 (export "test1332") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1333 (export "test1333") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1334 (export "test1334") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1335 (export "test1335") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1336 (export "test1336") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1337 (export "test1337") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1338 (export "test1338") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1339 (export "test1339") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1340 (export "test1340") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1341 (export "test1341") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1342 (export "test1342") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1343 (export "test1343") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1344 (export "test1344") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1345 (export "test1345") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1346 (export "test1346") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1347 (export "test1347") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1348 (export "test1348") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1349 (export "test1349") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1350 (export "test1350") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1351 (export "test1351") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1352 (export "test1352") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1353 (export "test1353") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1354 (export "test1354") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1355 (export "test1355") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1356 (export "test1356") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1357 (export "test1357") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1358 (export "test1358") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1359 (export "test1359") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1360 (export "test1360") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1361 (export "test1361") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1362 (export "test1362") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1363 (export "test1363") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1364 (export "test1364") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1365 (export "test1365") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1366 (export "test1366") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1367 (export "test1367") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1368 (export "test1368") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1369 (export "test1369") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1370 (export "test1370") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1371 (export "test1371") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1372 (export "test1372") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1373 (export "test1373") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1374 (export "test1374") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1375 (export "test1375") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1376 (export "test1376") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1377 (export "test1377") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1378 (export "test1378") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1379 (export "test1379") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1380 (export "test1380") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1381 (export "test1381") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1382 (export "test1382") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1383 (export "test1383") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1384 (export "test1384") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1385 (export "test1385") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1386 (export "test1386") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1387 (export "test1387") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1388 (export "test1388") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1389 (export "test1389") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1390 (export "test1390") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1391 (export "test1391") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1392 (export "test1392") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1393 (export "test1393") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1394 (export "test1394") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1395 (export "test1395") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1396 (export "test1396") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1397 (export "test1397") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1398 (export "test1398") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1399 (export "test1399") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1400 (export "test1400") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1401 (export "test1401") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1402 (export "test1402") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1403 (export "test1403") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1404 (export "test1404") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1405 (export "test1405") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1406 (export "test1406") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1407 (export "test1407") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1408 (export "test1408") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1409 (export "test1409") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1410 (export "test1410") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1411 (export "test1411") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1412 (export "test1412") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1413 (export "test1413") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1414 (export "test1414") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1415 (export "test1415") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1416 (export "test1416") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1417 (export "test1417") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1418 (export "test1418") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1419 (export "test1419") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1420 (export "test1420") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1421 (export "test1421") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1422 (export "test1422") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1423 (export "test1423") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1424 (export "test1424") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1425 (export "test1425") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1426 (export "test1426") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1427 (export "test1427") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1428 (export "test1428") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1429 (export "test1429") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1430 (export "test1430") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1431 (export "test1431") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1432 (export "test1432") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1433 (export "test1433") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1434 (export "test1434") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1435 (export "test1435") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1436 (export "test1436") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1437 (export "test1437") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1438 (export "test1438") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1439 (export "test1439") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1440 (export "test1440") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1441 (export "test1441") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1442 (export "test1442") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1443 (export "test1443") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1444 (export "test1444") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1445 (export "test1445") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1446 (export "test1446") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1447 (export "test1447") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1448 (export "test1448") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1449 (export "test1449") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1450 (export "test1450") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1451 (export "test1451") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1452 (export "test1452") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1453 (export "test1453") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1454 (export "test1454") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1455 (export "test1455") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1456 (export "test1456") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1457 (export "test1457") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1458 (export "test1458") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1459 (export "test1459") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1460 (export "test1460") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1461 (export "test1461") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1462 (export "test1462") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1463 (export "test1463") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1464 (export "test1464") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1465 (export "test1465") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1466 (export "test1466") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1467 (export "test1467") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1468 (export "test1468") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1469 (export "test1469") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1470 (export "test1470") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1471 (export "test1471") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1472 (export "test1472") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1473 (export "test1473") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1474 (export "test1474") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1475 (export "test1475") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1476 (export "test1476") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1477 (export "test1477") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1478 (export "test1478") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1479 (export "test1479") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1480 (export "test1480") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1481 (export "test1481") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1482 (export "test1482") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1483 (export "test1483") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1484 (export "test1484") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1485 (export "test1485") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1486 (export "test1486") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1487 (export "test1487") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1488 (export "test1488") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1489 (export "test1489") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1490 (export "test1490") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1491 (export "test1491") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1492 (export "test1492") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1493 (export "test1493") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1494 (export "test1494") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1495 (export "test1495") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1496 (export "test1496") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1497 (export "test1497") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1498 (export "test1498") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1499 (export "test1499") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1500 (export "test1500") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1501 (export "test1501") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1502 (export "test1502") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1503 (export "test1503") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1504 (export "test1504") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1505 (export "test1505") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1506 (export "test1506") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1507 (export "test1507") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1508 (export "test1508") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1509 (export "test1509") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1510 (export "test1510") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1511 (export "test1511") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1512 (export "test1512") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1513 (export "test1513") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1514 (export "test1514") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1515 (export "test1515") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1516 (export "test1516") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1517 (export "test1517") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1518 (export "test1518") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1519 (export "test1519") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1520 (export "test1520") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1521 (export "test1521") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1522 (export "test1522") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1523 (export "test1523") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1524 (export "test1524") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1525 (export "test1525") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1526 (export "test1526") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1527 (export "test1527") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1528 (export "test1528") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1529 (export "test1529") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1530 (export "test1530") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1531 (export "test1531") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1532 (export "test1532") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1533 (export "test1533") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1534 (export "test1534") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1535 (export "test1535") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1536 (export "test1536") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1537 (export "test1537") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1538 (export "test1538") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1539 (export "test1539") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1540 (export "test1540") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1541 (export "test1541") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1542 (export "test1542") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1543 (export "test1543") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1544 (export "test1544") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1545 (export "test1545") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1546 (export "test1546") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1547 (export "test1547") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1548 (export "test1548") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1549 (export "test1549") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1550 (export "test1550") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1551 (export "test1551") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1552 (export "test1552") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1553 (export "test1553") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1554 (export "test1554") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1555 (export "test1555") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1556 (export "test1556") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1557 (export "test1557") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1558 (export "test1558") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1559 (export "test1559") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1560 (export "test1560") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1561 (export "test1561") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1562 (export "test1562") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1563 (export "test1563") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1564 (export "test1564") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1565 (export "test1565") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1566 (export "test1566") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1567 (export "test1567") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1568 (export "test1568") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1569 (export "test1569") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1570 (export "test1570") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1571 (export "test1571") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1572 (export "test1572") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1573 (export "test1573") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1574 (export "test1574") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1575 (export "test1575") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1576 (export "test1576") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1577 (export "test1577") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1578 (export "test1578") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1579 (export "test1579") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1580 (export "test1580") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1581 (export "test1581") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1582 (export "test1582") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1583 (export "test1583") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1584 (export "test1584") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1585 (export "test1585") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1586 (export "test1586") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1587 (export "test1587") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1588 (export "test1588") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1589 (export "test1589") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1590 (export "test1590") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1591 (export "test1591") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1592 (export "test1592") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1593 (export "test1593") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1594 (export "test1594") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1595 (export "test1595") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1596 (export "test1596") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1597 (export "test1597") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1598 (export "test1598") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1599 (export "test1599") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1600 (export "test1600") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1601 (export "test1601") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1602 (export "test1602") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1603 (export "test1603") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1604 (export "test1604") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1605 (export "test1605") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1606 (export "test1606") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1607 (export "test1607") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1608 (export "test1608") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1609 (export "test1609") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1610 (export "test1610") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1611 (export "test1611") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1612 (export "test1612") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1613 (export "test1613") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1614 (export "test1614") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1615 (export "test1615") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1616 (export "test1616") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1617 (export "test1617") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1618 (export "test1618") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1619 (export "test1619") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1620 (export "test1620") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1621 (export "test1621") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1622 (export "test1622") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1623 (export "test1623") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1624 (export "test1624") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1625 (export "test1625") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1626 (export "test1626") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1627 (export "test1627") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1628 (export "test1628") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1629 (export "test1629") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1630 (export "test1630") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1631 (export "test1631") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1632 (export "test1632") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1633 (export "test1633") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1634 (export "test1634") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1635 (export "test1635") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1636 (export "test1636") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1637 (export "test1637") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1638 (export "test1638") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1639 (export "test1639") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1640 (export "test1640") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1641 (export "test1641") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1642 (export "test1642") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1643 (export "test1643") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1644 (export "test1644") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1645 (export "test1645") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1646 (export "test1646") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1647 (export "test1647") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1648 (export "test1648") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1649 (export "test1649") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1650 (export "test1650") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1651 (export "test1651") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1652 (export "test1652") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1653 (export "test1653") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1654 (export "test1654") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1655 (export "test1655") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1656 (export "test1656") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1657 (export "test1657") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1658 (export "test1658") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1659 (export "test1659") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1660 (export "test1660") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1661 (export "test1661") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1662 (export "test1662") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1663 (export "test1663") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1664 (export "test1664") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1665 (export "test1665") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1666 (export "test1666") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1667 (export "test1667") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1668 (export "test1668") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1669 (export "test1669") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1670 (export "test1670") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1671 (export "test1671") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1672 (export "test1672") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1673 (export "test1673") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1674 (export "test1674") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1675 (export "test1675") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1676 (export "test1676") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1677 (export "test1677") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1678 (export "test1678") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1679 (export "test1679") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1680 (export "test1680") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1681 (export "test1681") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1682 (export "test1682") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1683 (export "test1683") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1684 (export "test1684") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1685 (export "test1685") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1686 (export "test1686") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1687 (export "test1687") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1688 (export "test1688") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1689 (export "test1689") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1690 (export "test1690") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1691 (export "test1691") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1692 (export "test1692") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1693 (export "test1693") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1694 (export "test1694") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1695 (export "test1695") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1696 (export "test1696") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1697 (export "test1697") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1698 (export "test1698") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1699 (export "test1699") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1700 (export "test1700") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1701 (export "test1701") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1702 (export "test1702") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1703 (export "test1703") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1704 (export "test1704") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1705 (export "test1705") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1706 (export "test1706") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1707 (export "test1707") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1708 (export "test1708") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1709 (export "test1709") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1710 (export "test1710") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1711 (export "test1711") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1712 (export "test1712") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1713 (export "test1713") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1714 (export "test1714") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1715 (export "test1715") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1716 (export "test1716") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1717 (export "test1717") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1718 (export "test1718") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1719 (export "test1719") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1720 (export "test1720") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1721 (export "test1721") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1722 (export "test1722") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1723 (export "test1723") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1724 (export "test1724") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1725 (export "test1725") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1726 (export "test1726") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1727 (export "test1727") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1728 (export "test1728") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1729 (export "test1729") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1730 (export "test1730") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1731 (export "test1731") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1732 (export "test1732") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1733 (export "test1733") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1734 (export "test1734") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1735 (export "test1735") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1736 (export "test1736") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1737 (export "test1737") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1738 (export "test1738") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1739 (export "test1739") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1740 (export "test1740") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1741 (export "test1741") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1742 (export "test1742") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1743 (export "test1743") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1744 (export "test1744") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1745 (export "test1745") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1746 (export "test1746") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1747 (export "test1747") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1748 (export "test1748") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1749 (export "test1749") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1750 (export "test1750") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1751 (export "test1751") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1752 (export "test1752") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1753 (export "test1753") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1754 (export "test1754") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1755 (export "test1755") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1756 (export "test1756") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1757 (export "test1757") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1758 (export "test1758") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1759 (export "test1759") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1760 (export "test1760") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1761 (export "test1761") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1762 (export "test1762") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1763 (export "test1763") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1764 (export "test1764") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1765 (export "test1765") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1766 (export "test1766") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1767 (export "test1767") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1768 (export "test1768") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1769 (export "test1769") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1770 (export "test1770") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1771 (export "test1771") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1772 (export "test1772") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1773 (export "test1773") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1774 (export "test1774") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1775 (export "test1775") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1776 (export "test1776") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1777 (export "test1777") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1778 (export "test1778") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1779 (export "test1779") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1780 (export "test1780") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1781 (export "test1781") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1782 (export "test1782") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1783 (export "test1783") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1784 (export "test1784") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1785 (export "test1785") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1786 (export "test1786") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1787 (export "test1787") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1788 (export "test1788") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1789 (export "test1789") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1790 (export "test1790") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1791 (export "test1791") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1792 (export "test1792") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1793 (export "test1793") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1794 (export "test1794") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1795 (export "test1795") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1796 (export "test1796") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1797 (export "test1797") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1798 (export "test1798") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1799 (export "test1799") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1800 (export "test1800") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1801 (export "test1801") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1802 (export "test1802") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1803 (export "test1803") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1804 (export "test1804") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1805 (export "test1805") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1806 (export "test1806") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1807 (export "test1807") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1808 (export "test1808") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1809 (export "test1809") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1810 (export "test1810") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1811 (export "test1811") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1812 (export "test1812") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1813 (export "test1813") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1814 (export "test1814") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1815 (export "test1815") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1816 (export "test1816") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1817 (export "test1817") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1818 (export "test1818") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1819 (export "test1819") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1820 (export "test1820") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1821 (export "test1821") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1822 (export "test1822") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1823 (export "test1823") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1824 (export "test1824") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1825 (export "test1825") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1826 (export "test1826") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1827 (export "test1827") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1828 (export "test1828") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1829 (export "test1829") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1830 (export "test1830") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1831 (export "test1831") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1832 (export "test1832") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1833 (export "test1833") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1834 (export "test1834") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1835 (export "test1835") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1836 (export "test1836") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1837 (export "test1837") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1838 (export "test1838") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1839 (export "test1839") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1840 (export "test1840") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1841 (export "test1841") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1842 (export "test1842") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1843 (export "test1843") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1844 (export "test1844") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1845 (export "test1845") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1846 (export "test1846") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1847 (export "test1847") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1848 (export "test1848") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1849 (export "test1849") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1850 (export "test1850") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1851 (export "test1851") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1852 (export "test1852") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1853 (export "test1853") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1854 (export "test1854") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1855 (export "test1855") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1856 (export "test1856") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1857 (export "test1857") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1858 (export "test1858") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1859 (export "test1859") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1860 (export "test1860") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1861 (export "test1861") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1862 (export "test1862") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1863 (export "test1863") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1864 (export "test1864") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1865 (export "test1865") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1866 (export "test1866") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1867 (export "test1867") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1868 (export "test1868") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1869 (export "test1869") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1870 (export "test1870") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1871 (export "test1871") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1872 (export "test1872") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1873 (export "test1873") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1874 (export "test1874") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1875 (export "test1875") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1876 (export "test1876") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1877 (export "test1877") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1878 (export "test1878") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1879 (export "test1879") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1880 (export "test1880") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1881 (export "test1881") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1882 (export "test1882") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1883 (export "test1883") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1884 (export "test1884") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1885 (export "test1885") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1886 (export "test1886") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1887 (export "test1887") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1888 (export "test1888") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1889 (export "test1889") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1890 (export "test1890") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1891 (export "test1891") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1892 (export "test1892") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1893 (export "test1893") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1894 (export "test1894") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1895 (export "test1895") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1896 (export "test1896") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1897 (export "test1897") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1898 (export "test1898") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1899 (export "test1899") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1900 (export "test1900") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1901 (export "test1901") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1902 (export "test1902") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1903 (export "test1903") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1904 (export "test1904") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1905 (export "test1905") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1906 (export "test1906") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1907 (export "test1907") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1908 (export "test1908") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1909 (export "test1909") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1910 (export "test1910") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1911 (export "test1911") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1912 (export "test1912") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1913 (export "test1913") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1914 (export "test1914") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1915 (export "test1915") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1916 (export "test1916") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1917 (export "test1917") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1918 (export "test1918") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1919 (export "test1919") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1920 (export "test1920") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1921 (export "test1921") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1922 (export "test1922") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1923 (export "test1923") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1924 (export "test1924") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1925 (export "test1925") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1926 (export "test1926") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1927 (export "test1927") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1928 (export "test1928") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1929 (export "test1929") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1930 (export "test1930") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1931 (export "test1931") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1932 (export "test1932") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1933 (export "test1933") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1934 (export "test1934") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1935 (export "test1935") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1936 (export "test1936") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1937 (export "test1937") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1938 (export "test1938") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1939 (export "test1939") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1940 (export "test1940") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1941 (export "test1941") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1942 (export "test1942") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1943 (export "test1943") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1944 (export "test1944") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1945 (export "test1945") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1946 (export "test1946") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1947 (export "test1947") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1948 (export "test1948") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1949 (export "test1949") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1950 (export "test1950") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1951 (export "test1951") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1952 (export "test1952") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1953 (export "test1953") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1954 (export "test1954") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1955 (export "test1955") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1956 (export "test1956") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1957 (export "test1957") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1958 (export "test1958") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1959 (export "test1959") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1960 (export "test1960") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1961 (export "test1961") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1962 (export "test1962") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1963 (export "test1963") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1964 (export "test1964") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1965 (export "test1965") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1966 (export "test1966") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1967 (export "test1967") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1968 (export "test1968") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1969 (export "test1969") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1970 (export "test1970") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1971 (export "test1971") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1972 (export "test1972") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1973 (export "test1973") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1974 (export "test1974") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1975 (export "test1975") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1976 (export "test1976") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1977 (export "test1977") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1978 (export "test1978") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1979 (export "test1979") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1980 (export "test1980") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1981 (export "test1981") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1982 (export "test1982") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1983 (export "test1983") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1984 (export "test1984") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1985 (export "test1985") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1986 (export "test1986") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1987 (export "test1987") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1988 (export "test1988") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1989 (export "test1989") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1990 (export "test1990") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1991 (export "test1991") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1992 (export "test1992") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1993 (export "test1993") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1994 (export "test1994") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1995 (export "test1995") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1996 (export "test1996") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1997 (export "test1997") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1998 (export "test1998") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test1999 (export "test1999") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2000 (export "test2000") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2001 (export "test2001") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2002 (export "test2002") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2003 (export "test2003") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2004 (export "test2004") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2005 (export "test2005") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2006 (export "test2006") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2007 (export "test2007") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2008 (export "test2008") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2009 (export "test2009") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2010 (export "test2010") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2011 (export "test2011") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2012 (export "test2012") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2013 (export "test2013") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2014 (export "test2014") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2015 (export "test2015") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2016 (export "test2016") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2017 (export "test2017") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2018 (export "test2018") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2019 (export "test2019") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2020 (export "test2020") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2021 (export "test2021") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2022 (export "test2022") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2023 (export "test2023") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2024 (export "test2024") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2025 (export "test2025") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2026 (export "test2026") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2027 (export "test2027") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2028 (export "test2028") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2029 (export "test2029") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2030 (export "test2030") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2031 (export "test2031") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2032 (export "test2032") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2033 (export "test2033") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2034 (export "test2034") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2035 (export "test2035") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2036 (export "test2036") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2037 (export "test2037") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2038 (export "test2038") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2039 (export "test2039") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2040 (export "test2040") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2041 (export "test2041") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2042 (export "test2042") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2043 (export "test2043") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2044 (export "test2044") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2045 (export "test2045") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2046 (export "test2046") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2047 (export "test2047") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2048 (export "test2048") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2049 (export "test2049") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2050 (export "test2050") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2051 (export "test2051") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2052 (export "test2052") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2053 (export "test2053") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2054 (export "test2054") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2055 (export "test2055") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2056 (export "test2056") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2057 (export "test2057") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2058 (export "test2058") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2059 (export "test2059") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2060 (export "test2060") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2061 (export "test2061") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2062 (export "test2062") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2063 (export "test2063") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2064 (export "test2064") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2065 (export "test2065") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2066 (export "test2066") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2067 (export "test2067") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2068 (export "test2068") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2069 (export "test2069") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2070 (export "test2070") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2071 (export "test2071") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2072 (export "test2072") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2073 (export "test2073") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2074 (export "test2074") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2075 (export "test2075") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2076 (export "test2076") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2077 (export "test2077") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2078 (export "test2078") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2079 (export "test2079") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2080 (export "test2080") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2081 (export "test2081") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2082 (export "test2082") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2083 (export "test2083") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2084 (export "test2084") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2085 (export "test2085") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2086 (export "test2086") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2087 (export "test2087") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2088 (export "test2088") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2089 (export "test2089") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2090 (export "test2090") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2091 (export "test2091") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2092 (export "test2092") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2093 (export "test2093") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2094 (export "test2094") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2095 (export "test2095") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2096 (export "test2096") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2097 (export "test2097") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2098 (export "test2098") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2099 (export "test2099") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2100 (export "test2100") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2101 (export "test2101") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2102 (export "test2102") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2103 (export "test2103") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2104 (export "test2104") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2105 (export "test2105") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2106 (export "test2106") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2107 (export "test2107") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2108 (export "test2108") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2109 (export "test2109") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2110 (export "test2110") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2111 (export "test2111") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2112 (export "test2112") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2113 (export "test2113") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2114 (export "test2114") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2115 (export "test2115") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2116 (export "test2116") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2117 (export "test2117") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2118 (export "test2118") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2119 (export "test2119") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2120 (export "test2120") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2121 (export "test2121") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2122 (export "test2122") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2123 (export "test2123") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2124 (export "test2124") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2125 (export "test2125") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2126 (export "test2126") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2127 (export "test2127") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2128 (export "test2128") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2129 (export "test2129") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2130 (export "test2130") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2131 (export "test2131") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2132 (export "test2132") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2133 (export "test2133") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2134 (export "test2134") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2135 (export "test2135") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2136 (export "test2136") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2137 (export "test2137") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2138 (export "test2138") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2139 (export "test2139") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2140 (export "test2140") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2141 (export "test2141") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2142 (export "test2142") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2143 (export "test2143") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2144 (export "test2144") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2145 (export "test2145") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2146 (export "test2146") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2147 (export "test2147") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2148 (export "test2148") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2149 (export "test2149") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2150 (export "test2150") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2151 (export "test2151") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2152 (export "test2152") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2153 (export "test2153") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2154 (export "test2154") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2155 (export "test2155") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2156 (export "test2156") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2157 (export "test2157") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2158 (export "test2158") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2159 (export "test2159") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2160 (export "test2160") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2161 (export "test2161") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2162 (export "test2162") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2163 (export "test2163") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2164 (export "test2164") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2165 (export "test2165") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2166 (export "test2166") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2167 (export "test2167") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2168 (export "test2168") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2169 (export "test2169") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2170 (export "test2170") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2171 (export "test2171") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2172 (export "test2172") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2173 (export "test2173") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2174 (export "test2174") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2175 (export "test2175") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2176 (export "test2176") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2177 (export "test2177") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2178 (export "test2178") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2179 (export "test2179") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2180 (export "test2180") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2181 (export "test2181") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2182 (export "test2182") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2183 (export "test2183") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2184 (export "test2184") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2185 (export "test2185") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2186 (export "test2186") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2187 (export "test2187") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2188 (export "test2188") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2189 (export "test2189") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2190 (export "test2190") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2191 (export "test2191") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2192 (export "test2192") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2193 (export "test2193") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2194 (export "test2194") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2195 (export "test2195") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2196 (export "test2196") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2197 (export "test2197") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2198 (export "test2198") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2199 (export "test2199") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2200 (export "test2200") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2201 (export "test2201") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2202 (export "test2202") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2203 (export "test2203") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2204 (export "test2204") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2205 (export "test2205") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2206 (export "test2206") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2207 (export "test2207") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2208 (export "test2208") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2209 (export "test2209") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2210 (export "test2210") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2211 (export "test2211") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2212 (export "test2212") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2213 (export "test2213") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2214 (export "test2214") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2215 (export "test2215") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2216 (export "test2216") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2217 (export "test2217") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2218 (export "test2218") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2219 (export "test2219") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2220 (export "test2220") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2221 (export "test2221") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2222 (export "test2222") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2223 (export "test2223") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2224 (export "test2224") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2225 (export "test2225") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2226 (export "test2226") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2227 (export "test2227") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2228 (export "test2228") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2229 (export "test2229") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2230 (export "test2230") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2231 (export "test2231") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2232 (export "test2232") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2233 (export "test2233") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2234 (export "test2234") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2235 (export "test2235") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2236 (export "test2236") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2237 (export "test2237") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2238 (export "test2238") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2239 (export "test2239") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2240 (export "test2240") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2241 (export "test2241") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2242 (export "test2242") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2243 (export "test2243") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2244 (export "test2244") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2245 (export "test2245") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2246 (export "test2246") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2247 (export "test2247") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2248 (export "test2248") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2249 (export "test2249") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2250 (export "test2250") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2251 (export "test2251") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2252 (export "test2252") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2253 (export "test2253") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2254 (export "test2254") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2255 (export "test2255") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2256 (export "test2256") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2257 (export "test2257") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2258 (export "test2258") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2259 (export "test2259") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2260 (export "test2260") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2261 (export "test2261") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2262 (export "test2262") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2263 (export "test2263") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2264 (export "test2264") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2265 (export "test2265") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2266 (export "test2266") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2267 (export "test2267") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2268 (export "test2268") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2269 (export "test2269") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2270 (export "test2270") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2271 (export "test2271") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2272 (export "test2272") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2273 (export "test2273") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2274 (export "test2274") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2275 (export "test2275") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2276 (export "test2276") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2277 (export "test2277") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2278 (export "test2278") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2279 (export "test2279") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2280 (export "test2280") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2281 (export "test2281") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2282 (export "test2282") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2283 (export "test2283") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2284 (export "test2284") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2285 (export "test2285") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2286 (export "test2286") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2287 (export "test2287") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2288 (export "test2288") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2289 (export "test2289") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2290 (export "test2290") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2291 (export "test2291") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2292 (export "test2292") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2293 (export "test2293") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2294 (export "test2294") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2295 (export "test2295") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2296 (export "test2296") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2297 (export "test2297") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2298 (export "test2298") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2299 (export "test2299") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2300 (export "test2300") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2301 (export "test2301") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2302 (export "test2302") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2303 (export "test2303") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2304 (export "test2304") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2305 (export "test2305") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2306 (export "test2306") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2307 (export "test2307") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2308 (export "test2308") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2309 (export "test2309") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2310 (export "test2310") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2311 (export "test2311") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2312 (export "test2312") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2313 (export "test2313") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2314 (export "test2314") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2315 (export "test2315") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2316 (export "test2316") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2317 (export "test2317") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2318 (export "test2318") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2319 (export "test2319") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2320 (export "test2320") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2321 (export "test2321") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2322 (export "test2322") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2323 (export "test2323") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2324 (export "test2324") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2325 (export "test2325") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2326 (export "test2326") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2327 (export "test2327") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2328 (export "test2328") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2329 (export "test2329") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2330 (export "test2330") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2331 (export "test2331") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2332 (export "test2332") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2333 (export "test2333") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2334 (export "test2334") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2335 (export "test2335") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2336 (export "test2336") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2337 (export "test2337") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2338 (export "test2338") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2339 (export "test2339") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2340 (export "test2340") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2341 (export "test2341") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2342 (export "test2342") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2343 (export "test2343") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2344 (export "test2344") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2345 (export "test2345") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2346 (export "test2346") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2347 (export "test2347") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2348 (export "test2348") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2349 (export "test2349") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2350 (export "test2350") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2351 (export "test2351") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2352 (export "test2352") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2353 (export "test2353") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2354 (export "test2354") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2355 (export "test2355") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2356 (export "test2356") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2357 (export "test2357") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2358 (export "test2358") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2359 (export "test2359") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2360 (export "test2360") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2361 (export "test2361") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2362 (export "test2362") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2363 (export "test2363") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2364 (export "test2364") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2365 (export "test2365") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2366 (export "test2366") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2367 (export "test2367") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2368 (export "test2368") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2369 (export "test2369") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2370 (export "test2370") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2371 (export "test2371") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2372 (export "test2372") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2373 (export "test2373") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2374 (export "test2374") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2375 (export "test2375") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2376 (export "test2376") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2377 (export "test2377") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2378 (export "test2378") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2379 (export "test2379") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2380 (export "test2380") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2381 (export "test2381") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2382 (export "test2382") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2383 (export "test2383") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2384 (export "test2384") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2385 (export "test2385") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2386 (export "test2386") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2387 (export "test2387") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2388 (export "test2388") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2389 (export "test2389") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2390 (export "test2390") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2391 (export "test2391") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2392 (export "test2392") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2393 (export "test2393") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2394 (export "test2394") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2395 (export "test2395") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2396 (export "test2396") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2397 (export "test2397") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2398 (export "test2398") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2399 (export "test2399") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2400 (export "test2400") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2401 (export "test2401") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2402 (export "test2402") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2403 (export "test2403") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2404 (export "test2404") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2405 (export "test2405") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2406 (export "test2406") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2407 (export "test2407") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2408 (export "test2408") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2409 (export "test2409") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2410 (export "test2410") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2411 (export "test2411") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2412 (export "test2412") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2413 (export "test2413") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2414 (export "test2414") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2415 (export "test2415") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2416 (export "test2416") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2417 (export "test2417") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2418 (export "test2418") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2419 (export "test2419") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2420 (export "test2420") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2421 (export "test2421") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2422 (export "test2422") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2423 (export "test2423") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2424 (export "test2424") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2425 (export "test2425") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2426 (export "test2426") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2427 (export "test2427") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2428 (export "test2428") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2429 (export "test2429") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2430 (export "test2430") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2431 (export "test2431") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2432 (export "test2432") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2433 (export "test2433") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2434 (export "test2434") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2435 (export "test2435") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2436 (export "test2436") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2437 (export "test2437") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2438 (export "test2438") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2439 (export "test2439") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2440 (export "test2440") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2441 (export "test2441") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2442 (export "test2442") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2443 (export "test2443") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2444 (export "test2444") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2445 (export "test2445") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2446 (export "test2446") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2447 (export "test2447") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2448 (export "test2448") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2449 (export "test2449") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2450 (export "test2450") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2451 (export "test2451") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2452 (export "test2452") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2453 (export "test2453") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2454 (export "test2454") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2455 (export "test2455") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2456 (export "test2456") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2457 (export "test2457") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2458 (export "test2458") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2459 (export "test2459") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2460 (export "test2460") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2461 (export "test2461") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2462 (export "test2462") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2463 (export "test2463") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2464 (export "test2464") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2465 (export "test2465") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2466 (export "test2466") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2467 (export "test2467") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2468 (export "test2468") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2469 (export "test2469") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2470 (export "test2470") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2471 (export "test2471") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2472 (export "test2472") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2473 (export "test2473") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2474 (export "test2474") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2475 (export "test2475") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2476 (export "test2476") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2477 (export "test2477") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2478 (export "test2478") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2479 (export "test2479") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2480 (export "test2480") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2481 (export "test2481") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2482 (export "test2482") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2483 (export "test2483") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2484 (export "test2484") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2485 (export "test2485") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2486 (export "test2486") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2487 (export "test2487") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2488 (export "test2488") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2489 (export "test2489") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2490 (export "test2490") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2491 (export "test2491") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2492 (export "test2492") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2493 (export "test2493") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2494 (export "test2494") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2495 (export "test2495") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2496 (export "test2496") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2497 (export "test2497") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2498 (export "test2498") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2499 (export "test2499") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2500 (export "test2500") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2501 (export "test2501") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2502 (export "test2502") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2503 (export "test2503") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2504 (export "test2504") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2505 (export "test2505") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2506 (export "test2506") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2507 (export "test2507") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2508 (export "test2508") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2509 (export "test2509") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2510 (export "test2510") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2511 (export "test2511") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2512 (export "test2512") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2513 (export "test2513") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2514 (export "test2514") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2515 (export "test2515") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2516 (export "test2516") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2517 (export "test2517") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2518 (export "test2518") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2519 (export "test2519") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2520 (export "test2520") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2521 (export "test2521") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2522 (export "test2522") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2523 (export "test2523") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2524 (export "test2524") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2525 (export "test2525") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2526 (export "test2526") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2527 (export "test2527") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2528 (export "test2528") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2529 (export "test2529") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2530 (export "test2530") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2531 (export "test2531") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2532 (export "test2532") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2533 (export "test2533") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2534 (export "test2534") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2535 (export "test2535") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2536 (export "test2536") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2537 (export "test2537") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2538 (export "test2538") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2539 (export "test2539") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2540 (export "test2540") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2541 (export "test2541") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2542 (export "test2542") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2543 (export "test2543") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2544 (export "test2544") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2545 (export "test2545") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2546 (export "test2546") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2547 (export "test2547") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2548 (export "test2548") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2549 (export "test2549") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2550 (export "test2550") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2551 (export "test2551") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2552 (export "test2552") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2553 (export "test2553") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2554 (export "test2554") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2555 (export "test2555") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2556 (export "test2556") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2557 (export "test2557") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2558 (export "test2558") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2559 (export "test2559") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2560 (export "test2560") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2561 (export "test2561") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2562 (export "test2562") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2563 (export "test2563") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2564 (export "test2564") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2565 (export "test2565") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2566 (export "test2566") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2567 (export "test2567") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2568 (export "test2568") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2569 (export "test2569") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2570 (export "test2570") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2571 (export "test2571") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2572 (export "test2572") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2573 (export "test2573") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2574 (export "test2574") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2575 (export "test2575") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2576 (export "test2576") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2577 (export "test2577") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2578 (export "test2578") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2579 (export "test2579") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2580 (export "test2580") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2581 (export "test2581") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2582 (export "test2582") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2583 (export "test2583") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2584 (export "test2584") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2585 (export "test2585") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2586 (export "test2586") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2587 (export "test2587") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2588 (export "test2588") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2589 (export "test2589") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2590 (export "test2590") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2591 (export "test2591") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2592 (export "test2592") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2593 (export "test2593") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2594 (export "test2594") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2595 (export "test2595") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2596 (export "test2596") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2597 (export "test2597") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2598 (export "test2598") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2599 (export "test2599") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2600 (export "test2600") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2601 (export "test2601") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2602 (export "test2602") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2603 (export "test2603") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2604 (export "test2604") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2605 (export "test2605") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2606 (export "test2606") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2607 (export "test2607") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2608 (export "test2608") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2609 (export "test2609") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2610 (export "test2610") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2611 (export "test2611") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2612 (export "test2612") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2613 (export "test2613") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2614 (export "test2614") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2615 (export "test2615") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2616 (export "test2616") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2617 (export "test2617") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2618 (export "test2618") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2619 (export "test2619") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2620 (export "test2620") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2621 (export "test2621") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2622 (export "test2622") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2623 (export "test2623") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2624 (export "test2624") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2625 (export "test2625") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2626 (export "test2626") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2627 (export "test2627") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2628 (export "test2628") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2629 (export "test2629") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2630 (export "test2630") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2631 (export "test2631") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2632 (export "test2632") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2633 (export "test2633") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2634 (export "test2634") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2635 (export "test2635") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2636 (export "test2636") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2637 (export "test2637") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2638 (export "test2638") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2639 (export "test2639") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2640 (export "test2640") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2641 (export "test2641") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2642 (export "test2642") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2643 (export "test2643") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2644 (export "test2644") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2645 (export "test2645") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2646 (export "test2646") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2647 (export "test2647") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2648 (export "test2648") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2649 (export "test2649") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2650 (export "test2650") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2651 (export "test2651") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2652 (export "test2652") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2653 (export "test2653") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2654 (export "test2654") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2655 (export "test2655") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2656 (export "test2656") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2657 (export "test2657") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2658 (export "test2658") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2659 (export "test2659") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2660 (export "test2660") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2661 (export "test2661") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2662 (export "test2662") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2663 (export "test2663") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2664 (export "test2664") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2665 (export "test2665") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2666 (export "test2666") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2667 (export "test2667") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2668 (export "test2668") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2669 (export "test2669") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2670 (export "test2670") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2671 (export "test2671") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2672 (export "test2672") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2673 (export "test2673") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2674 (export "test2674") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2675 (export "test2675") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2676 (export "test2676") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2677 (export "test2677") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2678 (export "test2678") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2679 (export "test2679") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2680 (export "test2680") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2681 (export "test2681") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2682 (export "test2682") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2683 (export "test2683") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2684 (export "test2684") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2685 (export "test2685") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2686 (export "test2686") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2687 (export "test2687") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2688 (export "test2688") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2689 (export "test2689") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2690 (export "test2690") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2691 (export "test2691") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2692 (export "test2692") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2693 (export "test2693") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2694 (export "test2694") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2695 (export "test2695") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2696 (export "test2696") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2697 (export "test2697") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2698 (export "test2698") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2699 (export "test2699") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2700 (export "test2700") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2701 (export "test2701") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2702 (export "test2702") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2703 (export "test2703") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2704 (export "test2704") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2705 (export "test2705") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2706 (export "test2706") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2707 (export "test2707") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2708 (export "test2708") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2709 (export "test2709") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2710 (export "test2710") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2711 (export "test2711") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2712 (export "test2712") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2713 (export "test2713") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2714 (export "test2714") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2715 (export "test2715") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2716 (export "test2716") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2717 (export "test2717") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2718 (export "test2718") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2719 (export "test2719") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2720 (export "test2720") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2721 (export "test2721") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2722 (export "test2722") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2723 (export "test2723") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2724 (export "test2724") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2725 (export "test2725") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2726 (export "test2726") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2727 (export "test2727") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2728 (export "test2728") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2729 (export "test2729") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2730 (export "test2730") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2731 (export "test2731") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2732 (export "test2732") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2733 (export "test2733") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2734 (export "test2734") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2735 (export "test2735") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2736 (export "test2736") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2737 (export "test2737") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2738 (export "test2738") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2739 (export "test2739") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2740 (export "test2740") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2741 (export "test2741") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2742 (export "test2742") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2743 (export "test2743") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2744 (export "test2744") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2745 (export "test2745") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2746 (export "test2746") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2747 (export "test2747") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2748 (export "test2748") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2749 (export "test2749") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2750 (export "test2750") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2751 (export "test2751") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2752 (export "test2752") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2753 (export "test2753") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2754 (export "test2754") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2755 (export "test2755") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2756 (export "test2756") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2757 (export "test2757") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2758 (export "test2758") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2759 (export "test2759") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2760 (export "test2760") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2761 (export "test2761") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2762 (export "test2762") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2763 (export "test2763") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2764 (export "test2764") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2765 (export "test2765") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2766 (export "test2766") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2767 (export "test2767") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2768 (export "test2768") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2769 (export "test2769") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2770 (export "test2770") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2771 (export "test2771") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2772 (export "test2772") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2773 (export "test2773") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2774 (export "test2774") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2775 (export "test2775") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2776 (export "test2776") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2777 (export "test2777") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2778 (export "test2778") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2779 (export "test2779") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2780 (export "test2780") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2781 (export "test2781") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2782 (export "test2782") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2783 (export "test2783") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2784 (export "test2784") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2785 (export "test2785") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2786 (export "test2786") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2787 (export "test2787") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2788 (export "test2788") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2789 (export "test2789") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2790 (export "test2790") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2791 (export "test2791") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2792 (export "test2792") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2793 (export "test2793") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2794 (export "test2794") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2795 (export "test2795") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2796 (export "test2796") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2797 (export "test2797") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2798 (export "test2798") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2799 (export "test2799") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2800 (export "test2800") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2801 (export "test2801") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2802 (export "test2802") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2803 (export "test2803") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2804 (export "test2804") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2805 (export "test2805") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2806 (export "test2806") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2807 (export "test2807") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2808 (export "test2808") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2809 (export "test2809") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2810 (export "test2810") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2811 (export "test2811") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2812 (export "test2812") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2813 (export "test2813") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2814 (export "test2814") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2815 (export "test2815") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2816 (export "test2816") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2817 (export "test2817") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2818 (export "test2818") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2819 (export "test2819") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2820 (export "test2820") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2821 (export "test2821") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2822 (export "test2822") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2823 (export "test2823") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2824 (export "test2824") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2825 (export "test2825") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2826 (export "test2826") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2827 (export "test2827") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2828 (export "test2828") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2829 (export "test2829") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2830 (export "test2830") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2831 (export "test2831") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2832 (export "test2832") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2833 (export "test2833") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2834 (export "test2834") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2835 (export "test2835") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2836 (export "test2836") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2837 (export "test2837") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2838 (export "test2838") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2839 (export "test2839") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2840 (export "test2840") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2841 (export "test2841") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2842 (export "test2842") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2843 (export "test2843") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2844 (export "test2844") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2845 (export "test2845") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2846 (export "test2846") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2847 (export "test2847") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2848 (export "test2848") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2849 (export "test2849") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2850 (export "test2850") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2851 (export "test2851") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2852 (export "test2852") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2853 (export "test2853") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2854 (export "test2854") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2855 (export "test2855") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2856 (export "test2856") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2857 (export "test2857") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2858 (export "test2858") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2859 (export "test2859") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2860 (export "test2860") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2861 (export "test2861") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2862 (export "test2862") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2863 (export "test2863") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2864 (export "test2864") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2865 (export "test2865") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2866 (export "test2866") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2867 (export "test2867") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2868 (export "test2868") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2869 (export "test2869") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2870 (export "test2870") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2871 (export "test2871") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2872 (export "test2872") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2873 (export "test2873") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2874 (export "test2874") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2875 (export "test2875") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2876 (export "test2876") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2877 (export "test2877") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2878 (export "test2878") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2879 (export "test2879") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2880 (export "test2880") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2881 (export "test2881") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2882 (export "test2882") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2883 (export "test2883") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2884 (export "test2884") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2885 (export "test2885") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2886 (export "test2886") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2887 (export "test2887") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2888 (export "test2888") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2889 (export "test2889") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2890 (export "test2890") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2891 (export "test2891") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2892 (export "test2892") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2893 (export "test2893") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2894 (export "test2894") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2895 (export "test2895") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2896 (export "test2896") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2897 (export "test2897") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2898 (export "test2898") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2899 (export "test2899") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2900 (export "test2900") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2901 (export "test2901") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2902 (export "test2902") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2903 (export "test2903") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2904 (export "test2904") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2905 (export "test2905") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2906 (export "test2906") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2907 (export "test2907") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2908 (export "test2908") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2909 (export "test2909") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2910 (export "test2910") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2911 (export "test2911") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2912 (export "test2912") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2913 (export "test2913") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2914 (export "test2914") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2915 (export "test2915") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2916 (export "test2916") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2917 (export "test2917") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2918 (export "test2918") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2919 (export "test2919") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2920 (export "test2920") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2921 (export "test2921") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2922 (export "test2922") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2923 (export "test2923") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2924 (export "test2924") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2925 (export "test2925") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2926 (export "test2926") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2927 (export "test2927") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2928 (export "test2928") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2929 (export "test2929") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2930 (export "test2930") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2931 (export "test2931") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2932 (export "test2932") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2933 (export "test2933") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2934 (export "test2934") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2935 (export "test2935") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2936 (export "test2936") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2937 (export "test2937") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2938 (export "test2938") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2939 (export "test2939") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2940 (export "test2940") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2941 (export "test2941") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2942 (export "test2942") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2943 (export "test2943") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2944 (export "test2944") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2945 (export "test2945") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2946 (export "test2946") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2947 (export "test2947") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2948 (export "test2948") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2949 (export "test2949") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2950 (export "test2950") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2951 (export "test2951") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2952 (export "test2952") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2953 (export "test2953") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2954 (export "test2954") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2955 (export "test2955") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2956 (export "test2956") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2957 (export "test2957") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2958 (export "test2958") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2959 (export "test2959") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2960 (export "test2960") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2961 (export "test2961") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2962 (export "test2962") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2963 (export "test2963") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2964 (export "test2964") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2965 (export "test2965") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2966 (export "test2966") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2967 (export "test2967") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2968 (export "test2968") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2969 (export "test2969") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2970 (export "test2970") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2971 (export "test2971") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2972 (export "test2972") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2973 (export "test2973") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2974 (export "test2974") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2975 (export "test2975") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2976 (export "test2976") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2977 (export "test2977") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2978 (export "test2978") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2979 (export "test2979") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2980 (export "test2980") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2981 (export "test2981") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2982 (export "test2982") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2983 (export "test2983") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2984 (export "test2984") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2985 (export "test2985") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2986 (export "test2986") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2987 (export "test2987") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2988 (export "test2988") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2989 (export "test2989") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2990 (export "test2990") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2991 (export "test2991") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2992 (export "test2992") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2993 (export "test2993") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2994 (export "test2994") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2995 (export "test2995") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2996 (export "test2996") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2997 (export "test2997") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2998 (export "test2998") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test2999 (export "test2999") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3000 (export "test3000") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3001 (export "test3001") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3002 (export "test3002") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3003 (export "test3003") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3004 (export "test3004") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3005 (export "test3005") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3006 (export "test3006") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3007 (export "test3007") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3008 (export "test3008") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3009 (export "test3009") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3010 (export "test3010") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3011 (export "test3011") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3012 (export "test3012") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3013 (export "test3013") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3014 (export "test3014") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3015 (export "test3015") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3016 (export "test3016") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3017 (export "test3017") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3018 (export "test3018") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3019 (export "test3019") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3020 (export "test3020") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3021 (export "test3021") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3022 (export "test3022") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3023 (export "test3023") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3024 (export "test3024") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3025 (export "test3025") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3026 (export "test3026") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3027 (export "test3027") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3028 (export "test3028") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3029 (export "test3029") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3030 (export "test3030") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3031 (export "test3031") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3032 (export "test3032") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3033 (export "test3033") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3034 (export "test3034") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3035 (export "test3035") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3036 (export "test3036") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3037 (export "test3037") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3038 (export "test3038") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3039 (export "test3039") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3040 (export "test3040") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3041 (export "test3041") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3042 (export "test3042") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3043 (export "test3043") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3044 (export "test3044") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3045 (export "test3045") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3046 (export "test3046") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3047 (export "test3047") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3048 (export "test3048") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3049 (export "test3049") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3050 (export "test3050") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3051 (export "test3051") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3052 (export "test3052") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3053 (export "test3053") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3054 (export "test3054") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3055 (export "test3055") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3056 (export "test3056") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3057 (export "test3057") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3058 (export "test3058") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3059 (export "test3059") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3060 (export "test3060") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3061 (export "test3061") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3062 (export "test3062") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3063 (export "test3063") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3064 (export "test3064") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3065 (export "test3065") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3066 (export "test3066") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3067 (export "test3067") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3068 (export "test3068") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3069 (export "test3069") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3070 (export "test3070") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3071 (export "test3071") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3072 (export "test3072") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3073 (export "test3073") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3074 (export "test3074") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3075 (export "test3075") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3076 (export "test3076") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3077 (export "test3077") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3078 (export "test3078") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3079 (export "test3079") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3080 (export "test3080") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3081 (export "test3081") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3082 (export "test3082") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3083 (export "test3083") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3084 (export "test3084") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3085 (export "test3085") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3086 (export "test3086") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3087 (export "test3087") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3088 (export "test3088") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3089 (export "test3089") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3090 (export "test3090") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3091 (export "test3091") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3092 (export "test3092") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3093 (export "test3093") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3094 (export "test3094") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3095 (export "test3095") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3096 (export "test3096") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3097 (export "test3097") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3098 (export "test3098") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3099 (export "test3099") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3100 (export "test3100") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3101 (export "test3101") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3102 (export "test3102") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3103 (export "test3103") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3104 (export "test3104") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3105 (export "test3105") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3106 (export "test3106") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3107 (export "test3107") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3108 (export "test3108") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3109 (export "test3109") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3110 (export "test3110") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3111 (export "test3111") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3112 (export "test3112") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3113 (export "test3113") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3114 (export "test3114") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3115 (export "test3115") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3116 (export "test3116") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3117 (export "test3117") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3118 (export "test3118") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3119 (export "test3119") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3120 (export "test3120") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3121 (export "test3121") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3122 (export "test3122") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3123 (export "test3123") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3124 (export "test3124") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3125 (export "test3125") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3126 (export "test3126") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3127 (export "test3127") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3128 (export "test3128") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3129 (export "test3129") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3130 (export "test3130") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3131 (export "test3131") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3132 (export "test3132") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3133 (export "test3133") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3134 (export "test3134") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3135 (export "test3135") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3136 (export "test3136") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3137 (export "test3137") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3138 (export "test3138") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3139 (export "test3139") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3140 (export "test3140") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3141 (export "test3141") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3142 (export "test3142") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3143 (export "test3143") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3144 (export "test3144") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3145 (export "test3145") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3146 (export "test3146") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3147 (export "test3147") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3148 (export "test3148") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3149 (export "test3149") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3150 (export "test3150") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3151 (export "test3151") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3152 (export "test3152") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3153 (export "test3153") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3154 (export "test3154") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3155 (export "test3155") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3156 (export "test3156") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3157 (export "test3157") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3158 (export "test3158") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3159 (export "test3159") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3160 (export "test3160") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3161 (export "test3161") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3162 (export "test3162") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3163 (export "test3163") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3164 (export "test3164") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3165 (export "test3165") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3166 (export "test3166") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3167 (export "test3167") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3168 (export "test3168") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3169 (export "test3169") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3170 (export "test3170") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3171 (export "test3171") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3172 (export "test3172") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3173 (export "test3173") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3174 (export "test3174") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3175 (export "test3175") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3176 (export "test3176") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3177 (export "test3177") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3178 (export "test3178") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3179 (export "test3179") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3180 (export "test3180") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3181 (export "test3181") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3182 (export "test3182") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3183 (export "test3183") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3184 (export "test3184") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3185 (export "test3185") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3186 (export "test3186") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3187 (export "test3187") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3188 (export "test3188") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3189 (export "test3189") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3190 (export "test3190") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3191 (export "test3191") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3192 (export "test3192") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3193 (export "test3193") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3194 (export "test3194") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3195 (export "test3195") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3196 (export "test3196") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3197 (export "test3197") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3198 (export "test3198") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3199 (export "test3199") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3200 (export "test3200") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3201 (export "test3201") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3202 (export "test3202") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3203 (export "test3203") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3204 (export "test3204") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3205 (export "test3205") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3206 (export "test3206") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3207 (export "test3207") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3208 (export "test3208") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3209 (export "test3209") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3210 (export "test3210") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3211 (export "test3211") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3212 (export "test3212") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3213 (export "test3213") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3214 (export "test3214") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3215 (export "test3215") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3216 (export "test3216") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3217 (export "test3217") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3218 (export "test3218") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3219 (export "test3219") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3220 (export "test3220") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3221 (export "test3221") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3222 (export "test3222") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3223 (export "test3223") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3224 (export "test3224") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3225 (export "test3225") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3226 (export "test3226") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3227 (export "test3227") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3228 (export "test3228") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3229 (export "test3229") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3230 (export "test3230") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3231 (export "test3231") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3232 (export "test3232") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3233 (export "test3233") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3234 (export "test3234") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3235 (export "test3235") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3236 (export "test3236") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3237 (export "test3237") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3238 (export "test3238") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3239 (export "test3239") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3240 (export "test3240") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3241 (export "test3241") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3242 (export "test3242") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3243 (export "test3243") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3244 (export "test3244") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3245 (export "test3245") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3246 (export "test3246") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3247 (export "test3247") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3248 (export "test3248") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3249 (export "test3249") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3250 (export "test3250") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3251 (export "test3251") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3252 (export "test3252") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3253 (export "test3253") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3254 (export "test3254") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3255 (export "test3255") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3256 (export "test3256") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3257 (export "test3257") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3258 (export "test3258") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3259 (export "test3259") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3260 (export "test3260") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3261 (export "test3261") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3262 (export "test3262") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3263 (export "test3263") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3264 (export "test3264") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3265 (export "test3265") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3266 (export "test3266") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3267 (export "test3267") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3268 (export "test3268") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3269 (export "test3269") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3270 (export "test3270") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3271 (export "test3271") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3272 (export "test3272") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3273 (export "test3273") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3274 (export "test3274") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3275 (export "test3275") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3276 (export "test3276") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3277 (export "test3277") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3278 (export "test3278") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3279 (export "test3279") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3280 (export "test3280") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3281 (export "test3281") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3282 (export "test3282") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3283 (export "test3283") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3284 (export "test3284") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3285 (export "test3285") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3286 (export "test3286") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3287 (export "test3287") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3288 (export "test3288") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3289 (export "test3289") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3290 (export "test3290") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3291 (export "test3291") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3292 (export "test3292") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3293 (export "test3293") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3294 (export "test3294") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3295 (export "test3295") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3296 (export "test3296") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3297 (export "test3297") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3298 (export "test3298") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3299 (export "test3299") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3300 (export "test3300") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3301 (export "test3301") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3302 (export "test3302") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3303 (export "test3303") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3304 (export "test3304") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3305 (export "test3305") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3306 (export "test3306") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3307 (export "test3307") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3308 (export "test3308") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3309 (export "test3309") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3310 (export "test3310") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3311 (export "test3311") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3312 (export "test3312") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3313 (export "test3313") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3314 (export "test3314") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3315 (export "test3315") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3316 (export "test3316") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3317 (export "test3317") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3318 (export "test3318") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3319 (export "test3319") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3320 (export "test3320") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3321 (export "test3321") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3322 (export "test3322") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3323 (export "test3323") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3324 (export "test3324") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3325 (export "test3325") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3326 (export "test3326") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3327 (export "test3327") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3328 (export "test3328") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3329 (export "test3329") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3330 (export "test3330") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3331 (export "test3331") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3332 (export "test3332") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3333 (export "test3333") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3334 (export "test3334") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3335 (export "test3335") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3336 (export "test3336") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3337 (export "test3337") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3338 (export "test3338") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3339 (export "test3339") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3340 (export "test3340") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3341 (export "test3341") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3342 (export "test3342") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3343 (export "test3343") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3344 (export "test3344") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3345 (export "test3345") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3346 (export "test3346") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3347 (export "test3347") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3348 (export "test3348") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3349 (export "test3349") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3350 (export "test3350") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3351 (export "test3351") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3352 (export "test3352") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3353 (export "test3353") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3354 (export "test3354") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3355 (export "test3355") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3356 (export "test3356") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3357 (export "test3357") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3358 (export "test3358") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3359 (export "test3359") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3360 (export "test3360") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3361 (export "test3361") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3362 (export "test3362") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3363 (export "test3363") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3364 (export "test3364") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3365 (export "test3365") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3366 (export "test3366") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3367 (export "test3367") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3368 (export "test3368") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3369 (export "test3369") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3370 (export "test3370") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3371 (export "test3371") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3372 (export "test3372") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3373 (export "test3373") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3374 (export "test3374") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3375 (export "test3375") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3376 (export "test3376") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3377 (export "test3377") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3378 (export "test3378") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3379 (export "test3379") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3380 (export "test3380") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3381 (export "test3381") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3382 (export "test3382") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3383 (export "test3383") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3384 (export "test3384") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3385 (export "test3385") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3386 (export "test3386") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3387 (export "test3387") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3388 (export "test3388") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3389 (export "test3389") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3390 (export "test3390") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3391 (export "test3391") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3392 (export "test3392") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3393 (export "test3393") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3394 (export "test3394") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3395 (export "test3395") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3396 (export "test3396") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3397 (export "test3397") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3398 (export "test3398") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3399 (export "test3399") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3400 (export "test3400") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3401 (export "test3401") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3402 (export "test3402") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3403 (export "test3403") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3404 (export "test3404") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3405 (export "test3405") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3406 (export "test3406") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3407 (export "test3407") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3408 (export "test3408") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3409 (export "test3409") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3410 (export "test3410") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3411 (export "test3411") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3412 (export "test3412") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3413 (export "test3413") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3414 (export "test3414") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3415 (export "test3415") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3416 (export "test3416") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3417 (export "test3417") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3418 (export "test3418") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3419 (export "test3419") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3420 (export "test3420") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3421 (export "test3421") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3422 (export "test3422") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3423 (export "test3423") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3424 (export "test3424") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3425 (export "test3425") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3426 (export "test3426") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3427 (export "test3427") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3428 (export "test3428") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3429 (export "test3429") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3430 (export "test3430") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3431 (export "test3431") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3432 (export "test3432") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3433 (export "test3433") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3434 (export "test3434") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3435 (export "test3435") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3436 (export "test3436") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3437 (export "test3437") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3438 (export "test3438") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3439 (export "test3439") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3440 (export "test3440") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3441 (export "test3441") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3442 (export "test3442") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3443 (export "test3443") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3444 (export "test3444") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3445 (export "test3445") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3446 (export "test3446") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3447 (export "test3447") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3448 (export "test3448") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3449 (export "test3449") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3450 (export "test3450") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3451 (export "test3451") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3452 (export "test3452") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3453 (export "test3453") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3454 (export "test3454") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3455 (export "test3455") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3456 (export "test3456") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3457 (export "test3457") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3458 (export "test3458") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3459 (export "test3459") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3460 (export "test3460") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3461 (export "test3461") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3462 (export "test3462") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3463 (export "test3463") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3464 (export "test3464") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3465 (export "test3465") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3466 (export "test3466") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3467 (export "test3467") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3468 (export "test3468") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3469 (export "test3469") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3470 (export "test3470") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3471 (export "test3471") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3472 (export "test3472") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3473 (export "test3473") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3474 (export "test3474") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3475 (export "test3475") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3476 (export "test3476") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3477 (export "test3477") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3478 (export "test3478") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3479 (export "test3479") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3480 (export "test3480") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3481 (export "test3481") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3482 (export "test3482") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3483 (export "test3483") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3484 (export "test3484") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3485 (export "test3485") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3486 (export "test3486") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3487 (export "test3487") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3488 (export "test3488") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3489 (export "test3489") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3490 (export "test3490") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3491 (export "test3491") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3492 (export "test3492") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3493 (export "test3493") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3494 (export "test3494") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3495 (export "test3495") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3496 (export "test3496") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3497 (export "test3497") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3498 (export "test3498") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3499 (export "test3499") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3500 (export "test3500") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3501 (export "test3501") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3502 (export "test3502") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3503 (export "test3503") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3504 (export "test3504") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3505 (export "test3505") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3506 (export "test3506") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3507 (export "test3507") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3508 (export "test3508") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3509 (export "test3509") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3510 (export "test3510") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3511 (export "test3511") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3512 (export "test3512") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3513 (export "test3513") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3514 (export "test3514") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3515 (export "test3515") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3516 (export "test3516") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3517 (export "test3517") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3518 (export "test3518") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3519 (export "test3519") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3520 (export "test3520") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3521 (export "test3521") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3522 (export "test3522") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3523 (export "test3523") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3524 (export "test3524") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3525 (export "test3525") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3526 (export "test3526") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3527 (export "test3527") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3528 (export "test3528") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3529 (export "test3529") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3530 (export "test3530") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3531 (export "test3531") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3532 (export "test3532") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3533 (export "test3533") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3534 (export "test3534") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3535 (export "test3535") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3536 (export "test3536") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3537 (export "test3537") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3538 (export "test3538") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3539 (export "test3539") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3540 (export "test3540") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3541 (export "test3541") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3542 (export "test3542") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3543 (export "test3543") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3544 (export "test3544") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3545 (export "test3545") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3546 (export "test3546") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3547 (export "test3547") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3548 (export "test3548") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3549 (export "test3549") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3550 (export "test3550") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3551 (export "test3551") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3552 (export "test3552") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3553 (export "test3553") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3554 (export "test3554") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3555 (export "test3555") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3556 (export "test3556") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3557 (export "test3557") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3558 (export "test3558") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3559 (export "test3559") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3560 (export "test3560") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3561 (export "test3561") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3562 (export "test3562") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3563 (export "test3563") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3564 (export "test3564") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3565 (export "test3565") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3566 (export "test3566") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3567 (export "test3567") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3568 (export "test3568") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3569 (export "test3569") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3570 (export "test3570") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3571 (export "test3571") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3572 (export "test3572") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3573 (export "test3573") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3574 (export "test3574") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3575 (export "test3575") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3576 (export "test3576") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3577 (export "test3577") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3578 (export "test3578") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3579 (export "test3579") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3580 (export "test3580") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3581 (export "test3581") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3582 (export "test3582") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3583 (export "test3583") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3584 (export "test3584") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3585 (export "test3585") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3586 (export "test3586") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3587 (export "test3587") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3588 (export "test3588") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3589 (export "test3589") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3590 (export "test3590") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3591 (export "test3591") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3592 (export "test3592") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3593 (export "test3593") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3594 (export "test3594") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3595 (export "test3595") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3596 (export "test3596") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3597 (export "test3597") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3598 (export "test3598") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3599 (export "test3599") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3600 (export "test3600") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3601 (export "test3601") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3602 (export "test3602") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3603 (export "test3603") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3604 (export "test3604") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3605 (export "test3605") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3606 (export "test3606") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3607 (export "test3607") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3608 (export "test3608") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3609 (export "test3609") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3610 (export "test3610") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3611 (export "test3611") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3612 (export "test3612") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3613 (export "test3613") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3614 (export "test3614") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3615 (export "test3615") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3616 (export "test3616") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3617 (export "test3617") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3618 (export "test3618") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3619 (export "test3619") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3620 (export "test3620") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3621 (export "test3621") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3622 (export "test3622") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3623 (export "test3623") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3624 (export "test3624") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3625 (export "test3625") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3626 (export "test3626") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3627 (export "test3627") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3628 (export "test3628") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3629 (export "test3629") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3630 (export "test3630") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3631 (export "test3631") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3632 (export "test3632") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3633 (export "test3633") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3634 (export "test3634") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3635 (export "test3635") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3636 (export "test3636") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3637 (export "test3637") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3638 (export "test3638") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3639 (export "test3639") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3640 (export "test3640") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3641 (export "test3641") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3642 (export "test3642") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3643 (export "test3643") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3644 (export "test3644") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3645 (export "test3645") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3646 (export "test3646") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3647 (export "test3647") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3648 (export "test3648") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3649 (export "test3649") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3650 (export "test3650") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3651 (export "test3651") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3652 (export "test3652") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3653 (export "test3653") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3654 (export "test3654") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3655 (export "test3655") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3656 (export "test3656") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3657 (export "test3657") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3658 (export "test3658") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3659 (export "test3659") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3660 (export "test3660") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3661 (export "test3661") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3662 (export "test3662") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3663 (export "test3663") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3664 (export "test3664") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3665 (export "test3665") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3666 (export "test3666") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3667 (export "test3667") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3668 (export "test3668") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3669 (export "test3669") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3670 (export "test3670") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3671 (export "test3671") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3672 (export "test3672") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3673 (export "test3673") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3674 (export "test3674") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3675 (export "test3675") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3676 (export "test3676") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3677 (export "test3677") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3678 (export "test3678") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3679 (export "test3679") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3680 (export "test3680") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3681 (export "test3681") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3682 (export "test3682") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3683 (export "test3683") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3684 (export "test3684") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3685 (export "test3685") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3686 (export "test3686") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3687 (export "test3687") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3688 (export "test3688") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3689 (export "test3689") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3690 (export "test3690") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3691 (export "test3691") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3692 (export "test3692") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3693 (export "test3693") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3694 (export "test3694") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3695 (export "test3695") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3696 (export "test3696") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3697 (export "test3697") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3698 (export "test3698") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3699 (export "test3699") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3700 (export "test3700") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3701 (export "test3701") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3702 (export "test3702") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3703 (export "test3703") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3704 (export "test3704") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3705 (export "test3705") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3706 (export "test3706") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3707 (export "test3707") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3708 (export "test3708") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3709 (export "test3709") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3710 (export "test3710") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3711 (export "test3711") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3712 (export "test3712") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3713 (export "test3713") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3714 (export "test3714") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3715 (export "test3715") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3716 (export "test3716") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3717 (export "test3717") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3718 (export "test3718") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3719 (export "test3719") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3720 (export "test3720") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3721 (export "test3721") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3722 (export "test3722") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3723 (export "test3723") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3724 (export "test3724") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3725 (export "test3725") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3726 (export "test3726") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3727 (export "test3727") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3728 (export "test3728") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3729 (export "test3729") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3730 (export "test3730") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3731 (export "test3731") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3732 (export "test3732") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3733 (export "test3733") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3734 (export "test3734") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3735 (export "test3735") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3736 (export "test3736") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3737 (export "test3737") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3738 (export "test3738") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3739 (export "test3739") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3740 (export "test3740") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3741 (export "test3741") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3742 (export "test3742") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3743 (export "test3743") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3744 (export "test3744") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3745 (export "test3745") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3746 (export "test3746") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3747 (export "test3747") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3748 (export "test3748") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3749 (export "test3749") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3750 (export "test3750") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3751 (export "test3751") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3752 (export "test3752") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3753 (export "test3753") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3754 (export "test3754") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3755 (export "test3755") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3756 (export "test3756") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3757 (export "test3757") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3758 (export "test3758") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3759 (export "test3759") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3760 (export "test3760") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3761 (export "test3761") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3762 (export "test3762") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3763 (export "test3763") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3764 (export "test3764") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3765 (export "test3765") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3766 (export "test3766") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3767 (export "test3767") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3768 (export "test3768") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3769 (export "test3769") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3770 (export "test3770") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3771 (export "test3771") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3772 (export "test3772") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3773 (export "test3773") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3774 (export "test3774") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3775 (export "test3775") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3776 (export "test3776") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3777 (export "test3777") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3778 (export "test3778") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3779 (export "test3779") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3780 (export "test3780") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3781 (export "test3781") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3782 (export "test3782") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3783 (export "test3783") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3784 (export "test3784") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3785 (export "test3785") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3786 (export "test3786") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3787 (export "test3787") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3788 (export "test3788") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3789 (export "test3789") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3790 (export "test3790") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3791 (export "test3791") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3792 (export "test3792") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3793 (export "test3793") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3794 (export "test3794") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3795 (export "test3795") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3796 (export "test3796") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3797 (export "test3797") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3798 (export "test3798") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3799 (export "test3799") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3800 (export "test3800") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3801 (export "test3801") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3802 (export "test3802") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3803 (export "test3803") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3804 (export "test3804") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3805 (export "test3805") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3806 (export "test3806") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3807 (export "test3807") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3808 (export "test3808") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3809 (export "test3809") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3810 (export "test3810") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3811 (export "test3811") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3812 (export "test3812") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3813 (export "test3813") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3814 (export "test3814") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3815 (export "test3815") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3816 (export "test3816") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3817 (export "test3817") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3818 (export "test3818") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3819 (export "test3819") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3820 (export "test3820") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3821 (export "test3821") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3822 (export "test3822") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3823 (export "test3823") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3824 (export "test3824") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3825 (export "test3825") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3826 (export "test3826") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3827 (export "test3827") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3828 (export "test3828") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3829 (export "test3829") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3830 (export "test3830") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3831 (export "test3831") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3832 (export "test3832") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3833 (export "test3833") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3834 (export "test3834") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3835 (export "test3835") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3836 (export "test3836") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3837 (export "test3837") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3838 (export "test3838") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3839 (export "test3839") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3840 (export "test3840") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3841 (export "test3841") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3842 (export "test3842") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3843 (export "test3843") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3844 (export "test3844") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3845 (export "test3845") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3846 (export "test3846") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3847 (export "test3847") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3848 (export "test3848") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3849 (export "test3849") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3850 (export "test3850") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3851 (export "test3851") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3852 (export "test3852") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3853 (export "test3853") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3854 (export "test3854") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3855 (export "test3855") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3856 (export "test3856") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3857 (export "test3857") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3858 (export "test3858") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3859 (export "test3859") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3860 (export "test3860") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3861 (export "test3861") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3862 (export "test3862") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3863 (export "test3863") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3864 (export "test3864") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3865 (export "test3865") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3866 (export "test3866") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3867 (export "test3867") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3868 (export "test3868") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3869 (export "test3869") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3870 (export "test3870") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3871 (export "test3871") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3872 (export "test3872") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3873 (export "test3873") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3874 (export "test3874") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3875 (export "test3875") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3876 (export "test3876") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3877 (export "test3877") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3878 (export "test3878") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3879 (export "test3879") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3880 (export "test3880") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3881 (export "test3881") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3882 (export "test3882") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3883 (export "test3883") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3884 (export "test3884") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3885 (export "test3885") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3886 (export "test3886") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3887 (export "test3887") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3888 (export "test3888") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3889 (export "test3889") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3890 (export "test3890") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3891 (export "test3891") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3892 (export "test3892") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3893 (export "test3893") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3894 (export "test3894") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3895 (export "test3895") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3896 (export "test3896") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3897 (export "test3897") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3898 (export "test3898") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3899 (export "test3899") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3900 (export "test3900") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3901 (export "test3901") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3902 (export "test3902") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3903 (export "test3903") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3904 (export "test3904") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3905 (export "test3905") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3906 (export "test3906") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3907 (export "test3907") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3908 (export "test3908") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3909 (export "test3909") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3910 (export "test3910") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3911 (export "test3911") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3912 (export "test3912") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3913 (export "test3913") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3914 (export "test3914") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3915 (export "test3915") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3916 (export "test3916") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3917 (export "test3917") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3918 (export "test3918") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3919 (export "test3919") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3920 (export "test3920") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3921 (export "test3921") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3922 (export "test3922") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3923 (export "test3923") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3924 (export "test3924") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3925 (export "test3925") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3926 (export "test3926") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3927 (export "test3927") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3928 (export "test3928") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3929 (export "test3929") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3930 (export "test3930") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3931 (export "test3931") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3932 (export "test3932") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3933 (export "test3933") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3934 (export "test3934") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3935 (export "test3935") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3936 (export "test3936") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3937 (export "test3937") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3938 (export "test3938") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3939 (export "test3939") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3940 (export "test3940") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3941 (export "test3941") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3942 (export "test3942") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3943 (export "test3943") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3944 (export "test3944") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3945 (export "test3945") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3946 (export "test3946") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3947 (export "test3947") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3948 (export "test3948") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3949 (export "test3949") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3950 (export "test3950") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3951 (export "test3951") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3952 (export "test3952") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3953 (export "test3953") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3954 (export "test3954") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3955 (export "test3955") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3956 (export "test3956") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3957 (export "test3957") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3958 (export "test3958") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3959 (export "test3959") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3960 (export "test3960") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3961 (export "test3961") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3962 (export "test3962") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3963 (export "test3963") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3964 (export "test3964") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3965 (export "test3965") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3966 (export "test3966") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3967 (export "test3967") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3968 (export "test3968") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3969 (export "test3969") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3970 (export "test3970") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3971 (export "test3971") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3972 (export "test3972") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3973 (export "test3973") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3974 (export "test3974") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3975 (export "test3975") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3976 (export "test3976") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3977 (export "test3977") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3978 (export "test3978") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3979 (export "test3979") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3980 (export "test3980") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3981 (export "test3981") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3982 (export "test3982") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3983 (export "test3983") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3984 (export "test3984") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3985 (export "test3985") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3986 (export "test3986") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3987 (export "test3987") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3988 (export "test3988") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3989 (export "test3989") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3990 (export "test3990") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3991 (export "test3991") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3992 (export "test3992") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3993 (export "test3993") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3994 (export "test3994") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3995 (export "test3995") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3996 (export "test3996") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3997 (export "test3997") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3998 (export "test3998") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test3999 (export "test3999") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4000 (export "test4000") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4001 (export "test4001") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4002 (export "test4002") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4003 (export "test4003") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4004 (export "test4004") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4005 (export "test4005") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4006 (export "test4006") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4007 (export "test4007") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4008 (export "test4008") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4009 (export "test4009") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4010 (export "test4010") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4011 (export "test4011") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4012 (export "test4012") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4013 (export "test4013") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4014 (export "test4014") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4015 (export "test4015") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4016 (export "test4016") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4017 (export "test4017") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4018 (export "test4018") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4019 (export "test4019") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4020 (export "test4020") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4021 (export "test4021") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4022 (export "test4022") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4023 (export "test4023") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4024 (export "test4024") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4025 (export "test4025") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4026 (export "test4026") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4027 (export "test4027") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4028 (export "test4028") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4029 (export "test4029") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4030 (export "test4030") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4031 (export "test4031") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4032 (export "test4032") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4033 (export "test4033") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4034 (export "test4034") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4035 (export "test4035") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4036 (export "test4036") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4037 (export "test4037") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4038 (export "test4038") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4039 (export "test4039") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4040 (export "test4040") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4041 (export "test4041") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4042 (export "test4042") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4043 (export "test4043") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4044 (export "test4044") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4045 (export "test4045") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4046 (export "test4046") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4047 (export "test4047") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4048 (export "test4048") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4049 (export "test4049") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4050 (export "test4050") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4051 (export "test4051") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4052 (export "test4052") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4053 (export "test4053") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4054 (export "test4054") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4055 (export "test4055") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4056 (export "test4056") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4057 (export "test4057") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4058 (export "test4058") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4059 (export "test4059") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4060 (export "test4060") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4061 (export "test4061") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4062 (export "test4062") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4063 (export "test4063") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4064 (export "test4064") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4065 (export "test4065") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4066 (export "test4066") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4067 (export "test4067") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4068 (export "test4068") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4069 (export "test4069") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4070 (export "test4070") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4071 (export "test4071") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4072 (export "test4072") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4073 (export "test4073") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4074 (export "test4074") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4075 (export "test4075") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4076 (export "test4076") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4077 (export "test4077") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4078 (export "test4078") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4079 (export "test4079") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4080 (export "test4080") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4081 (export "test4081") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4082 (export "test4082") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4083 (export "test4083") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4084 (export "test4084") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4085 (export "test4085") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4086 (export "test4086") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4087 (export "test4087") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4088 (export "test4088") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4089 (export "test4089") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4090 (export "test4090") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4091 (export "test4091") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4092 (export "test4092") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4093 (export "test4093") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4094 (export "test4094") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4095 (export "test4095") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4096 (export "test4096") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4097 (export "test4097") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4098 (export "test4098") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4099 (export "test4099") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4100 (export "test4100") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4101 (export "test4101") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4102 (export "test4102") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4103 (export "test4103") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4104 (export "test4104") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4105 (export "test4105") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4106 (export "test4106") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4107 (export "test4107") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4108 (export "test4108") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4109 (export "test4109") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4110 (export "test4110") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4111 (export "test4111") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4112 (export "test4112") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4113 (export "test4113") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4114 (export "test4114") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4115 (export "test4115") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4116 (export "test4116") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4117 (export "test4117") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4118 (export "test4118") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4119 (export "test4119") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4120 (export "test4120") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4121 (export "test4121") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4122 (export "test4122") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4123 (export "test4123") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4124 (export "test4124") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4125 (export "test4125") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4126 (export "test4126") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4127 (export "test4127") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4128 (export "test4128") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4129 (export "test4129") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4130 (export "test4130") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4131 (export "test4131") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4132 (export "test4132") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4133 (export "test4133") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4134 (export "test4134") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4135 (export "test4135") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4136 (export "test4136") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4137 (export "test4137") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4138 (export "test4138") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4139 (export "test4139") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4140 (export "test4140") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4141 (export "test4141") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4142 (export "test4142") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4143 (export "test4143") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4144 (export "test4144") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4145 (export "test4145") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4146 (export "test4146") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4147 (export "test4147") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4148 (export "test4148") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4149 (export "test4149") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4150 (export "test4150") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4151 (export "test4151") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4152 (export "test4152") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4153 (export "test4153") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4154 (export "test4154") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4155 (export "test4155") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4156 (export "test4156") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4157 (export "test4157") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4158 (export "test4158") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4159 (export "test4159") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4160 (export "test4160") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4161 (export "test4161") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4162 (export "test4162") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4163 (export "test4163") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4164 (export "test4164") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4165 (export "test4165") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4166 (export "test4166") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4167 (export "test4167") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4168 (export "test4168") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4169 (export "test4169") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4170 (export "test4170") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4171 (export "test4171") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4172 (export "test4172") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4173 (export "test4173") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4174 (export "test4174") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4175 (export "test4175") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4176 (export "test4176") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4177 (export "test4177") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4178 (export "test4178") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4179 (export "test4179") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4180 (export "test4180") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4181 (export "test4181") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4182 (export "test4182") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4183 (export "test4183") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4184 (export "test4184") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4185 (export "test4185") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4186 (export "test4186") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4187 (export "test4187") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4188 (export "test4188") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4189 (export "test4189") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4190 (export "test4190") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4191 (export "test4191") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4192 (export "test4192") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4193 (export "test4193") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4194 (export "test4194") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4195 (export "test4195") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4196 (export "test4196") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4197 (export "test4197") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4198 (export "test4198") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4199 (export "test4199") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4200 (export "test4200") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4201 (export "test4201") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4202 (export "test4202") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4203 (export "test4203") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4204 (export "test4204") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4205 (export "test4205") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4206 (export "test4206") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4207 (export "test4207") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4208 (export "test4208") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4209 (export "test4209") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4210 (export "test4210") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4211 (export "test4211") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4212 (export "test4212") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4213 (export "test4213") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4214 (export "test4214") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4215 (export "test4215") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4216 (export "test4216") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4217 (export "test4217") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4218 (export "test4218") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4219 (export "test4219") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4220 (export "test4220") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4221 (export "test4221") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4222 (export "test4222") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4223 (export "test4223") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4224 (export "test4224") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4225 (export "test4225") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4226 (export "test4226") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4227 (export "test4227") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4228 (export "test4228") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4229 (export "test4229") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4230 (export "test4230") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4231 (export "test4231") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4232 (export "test4232") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4233 (export "test4233") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4234 (export "test4234") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4235 (export "test4235") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4236 (export "test4236") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4237 (export "test4237") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4238 (export "test4238") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4239 (export "test4239") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4240 (export "test4240") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4241 (export "test4241") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4242 (export "test4242") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4243 (export "test4243") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4244 (export "test4244") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4245 (export "test4245") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4246 (export "test4246") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4247 (export "test4247") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4248 (export "test4248") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4249 (export "test4249") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4250 (export "test4250") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4251 (export "test4251") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4252 (export "test4252") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4253 (export "test4253") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4254 (export "test4254") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4255 (export "test4255") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4256 (export "test4256") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4257 (export "test4257") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4258 (export "test4258") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4259 (export "test4259") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4260 (export "test4260") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4261 (export "test4261") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4262 (export "test4262") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4263 (export "test4263") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4264 (export "test4264") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4265 (export "test4265") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4266 (export "test4266") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4267 (export "test4267") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4268 (export "test4268") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4269 (export "test4269") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4270 (export "test4270") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4271 (export "test4271") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4272 (export "test4272") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4273 (export "test4273") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4274 (export "test4274") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4275 (export "test4275") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4276 (export "test4276") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4277 (export "test4277") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4278 (export "test4278") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4279 (export "test4279") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4280 (export "test4280") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4281 (export "test4281") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4282 (export "test4282") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4283 (export "test4283") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4284 (export "test4284") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4285 (export "test4285") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4286 (export "test4286") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4287 (export "test4287") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4288 (export "test4288") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4289 (export "test4289") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4290 (export "test4290") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4291 (export "test4291") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4292 (export "test4292") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4293 (export "test4293") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4294 (export "test4294") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4295 (export "test4295") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4296 (export "test4296") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4297 (export "test4297") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4298 (export "test4298") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4299 (export "test4299") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4300 (export "test4300") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4301 (export "test4301") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4302 (export "test4302") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4303 (export "test4303") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4304 (export "test4304") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4305 (export "test4305") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4306 (export "test4306") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4307 (export "test4307") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4308 (export "test4308") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4309 (export "test4309") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4310 (export "test4310") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4311 (export "test4311") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4312 (export "test4312") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4313 (export "test4313") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4314 (export "test4314") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4315 (export "test4315") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4316 (export "test4316") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4317 (export "test4317") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4318 (export "test4318") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4319 (export "test4319") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4320 (export "test4320") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4321 (export "test4321") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4322 (export "test4322") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4323 (export "test4323") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4324 (export "test4324") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4325 (export "test4325") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4326 (export "test4326") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4327 (export "test4327") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4328 (export "test4328") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4329 (export "test4329") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4330 (export "test4330") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4331 (export "test4331") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4332 (export "test4332") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4333 (export "test4333") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4334 (export "test4334") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4335 (export "test4335") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4336 (export "test4336") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4337 (export "test4337") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4338 (export "test4338") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4339 (export "test4339") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4340 (export "test4340") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4341 (export "test4341") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4342 (export "test4342") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4343 (export "test4343") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4344 (export "test4344") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4345 (export "test4345") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4346 (export "test4346") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4347 (export "test4347") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4348 (export "test4348") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4349 (export "test4349") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4350 (export "test4350") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4351 (export "test4351") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4352 (export "test4352") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4353 (export "test4353") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4354 (export "test4354") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4355 (export "test4355") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4356 (export "test4356") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4357 (export "test4357") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4358 (export "test4358") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4359 (export "test4359") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4360 (export "test4360") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4361 (export "test4361") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4362 (export "test4362") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4363 (export "test4363") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4364 (export "test4364") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4365 (export "test4365") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4366 (export "test4366") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4367 (export "test4367") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4368 (export "test4368") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4369 (export "test4369") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4370 (export "test4370") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4371 (export "test4371") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4372 (export "test4372") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4373 (export "test4373") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4374 (export "test4374") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4375 (export "test4375") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4376 (export "test4376") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4377 (export "test4377") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4378 (export "test4378") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4379 (export "test4379") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4380 (export "test4380") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4381 (export "test4381") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4382 (export "test4382") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4383 (export "test4383") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4384 (export "test4384") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4385 (export "test4385") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4386 (export "test4386") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4387 (export "test4387") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4388 (export "test4388") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4389 (export "test4389") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4390 (export "test4390") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4391 (export "test4391") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4392 (export "test4392") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4393 (export "test4393") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4394 (export "test4394") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4395 (export "test4395") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4396 (export "test4396") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4397 (export "test4397") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4398 (export "test4398") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4399 (export "test4399") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4400 (export "test4400") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4401 (export "test4401") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4402 (export "test4402") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4403 (export "test4403") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4404 (export "test4404") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4405 (export "test4405") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4406 (export "test4406") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4407 (export "test4407") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4408 (export "test4408") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4409 (export "test4409") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4410 (export "test4410") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4411 (export "test4411") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4412 (export "test4412") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4413 (export "test4413") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4414 (export "test4414") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4415 (export "test4415") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4416 (export "test4416") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4417 (export "test4417") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4418 (export "test4418") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4419 (export "test4419") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4420 (export "test4420") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4421 (export "test4421") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4422 (export "test4422") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4423 (export "test4423") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4424 (export "test4424") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4425 (export "test4425") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4426 (export "test4426") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4427 (export "test4427") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4428 (export "test4428") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4429 (export "test4429") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4430 (export "test4430") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4431 (export "test4431") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4432 (export "test4432") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4433 (export "test4433") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4434 (export "test4434") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4435 (export "test4435") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4436 (export "test4436") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4437 (export "test4437") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4438 (export "test4438") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4439 (export "test4439") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4440 (export "test4440") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4441 (export "test4441") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4442 (export "test4442") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4443 (export "test4443") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4444 (export "test4444") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4445 (export "test4445") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4446 (export "test4446") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4447 (export "test4447") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4448 (export "test4448") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4449 (export "test4449") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4450 (export "test4450") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4451 (export "test4451") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4452 (export "test4452") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4453 (export "test4453") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4454 (export "test4454") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4455 (export "test4455") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4456 (export "test4456") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4457 (export "test4457") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4458 (export "test4458") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4459 (export "test4459") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4460 (export "test4460") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4461 (export "test4461") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4462 (export "test4462") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4463 (export "test4463") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4464 (export "test4464") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4465 (export "test4465") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4466 (export "test4466") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4467 (export "test4467") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4468 (export "test4468") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4469 (export "test4469") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4470 (export "test4470") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4471 (export "test4471") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4472 (export "test4472") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4473 (export "test4473") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4474 (export "test4474") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4475 (export "test4475") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4476 (export "test4476") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4477 (export "test4477") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4478 (export "test4478") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4479 (export "test4479") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4480 (export "test4480") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4481 (export "test4481") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4482 (export "test4482") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4483 (export "test4483") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4484 (export "test4484") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4485 (export "test4485") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4486 (export "test4486") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4487 (export "test4487") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4488 (export "test4488") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4489 (export "test4489") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4490 (export "test4490") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4491 (export "test4491") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4492 (export "test4492") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4493 (export "test4493") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4494 (export "test4494") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4495 (export "test4495") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4496 (export "test4496") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4497 (export "test4497") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4498 (export "test4498") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4499 (export "test4499") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4500 (export "test4500") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4501 (export "test4501") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4502 (export "test4502") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4503 (export "test4503") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4504 (export "test4504") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4505 (export "test4505") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4506 (export "test4506") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4507 (export "test4507") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4508 (export "test4508") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4509 (export "test4509") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4510 (export "test4510") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4511 (export "test4511") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4512 (export "test4512") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4513 (export "test4513") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4514 (export "test4514") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4515 (export "test4515") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4516 (export "test4516") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4517 (export "test4517") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4518 (export "test4518") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4519 (export "test4519") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4520 (export "test4520") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4521 (export "test4521") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4522 (export "test4522") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4523 (export "test4523") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4524 (export "test4524") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4525 (export "test4525") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4526 (export "test4526") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4527 (export "test4527") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4528 (export "test4528") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4529 (export "test4529") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4530 (export "test4530") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4531 (export "test4531") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4532 (export "test4532") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4533 (export "test4533") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4534 (export "test4534") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4535 (export "test4535") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4536 (export "test4536") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4537 (export "test4537") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4538 (export "test4538") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4539 (export "test4539") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4540 (export "test4540") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4541 (export "test4541") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4542 (export "test4542") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4543 (export "test4543") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4544 (export "test4544") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4545 (export "test4545") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4546 (export "test4546") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4547 (export "test4547") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4548 (export "test4548") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4549 (export "test4549") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4550 (export "test4550") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4551 (export "test4551") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4552 (export "test4552") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4553 (export "test4553") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4554 (export "test4554") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4555 (export "test4555") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4556 (export "test4556") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4557 (export "test4557") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4558 (export "test4558") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4559 (export "test4559") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4560 (export "test4560") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4561 (export "test4561") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4562 (export "test4562") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4563 (export "test4563") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4564 (export "test4564") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4565 (export "test4565") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4566 (export "test4566") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4567 (export "test4567") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4568 (export "test4568") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4569 (export "test4569") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4570 (export "test4570") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4571 (export "test4571") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4572 (export "test4572") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4573 (export "test4573") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4574 (export "test4574") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4575 (export "test4575") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4576 (export "test4576") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4577 (export "test4577") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4578 (export "test4578") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4579 (export "test4579") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4580 (export "test4580") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4581 (export "test4581") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4582 (export "test4582") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4583 (export "test4583") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4584 (export "test4584") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4585 (export "test4585") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4586 (export "test4586") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4587 (export "test4587") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4588 (export "test4588") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4589 (export "test4589") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4590 (export "test4590") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4591 (export "test4591") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4592 (export "test4592") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4593 (export "test4593") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4594 (export "test4594") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4595 (export "test4595") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4596 (export "test4596") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4597 (export "test4597") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4598 (export "test4598") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4599 (export "test4599") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4600 (export "test4600") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4601 (export "test4601") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4602 (export "test4602") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4603 (export "test4603") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4604 (export "test4604") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4605 (export "test4605") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4606 (export "test4606") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4607 (export "test4607") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4608 (export "test4608") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4609 (export "test4609") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4610 (export "test4610") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4611 (export "test4611") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4612 (export "test4612") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4613 (export "test4613") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4614 (export "test4614") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4615 (export "test4615") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4616 (export "test4616") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4617 (export "test4617") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4618 (export "test4618") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4619 (export "test4619") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4620 (export "test4620") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4621 (export "test4621") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4622 (export "test4622") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4623 (export "test4623") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4624 (export "test4624") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4625 (export "test4625") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4626 (export "test4626") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4627 (export "test4627") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4628 (export "test4628") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4629 (export "test4629") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4630 (export "test4630") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4631 (export "test4631") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4632 (export "test4632") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4633 (export "test4633") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4634 (export "test4634") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4635 (export "test4635") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4636 (export "test4636") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4637 (export "test4637") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4638 (export "test4638") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4639 (export "test4639") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4640 (export "test4640") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4641 (export "test4641") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4642 (export "test4642") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4643 (export "test4643") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4644 (export "test4644") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4645 (export "test4645") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4646 (export "test4646") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4647 (export "test4647") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4648 (export "test4648") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4649 (export "test4649") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4650 (export "test4650") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4651 (export "test4651") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4652 (export "test4652") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4653 (export "test4653") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4654 (export "test4654") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4655 (export "test4655") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4656 (export "test4656") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4657 (export "test4657") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4658 (export "test4658") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4659 (export "test4659") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4660 (export "test4660") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4661 (export "test4661") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4662 (export "test4662") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4663 (export "test4663") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4664 (export "test4664") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4665 (export "test4665") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4666 (export "test4666") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4667 (export "test4667") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4668 (export "test4668") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4669 (export "test4669") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4670 (export "test4670") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4671 (export "test4671") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4672 (export "test4672") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4673 (export "test4673") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4674 (export "test4674") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4675 (export "test4675") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4676 (export "test4676") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4677 (export "test4677") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4678 (export "test4678") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4679 (export "test4679") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4680 (export "test4680") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4681 (export "test4681") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4682 (export "test4682") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4683 (export "test4683") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4684 (export "test4684") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4685 (export "test4685") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4686 (export "test4686") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4687 (export "test4687") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4688 (export "test4688") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4689 (export "test4689") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4690 (export "test4690") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4691 (export "test4691") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4692 (export "test4692") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4693 (export "test4693") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4694 (export "test4694") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4695 (export "test4695") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4696 (export "test4696") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4697 (export "test4697") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4698 (export "test4698") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4699 (export "test4699") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4700 (export "test4700") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4701 (export "test4701") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4702 (export "test4702") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4703 (export "test4703") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4704 (export "test4704") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4705 (export "test4705") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4706 (export "test4706") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4707 (export "test4707") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4708 (export "test4708") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4709 (export "test4709") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4710 (export "test4710") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4711 (export "test4711") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4712 (export "test4712") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4713 (export "test4713") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4714 (export "test4714") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4715 (export "test4715") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4716 (export "test4716") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4717 (export "test4717") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4718 (export "test4718") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4719 (export "test4719") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4720 (export "test4720") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4721 (export "test4721") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4722 (export "test4722") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4723 (export "test4723") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4724 (export "test4724") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4725 (export "test4725") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4726 (export "test4726") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4727 (export "test4727") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4728 (export "test4728") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4729 (export "test4729") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4730 (export "test4730") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4731 (export "test4731") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4732 (export "test4732") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4733 (export "test4733") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4734 (export "test4734") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4735 (export "test4735") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4736 (export "test4736") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4737 (export "test4737") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4738 (export "test4738") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4739 (export "test4739") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4740 (export "test4740") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4741 (export "test4741") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4742 (export "test4742") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4743 (export "test4743") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4744 (export "test4744") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4745 (export "test4745") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4746 (export "test4746") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4747 (export "test4747") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4748 (export "test4748") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4749 (export "test4749") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4750 (export "test4750") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4751 (export "test4751") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4752 (export "test4752") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4753 (export "test4753") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4754 (export "test4754") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4755 (export "test4755") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4756 (export "test4756") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4757 (export "test4757") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4758 (export "test4758") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4759 (export "test4759") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4760 (export "test4760") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4761 (export "test4761") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4762 (export "test4762") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4763 (export "test4763") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4764 (export "test4764") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4765 (export "test4765") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4766 (export "test4766") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4767 (export "test4767") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4768 (export "test4768") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4769 (export "test4769") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4770 (export "test4770") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4771 (export "test4771") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4772 (export "test4772") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4773 (export "test4773") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4774 (export "test4774") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4775 (export "test4775") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4776 (export "test4776") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4777 (export "test4777") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4778 (export "test4778") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4779 (export "test4779") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4780 (export "test4780") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4781 (export "test4781") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4782 (export "test4782") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4783 (export "test4783") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4784 (export "test4784") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4785 (export "test4785") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4786 (export "test4786") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4787 (export "test4787") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4788 (export "test4788") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4789 (export "test4789") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4790 (export "test4790") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4791 (export "test4791") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4792 (export "test4792") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4793 (export "test4793") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4794 (export "test4794") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4795 (export "test4795") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4796 (export "test4796") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4797 (export "test4797") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4798 (export "test4798") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4799 (export "test4799") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4800 (export "test4800") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4801 (export "test4801") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4802 (export "test4802") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4803 (export "test4803") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4804 (export "test4804") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4805 (export "test4805") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4806 (export "test4806") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4807 (export "test4807") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4808 (export "test4808") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4809 (export "test4809") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4810 (export "test4810") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4811 (export "test4811") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4812 (export "test4812") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4813 (export "test4813") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4814 (export "test4814") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4815 (export "test4815") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4816 (export "test4816") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4817 (export "test4817") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4818 (export "test4818") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4819 (export "test4819") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4820 (export "test4820") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4821 (export "test4821") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4822 (export "test4822") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4823 (export "test4823") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4824 (export "test4824") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4825 (export "test4825") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4826 (export "test4826") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4827 (export "test4827") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4828 (export "test4828") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4829 (export "test4829") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4830 (export "test4830") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4831 (export "test4831") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4832 (export "test4832") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4833 (export "test4833") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4834 (export "test4834") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4835 (export "test4835") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4836 (export "test4836") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4837 (export "test4837") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4838 (export "test4838") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4839 (export "test4839") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4840 (export "test4840") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4841 (export "test4841") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4842 (export "test4842") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4843 (export "test4843") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4844 (export "test4844") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4845 (export "test4845") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4846 (export "test4846") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4847 (export "test4847") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4848 (export "test4848") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4849 (export "test4849") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4850 (export "test4850") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4851 (export "test4851") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4852 (export "test4852") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4853 (export "test4853") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4854 (export "test4854") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4855 (export "test4855") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4856 (export "test4856") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4857 (export "test4857") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4858 (export "test4858") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4859 (export "test4859") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4860 (export "test4860") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4861 (export "test4861") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4862 (export "test4862") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4863 (export "test4863") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4864 (export "test4864") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4865 (export "test4865") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4866 (export "test4866") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4867 (export "test4867") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4868 (export "test4868") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4869 (export "test4869") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4870 (export "test4870") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4871 (export "test4871") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4872 (export "test4872") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4873 (export "test4873") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4874 (export "test4874") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4875 (export "test4875") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4876 (export "test4876") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4877 (export "test4877") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4878 (export "test4878") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4879 (export "test4879") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4880 (export "test4880") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4881 (export "test4881") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4882 (export "test4882") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4883 (export "test4883") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4884 (export "test4884") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4885 (export "test4885") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4886 (export "test4886") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4887 (export "test4887") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4888 (export "test4888") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4889 (export "test4889") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4890 (export "test4890") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4891 (export "test4891") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4892 (export "test4892") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4893 (export "test4893") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4894 (export "test4894") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4895 (export "test4895") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4896 (export "test4896") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4897 (export "test4897") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4898 (export "test4898") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4899 (export "test4899") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4900 (export "test4900") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4901 (export "test4901") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4902 (export "test4902") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4903 (export "test4903") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4904 (export "test4904") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4905 (export "test4905") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4906 (export "test4906") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4907 (export "test4907") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4908 (export "test4908") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4909 (export "test4909") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4910 (export "test4910") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4911 (export "test4911") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4912 (export "test4912") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4913 (export "test4913") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4914 (export "test4914") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4915 (export "test4915") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4916 (export "test4916") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4917 (export "test4917") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4918 (export "test4918") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4919 (export "test4919") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4920 (export "test4920") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4921 (export "test4921") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4922 (export "test4922") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4923 (export "test4923") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4924 (export "test4924") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4925 (export "test4925") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4926 (export "test4926") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4927 (export "test4927") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4928 (export "test4928") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4929 (export "test4929") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4930 (export "test4930") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4931 (export "test4931") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4932 (export "test4932") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4933 (export "test4933") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4934 (export "test4934") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4935 (export "test4935") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4936 (export "test4936") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4937 (export "test4937") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4938 (export "test4938") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4939 (export "test4939") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4940 (export "test4940") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4941 (export "test4941") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4942 (export "test4942") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4943 (export "test4943") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4944 (export "test4944") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4945 (export "test4945") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4946 (export "test4946") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4947 (export "test4947") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4948 (export "test4948") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4949 (export "test4949") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4950 (export "test4950") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4951 (export "test4951") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4952 (export "test4952") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4953 (export "test4953") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4954 (export "test4954") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4955 (export "test4955") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4956 (export "test4956") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4957 (export "test4957") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4958 (export "test4958") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4959 (export "test4959") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4960 (export "test4960") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4961 (export "test4961") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4962 (export "test4962") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4963 (export "test4963") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4964 (export "test4964") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4965 (export "test4965") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4966 (export "test4966") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4967 (export "test4967") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4968 (export "test4968") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4969 (export "test4969") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4970 (export "test4970") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4971 (export "test4971") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4972 (export "test4972") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4973 (export "test4973") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4974 (export "test4974") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4975 (export "test4975") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4976 (export "test4976") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4977 (export "test4977") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4978 (export "test4978") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4979 (export "test4979") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4980 (export "test4980") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4981 (export "test4981") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4982 (export "test4982") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4983 (export "test4983") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4984 (export "test4984") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4985 (export "test4985") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4986 (export "test4986") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4987 (export "test4987") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4988 (export "test4988") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4989 (export "test4989") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4990 (export "test4990") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4991 (export "test4991") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4992 (export "test4992") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4993 (export "test4993") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4994 (export "test4994") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4995 (export "test4995") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4996 (export "test4996") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4997 (export "test4997") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4998 (export "test4998") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) + (func $test4999 (export "test4999") + (param $p0 i32) + (param $p1 i32) + (result i32) + ;; Return sum of p0 + p1 + local.get $p0 + local.get $p1 + i32.add + ) +) diff --git a/src/test/app/wasm_fixtures/wat/locals_10k.wat b/src/test/app/wasm_fixtures/wat/locals_10k.wat new file mode 100644 index 0000000000..6cd6d4baf4 --- /dev/null +++ b/src/test/app/wasm_fixtures/wat/locals_10k.wat @@ -0,0 +1,50000 @@ +(module + (func $test (export "test") + (param $p0 i32) + (param $p1 i32) + (result i32) + (local $l2 i32) + (local $l3 i32) + (local $l4 i32) + (local $l5 i32) + (local $l6 i32) + (local $l7 i32) + (local $l8 i32) + (local $l9 i32) + (local $l10 i32) + (local $l11 i32) + (local $l12 i32) + (local $l13 i32) + (local $l14 i32) + (local $l15 i32) + (local $l16 i32) + (local $l17 i32) + (local $l18 i32) + (local $l19 i32) + (local $l20 i32) + (local $l21 i32) + (local $l22 i32) + (local $l23 i32) + (local $l24 i32) + (local $l25 i32) + (local $l26 i32) + (local $l27 i32) + (local $l28 i32) + (local $l29 i32) + (local $l30 i32) + (local $l31 i32) + (local $l32 i32) + (local $l33 i32) + (local $l34 i32) + (local $l35 i32) + (local $l36 i32) + (local $l37 i32) + (local $l38 i32) + (local $l39 i32) + (local $l40 i32) + (local $l41 i32) + (local $l42 i32) + (local $l43 i32) + (local $l44 i32) + (local $l45 i32) + (local $l46 i32) + (local $l47 i32) + (local $l48 i32) + (local $l49 i32) + (local $l50 i32) + (local $l51 i32) + (local $l52 i32) + (local $l53 i32) + (local $l54 i32) + (local $l55 i32) + (local $l56 i32) + (local $l57 i32) + (local $l58 i32) + (local $l59 i32) + (local $l60 i32) + (local $l61 i32) + (local $l62 i32) + (local $l63 i32) + (local $l64 i32) + (local $l65 i32) + (local $l66 i32) + (local $l67 i32) + (local $l68 i32) + (local $l69 i32) + (local $l70 i32) + (local $l71 i32) + (local $l72 i32) + (local $l73 i32) + (local $l74 i32) + (local $l75 i32) + (local $l76 i32) + (local $l77 i32) + (local $l78 i32) + (local $l79 i32) + (local $l80 i32) + (local $l81 i32) + (local $l82 i32) + (local $l83 i32) + (local $l84 i32) + (local $l85 i32) + (local $l86 i32) + (local $l87 i32) + (local $l88 i32) + (local $l89 i32) + (local $l90 i32) + (local $l91 i32) + (local $l92 i32) + (local $l93 i32) + (local $l94 i32) + (local $l95 i32) + (local $l96 i32) + (local $l97 i32) + (local $l98 i32) + (local $l99 i32) + (local $l100 i32) + (local $l101 i32) + (local $l102 i32) + (local $l103 i32) + (local $l104 i32) + (local $l105 i32) + (local $l106 i32) + (local $l107 i32) + (local $l108 i32) + (local $l109 i32) + (local $l110 i32) + (local $l111 i32) + (local $l112 i32) + (local $l113 i32) + (local $l114 i32) + (local $l115 i32) + (local $l116 i32) + (local $l117 i32) + (local $l118 i32) + (local $l119 i32) + (local $l120 i32) + (local $l121 i32) + (local $l122 i32) + (local $l123 i32) + (local $l124 i32) + (local $l125 i32) + (local $l126 i32) + (local $l127 i32) + (local $l128 i32) + (local $l129 i32) + (local $l130 i32) + (local $l131 i32) + (local $l132 i32) + (local $l133 i32) + (local $l134 i32) + (local $l135 i32) + (local $l136 i32) + (local $l137 i32) + (local $l138 i32) + (local $l139 i32) + (local $l140 i32) + (local $l141 i32) + (local $l142 i32) + (local $l143 i32) + (local $l144 i32) + (local $l145 i32) + (local $l146 i32) + (local $l147 i32) + (local $l148 i32) + (local $l149 i32) + (local $l150 i32) + (local $l151 i32) + (local $l152 i32) + (local $l153 i32) + (local $l154 i32) + (local $l155 i32) + (local $l156 i32) + (local $l157 i32) + (local $l158 i32) + (local $l159 i32) + (local $l160 i32) + (local $l161 i32) + (local $l162 i32) + (local $l163 i32) + (local $l164 i32) + (local $l165 i32) + (local $l166 i32) + (local $l167 i32) + (local $l168 i32) + (local $l169 i32) + (local $l170 i32) + (local $l171 i32) + (local $l172 i32) + (local $l173 i32) + (local $l174 i32) + (local $l175 i32) + (local $l176 i32) + (local $l177 i32) + (local $l178 i32) + (local $l179 i32) + (local $l180 i32) + (local $l181 i32) + (local $l182 i32) + (local $l183 i32) + (local $l184 i32) + (local $l185 i32) + (local $l186 i32) + (local $l187 i32) + (local $l188 i32) + (local $l189 i32) + (local $l190 i32) + (local $l191 i32) + (local $l192 i32) + (local $l193 i32) + (local $l194 i32) + (local $l195 i32) + (local $l196 i32) + (local $l197 i32) + (local $l198 i32) + (local $l199 i32) + (local $l200 i32) + (local $l201 i32) + (local $l202 i32) + (local $l203 i32) + (local $l204 i32) + (local $l205 i32) + (local $l206 i32) + (local $l207 i32) + (local $l208 i32) + (local $l209 i32) + (local $l210 i32) + (local $l211 i32) + (local $l212 i32) + (local $l213 i32) + (local $l214 i32) + (local $l215 i32) + (local $l216 i32) + (local $l217 i32) + (local $l218 i32) + (local $l219 i32) + (local $l220 i32) + (local $l221 i32) + (local $l222 i32) + (local $l223 i32) + (local $l224 i32) + (local $l225 i32) + (local $l226 i32) + (local $l227 i32) + (local $l228 i32) + (local $l229 i32) + (local $l230 i32) + (local $l231 i32) + (local $l232 i32) + (local $l233 i32) + (local $l234 i32) + (local $l235 i32) + (local $l236 i32) + (local $l237 i32) + (local $l238 i32) + (local $l239 i32) + (local $l240 i32) + (local $l241 i32) + (local $l242 i32) + (local $l243 i32) + (local $l244 i32) + (local $l245 i32) + (local $l246 i32) + (local $l247 i32) + (local $l248 i32) + (local $l249 i32) + (local $l250 i32) + (local $l251 i32) + (local $l252 i32) + (local $l253 i32) + (local $l254 i32) + (local $l255 i32) + (local $l256 i32) + (local $l257 i32) + (local $l258 i32) + (local $l259 i32) + (local $l260 i32) + (local $l261 i32) + (local $l262 i32) + (local $l263 i32) + (local $l264 i32) + (local $l265 i32) + (local $l266 i32) + (local $l267 i32) + (local $l268 i32) + (local $l269 i32) + (local $l270 i32) + (local $l271 i32) + (local $l272 i32) + (local $l273 i32) + (local $l274 i32) + (local $l275 i32) + (local $l276 i32) + (local $l277 i32) + (local $l278 i32) + (local $l279 i32) + (local $l280 i32) + (local $l281 i32) + (local $l282 i32) + (local $l283 i32) + (local $l284 i32) + (local $l285 i32) + (local $l286 i32) + (local $l287 i32) + (local $l288 i32) + (local $l289 i32) + (local $l290 i32) + (local $l291 i32) + (local $l292 i32) + (local $l293 i32) + (local $l294 i32) + (local $l295 i32) + (local $l296 i32) + (local $l297 i32) + (local $l298 i32) + (local $l299 i32) + (local $l300 i32) + (local $l301 i32) + (local $l302 i32) + (local $l303 i32) + (local $l304 i32) + (local $l305 i32) + (local $l306 i32) + (local $l307 i32) + (local $l308 i32) + (local $l309 i32) + (local $l310 i32) + (local $l311 i32) + (local $l312 i32) + (local $l313 i32) + (local $l314 i32) + (local $l315 i32) + (local $l316 i32) + (local $l317 i32) + (local $l318 i32) + (local $l319 i32) + (local $l320 i32) + (local $l321 i32) + (local $l322 i32) + (local $l323 i32) + (local $l324 i32) + (local $l325 i32) + (local $l326 i32) + (local $l327 i32) + (local $l328 i32) + (local $l329 i32) + (local $l330 i32) + (local $l331 i32) + (local $l332 i32) + (local $l333 i32) + (local $l334 i32) + (local $l335 i32) + (local $l336 i32) + (local $l337 i32) + (local $l338 i32) + (local $l339 i32) + (local $l340 i32) + (local $l341 i32) + (local $l342 i32) + (local $l343 i32) + (local $l344 i32) + (local $l345 i32) + (local $l346 i32) + (local $l347 i32) + (local $l348 i32) + (local $l349 i32) + (local $l350 i32) + (local $l351 i32) + (local $l352 i32) + (local $l353 i32) + (local $l354 i32) + (local $l355 i32) + (local $l356 i32) + (local $l357 i32) + (local $l358 i32) + (local $l359 i32) + (local $l360 i32) + (local $l361 i32) + (local $l362 i32) + (local $l363 i32) + (local $l364 i32) + (local $l365 i32) + (local $l366 i32) + (local $l367 i32) + (local $l368 i32) + (local $l369 i32) + (local $l370 i32) + (local $l371 i32) + (local $l372 i32) + (local $l373 i32) + (local $l374 i32) + (local $l375 i32) + (local $l376 i32) + (local $l377 i32) + (local $l378 i32) + (local $l379 i32) + (local $l380 i32) + (local $l381 i32) + (local $l382 i32) + (local $l383 i32) + (local $l384 i32) + (local $l385 i32) + (local $l386 i32) + (local $l387 i32) + (local $l388 i32) + (local $l389 i32) + (local $l390 i32) + (local $l391 i32) + (local $l392 i32) + (local $l393 i32) + (local $l394 i32) + (local $l395 i32) + (local $l396 i32) + (local $l397 i32) + (local $l398 i32) + (local $l399 i32) + (local $l400 i32) + (local $l401 i32) + (local $l402 i32) + (local $l403 i32) + (local $l404 i32) + (local $l405 i32) + (local $l406 i32) + (local $l407 i32) + (local $l408 i32) + (local $l409 i32) + (local $l410 i32) + (local $l411 i32) + (local $l412 i32) + (local $l413 i32) + (local $l414 i32) + (local $l415 i32) + (local $l416 i32) + (local $l417 i32) + (local $l418 i32) + (local $l419 i32) + (local $l420 i32) + (local $l421 i32) + (local $l422 i32) + (local $l423 i32) + (local $l424 i32) + (local $l425 i32) + (local $l426 i32) + (local $l427 i32) + (local $l428 i32) + (local $l429 i32) + (local $l430 i32) + (local $l431 i32) + (local $l432 i32) + (local $l433 i32) + (local $l434 i32) + (local $l435 i32) + (local $l436 i32) + (local $l437 i32) + (local $l438 i32) + (local $l439 i32) + (local $l440 i32) + (local $l441 i32) + (local $l442 i32) + (local $l443 i32) + (local $l444 i32) + (local $l445 i32) + (local $l446 i32) + (local $l447 i32) + (local $l448 i32) + (local $l449 i32) + (local $l450 i32) + (local $l451 i32) + (local $l452 i32) + (local $l453 i32) + (local $l454 i32) + (local $l455 i32) + (local $l456 i32) + (local $l457 i32) + (local $l458 i32) + (local $l459 i32) + (local $l460 i32) + (local $l461 i32) + (local $l462 i32) + (local $l463 i32) + (local $l464 i32) + (local $l465 i32) + (local $l466 i32) + (local $l467 i32) + (local $l468 i32) + (local $l469 i32) + (local $l470 i32) + (local $l471 i32) + (local $l472 i32) + (local $l473 i32) + (local $l474 i32) + (local $l475 i32) + (local $l476 i32) + (local $l477 i32) + (local $l478 i32) + (local $l479 i32) + (local $l480 i32) + (local $l481 i32) + (local $l482 i32) + (local $l483 i32) + (local $l484 i32) + (local $l485 i32) + (local $l486 i32) + (local $l487 i32) + (local $l488 i32) + (local $l489 i32) + (local $l490 i32) + (local $l491 i32) + (local $l492 i32) + (local $l493 i32) + (local $l494 i32) + (local $l495 i32) + (local $l496 i32) + (local $l497 i32) + (local $l498 i32) + (local $l499 i32) + (local $l500 i32) + (local $l501 i32) + (local $l502 i32) + (local $l503 i32) + (local $l504 i32) + (local $l505 i32) + (local $l506 i32) + (local $l507 i32) + (local $l508 i32) + (local $l509 i32) + (local $l510 i32) + (local $l511 i32) + (local $l512 i32) + (local $l513 i32) + (local $l514 i32) + (local $l515 i32) + (local $l516 i32) + (local $l517 i32) + (local $l518 i32) + (local $l519 i32) + (local $l520 i32) + (local $l521 i32) + (local $l522 i32) + (local $l523 i32) + (local $l524 i32) + (local $l525 i32) + (local $l526 i32) + (local $l527 i32) + (local $l528 i32) + (local $l529 i32) + (local $l530 i32) + (local $l531 i32) + (local $l532 i32) + (local $l533 i32) + (local $l534 i32) + (local $l535 i32) + (local $l536 i32) + (local $l537 i32) + (local $l538 i32) + (local $l539 i32) + (local $l540 i32) + (local $l541 i32) + (local $l542 i32) + (local $l543 i32) + (local $l544 i32) + (local $l545 i32) + (local $l546 i32) + (local $l547 i32) + (local $l548 i32) + (local $l549 i32) + (local $l550 i32) + (local $l551 i32) + (local $l552 i32) + (local $l553 i32) + (local $l554 i32) + (local $l555 i32) + (local $l556 i32) + (local $l557 i32) + (local $l558 i32) + (local $l559 i32) + (local $l560 i32) + (local $l561 i32) + (local $l562 i32) + (local $l563 i32) + (local $l564 i32) + (local $l565 i32) + (local $l566 i32) + (local $l567 i32) + (local $l568 i32) + (local $l569 i32) + (local $l570 i32) + (local $l571 i32) + (local $l572 i32) + (local $l573 i32) + (local $l574 i32) + (local $l575 i32) + (local $l576 i32) + (local $l577 i32) + (local $l578 i32) + (local $l579 i32) + (local $l580 i32) + (local $l581 i32) + (local $l582 i32) + (local $l583 i32) + (local $l584 i32) + (local $l585 i32) + (local $l586 i32) + (local $l587 i32) + (local $l588 i32) + (local $l589 i32) + (local $l590 i32) + (local $l591 i32) + (local $l592 i32) + (local $l593 i32) + (local $l594 i32) + (local $l595 i32) + (local $l596 i32) + (local $l597 i32) + (local $l598 i32) + (local $l599 i32) + (local $l600 i32) + (local $l601 i32) + (local $l602 i32) + (local $l603 i32) + (local $l604 i32) + (local $l605 i32) + (local $l606 i32) + (local $l607 i32) + (local $l608 i32) + (local $l609 i32) + (local $l610 i32) + (local $l611 i32) + (local $l612 i32) + (local $l613 i32) + (local $l614 i32) + (local $l615 i32) + (local $l616 i32) + (local $l617 i32) + (local $l618 i32) + (local $l619 i32) + (local $l620 i32) + (local $l621 i32) + (local $l622 i32) + (local $l623 i32) + (local $l624 i32) + (local $l625 i32) + (local $l626 i32) + (local $l627 i32) + (local $l628 i32) + (local $l629 i32) + (local $l630 i32) + (local $l631 i32) + (local $l632 i32) + (local $l633 i32) + (local $l634 i32) + (local $l635 i32) + (local $l636 i32) + (local $l637 i32) + (local $l638 i32) + (local $l639 i32) + (local $l640 i32) + (local $l641 i32) + (local $l642 i32) + (local $l643 i32) + (local $l644 i32) + (local $l645 i32) + (local $l646 i32) + (local $l647 i32) + (local $l648 i32) + (local $l649 i32) + (local $l650 i32) + (local $l651 i32) + (local $l652 i32) + (local $l653 i32) + (local $l654 i32) + (local $l655 i32) + (local $l656 i32) + (local $l657 i32) + (local $l658 i32) + (local $l659 i32) + (local $l660 i32) + (local $l661 i32) + (local $l662 i32) + (local $l663 i32) + (local $l664 i32) + (local $l665 i32) + (local $l666 i32) + (local $l667 i32) + (local $l668 i32) + (local $l669 i32) + (local $l670 i32) + (local $l671 i32) + (local $l672 i32) + (local $l673 i32) + (local $l674 i32) + (local $l675 i32) + (local $l676 i32) + (local $l677 i32) + (local $l678 i32) + (local $l679 i32) + (local $l680 i32) + (local $l681 i32) + (local $l682 i32) + (local $l683 i32) + (local $l684 i32) + (local $l685 i32) + (local $l686 i32) + (local $l687 i32) + (local $l688 i32) + (local $l689 i32) + (local $l690 i32) + (local $l691 i32) + (local $l692 i32) + (local $l693 i32) + (local $l694 i32) + (local $l695 i32) + (local $l696 i32) + (local $l697 i32) + (local $l698 i32) + (local $l699 i32) + (local $l700 i32) + (local $l701 i32) + (local $l702 i32) + (local $l703 i32) + (local $l704 i32) + (local $l705 i32) + (local $l706 i32) + (local $l707 i32) + (local $l708 i32) + (local $l709 i32) + (local $l710 i32) + (local $l711 i32) + (local $l712 i32) + (local $l713 i32) + (local $l714 i32) + (local $l715 i32) + (local $l716 i32) + (local $l717 i32) + (local $l718 i32) + (local $l719 i32) + (local $l720 i32) + (local $l721 i32) + (local $l722 i32) + (local $l723 i32) + (local $l724 i32) + (local $l725 i32) + (local $l726 i32) + (local $l727 i32) + (local $l728 i32) + (local $l729 i32) + (local $l730 i32) + (local $l731 i32) + (local $l732 i32) + (local $l733 i32) + (local $l734 i32) + (local $l735 i32) + (local $l736 i32) + (local $l737 i32) + (local $l738 i32) + (local $l739 i32) + (local $l740 i32) + (local $l741 i32) + (local $l742 i32) + (local $l743 i32) + (local $l744 i32) + (local $l745 i32) + (local $l746 i32) + (local $l747 i32) + (local $l748 i32) + (local $l749 i32) + (local $l750 i32) + (local $l751 i32) + (local $l752 i32) + (local $l753 i32) + (local $l754 i32) + (local $l755 i32) + (local $l756 i32) + (local $l757 i32) + (local $l758 i32) + (local $l759 i32) + (local $l760 i32) + (local $l761 i32) + (local $l762 i32) + (local $l763 i32) + (local $l764 i32) + (local $l765 i32) + (local $l766 i32) + (local $l767 i32) + (local $l768 i32) + (local $l769 i32) + (local $l770 i32) + (local $l771 i32) + (local $l772 i32) + (local $l773 i32) + (local $l774 i32) + (local $l775 i32) + (local $l776 i32) + (local $l777 i32) + (local $l778 i32) + (local $l779 i32) + (local $l780 i32) + (local $l781 i32) + (local $l782 i32) + (local $l783 i32) + (local $l784 i32) + (local $l785 i32) + (local $l786 i32) + (local $l787 i32) + (local $l788 i32) + (local $l789 i32) + (local $l790 i32) + (local $l791 i32) + (local $l792 i32) + (local $l793 i32) + (local $l794 i32) + (local $l795 i32) + (local $l796 i32) + (local $l797 i32) + (local $l798 i32) + (local $l799 i32) + (local $l800 i32) + (local $l801 i32) + (local $l802 i32) + (local $l803 i32) + (local $l804 i32) + (local $l805 i32) + (local $l806 i32) + (local $l807 i32) + (local $l808 i32) + (local $l809 i32) + (local $l810 i32) + (local $l811 i32) + (local $l812 i32) + (local $l813 i32) + (local $l814 i32) + (local $l815 i32) + (local $l816 i32) + (local $l817 i32) + (local $l818 i32) + (local $l819 i32) + (local $l820 i32) + (local $l821 i32) + (local $l822 i32) + (local $l823 i32) + (local $l824 i32) + (local $l825 i32) + (local $l826 i32) + (local $l827 i32) + (local $l828 i32) + (local $l829 i32) + (local $l830 i32) + (local $l831 i32) + (local $l832 i32) + (local $l833 i32) + (local $l834 i32) + (local $l835 i32) + (local $l836 i32) + (local $l837 i32) + (local $l838 i32) + (local $l839 i32) + (local $l840 i32) + (local $l841 i32) + (local $l842 i32) + (local $l843 i32) + (local $l844 i32) + (local $l845 i32) + (local $l846 i32) + (local $l847 i32) + (local $l848 i32) + (local $l849 i32) + (local $l850 i32) + (local $l851 i32) + (local $l852 i32) + (local $l853 i32) + (local $l854 i32) + (local $l855 i32) + (local $l856 i32) + (local $l857 i32) + (local $l858 i32) + (local $l859 i32) + (local $l860 i32) + (local $l861 i32) + (local $l862 i32) + (local $l863 i32) + (local $l864 i32) + (local $l865 i32) + (local $l866 i32) + (local $l867 i32) + (local $l868 i32) + (local $l869 i32) + (local $l870 i32) + (local $l871 i32) + (local $l872 i32) + (local $l873 i32) + (local $l874 i32) + (local $l875 i32) + (local $l876 i32) + (local $l877 i32) + (local $l878 i32) + (local $l879 i32) + (local $l880 i32) + (local $l881 i32) + (local $l882 i32) + (local $l883 i32) + (local $l884 i32) + (local $l885 i32) + (local $l886 i32) + (local $l887 i32) + (local $l888 i32) + (local $l889 i32) + (local $l890 i32) + (local $l891 i32) + (local $l892 i32) + (local $l893 i32) + (local $l894 i32) + (local $l895 i32) + (local $l896 i32) + (local $l897 i32) + (local $l898 i32) + (local $l899 i32) + (local $l900 i32) + (local $l901 i32) + (local $l902 i32) + (local $l903 i32) + (local $l904 i32) + (local $l905 i32) + (local $l906 i32) + (local $l907 i32) + (local $l908 i32) + (local $l909 i32) + (local $l910 i32) + (local $l911 i32) + (local $l912 i32) + (local $l913 i32) + (local $l914 i32) + (local $l915 i32) + (local $l916 i32) + (local $l917 i32) + (local $l918 i32) + (local $l919 i32) + (local $l920 i32) + (local $l921 i32) + (local $l922 i32) + (local $l923 i32) + (local $l924 i32) + (local $l925 i32) + (local $l926 i32) + (local $l927 i32) + (local $l928 i32) + (local $l929 i32) + (local $l930 i32) + (local $l931 i32) + (local $l932 i32) + (local $l933 i32) + (local $l934 i32) + (local $l935 i32) + (local $l936 i32) + (local $l937 i32) + (local $l938 i32) + (local $l939 i32) + (local $l940 i32) + (local $l941 i32) + (local $l942 i32) + (local $l943 i32) + (local $l944 i32) + (local $l945 i32) + (local $l946 i32) + (local $l947 i32) + (local $l948 i32) + (local $l949 i32) + (local $l950 i32) + (local $l951 i32) + (local $l952 i32) + (local $l953 i32) + (local $l954 i32) + (local $l955 i32) + (local $l956 i32) + (local $l957 i32) + (local $l958 i32) + (local $l959 i32) + (local $l960 i32) + (local $l961 i32) + (local $l962 i32) + (local $l963 i32) + (local $l964 i32) + (local $l965 i32) + (local $l966 i32) + (local $l967 i32) + (local $l968 i32) + (local $l969 i32) + (local $l970 i32) + (local $l971 i32) + (local $l972 i32) + (local $l973 i32) + (local $l974 i32) + (local $l975 i32) + (local $l976 i32) + (local $l977 i32) + (local $l978 i32) + (local $l979 i32) + (local $l980 i32) + (local $l981 i32) + (local $l982 i32) + (local $l983 i32) + (local $l984 i32) + (local $l985 i32) + (local $l986 i32) + (local $l987 i32) + (local $l988 i32) + (local $l989 i32) + (local $l990 i32) + (local $l991 i32) + (local $l992 i32) + (local $l993 i32) + (local $l994 i32) + (local $l995 i32) + (local $l996 i32) + (local $l997 i32) + (local $l998 i32) + (local $l999 i32) + (local $l1000 i32) + (local $l1001 i32) + (local $l1002 i32) + (local $l1003 i32) + (local $l1004 i32) + (local $l1005 i32) + (local $l1006 i32) + (local $l1007 i32) + (local $l1008 i32) + (local $l1009 i32) + (local $l1010 i32) + (local $l1011 i32) + (local $l1012 i32) + (local $l1013 i32) + (local $l1014 i32) + (local $l1015 i32) + (local $l1016 i32) + (local $l1017 i32) + (local $l1018 i32) + (local $l1019 i32) + (local $l1020 i32) + (local $l1021 i32) + (local $l1022 i32) + (local $l1023 i32) + (local $l1024 i32) + (local $l1025 i32) + (local $l1026 i32) + (local $l1027 i32) + (local $l1028 i32) + (local $l1029 i32) + (local $l1030 i32) + (local $l1031 i32) + (local $l1032 i32) + (local $l1033 i32) + (local $l1034 i32) + (local $l1035 i32) + (local $l1036 i32) + (local $l1037 i32) + (local $l1038 i32) + (local $l1039 i32) + (local $l1040 i32) + (local $l1041 i32) + (local $l1042 i32) + (local $l1043 i32) + (local $l1044 i32) + (local $l1045 i32) + (local $l1046 i32) + (local $l1047 i32) + (local $l1048 i32) + (local $l1049 i32) + (local $l1050 i32) + (local $l1051 i32) + (local $l1052 i32) + (local $l1053 i32) + (local $l1054 i32) + (local $l1055 i32) + (local $l1056 i32) + (local $l1057 i32) + (local $l1058 i32) + (local $l1059 i32) + (local $l1060 i32) + (local $l1061 i32) + (local $l1062 i32) + (local $l1063 i32) + (local $l1064 i32) + (local $l1065 i32) + (local $l1066 i32) + (local $l1067 i32) + (local $l1068 i32) + (local $l1069 i32) + (local $l1070 i32) + (local $l1071 i32) + (local $l1072 i32) + (local $l1073 i32) + (local $l1074 i32) + (local $l1075 i32) + (local $l1076 i32) + (local $l1077 i32) + (local $l1078 i32) + (local $l1079 i32) + (local $l1080 i32) + (local $l1081 i32) + (local $l1082 i32) + (local $l1083 i32) + (local $l1084 i32) + (local $l1085 i32) + (local $l1086 i32) + (local $l1087 i32) + (local $l1088 i32) + (local $l1089 i32) + (local $l1090 i32) + (local $l1091 i32) + (local $l1092 i32) + (local $l1093 i32) + (local $l1094 i32) + (local $l1095 i32) + (local $l1096 i32) + (local $l1097 i32) + (local $l1098 i32) + (local $l1099 i32) + (local $l1100 i32) + (local $l1101 i32) + (local $l1102 i32) + (local $l1103 i32) + (local $l1104 i32) + (local $l1105 i32) + (local $l1106 i32) + (local $l1107 i32) + (local $l1108 i32) + (local $l1109 i32) + (local $l1110 i32) + (local $l1111 i32) + (local $l1112 i32) + (local $l1113 i32) + (local $l1114 i32) + (local $l1115 i32) + (local $l1116 i32) + (local $l1117 i32) + (local $l1118 i32) + (local $l1119 i32) + (local $l1120 i32) + (local $l1121 i32) + (local $l1122 i32) + (local $l1123 i32) + (local $l1124 i32) + (local $l1125 i32) + (local $l1126 i32) + (local $l1127 i32) + (local $l1128 i32) + (local $l1129 i32) + (local $l1130 i32) + (local $l1131 i32) + (local $l1132 i32) + (local $l1133 i32) + (local $l1134 i32) + (local $l1135 i32) + (local $l1136 i32) + (local $l1137 i32) + (local $l1138 i32) + (local $l1139 i32) + (local $l1140 i32) + (local $l1141 i32) + (local $l1142 i32) + (local $l1143 i32) + (local $l1144 i32) + (local $l1145 i32) + (local $l1146 i32) + (local $l1147 i32) + (local $l1148 i32) + (local $l1149 i32) + (local $l1150 i32) + (local $l1151 i32) + (local $l1152 i32) + (local $l1153 i32) + (local $l1154 i32) + (local $l1155 i32) + (local $l1156 i32) + (local $l1157 i32) + (local $l1158 i32) + (local $l1159 i32) + (local $l1160 i32) + (local $l1161 i32) + (local $l1162 i32) + (local $l1163 i32) + (local $l1164 i32) + (local $l1165 i32) + (local $l1166 i32) + (local $l1167 i32) + (local $l1168 i32) + (local $l1169 i32) + (local $l1170 i32) + (local $l1171 i32) + (local $l1172 i32) + (local $l1173 i32) + (local $l1174 i32) + (local $l1175 i32) + (local $l1176 i32) + (local $l1177 i32) + (local $l1178 i32) + (local $l1179 i32) + (local $l1180 i32) + (local $l1181 i32) + (local $l1182 i32) + (local $l1183 i32) + (local $l1184 i32) + (local $l1185 i32) + (local $l1186 i32) + (local $l1187 i32) + (local $l1188 i32) + (local $l1189 i32) + (local $l1190 i32) + (local $l1191 i32) + (local $l1192 i32) + (local $l1193 i32) + (local $l1194 i32) + (local $l1195 i32) + (local $l1196 i32) + (local $l1197 i32) + (local $l1198 i32) + (local $l1199 i32) + (local $l1200 i32) + (local $l1201 i32) + (local $l1202 i32) + (local $l1203 i32) + (local $l1204 i32) + (local $l1205 i32) + (local $l1206 i32) + (local $l1207 i32) + (local $l1208 i32) + (local $l1209 i32) + (local $l1210 i32) + (local $l1211 i32) + (local $l1212 i32) + (local $l1213 i32) + (local $l1214 i32) + (local $l1215 i32) + (local $l1216 i32) + (local $l1217 i32) + (local $l1218 i32) + (local $l1219 i32) + (local $l1220 i32) + (local $l1221 i32) + (local $l1222 i32) + (local $l1223 i32) + (local $l1224 i32) + (local $l1225 i32) + (local $l1226 i32) + (local $l1227 i32) + (local $l1228 i32) + (local $l1229 i32) + (local $l1230 i32) + (local $l1231 i32) + (local $l1232 i32) + (local $l1233 i32) + (local $l1234 i32) + (local $l1235 i32) + (local $l1236 i32) + (local $l1237 i32) + (local $l1238 i32) + (local $l1239 i32) + (local $l1240 i32) + (local $l1241 i32) + (local $l1242 i32) + (local $l1243 i32) + (local $l1244 i32) + (local $l1245 i32) + (local $l1246 i32) + (local $l1247 i32) + (local $l1248 i32) + (local $l1249 i32) + (local $l1250 i32) + (local $l1251 i32) + (local $l1252 i32) + (local $l1253 i32) + (local $l1254 i32) + (local $l1255 i32) + (local $l1256 i32) + (local $l1257 i32) + (local $l1258 i32) + (local $l1259 i32) + (local $l1260 i32) + (local $l1261 i32) + (local $l1262 i32) + (local $l1263 i32) + (local $l1264 i32) + (local $l1265 i32) + (local $l1266 i32) + (local $l1267 i32) + (local $l1268 i32) + (local $l1269 i32) + (local $l1270 i32) + (local $l1271 i32) + (local $l1272 i32) + (local $l1273 i32) + (local $l1274 i32) + (local $l1275 i32) + (local $l1276 i32) + (local $l1277 i32) + (local $l1278 i32) + (local $l1279 i32) + (local $l1280 i32) + (local $l1281 i32) + (local $l1282 i32) + (local $l1283 i32) + (local $l1284 i32) + (local $l1285 i32) + (local $l1286 i32) + (local $l1287 i32) + (local $l1288 i32) + (local $l1289 i32) + (local $l1290 i32) + (local $l1291 i32) + (local $l1292 i32) + (local $l1293 i32) + (local $l1294 i32) + (local $l1295 i32) + (local $l1296 i32) + (local $l1297 i32) + (local $l1298 i32) + (local $l1299 i32) + (local $l1300 i32) + (local $l1301 i32) + (local $l1302 i32) + (local $l1303 i32) + (local $l1304 i32) + (local $l1305 i32) + (local $l1306 i32) + (local $l1307 i32) + (local $l1308 i32) + (local $l1309 i32) + (local $l1310 i32) + (local $l1311 i32) + (local $l1312 i32) + (local $l1313 i32) + (local $l1314 i32) + (local $l1315 i32) + (local $l1316 i32) + (local $l1317 i32) + (local $l1318 i32) + (local $l1319 i32) + (local $l1320 i32) + (local $l1321 i32) + (local $l1322 i32) + (local $l1323 i32) + (local $l1324 i32) + (local $l1325 i32) + (local $l1326 i32) + (local $l1327 i32) + (local $l1328 i32) + (local $l1329 i32) + (local $l1330 i32) + (local $l1331 i32) + (local $l1332 i32) + (local $l1333 i32) + (local $l1334 i32) + (local $l1335 i32) + (local $l1336 i32) + (local $l1337 i32) + (local $l1338 i32) + (local $l1339 i32) + (local $l1340 i32) + (local $l1341 i32) + (local $l1342 i32) + (local $l1343 i32) + (local $l1344 i32) + (local $l1345 i32) + (local $l1346 i32) + (local $l1347 i32) + (local $l1348 i32) + (local $l1349 i32) + (local $l1350 i32) + (local $l1351 i32) + (local $l1352 i32) + (local $l1353 i32) + (local $l1354 i32) + (local $l1355 i32) + (local $l1356 i32) + (local $l1357 i32) + (local $l1358 i32) + (local $l1359 i32) + (local $l1360 i32) + (local $l1361 i32) + (local $l1362 i32) + (local $l1363 i32) + (local $l1364 i32) + (local $l1365 i32) + (local $l1366 i32) + (local $l1367 i32) + (local $l1368 i32) + (local $l1369 i32) + (local $l1370 i32) + (local $l1371 i32) + (local $l1372 i32) + (local $l1373 i32) + (local $l1374 i32) + (local $l1375 i32) + (local $l1376 i32) + (local $l1377 i32) + (local $l1378 i32) + (local $l1379 i32) + (local $l1380 i32) + (local $l1381 i32) + (local $l1382 i32) + (local $l1383 i32) + (local $l1384 i32) + (local $l1385 i32) + (local $l1386 i32) + (local $l1387 i32) + (local $l1388 i32) + (local $l1389 i32) + (local $l1390 i32) + (local $l1391 i32) + (local $l1392 i32) + (local $l1393 i32) + (local $l1394 i32) + (local $l1395 i32) + (local $l1396 i32) + (local $l1397 i32) + (local $l1398 i32) + (local $l1399 i32) + (local $l1400 i32) + (local $l1401 i32) + (local $l1402 i32) + (local $l1403 i32) + (local $l1404 i32) + (local $l1405 i32) + (local $l1406 i32) + (local $l1407 i32) + (local $l1408 i32) + (local $l1409 i32) + (local $l1410 i32) + (local $l1411 i32) + (local $l1412 i32) + (local $l1413 i32) + (local $l1414 i32) + (local $l1415 i32) + (local $l1416 i32) + (local $l1417 i32) + (local $l1418 i32) + (local $l1419 i32) + (local $l1420 i32) + (local $l1421 i32) + (local $l1422 i32) + (local $l1423 i32) + (local $l1424 i32) + (local $l1425 i32) + (local $l1426 i32) + (local $l1427 i32) + (local $l1428 i32) + (local $l1429 i32) + (local $l1430 i32) + (local $l1431 i32) + (local $l1432 i32) + (local $l1433 i32) + (local $l1434 i32) + (local $l1435 i32) + (local $l1436 i32) + (local $l1437 i32) + (local $l1438 i32) + (local $l1439 i32) + (local $l1440 i32) + (local $l1441 i32) + (local $l1442 i32) + (local $l1443 i32) + (local $l1444 i32) + (local $l1445 i32) + (local $l1446 i32) + (local $l1447 i32) + (local $l1448 i32) + (local $l1449 i32) + (local $l1450 i32) + (local $l1451 i32) + (local $l1452 i32) + (local $l1453 i32) + (local $l1454 i32) + (local $l1455 i32) + (local $l1456 i32) + (local $l1457 i32) + (local $l1458 i32) + (local $l1459 i32) + (local $l1460 i32) + (local $l1461 i32) + (local $l1462 i32) + (local $l1463 i32) + (local $l1464 i32) + (local $l1465 i32) + (local $l1466 i32) + (local $l1467 i32) + (local $l1468 i32) + (local $l1469 i32) + (local $l1470 i32) + (local $l1471 i32) + (local $l1472 i32) + (local $l1473 i32) + (local $l1474 i32) + (local $l1475 i32) + (local $l1476 i32) + (local $l1477 i32) + (local $l1478 i32) + (local $l1479 i32) + (local $l1480 i32) + (local $l1481 i32) + (local $l1482 i32) + (local $l1483 i32) + (local $l1484 i32) + (local $l1485 i32) + (local $l1486 i32) + (local $l1487 i32) + (local $l1488 i32) + (local $l1489 i32) + (local $l1490 i32) + (local $l1491 i32) + (local $l1492 i32) + (local $l1493 i32) + (local $l1494 i32) + (local $l1495 i32) + (local $l1496 i32) + (local $l1497 i32) + (local $l1498 i32) + (local $l1499 i32) + (local $l1500 i32) + (local $l1501 i32) + (local $l1502 i32) + (local $l1503 i32) + (local $l1504 i32) + (local $l1505 i32) + (local $l1506 i32) + (local $l1507 i32) + (local $l1508 i32) + (local $l1509 i32) + (local $l1510 i32) + (local $l1511 i32) + (local $l1512 i32) + (local $l1513 i32) + (local $l1514 i32) + (local $l1515 i32) + (local $l1516 i32) + (local $l1517 i32) + (local $l1518 i32) + (local $l1519 i32) + (local $l1520 i32) + (local $l1521 i32) + (local $l1522 i32) + (local $l1523 i32) + (local $l1524 i32) + (local $l1525 i32) + (local $l1526 i32) + (local $l1527 i32) + (local $l1528 i32) + (local $l1529 i32) + (local $l1530 i32) + (local $l1531 i32) + (local $l1532 i32) + (local $l1533 i32) + (local $l1534 i32) + (local $l1535 i32) + (local $l1536 i32) + (local $l1537 i32) + (local $l1538 i32) + (local $l1539 i32) + (local $l1540 i32) + (local $l1541 i32) + (local $l1542 i32) + (local $l1543 i32) + (local $l1544 i32) + (local $l1545 i32) + (local $l1546 i32) + (local $l1547 i32) + (local $l1548 i32) + (local $l1549 i32) + (local $l1550 i32) + (local $l1551 i32) + (local $l1552 i32) + (local $l1553 i32) + (local $l1554 i32) + (local $l1555 i32) + (local $l1556 i32) + (local $l1557 i32) + (local $l1558 i32) + (local $l1559 i32) + (local $l1560 i32) + (local $l1561 i32) + (local $l1562 i32) + (local $l1563 i32) + (local $l1564 i32) + (local $l1565 i32) + (local $l1566 i32) + (local $l1567 i32) + (local $l1568 i32) + (local $l1569 i32) + (local $l1570 i32) + (local $l1571 i32) + (local $l1572 i32) + (local $l1573 i32) + (local $l1574 i32) + (local $l1575 i32) + (local $l1576 i32) + (local $l1577 i32) + (local $l1578 i32) + (local $l1579 i32) + (local $l1580 i32) + (local $l1581 i32) + (local $l1582 i32) + (local $l1583 i32) + (local $l1584 i32) + (local $l1585 i32) + (local $l1586 i32) + (local $l1587 i32) + (local $l1588 i32) + (local $l1589 i32) + (local $l1590 i32) + (local $l1591 i32) + (local $l1592 i32) + (local $l1593 i32) + (local $l1594 i32) + (local $l1595 i32) + (local $l1596 i32) + (local $l1597 i32) + (local $l1598 i32) + (local $l1599 i32) + (local $l1600 i32) + (local $l1601 i32) + (local $l1602 i32) + (local $l1603 i32) + (local $l1604 i32) + (local $l1605 i32) + (local $l1606 i32) + (local $l1607 i32) + (local $l1608 i32) + (local $l1609 i32) + (local $l1610 i32) + (local $l1611 i32) + (local $l1612 i32) + (local $l1613 i32) + (local $l1614 i32) + (local $l1615 i32) + (local $l1616 i32) + (local $l1617 i32) + (local $l1618 i32) + (local $l1619 i32) + (local $l1620 i32) + (local $l1621 i32) + (local $l1622 i32) + (local $l1623 i32) + (local $l1624 i32) + (local $l1625 i32) + (local $l1626 i32) + (local $l1627 i32) + (local $l1628 i32) + (local $l1629 i32) + (local $l1630 i32) + (local $l1631 i32) + (local $l1632 i32) + (local $l1633 i32) + (local $l1634 i32) + (local $l1635 i32) + (local $l1636 i32) + (local $l1637 i32) + (local $l1638 i32) + (local $l1639 i32) + (local $l1640 i32) + (local $l1641 i32) + (local $l1642 i32) + (local $l1643 i32) + (local $l1644 i32) + (local $l1645 i32) + (local $l1646 i32) + (local $l1647 i32) + (local $l1648 i32) + (local $l1649 i32) + (local $l1650 i32) + (local $l1651 i32) + (local $l1652 i32) + (local $l1653 i32) + (local $l1654 i32) + (local $l1655 i32) + (local $l1656 i32) + (local $l1657 i32) + (local $l1658 i32) + (local $l1659 i32) + (local $l1660 i32) + (local $l1661 i32) + (local $l1662 i32) + (local $l1663 i32) + (local $l1664 i32) + (local $l1665 i32) + (local $l1666 i32) + (local $l1667 i32) + (local $l1668 i32) + (local $l1669 i32) + (local $l1670 i32) + (local $l1671 i32) + (local $l1672 i32) + (local $l1673 i32) + (local $l1674 i32) + (local $l1675 i32) + (local $l1676 i32) + (local $l1677 i32) + (local $l1678 i32) + (local $l1679 i32) + (local $l1680 i32) + (local $l1681 i32) + (local $l1682 i32) + (local $l1683 i32) + (local $l1684 i32) + (local $l1685 i32) + (local $l1686 i32) + (local $l1687 i32) + (local $l1688 i32) + (local $l1689 i32) + (local $l1690 i32) + (local $l1691 i32) + (local $l1692 i32) + (local $l1693 i32) + (local $l1694 i32) + (local $l1695 i32) + (local $l1696 i32) + (local $l1697 i32) + (local $l1698 i32) + (local $l1699 i32) + (local $l1700 i32) + (local $l1701 i32) + (local $l1702 i32) + (local $l1703 i32) + (local $l1704 i32) + (local $l1705 i32) + (local $l1706 i32) + (local $l1707 i32) + (local $l1708 i32) + (local $l1709 i32) + (local $l1710 i32) + (local $l1711 i32) + (local $l1712 i32) + (local $l1713 i32) + (local $l1714 i32) + (local $l1715 i32) + (local $l1716 i32) + (local $l1717 i32) + (local $l1718 i32) + (local $l1719 i32) + (local $l1720 i32) + (local $l1721 i32) + (local $l1722 i32) + (local $l1723 i32) + (local $l1724 i32) + (local $l1725 i32) + (local $l1726 i32) + (local $l1727 i32) + (local $l1728 i32) + (local $l1729 i32) + (local $l1730 i32) + (local $l1731 i32) + (local $l1732 i32) + (local $l1733 i32) + (local $l1734 i32) + (local $l1735 i32) + (local $l1736 i32) + (local $l1737 i32) + (local $l1738 i32) + (local $l1739 i32) + (local $l1740 i32) + (local $l1741 i32) + (local $l1742 i32) + (local $l1743 i32) + (local $l1744 i32) + (local $l1745 i32) + (local $l1746 i32) + (local $l1747 i32) + (local $l1748 i32) + (local $l1749 i32) + (local $l1750 i32) + (local $l1751 i32) + (local $l1752 i32) + (local $l1753 i32) + (local $l1754 i32) + (local $l1755 i32) + (local $l1756 i32) + (local $l1757 i32) + (local $l1758 i32) + (local $l1759 i32) + (local $l1760 i32) + (local $l1761 i32) + (local $l1762 i32) + (local $l1763 i32) + (local $l1764 i32) + (local $l1765 i32) + (local $l1766 i32) + (local $l1767 i32) + (local $l1768 i32) + (local $l1769 i32) + (local $l1770 i32) + (local $l1771 i32) + (local $l1772 i32) + (local $l1773 i32) + (local $l1774 i32) + (local $l1775 i32) + (local $l1776 i32) + (local $l1777 i32) + (local $l1778 i32) + (local $l1779 i32) + (local $l1780 i32) + (local $l1781 i32) + (local $l1782 i32) + (local $l1783 i32) + (local $l1784 i32) + (local $l1785 i32) + (local $l1786 i32) + (local $l1787 i32) + (local $l1788 i32) + (local $l1789 i32) + (local $l1790 i32) + (local $l1791 i32) + (local $l1792 i32) + (local $l1793 i32) + (local $l1794 i32) + (local $l1795 i32) + (local $l1796 i32) + (local $l1797 i32) + (local $l1798 i32) + (local $l1799 i32) + (local $l1800 i32) + (local $l1801 i32) + (local $l1802 i32) + (local $l1803 i32) + (local $l1804 i32) + (local $l1805 i32) + (local $l1806 i32) + (local $l1807 i32) + (local $l1808 i32) + (local $l1809 i32) + (local $l1810 i32) + (local $l1811 i32) + (local $l1812 i32) + (local $l1813 i32) + (local $l1814 i32) + (local $l1815 i32) + (local $l1816 i32) + (local $l1817 i32) + (local $l1818 i32) + (local $l1819 i32) + (local $l1820 i32) + (local $l1821 i32) + (local $l1822 i32) + (local $l1823 i32) + (local $l1824 i32) + (local $l1825 i32) + (local $l1826 i32) + (local $l1827 i32) + (local $l1828 i32) + (local $l1829 i32) + (local $l1830 i32) + (local $l1831 i32) + (local $l1832 i32) + (local $l1833 i32) + (local $l1834 i32) + (local $l1835 i32) + (local $l1836 i32) + (local $l1837 i32) + (local $l1838 i32) + (local $l1839 i32) + (local $l1840 i32) + (local $l1841 i32) + (local $l1842 i32) + (local $l1843 i32) + (local $l1844 i32) + (local $l1845 i32) + (local $l1846 i32) + (local $l1847 i32) + (local $l1848 i32) + (local $l1849 i32) + (local $l1850 i32) + (local $l1851 i32) + (local $l1852 i32) + (local $l1853 i32) + (local $l1854 i32) + (local $l1855 i32) + (local $l1856 i32) + (local $l1857 i32) + (local $l1858 i32) + (local $l1859 i32) + (local $l1860 i32) + (local $l1861 i32) + (local $l1862 i32) + (local $l1863 i32) + (local $l1864 i32) + (local $l1865 i32) + (local $l1866 i32) + (local $l1867 i32) + (local $l1868 i32) + (local $l1869 i32) + (local $l1870 i32) + (local $l1871 i32) + (local $l1872 i32) + (local $l1873 i32) + (local $l1874 i32) + (local $l1875 i32) + (local $l1876 i32) + (local $l1877 i32) + (local $l1878 i32) + (local $l1879 i32) + (local $l1880 i32) + (local $l1881 i32) + (local $l1882 i32) + (local $l1883 i32) + (local $l1884 i32) + (local $l1885 i32) + (local $l1886 i32) + (local $l1887 i32) + (local $l1888 i32) + (local $l1889 i32) + (local $l1890 i32) + (local $l1891 i32) + (local $l1892 i32) + (local $l1893 i32) + (local $l1894 i32) + (local $l1895 i32) + (local $l1896 i32) + (local $l1897 i32) + (local $l1898 i32) + (local $l1899 i32) + (local $l1900 i32) + (local $l1901 i32) + (local $l1902 i32) + (local $l1903 i32) + (local $l1904 i32) + (local $l1905 i32) + (local $l1906 i32) + (local $l1907 i32) + (local $l1908 i32) + (local $l1909 i32) + (local $l1910 i32) + (local $l1911 i32) + (local $l1912 i32) + (local $l1913 i32) + (local $l1914 i32) + (local $l1915 i32) + (local $l1916 i32) + (local $l1917 i32) + (local $l1918 i32) + (local $l1919 i32) + (local $l1920 i32) + (local $l1921 i32) + (local $l1922 i32) + (local $l1923 i32) + (local $l1924 i32) + (local $l1925 i32) + (local $l1926 i32) + (local $l1927 i32) + (local $l1928 i32) + (local $l1929 i32) + (local $l1930 i32) + (local $l1931 i32) + (local $l1932 i32) + (local $l1933 i32) + (local $l1934 i32) + (local $l1935 i32) + (local $l1936 i32) + (local $l1937 i32) + (local $l1938 i32) + (local $l1939 i32) + (local $l1940 i32) + (local $l1941 i32) + (local $l1942 i32) + (local $l1943 i32) + (local $l1944 i32) + (local $l1945 i32) + (local $l1946 i32) + (local $l1947 i32) + (local $l1948 i32) + (local $l1949 i32) + (local $l1950 i32) + (local $l1951 i32) + (local $l1952 i32) + (local $l1953 i32) + (local $l1954 i32) + (local $l1955 i32) + (local $l1956 i32) + (local $l1957 i32) + (local $l1958 i32) + (local $l1959 i32) + (local $l1960 i32) + (local $l1961 i32) + (local $l1962 i32) + (local $l1963 i32) + (local $l1964 i32) + (local $l1965 i32) + (local $l1966 i32) + (local $l1967 i32) + (local $l1968 i32) + (local $l1969 i32) + (local $l1970 i32) + (local $l1971 i32) + (local $l1972 i32) + (local $l1973 i32) + (local $l1974 i32) + (local $l1975 i32) + (local $l1976 i32) + (local $l1977 i32) + (local $l1978 i32) + (local $l1979 i32) + (local $l1980 i32) + (local $l1981 i32) + (local $l1982 i32) + (local $l1983 i32) + (local $l1984 i32) + (local $l1985 i32) + (local $l1986 i32) + (local $l1987 i32) + (local $l1988 i32) + (local $l1989 i32) + (local $l1990 i32) + (local $l1991 i32) + (local $l1992 i32) + (local $l1993 i32) + (local $l1994 i32) + (local $l1995 i32) + (local $l1996 i32) + (local $l1997 i32) + (local $l1998 i32) + (local $l1999 i32) + (local $l2000 i32) + (local $l2001 i32) + (local $l2002 i32) + (local $l2003 i32) + (local $l2004 i32) + (local $l2005 i32) + (local $l2006 i32) + (local $l2007 i32) + (local $l2008 i32) + (local $l2009 i32) + (local $l2010 i32) + (local $l2011 i32) + (local $l2012 i32) + (local $l2013 i32) + (local $l2014 i32) + (local $l2015 i32) + (local $l2016 i32) + (local $l2017 i32) + (local $l2018 i32) + (local $l2019 i32) + (local $l2020 i32) + (local $l2021 i32) + (local $l2022 i32) + (local $l2023 i32) + (local $l2024 i32) + (local $l2025 i32) + (local $l2026 i32) + (local $l2027 i32) + (local $l2028 i32) + (local $l2029 i32) + (local $l2030 i32) + (local $l2031 i32) + (local $l2032 i32) + (local $l2033 i32) + (local $l2034 i32) + (local $l2035 i32) + (local $l2036 i32) + (local $l2037 i32) + (local $l2038 i32) + (local $l2039 i32) + (local $l2040 i32) + (local $l2041 i32) + (local $l2042 i32) + (local $l2043 i32) + (local $l2044 i32) + (local $l2045 i32) + (local $l2046 i32) + (local $l2047 i32) + (local $l2048 i32) + (local $l2049 i32) + (local $l2050 i32) + (local $l2051 i32) + (local $l2052 i32) + (local $l2053 i32) + (local $l2054 i32) + (local $l2055 i32) + (local $l2056 i32) + (local $l2057 i32) + (local $l2058 i32) + (local $l2059 i32) + (local $l2060 i32) + (local $l2061 i32) + (local $l2062 i32) + (local $l2063 i32) + (local $l2064 i32) + (local $l2065 i32) + (local $l2066 i32) + (local $l2067 i32) + (local $l2068 i32) + (local $l2069 i32) + (local $l2070 i32) + (local $l2071 i32) + (local $l2072 i32) + (local $l2073 i32) + (local $l2074 i32) + (local $l2075 i32) + (local $l2076 i32) + (local $l2077 i32) + (local $l2078 i32) + (local $l2079 i32) + (local $l2080 i32) + (local $l2081 i32) + (local $l2082 i32) + (local $l2083 i32) + (local $l2084 i32) + (local $l2085 i32) + (local $l2086 i32) + (local $l2087 i32) + (local $l2088 i32) + (local $l2089 i32) + (local $l2090 i32) + (local $l2091 i32) + (local $l2092 i32) + (local $l2093 i32) + (local $l2094 i32) + (local $l2095 i32) + (local $l2096 i32) + (local $l2097 i32) + (local $l2098 i32) + (local $l2099 i32) + (local $l2100 i32) + (local $l2101 i32) + (local $l2102 i32) + (local $l2103 i32) + (local $l2104 i32) + (local $l2105 i32) + (local $l2106 i32) + (local $l2107 i32) + (local $l2108 i32) + (local $l2109 i32) + (local $l2110 i32) + (local $l2111 i32) + (local $l2112 i32) + (local $l2113 i32) + (local $l2114 i32) + (local $l2115 i32) + (local $l2116 i32) + (local $l2117 i32) + (local $l2118 i32) + (local $l2119 i32) + (local $l2120 i32) + (local $l2121 i32) + (local $l2122 i32) + (local $l2123 i32) + (local $l2124 i32) + (local $l2125 i32) + (local $l2126 i32) + (local $l2127 i32) + (local $l2128 i32) + (local $l2129 i32) + (local $l2130 i32) + (local $l2131 i32) + (local $l2132 i32) + (local $l2133 i32) + (local $l2134 i32) + (local $l2135 i32) + (local $l2136 i32) + (local $l2137 i32) + (local $l2138 i32) + (local $l2139 i32) + (local $l2140 i32) + (local $l2141 i32) + (local $l2142 i32) + (local $l2143 i32) + (local $l2144 i32) + (local $l2145 i32) + (local $l2146 i32) + (local $l2147 i32) + (local $l2148 i32) + (local $l2149 i32) + (local $l2150 i32) + (local $l2151 i32) + (local $l2152 i32) + (local $l2153 i32) + (local $l2154 i32) + (local $l2155 i32) + (local $l2156 i32) + (local $l2157 i32) + (local $l2158 i32) + (local $l2159 i32) + (local $l2160 i32) + (local $l2161 i32) + (local $l2162 i32) + (local $l2163 i32) + (local $l2164 i32) + (local $l2165 i32) + (local $l2166 i32) + (local $l2167 i32) + (local $l2168 i32) + (local $l2169 i32) + (local $l2170 i32) + (local $l2171 i32) + (local $l2172 i32) + (local $l2173 i32) + (local $l2174 i32) + (local $l2175 i32) + (local $l2176 i32) + (local $l2177 i32) + (local $l2178 i32) + (local $l2179 i32) + (local $l2180 i32) + (local $l2181 i32) + (local $l2182 i32) + (local $l2183 i32) + (local $l2184 i32) + (local $l2185 i32) + (local $l2186 i32) + (local $l2187 i32) + (local $l2188 i32) + (local $l2189 i32) + (local $l2190 i32) + (local $l2191 i32) + (local $l2192 i32) + (local $l2193 i32) + (local $l2194 i32) + (local $l2195 i32) + (local $l2196 i32) + (local $l2197 i32) + (local $l2198 i32) + (local $l2199 i32) + (local $l2200 i32) + (local $l2201 i32) + (local $l2202 i32) + (local $l2203 i32) + (local $l2204 i32) + (local $l2205 i32) + (local $l2206 i32) + (local $l2207 i32) + (local $l2208 i32) + (local $l2209 i32) + (local $l2210 i32) + (local $l2211 i32) + (local $l2212 i32) + (local $l2213 i32) + (local $l2214 i32) + (local $l2215 i32) + (local $l2216 i32) + (local $l2217 i32) + (local $l2218 i32) + (local $l2219 i32) + (local $l2220 i32) + (local $l2221 i32) + (local $l2222 i32) + (local $l2223 i32) + (local $l2224 i32) + (local $l2225 i32) + (local $l2226 i32) + (local $l2227 i32) + (local $l2228 i32) + (local $l2229 i32) + (local $l2230 i32) + (local $l2231 i32) + (local $l2232 i32) + (local $l2233 i32) + (local $l2234 i32) + (local $l2235 i32) + (local $l2236 i32) + (local $l2237 i32) + (local $l2238 i32) + (local $l2239 i32) + (local $l2240 i32) + (local $l2241 i32) + (local $l2242 i32) + (local $l2243 i32) + (local $l2244 i32) + (local $l2245 i32) + (local $l2246 i32) + (local $l2247 i32) + (local $l2248 i32) + (local $l2249 i32) + (local $l2250 i32) + (local $l2251 i32) + (local $l2252 i32) + (local $l2253 i32) + (local $l2254 i32) + (local $l2255 i32) + (local $l2256 i32) + (local $l2257 i32) + (local $l2258 i32) + (local $l2259 i32) + (local $l2260 i32) + (local $l2261 i32) + (local $l2262 i32) + (local $l2263 i32) + (local $l2264 i32) + (local $l2265 i32) + (local $l2266 i32) + (local $l2267 i32) + (local $l2268 i32) + (local $l2269 i32) + (local $l2270 i32) + (local $l2271 i32) + (local $l2272 i32) + (local $l2273 i32) + (local $l2274 i32) + (local $l2275 i32) + (local $l2276 i32) + (local $l2277 i32) + (local $l2278 i32) + (local $l2279 i32) + (local $l2280 i32) + (local $l2281 i32) + (local $l2282 i32) + (local $l2283 i32) + (local $l2284 i32) + (local $l2285 i32) + (local $l2286 i32) + (local $l2287 i32) + (local $l2288 i32) + (local $l2289 i32) + (local $l2290 i32) + (local $l2291 i32) + (local $l2292 i32) + (local $l2293 i32) + (local $l2294 i32) + (local $l2295 i32) + (local $l2296 i32) + (local $l2297 i32) + (local $l2298 i32) + (local $l2299 i32) + (local $l2300 i32) + (local $l2301 i32) + (local $l2302 i32) + (local $l2303 i32) + (local $l2304 i32) + (local $l2305 i32) + (local $l2306 i32) + (local $l2307 i32) + (local $l2308 i32) + (local $l2309 i32) + (local $l2310 i32) + (local $l2311 i32) + (local $l2312 i32) + (local $l2313 i32) + (local $l2314 i32) + (local $l2315 i32) + (local $l2316 i32) + (local $l2317 i32) + (local $l2318 i32) + (local $l2319 i32) + (local $l2320 i32) + (local $l2321 i32) + (local $l2322 i32) + (local $l2323 i32) + (local $l2324 i32) + (local $l2325 i32) + (local $l2326 i32) + (local $l2327 i32) + (local $l2328 i32) + (local $l2329 i32) + (local $l2330 i32) + (local $l2331 i32) + (local $l2332 i32) + (local $l2333 i32) + (local $l2334 i32) + (local $l2335 i32) + (local $l2336 i32) + (local $l2337 i32) + (local $l2338 i32) + (local $l2339 i32) + (local $l2340 i32) + (local $l2341 i32) + (local $l2342 i32) + (local $l2343 i32) + (local $l2344 i32) + (local $l2345 i32) + (local $l2346 i32) + (local $l2347 i32) + (local $l2348 i32) + (local $l2349 i32) + (local $l2350 i32) + (local $l2351 i32) + (local $l2352 i32) + (local $l2353 i32) + (local $l2354 i32) + (local $l2355 i32) + (local $l2356 i32) + (local $l2357 i32) + (local $l2358 i32) + (local $l2359 i32) + (local $l2360 i32) + (local $l2361 i32) + (local $l2362 i32) + (local $l2363 i32) + (local $l2364 i32) + (local $l2365 i32) + (local $l2366 i32) + (local $l2367 i32) + (local $l2368 i32) + (local $l2369 i32) + (local $l2370 i32) + (local $l2371 i32) + (local $l2372 i32) + (local $l2373 i32) + (local $l2374 i32) + (local $l2375 i32) + (local $l2376 i32) + (local $l2377 i32) + (local $l2378 i32) + (local $l2379 i32) + (local $l2380 i32) + (local $l2381 i32) + (local $l2382 i32) + (local $l2383 i32) + (local $l2384 i32) + (local $l2385 i32) + (local $l2386 i32) + (local $l2387 i32) + (local $l2388 i32) + (local $l2389 i32) + (local $l2390 i32) + (local $l2391 i32) + (local $l2392 i32) + (local $l2393 i32) + (local $l2394 i32) + (local $l2395 i32) + (local $l2396 i32) + (local $l2397 i32) + (local $l2398 i32) + (local $l2399 i32) + (local $l2400 i32) + (local $l2401 i32) + (local $l2402 i32) + (local $l2403 i32) + (local $l2404 i32) + (local $l2405 i32) + (local $l2406 i32) + (local $l2407 i32) + (local $l2408 i32) + (local $l2409 i32) + (local $l2410 i32) + (local $l2411 i32) + (local $l2412 i32) + (local $l2413 i32) + (local $l2414 i32) + (local $l2415 i32) + (local $l2416 i32) + (local $l2417 i32) + (local $l2418 i32) + (local $l2419 i32) + (local $l2420 i32) + (local $l2421 i32) + (local $l2422 i32) + (local $l2423 i32) + (local $l2424 i32) + (local $l2425 i32) + (local $l2426 i32) + (local $l2427 i32) + (local $l2428 i32) + (local $l2429 i32) + (local $l2430 i32) + (local $l2431 i32) + (local $l2432 i32) + (local $l2433 i32) + (local $l2434 i32) + (local $l2435 i32) + (local $l2436 i32) + (local $l2437 i32) + (local $l2438 i32) + (local $l2439 i32) + (local $l2440 i32) + (local $l2441 i32) + (local $l2442 i32) + (local $l2443 i32) + (local $l2444 i32) + (local $l2445 i32) + (local $l2446 i32) + (local $l2447 i32) + (local $l2448 i32) + (local $l2449 i32) + (local $l2450 i32) + (local $l2451 i32) + (local $l2452 i32) + (local $l2453 i32) + (local $l2454 i32) + (local $l2455 i32) + (local $l2456 i32) + (local $l2457 i32) + (local $l2458 i32) + (local $l2459 i32) + (local $l2460 i32) + (local $l2461 i32) + (local $l2462 i32) + (local $l2463 i32) + (local $l2464 i32) + (local $l2465 i32) + (local $l2466 i32) + (local $l2467 i32) + (local $l2468 i32) + (local $l2469 i32) + (local $l2470 i32) + (local $l2471 i32) + (local $l2472 i32) + (local $l2473 i32) + (local $l2474 i32) + (local $l2475 i32) + (local $l2476 i32) + (local $l2477 i32) + (local $l2478 i32) + (local $l2479 i32) + (local $l2480 i32) + (local $l2481 i32) + (local $l2482 i32) + (local $l2483 i32) + (local $l2484 i32) + (local $l2485 i32) + (local $l2486 i32) + (local $l2487 i32) + (local $l2488 i32) + (local $l2489 i32) + (local $l2490 i32) + (local $l2491 i32) + (local $l2492 i32) + (local $l2493 i32) + (local $l2494 i32) + (local $l2495 i32) + (local $l2496 i32) + (local $l2497 i32) + (local $l2498 i32) + (local $l2499 i32) + (local $l2500 i32) + (local $l2501 i32) + (local $l2502 i32) + (local $l2503 i32) + (local $l2504 i32) + (local $l2505 i32) + (local $l2506 i32) + (local $l2507 i32) + (local $l2508 i32) + (local $l2509 i32) + (local $l2510 i32) + (local $l2511 i32) + (local $l2512 i32) + (local $l2513 i32) + (local $l2514 i32) + (local $l2515 i32) + (local $l2516 i32) + (local $l2517 i32) + (local $l2518 i32) + (local $l2519 i32) + (local $l2520 i32) + (local $l2521 i32) + (local $l2522 i32) + (local $l2523 i32) + (local $l2524 i32) + (local $l2525 i32) + (local $l2526 i32) + (local $l2527 i32) + (local $l2528 i32) + (local $l2529 i32) + (local $l2530 i32) + (local $l2531 i32) + (local $l2532 i32) + (local $l2533 i32) + (local $l2534 i32) + (local $l2535 i32) + (local $l2536 i32) + (local $l2537 i32) + (local $l2538 i32) + (local $l2539 i32) + (local $l2540 i32) + (local $l2541 i32) + (local $l2542 i32) + (local $l2543 i32) + (local $l2544 i32) + (local $l2545 i32) + (local $l2546 i32) + (local $l2547 i32) + (local $l2548 i32) + (local $l2549 i32) + (local $l2550 i32) + (local $l2551 i32) + (local $l2552 i32) + (local $l2553 i32) + (local $l2554 i32) + (local $l2555 i32) + (local $l2556 i32) + (local $l2557 i32) + (local $l2558 i32) + (local $l2559 i32) + (local $l2560 i32) + (local $l2561 i32) + (local $l2562 i32) + (local $l2563 i32) + (local $l2564 i32) + (local $l2565 i32) + (local $l2566 i32) + (local $l2567 i32) + (local $l2568 i32) + (local $l2569 i32) + (local $l2570 i32) + (local $l2571 i32) + (local $l2572 i32) + (local $l2573 i32) + (local $l2574 i32) + (local $l2575 i32) + (local $l2576 i32) + (local $l2577 i32) + (local $l2578 i32) + (local $l2579 i32) + (local $l2580 i32) + (local $l2581 i32) + (local $l2582 i32) + (local $l2583 i32) + (local $l2584 i32) + (local $l2585 i32) + (local $l2586 i32) + (local $l2587 i32) + (local $l2588 i32) + (local $l2589 i32) + (local $l2590 i32) + (local $l2591 i32) + (local $l2592 i32) + (local $l2593 i32) + (local $l2594 i32) + (local $l2595 i32) + (local $l2596 i32) + (local $l2597 i32) + (local $l2598 i32) + (local $l2599 i32) + (local $l2600 i32) + (local $l2601 i32) + (local $l2602 i32) + (local $l2603 i32) + (local $l2604 i32) + (local $l2605 i32) + (local $l2606 i32) + (local $l2607 i32) + (local $l2608 i32) + (local $l2609 i32) + (local $l2610 i32) + (local $l2611 i32) + (local $l2612 i32) + (local $l2613 i32) + (local $l2614 i32) + (local $l2615 i32) + (local $l2616 i32) + (local $l2617 i32) + (local $l2618 i32) + (local $l2619 i32) + (local $l2620 i32) + (local $l2621 i32) + (local $l2622 i32) + (local $l2623 i32) + (local $l2624 i32) + (local $l2625 i32) + (local $l2626 i32) + (local $l2627 i32) + (local $l2628 i32) + (local $l2629 i32) + (local $l2630 i32) + (local $l2631 i32) + (local $l2632 i32) + (local $l2633 i32) + (local $l2634 i32) + (local $l2635 i32) + (local $l2636 i32) + (local $l2637 i32) + (local $l2638 i32) + (local $l2639 i32) + (local $l2640 i32) + (local $l2641 i32) + (local $l2642 i32) + (local $l2643 i32) + (local $l2644 i32) + (local $l2645 i32) + (local $l2646 i32) + (local $l2647 i32) + (local $l2648 i32) + (local $l2649 i32) + (local $l2650 i32) + (local $l2651 i32) + (local $l2652 i32) + (local $l2653 i32) + (local $l2654 i32) + (local $l2655 i32) + (local $l2656 i32) + (local $l2657 i32) + (local $l2658 i32) + (local $l2659 i32) + (local $l2660 i32) + (local $l2661 i32) + (local $l2662 i32) + (local $l2663 i32) + (local $l2664 i32) + (local $l2665 i32) + (local $l2666 i32) + (local $l2667 i32) + (local $l2668 i32) + (local $l2669 i32) + (local $l2670 i32) + (local $l2671 i32) + (local $l2672 i32) + (local $l2673 i32) + (local $l2674 i32) + (local $l2675 i32) + (local $l2676 i32) + (local $l2677 i32) + (local $l2678 i32) + (local $l2679 i32) + (local $l2680 i32) + (local $l2681 i32) + (local $l2682 i32) + (local $l2683 i32) + (local $l2684 i32) + (local $l2685 i32) + (local $l2686 i32) + (local $l2687 i32) + (local $l2688 i32) + (local $l2689 i32) + (local $l2690 i32) + (local $l2691 i32) + (local $l2692 i32) + (local $l2693 i32) + (local $l2694 i32) + (local $l2695 i32) + (local $l2696 i32) + (local $l2697 i32) + (local $l2698 i32) + (local $l2699 i32) + (local $l2700 i32) + (local $l2701 i32) + (local $l2702 i32) + (local $l2703 i32) + (local $l2704 i32) + (local $l2705 i32) + (local $l2706 i32) + (local $l2707 i32) + (local $l2708 i32) + (local $l2709 i32) + (local $l2710 i32) + (local $l2711 i32) + (local $l2712 i32) + (local $l2713 i32) + (local $l2714 i32) + (local $l2715 i32) + (local $l2716 i32) + (local $l2717 i32) + (local $l2718 i32) + (local $l2719 i32) + (local $l2720 i32) + (local $l2721 i32) + (local $l2722 i32) + (local $l2723 i32) + (local $l2724 i32) + (local $l2725 i32) + (local $l2726 i32) + (local $l2727 i32) + (local $l2728 i32) + (local $l2729 i32) + (local $l2730 i32) + (local $l2731 i32) + (local $l2732 i32) + (local $l2733 i32) + (local $l2734 i32) + (local $l2735 i32) + (local $l2736 i32) + (local $l2737 i32) + (local $l2738 i32) + (local $l2739 i32) + (local $l2740 i32) + (local $l2741 i32) + (local $l2742 i32) + (local $l2743 i32) + (local $l2744 i32) + (local $l2745 i32) + (local $l2746 i32) + (local $l2747 i32) + (local $l2748 i32) + (local $l2749 i32) + (local $l2750 i32) + (local $l2751 i32) + (local $l2752 i32) + (local $l2753 i32) + (local $l2754 i32) + (local $l2755 i32) + (local $l2756 i32) + (local $l2757 i32) + (local $l2758 i32) + (local $l2759 i32) + (local $l2760 i32) + (local $l2761 i32) + (local $l2762 i32) + (local $l2763 i32) + (local $l2764 i32) + (local $l2765 i32) + (local $l2766 i32) + (local $l2767 i32) + (local $l2768 i32) + (local $l2769 i32) + (local $l2770 i32) + (local $l2771 i32) + (local $l2772 i32) + (local $l2773 i32) + (local $l2774 i32) + (local $l2775 i32) + (local $l2776 i32) + (local $l2777 i32) + (local $l2778 i32) + (local $l2779 i32) + (local $l2780 i32) + (local $l2781 i32) + (local $l2782 i32) + (local $l2783 i32) + (local $l2784 i32) + (local $l2785 i32) + (local $l2786 i32) + (local $l2787 i32) + (local $l2788 i32) + (local $l2789 i32) + (local $l2790 i32) + (local $l2791 i32) + (local $l2792 i32) + (local $l2793 i32) + (local $l2794 i32) + (local $l2795 i32) + (local $l2796 i32) + (local $l2797 i32) + (local $l2798 i32) + (local $l2799 i32) + (local $l2800 i32) + (local $l2801 i32) + (local $l2802 i32) + (local $l2803 i32) + (local $l2804 i32) + (local $l2805 i32) + (local $l2806 i32) + (local $l2807 i32) + (local $l2808 i32) + (local $l2809 i32) + (local $l2810 i32) + (local $l2811 i32) + (local $l2812 i32) + (local $l2813 i32) + (local $l2814 i32) + (local $l2815 i32) + (local $l2816 i32) + (local $l2817 i32) + (local $l2818 i32) + (local $l2819 i32) + (local $l2820 i32) + (local $l2821 i32) + (local $l2822 i32) + (local $l2823 i32) + (local $l2824 i32) + (local $l2825 i32) + (local $l2826 i32) + (local $l2827 i32) + (local $l2828 i32) + (local $l2829 i32) + (local $l2830 i32) + (local $l2831 i32) + (local $l2832 i32) + (local $l2833 i32) + (local $l2834 i32) + (local $l2835 i32) + (local $l2836 i32) + (local $l2837 i32) + (local $l2838 i32) + (local $l2839 i32) + (local $l2840 i32) + (local $l2841 i32) + (local $l2842 i32) + (local $l2843 i32) + (local $l2844 i32) + (local $l2845 i32) + (local $l2846 i32) + (local $l2847 i32) + (local $l2848 i32) + (local $l2849 i32) + (local $l2850 i32) + (local $l2851 i32) + (local $l2852 i32) + (local $l2853 i32) + (local $l2854 i32) + (local $l2855 i32) + (local $l2856 i32) + (local $l2857 i32) + (local $l2858 i32) + (local $l2859 i32) + (local $l2860 i32) + (local $l2861 i32) + (local $l2862 i32) + (local $l2863 i32) + (local $l2864 i32) + (local $l2865 i32) + (local $l2866 i32) + (local $l2867 i32) + (local $l2868 i32) + (local $l2869 i32) + (local $l2870 i32) + (local $l2871 i32) + (local $l2872 i32) + (local $l2873 i32) + (local $l2874 i32) + (local $l2875 i32) + (local $l2876 i32) + (local $l2877 i32) + (local $l2878 i32) + (local $l2879 i32) + (local $l2880 i32) + (local $l2881 i32) + (local $l2882 i32) + (local $l2883 i32) + (local $l2884 i32) + (local $l2885 i32) + (local $l2886 i32) + (local $l2887 i32) + (local $l2888 i32) + (local $l2889 i32) + (local $l2890 i32) + (local $l2891 i32) + (local $l2892 i32) + (local $l2893 i32) + (local $l2894 i32) + (local $l2895 i32) + (local $l2896 i32) + (local $l2897 i32) + (local $l2898 i32) + (local $l2899 i32) + (local $l2900 i32) + (local $l2901 i32) + (local $l2902 i32) + (local $l2903 i32) + (local $l2904 i32) + (local $l2905 i32) + (local $l2906 i32) + (local $l2907 i32) + (local $l2908 i32) + (local $l2909 i32) + (local $l2910 i32) + (local $l2911 i32) + (local $l2912 i32) + (local $l2913 i32) + (local $l2914 i32) + (local $l2915 i32) + (local $l2916 i32) + (local $l2917 i32) + (local $l2918 i32) + (local $l2919 i32) + (local $l2920 i32) + (local $l2921 i32) + (local $l2922 i32) + (local $l2923 i32) + (local $l2924 i32) + (local $l2925 i32) + (local $l2926 i32) + (local $l2927 i32) + (local $l2928 i32) + (local $l2929 i32) + (local $l2930 i32) + (local $l2931 i32) + (local $l2932 i32) + (local $l2933 i32) + (local $l2934 i32) + (local $l2935 i32) + (local $l2936 i32) + (local $l2937 i32) + (local $l2938 i32) + (local $l2939 i32) + (local $l2940 i32) + (local $l2941 i32) + (local $l2942 i32) + (local $l2943 i32) + (local $l2944 i32) + (local $l2945 i32) + (local $l2946 i32) + (local $l2947 i32) + (local $l2948 i32) + (local $l2949 i32) + (local $l2950 i32) + (local $l2951 i32) + (local $l2952 i32) + (local $l2953 i32) + (local $l2954 i32) + (local $l2955 i32) + (local $l2956 i32) + (local $l2957 i32) + (local $l2958 i32) + (local $l2959 i32) + (local $l2960 i32) + (local $l2961 i32) + (local $l2962 i32) + (local $l2963 i32) + (local $l2964 i32) + (local $l2965 i32) + (local $l2966 i32) + (local $l2967 i32) + (local $l2968 i32) + (local $l2969 i32) + (local $l2970 i32) + (local $l2971 i32) + (local $l2972 i32) + (local $l2973 i32) + (local $l2974 i32) + (local $l2975 i32) + (local $l2976 i32) + (local $l2977 i32) + (local $l2978 i32) + (local $l2979 i32) + (local $l2980 i32) + (local $l2981 i32) + (local $l2982 i32) + (local $l2983 i32) + (local $l2984 i32) + (local $l2985 i32) + (local $l2986 i32) + (local $l2987 i32) + (local $l2988 i32) + (local $l2989 i32) + (local $l2990 i32) + (local $l2991 i32) + (local $l2992 i32) + (local $l2993 i32) + (local $l2994 i32) + (local $l2995 i32) + (local $l2996 i32) + (local $l2997 i32) + (local $l2998 i32) + (local $l2999 i32) + (local $l3000 i32) + (local $l3001 i32) + (local $l3002 i32) + (local $l3003 i32) + (local $l3004 i32) + (local $l3005 i32) + (local $l3006 i32) + (local $l3007 i32) + (local $l3008 i32) + (local $l3009 i32) + (local $l3010 i32) + (local $l3011 i32) + (local $l3012 i32) + (local $l3013 i32) + (local $l3014 i32) + (local $l3015 i32) + (local $l3016 i32) + (local $l3017 i32) + (local $l3018 i32) + (local $l3019 i32) + (local $l3020 i32) + (local $l3021 i32) + (local $l3022 i32) + (local $l3023 i32) + (local $l3024 i32) + (local $l3025 i32) + (local $l3026 i32) + (local $l3027 i32) + (local $l3028 i32) + (local $l3029 i32) + (local $l3030 i32) + (local $l3031 i32) + (local $l3032 i32) + (local $l3033 i32) + (local $l3034 i32) + (local $l3035 i32) + (local $l3036 i32) + (local $l3037 i32) + (local $l3038 i32) + (local $l3039 i32) + (local $l3040 i32) + (local $l3041 i32) + (local $l3042 i32) + (local $l3043 i32) + (local $l3044 i32) + (local $l3045 i32) + (local $l3046 i32) + (local $l3047 i32) + (local $l3048 i32) + (local $l3049 i32) + (local $l3050 i32) + (local $l3051 i32) + (local $l3052 i32) + (local $l3053 i32) + (local $l3054 i32) + (local $l3055 i32) + (local $l3056 i32) + (local $l3057 i32) + (local $l3058 i32) + (local $l3059 i32) + (local $l3060 i32) + (local $l3061 i32) + (local $l3062 i32) + (local $l3063 i32) + (local $l3064 i32) + (local $l3065 i32) + (local $l3066 i32) + (local $l3067 i32) + (local $l3068 i32) + (local $l3069 i32) + (local $l3070 i32) + (local $l3071 i32) + (local $l3072 i32) + (local $l3073 i32) + (local $l3074 i32) + (local $l3075 i32) + (local $l3076 i32) + (local $l3077 i32) + (local $l3078 i32) + (local $l3079 i32) + (local $l3080 i32) + (local $l3081 i32) + (local $l3082 i32) + (local $l3083 i32) + (local $l3084 i32) + (local $l3085 i32) + (local $l3086 i32) + (local $l3087 i32) + (local $l3088 i32) + (local $l3089 i32) + (local $l3090 i32) + (local $l3091 i32) + (local $l3092 i32) + (local $l3093 i32) + (local $l3094 i32) + (local $l3095 i32) + (local $l3096 i32) + (local $l3097 i32) + (local $l3098 i32) + (local $l3099 i32) + (local $l3100 i32) + (local $l3101 i32) + (local $l3102 i32) + (local $l3103 i32) + (local $l3104 i32) + (local $l3105 i32) + (local $l3106 i32) + (local $l3107 i32) + (local $l3108 i32) + (local $l3109 i32) + (local $l3110 i32) + (local $l3111 i32) + (local $l3112 i32) + (local $l3113 i32) + (local $l3114 i32) + (local $l3115 i32) + (local $l3116 i32) + (local $l3117 i32) + (local $l3118 i32) + (local $l3119 i32) + (local $l3120 i32) + (local $l3121 i32) + (local $l3122 i32) + (local $l3123 i32) + (local $l3124 i32) + (local $l3125 i32) + (local $l3126 i32) + (local $l3127 i32) + (local $l3128 i32) + (local $l3129 i32) + (local $l3130 i32) + (local $l3131 i32) + (local $l3132 i32) + (local $l3133 i32) + (local $l3134 i32) + (local $l3135 i32) + (local $l3136 i32) + (local $l3137 i32) + (local $l3138 i32) + (local $l3139 i32) + (local $l3140 i32) + (local $l3141 i32) + (local $l3142 i32) + (local $l3143 i32) + (local $l3144 i32) + (local $l3145 i32) + (local $l3146 i32) + (local $l3147 i32) + (local $l3148 i32) + (local $l3149 i32) + (local $l3150 i32) + (local $l3151 i32) + (local $l3152 i32) + (local $l3153 i32) + (local $l3154 i32) + (local $l3155 i32) + (local $l3156 i32) + (local $l3157 i32) + (local $l3158 i32) + (local $l3159 i32) + (local $l3160 i32) + (local $l3161 i32) + (local $l3162 i32) + (local $l3163 i32) + (local $l3164 i32) + (local $l3165 i32) + (local $l3166 i32) + (local $l3167 i32) + (local $l3168 i32) + (local $l3169 i32) + (local $l3170 i32) + (local $l3171 i32) + (local $l3172 i32) + (local $l3173 i32) + (local $l3174 i32) + (local $l3175 i32) + (local $l3176 i32) + (local $l3177 i32) + (local $l3178 i32) + (local $l3179 i32) + (local $l3180 i32) + (local $l3181 i32) + (local $l3182 i32) + (local $l3183 i32) + (local $l3184 i32) + (local $l3185 i32) + (local $l3186 i32) + (local $l3187 i32) + (local $l3188 i32) + (local $l3189 i32) + (local $l3190 i32) + (local $l3191 i32) + (local $l3192 i32) + (local $l3193 i32) + (local $l3194 i32) + (local $l3195 i32) + (local $l3196 i32) + (local $l3197 i32) + (local $l3198 i32) + (local $l3199 i32) + (local $l3200 i32) + (local $l3201 i32) + (local $l3202 i32) + (local $l3203 i32) + (local $l3204 i32) + (local $l3205 i32) + (local $l3206 i32) + (local $l3207 i32) + (local $l3208 i32) + (local $l3209 i32) + (local $l3210 i32) + (local $l3211 i32) + (local $l3212 i32) + (local $l3213 i32) + (local $l3214 i32) + (local $l3215 i32) + (local $l3216 i32) + (local $l3217 i32) + (local $l3218 i32) + (local $l3219 i32) + (local $l3220 i32) + (local $l3221 i32) + (local $l3222 i32) + (local $l3223 i32) + (local $l3224 i32) + (local $l3225 i32) + (local $l3226 i32) + (local $l3227 i32) + (local $l3228 i32) + (local $l3229 i32) + (local $l3230 i32) + (local $l3231 i32) + (local $l3232 i32) + (local $l3233 i32) + (local $l3234 i32) + (local $l3235 i32) + (local $l3236 i32) + (local $l3237 i32) + (local $l3238 i32) + (local $l3239 i32) + (local $l3240 i32) + (local $l3241 i32) + (local $l3242 i32) + (local $l3243 i32) + (local $l3244 i32) + (local $l3245 i32) + (local $l3246 i32) + (local $l3247 i32) + (local $l3248 i32) + (local $l3249 i32) + (local $l3250 i32) + (local $l3251 i32) + (local $l3252 i32) + (local $l3253 i32) + (local $l3254 i32) + (local $l3255 i32) + (local $l3256 i32) + (local $l3257 i32) + (local $l3258 i32) + (local $l3259 i32) + (local $l3260 i32) + (local $l3261 i32) + (local $l3262 i32) + (local $l3263 i32) + (local $l3264 i32) + (local $l3265 i32) + (local $l3266 i32) + (local $l3267 i32) + (local $l3268 i32) + (local $l3269 i32) + (local $l3270 i32) + (local $l3271 i32) + (local $l3272 i32) + (local $l3273 i32) + (local $l3274 i32) + (local $l3275 i32) + (local $l3276 i32) + (local $l3277 i32) + (local $l3278 i32) + (local $l3279 i32) + (local $l3280 i32) + (local $l3281 i32) + (local $l3282 i32) + (local $l3283 i32) + (local $l3284 i32) + (local $l3285 i32) + (local $l3286 i32) + (local $l3287 i32) + (local $l3288 i32) + (local $l3289 i32) + (local $l3290 i32) + (local $l3291 i32) + (local $l3292 i32) + (local $l3293 i32) + (local $l3294 i32) + (local $l3295 i32) + (local $l3296 i32) + (local $l3297 i32) + (local $l3298 i32) + (local $l3299 i32) + (local $l3300 i32) + (local $l3301 i32) + (local $l3302 i32) + (local $l3303 i32) + (local $l3304 i32) + (local $l3305 i32) + (local $l3306 i32) + (local $l3307 i32) + (local $l3308 i32) + (local $l3309 i32) + (local $l3310 i32) + (local $l3311 i32) + (local $l3312 i32) + (local $l3313 i32) + (local $l3314 i32) + (local $l3315 i32) + (local $l3316 i32) + (local $l3317 i32) + (local $l3318 i32) + (local $l3319 i32) + (local $l3320 i32) + (local $l3321 i32) + (local $l3322 i32) + (local $l3323 i32) + (local $l3324 i32) + (local $l3325 i32) + (local $l3326 i32) + (local $l3327 i32) + (local $l3328 i32) + (local $l3329 i32) + (local $l3330 i32) + (local $l3331 i32) + (local $l3332 i32) + (local $l3333 i32) + (local $l3334 i32) + (local $l3335 i32) + (local $l3336 i32) + (local $l3337 i32) + (local $l3338 i32) + (local $l3339 i32) + (local $l3340 i32) + (local $l3341 i32) + (local $l3342 i32) + (local $l3343 i32) + (local $l3344 i32) + (local $l3345 i32) + (local $l3346 i32) + (local $l3347 i32) + (local $l3348 i32) + (local $l3349 i32) + (local $l3350 i32) + (local $l3351 i32) + (local $l3352 i32) + (local $l3353 i32) + (local $l3354 i32) + (local $l3355 i32) + (local $l3356 i32) + (local $l3357 i32) + (local $l3358 i32) + (local $l3359 i32) + (local $l3360 i32) + (local $l3361 i32) + (local $l3362 i32) + (local $l3363 i32) + (local $l3364 i32) + (local $l3365 i32) + (local $l3366 i32) + (local $l3367 i32) + (local $l3368 i32) + (local $l3369 i32) + (local $l3370 i32) + (local $l3371 i32) + (local $l3372 i32) + (local $l3373 i32) + (local $l3374 i32) + (local $l3375 i32) + (local $l3376 i32) + (local $l3377 i32) + (local $l3378 i32) + (local $l3379 i32) + (local $l3380 i32) + (local $l3381 i32) + (local $l3382 i32) + (local $l3383 i32) + (local $l3384 i32) + (local $l3385 i32) + (local $l3386 i32) + (local $l3387 i32) + (local $l3388 i32) + (local $l3389 i32) + (local $l3390 i32) + (local $l3391 i32) + (local $l3392 i32) + (local $l3393 i32) + (local $l3394 i32) + (local $l3395 i32) + (local $l3396 i32) + (local $l3397 i32) + (local $l3398 i32) + (local $l3399 i32) + (local $l3400 i32) + (local $l3401 i32) + (local $l3402 i32) + (local $l3403 i32) + (local $l3404 i32) + (local $l3405 i32) + (local $l3406 i32) + (local $l3407 i32) + (local $l3408 i32) + (local $l3409 i32) + (local $l3410 i32) + (local $l3411 i32) + (local $l3412 i32) + (local $l3413 i32) + (local $l3414 i32) + (local $l3415 i32) + (local $l3416 i32) + (local $l3417 i32) + (local $l3418 i32) + (local $l3419 i32) + (local $l3420 i32) + (local $l3421 i32) + (local $l3422 i32) + (local $l3423 i32) + (local $l3424 i32) + (local $l3425 i32) + (local $l3426 i32) + (local $l3427 i32) + (local $l3428 i32) + (local $l3429 i32) + (local $l3430 i32) + (local $l3431 i32) + (local $l3432 i32) + (local $l3433 i32) + (local $l3434 i32) + (local $l3435 i32) + (local $l3436 i32) + (local $l3437 i32) + (local $l3438 i32) + (local $l3439 i32) + (local $l3440 i32) + (local $l3441 i32) + (local $l3442 i32) + (local $l3443 i32) + (local $l3444 i32) + (local $l3445 i32) + (local $l3446 i32) + (local $l3447 i32) + (local $l3448 i32) + (local $l3449 i32) + (local $l3450 i32) + (local $l3451 i32) + (local $l3452 i32) + (local $l3453 i32) + (local $l3454 i32) + (local $l3455 i32) + (local $l3456 i32) + (local $l3457 i32) + (local $l3458 i32) + (local $l3459 i32) + (local $l3460 i32) + (local $l3461 i32) + (local $l3462 i32) + (local $l3463 i32) + (local $l3464 i32) + (local $l3465 i32) + (local $l3466 i32) + (local $l3467 i32) + (local $l3468 i32) + (local $l3469 i32) + (local $l3470 i32) + (local $l3471 i32) + (local $l3472 i32) + (local $l3473 i32) + (local $l3474 i32) + (local $l3475 i32) + (local $l3476 i32) + (local $l3477 i32) + (local $l3478 i32) + (local $l3479 i32) + (local $l3480 i32) + (local $l3481 i32) + (local $l3482 i32) + (local $l3483 i32) + (local $l3484 i32) + (local $l3485 i32) + (local $l3486 i32) + (local $l3487 i32) + (local $l3488 i32) + (local $l3489 i32) + (local $l3490 i32) + (local $l3491 i32) + (local $l3492 i32) + (local $l3493 i32) + (local $l3494 i32) + (local $l3495 i32) + (local $l3496 i32) + (local $l3497 i32) + (local $l3498 i32) + (local $l3499 i32) + (local $l3500 i32) + (local $l3501 i32) + (local $l3502 i32) + (local $l3503 i32) + (local $l3504 i32) + (local $l3505 i32) + (local $l3506 i32) + (local $l3507 i32) + (local $l3508 i32) + (local $l3509 i32) + (local $l3510 i32) + (local $l3511 i32) + (local $l3512 i32) + (local $l3513 i32) + (local $l3514 i32) + (local $l3515 i32) + (local $l3516 i32) + (local $l3517 i32) + (local $l3518 i32) + (local $l3519 i32) + (local $l3520 i32) + (local $l3521 i32) + (local $l3522 i32) + (local $l3523 i32) + (local $l3524 i32) + (local $l3525 i32) + (local $l3526 i32) + (local $l3527 i32) + (local $l3528 i32) + (local $l3529 i32) + (local $l3530 i32) + (local $l3531 i32) + (local $l3532 i32) + (local $l3533 i32) + (local $l3534 i32) + (local $l3535 i32) + (local $l3536 i32) + (local $l3537 i32) + (local $l3538 i32) + (local $l3539 i32) + (local $l3540 i32) + (local $l3541 i32) + (local $l3542 i32) + (local $l3543 i32) + (local $l3544 i32) + (local $l3545 i32) + (local $l3546 i32) + (local $l3547 i32) + (local $l3548 i32) + (local $l3549 i32) + (local $l3550 i32) + (local $l3551 i32) + (local $l3552 i32) + (local $l3553 i32) + (local $l3554 i32) + (local $l3555 i32) + (local $l3556 i32) + (local $l3557 i32) + (local $l3558 i32) + (local $l3559 i32) + (local $l3560 i32) + (local $l3561 i32) + (local $l3562 i32) + (local $l3563 i32) + (local $l3564 i32) + (local $l3565 i32) + (local $l3566 i32) + (local $l3567 i32) + (local $l3568 i32) + (local $l3569 i32) + (local $l3570 i32) + (local $l3571 i32) + (local $l3572 i32) + (local $l3573 i32) + (local $l3574 i32) + (local $l3575 i32) + (local $l3576 i32) + (local $l3577 i32) + (local $l3578 i32) + (local $l3579 i32) + (local $l3580 i32) + (local $l3581 i32) + (local $l3582 i32) + (local $l3583 i32) + (local $l3584 i32) + (local $l3585 i32) + (local $l3586 i32) + (local $l3587 i32) + (local $l3588 i32) + (local $l3589 i32) + (local $l3590 i32) + (local $l3591 i32) + (local $l3592 i32) + (local $l3593 i32) + (local $l3594 i32) + (local $l3595 i32) + (local $l3596 i32) + (local $l3597 i32) + (local $l3598 i32) + (local $l3599 i32) + (local $l3600 i32) + (local $l3601 i32) + (local $l3602 i32) + (local $l3603 i32) + (local $l3604 i32) + (local $l3605 i32) + (local $l3606 i32) + (local $l3607 i32) + (local $l3608 i32) + (local $l3609 i32) + (local $l3610 i32) + (local $l3611 i32) + (local $l3612 i32) + (local $l3613 i32) + (local $l3614 i32) + (local $l3615 i32) + (local $l3616 i32) + (local $l3617 i32) + (local $l3618 i32) + (local $l3619 i32) + (local $l3620 i32) + (local $l3621 i32) + (local $l3622 i32) + (local $l3623 i32) + (local $l3624 i32) + (local $l3625 i32) + (local $l3626 i32) + (local $l3627 i32) + (local $l3628 i32) + (local $l3629 i32) + (local $l3630 i32) + (local $l3631 i32) + (local $l3632 i32) + (local $l3633 i32) + (local $l3634 i32) + (local $l3635 i32) + (local $l3636 i32) + (local $l3637 i32) + (local $l3638 i32) + (local $l3639 i32) + (local $l3640 i32) + (local $l3641 i32) + (local $l3642 i32) + (local $l3643 i32) + (local $l3644 i32) + (local $l3645 i32) + (local $l3646 i32) + (local $l3647 i32) + (local $l3648 i32) + (local $l3649 i32) + (local $l3650 i32) + (local $l3651 i32) + (local $l3652 i32) + (local $l3653 i32) + (local $l3654 i32) + (local $l3655 i32) + (local $l3656 i32) + (local $l3657 i32) + (local $l3658 i32) + (local $l3659 i32) + (local $l3660 i32) + (local $l3661 i32) + (local $l3662 i32) + (local $l3663 i32) + (local $l3664 i32) + (local $l3665 i32) + (local $l3666 i32) + (local $l3667 i32) + (local $l3668 i32) + (local $l3669 i32) + (local $l3670 i32) + (local $l3671 i32) + (local $l3672 i32) + (local $l3673 i32) + (local $l3674 i32) + (local $l3675 i32) + (local $l3676 i32) + (local $l3677 i32) + (local $l3678 i32) + (local $l3679 i32) + (local $l3680 i32) + (local $l3681 i32) + (local $l3682 i32) + (local $l3683 i32) + (local $l3684 i32) + (local $l3685 i32) + (local $l3686 i32) + (local $l3687 i32) + (local $l3688 i32) + (local $l3689 i32) + (local $l3690 i32) + (local $l3691 i32) + (local $l3692 i32) + (local $l3693 i32) + (local $l3694 i32) + (local $l3695 i32) + (local $l3696 i32) + (local $l3697 i32) + (local $l3698 i32) + (local $l3699 i32) + (local $l3700 i32) + (local $l3701 i32) + (local $l3702 i32) + (local $l3703 i32) + (local $l3704 i32) + (local $l3705 i32) + (local $l3706 i32) + (local $l3707 i32) + (local $l3708 i32) + (local $l3709 i32) + (local $l3710 i32) + (local $l3711 i32) + (local $l3712 i32) + (local $l3713 i32) + (local $l3714 i32) + (local $l3715 i32) + (local $l3716 i32) + (local $l3717 i32) + (local $l3718 i32) + (local $l3719 i32) + (local $l3720 i32) + (local $l3721 i32) + (local $l3722 i32) + (local $l3723 i32) + (local $l3724 i32) + (local $l3725 i32) + (local $l3726 i32) + (local $l3727 i32) + (local $l3728 i32) + (local $l3729 i32) + (local $l3730 i32) + (local $l3731 i32) + (local $l3732 i32) + (local $l3733 i32) + (local $l3734 i32) + (local $l3735 i32) + (local $l3736 i32) + (local $l3737 i32) + (local $l3738 i32) + (local $l3739 i32) + (local $l3740 i32) + (local $l3741 i32) + (local $l3742 i32) + (local $l3743 i32) + (local $l3744 i32) + (local $l3745 i32) + (local $l3746 i32) + (local $l3747 i32) + (local $l3748 i32) + (local $l3749 i32) + (local $l3750 i32) + (local $l3751 i32) + (local $l3752 i32) + (local $l3753 i32) + (local $l3754 i32) + (local $l3755 i32) + (local $l3756 i32) + (local $l3757 i32) + (local $l3758 i32) + (local $l3759 i32) + (local $l3760 i32) + (local $l3761 i32) + (local $l3762 i32) + (local $l3763 i32) + (local $l3764 i32) + (local $l3765 i32) + (local $l3766 i32) + (local $l3767 i32) + (local $l3768 i32) + (local $l3769 i32) + (local $l3770 i32) + (local $l3771 i32) + (local $l3772 i32) + (local $l3773 i32) + (local $l3774 i32) + (local $l3775 i32) + (local $l3776 i32) + (local $l3777 i32) + (local $l3778 i32) + (local $l3779 i32) + (local $l3780 i32) + (local $l3781 i32) + (local $l3782 i32) + (local $l3783 i32) + (local $l3784 i32) + (local $l3785 i32) + (local $l3786 i32) + (local $l3787 i32) + (local $l3788 i32) + (local $l3789 i32) + (local $l3790 i32) + (local $l3791 i32) + (local $l3792 i32) + (local $l3793 i32) + (local $l3794 i32) + (local $l3795 i32) + (local $l3796 i32) + (local $l3797 i32) + (local $l3798 i32) + (local $l3799 i32) + (local $l3800 i32) + (local $l3801 i32) + (local $l3802 i32) + (local $l3803 i32) + (local $l3804 i32) + (local $l3805 i32) + (local $l3806 i32) + (local $l3807 i32) + (local $l3808 i32) + (local $l3809 i32) + (local $l3810 i32) + (local $l3811 i32) + (local $l3812 i32) + (local $l3813 i32) + (local $l3814 i32) + (local $l3815 i32) + (local $l3816 i32) + (local $l3817 i32) + (local $l3818 i32) + (local $l3819 i32) + (local $l3820 i32) + (local $l3821 i32) + (local $l3822 i32) + (local $l3823 i32) + (local $l3824 i32) + (local $l3825 i32) + (local $l3826 i32) + (local $l3827 i32) + (local $l3828 i32) + (local $l3829 i32) + (local $l3830 i32) + (local $l3831 i32) + (local $l3832 i32) + (local $l3833 i32) + (local $l3834 i32) + (local $l3835 i32) + (local $l3836 i32) + (local $l3837 i32) + (local $l3838 i32) + (local $l3839 i32) + (local $l3840 i32) + (local $l3841 i32) + (local $l3842 i32) + (local $l3843 i32) + (local $l3844 i32) + (local $l3845 i32) + (local $l3846 i32) + (local $l3847 i32) + (local $l3848 i32) + (local $l3849 i32) + (local $l3850 i32) + (local $l3851 i32) + (local $l3852 i32) + (local $l3853 i32) + (local $l3854 i32) + (local $l3855 i32) + (local $l3856 i32) + (local $l3857 i32) + (local $l3858 i32) + (local $l3859 i32) + (local $l3860 i32) + (local $l3861 i32) + (local $l3862 i32) + (local $l3863 i32) + (local $l3864 i32) + (local $l3865 i32) + (local $l3866 i32) + (local $l3867 i32) + (local $l3868 i32) + (local $l3869 i32) + (local $l3870 i32) + (local $l3871 i32) + (local $l3872 i32) + (local $l3873 i32) + (local $l3874 i32) + (local $l3875 i32) + (local $l3876 i32) + (local $l3877 i32) + (local $l3878 i32) + (local $l3879 i32) + (local $l3880 i32) + (local $l3881 i32) + (local $l3882 i32) + (local $l3883 i32) + (local $l3884 i32) + (local $l3885 i32) + (local $l3886 i32) + (local $l3887 i32) + (local $l3888 i32) + (local $l3889 i32) + (local $l3890 i32) + (local $l3891 i32) + (local $l3892 i32) + (local $l3893 i32) + (local $l3894 i32) + (local $l3895 i32) + (local $l3896 i32) + (local $l3897 i32) + (local $l3898 i32) + (local $l3899 i32) + (local $l3900 i32) + (local $l3901 i32) + (local $l3902 i32) + (local $l3903 i32) + (local $l3904 i32) + (local $l3905 i32) + (local $l3906 i32) + (local $l3907 i32) + (local $l3908 i32) + (local $l3909 i32) + (local $l3910 i32) + (local $l3911 i32) + (local $l3912 i32) + (local $l3913 i32) + (local $l3914 i32) + (local $l3915 i32) + (local $l3916 i32) + (local $l3917 i32) + (local $l3918 i32) + (local $l3919 i32) + (local $l3920 i32) + (local $l3921 i32) + (local $l3922 i32) + (local $l3923 i32) + (local $l3924 i32) + (local $l3925 i32) + (local $l3926 i32) + (local $l3927 i32) + (local $l3928 i32) + (local $l3929 i32) + (local $l3930 i32) + (local $l3931 i32) + (local $l3932 i32) + (local $l3933 i32) + (local $l3934 i32) + (local $l3935 i32) + (local $l3936 i32) + (local $l3937 i32) + (local $l3938 i32) + (local $l3939 i32) + (local $l3940 i32) + (local $l3941 i32) + (local $l3942 i32) + (local $l3943 i32) + (local $l3944 i32) + (local $l3945 i32) + (local $l3946 i32) + (local $l3947 i32) + (local $l3948 i32) + (local $l3949 i32) + (local $l3950 i32) + (local $l3951 i32) + (local $l3952 i32) + (local $l3953 i32) + (local $l3954 i32) + (local $l3955 i32) + (local $l3956 i32) + (local $l3957 i32) + (local $l3958 i32) + (local $l3959 i32) + (local $l3960 i32) + (local $l3961 i32) + (local $l3962 i32) + (local $l3963 i32) + (local $l3964 i32) + (local $l3965 i32) + (local $l3966 i32) + (local $l3967 i32) + (local $l3968 i32) + (local $l3969 i32) + (local $l3970 i32) + (local $l3971 i32) + (local $l3972 i32) + (local $l3973 i32) + (local $l3974 i32) + (local $l3975 i32) + (local $l3976 i32) + (local $l3977 i32) + (local $l3978 i32) + (local $l3979 i32) + (local $l3980 i32) + (local $l3981 i32) + (local $l3982 i32) + (local $l3983 i32) + (local $l3984 i32) + (local $l3985 i32) + (local $l3986 i32) + (local $l3987 i32) + (local $l3988 i32) + (local $l3989 i32) + (local $l3990 i32) + (local $l3991 i32) + (local $l3992 i32) + (local $l3993 i32) + (local $l3994 i32) + (local $l3995 i32) + (local $l3996 i32) + (local $l3997 i32) + (local $l3998 i32) + (local $l3999 i32) + (local $l4000 i32) + (local $l4001 i32) + (local $l4002 i32) + (local $l4003 i32) + (local $l4004 i32) + (local $l4005 i32) + (local $l4006 i32) + (local $l4007 i32) + (local $l4008 i32) + (local $l4009 i32) + (local $l4010 i32) + (local $l4011 i32) + (local $l4012 i32) + (local $l4013 i32) + (local $l4014 i32) + (local $l4015 i32) + (local $l4016 i32) + (local $l4017 i32) + (local $l4018 i32) + (local $l4019 i32) + (local $l4020 i32) + (local $l4021 i32) + (local $l4022 i32) + (local $l4023 i32) + (local $l4024 i32) + (local $l4025 i32) + (local $l4026 i32) + (local $l4027 i32) + (local $l4028 i32) + (local $l4029 i32) + (local $l4030 i32) + (local $l4031 i32) + (local $l4032 i32) + (local $l4033 i32) + (local $l4034 i32) + (local $l4035 i32) + (local $l4036 i32) + (local $l4037 i32) + (local $l4038 i32) + (local $l4039 i32) + (local $l4040 i32) + (local $l4041 i32) + (local $l4042 i32) + (local $l4043 i32) + (local $l4044 i32) + (local $l4045 i32) + (local $l4046 i32) + (local $l4047 i32) + (local $l4048 i32) + (local $l4049 i32) + (local $l4050 i32) + (local $l4051 i32) + (local $l4052 i32) + (local $l4053 i32) + (local $l4054 i32) + (local $l4055 i32) + (local $l4056 i32) + (local $l4057 i32) + (local $l4058 i32) + (local $l4059 i32) + (local $l4060 i32) + (local $l4061 i32) + (local $l4062 i32) + (local $l4063 i32) + (local $l4064 i32) + (local $l4065 i32) + (local $l4066 i32) + (local $l4067 i32) + (local $l4068 i32) + (local $l4069 i32) + (local $l4070 i32) + (local $l4071 i32) + (local $l4072 i32) + (local $l4073 i32) + (local $l4074 i32) + (local $l4075 i32) + (local $l4076 i32) + (local $l4077 i32) + (local $l4078 i32) + (local $l4079 i32) + (local $l4080 i32) + (local $l4081 i32) + (local $l4082 i32) + (local $l4083 i32) + (local $l4084 i32) + (local $l4085 i32) + (local $l4086 i32) + (local $l4087 i32) + (local $l4088 i32) + (local $l4089 i32) + (local $l4090 i32) + (local $l4091 i32) + (local $l4092 i32) + (local $l4093 i32) + (local $l4094 i32) + (local $l4095 i32) + (local $l4096 i32) + (local $l4097 i32) + (local $l4098 i32) + (local $l4099 i32) + (local $l4100 i32) + (local $l4101 i32) + (local $l4102 i32) + (local $l4103 i32) + (local $l4104 i32) + (local $l4105 i32) + (local $l4106 i32) + (local $l4107 i32) + (local $l4108 i32) + (local $l4109 i32) + (local $l4110 i32) + (local $l4111 i32) + (local $l4112 i32) + (local $l4113 i32) + (local $l4114 i32) + (local $l4115 i32) + (local $l4116 i32) + (local $l4117 i32) + (local $l4118 i32) + (local $l4119 i32) + (local $l4120 i32) + (local $l4121 i32) + (local $l4122 i32) + (local $l4123 i32) + (local $l4124 i32) + (local $l4125 i32) + (local $l4126 i32) + (local $l4127 i32) + (local $l4128 i32) + (local $l4129 i32) + (local $l4130 i32) + (local $l4131 i32) + (local $l4132 i32) + (local $l4133 i32) + (local $l4134 i32) + (local $l4135 i32) + (local $l4136 i32) + (local $l4137 i32) + (local $l4138 i32) + (local $l4139 i32) + (local $l4140 i32) + (local $l4141 i32) + (local $l4142 i32) + (local $l4143 i32) + (local $l4144 i32) + (local $l4145 i32) + (local $l4146 i32) + (local $l4147 i32) + (local $l4148 i32) + (local $l4149 i32) + (local $l4150 i32) + (local $l4151 i32) + (local $l4152 i32) + (local $l4153 i32) + (local $l4154 i32) + (local $l4155 i32) + (local $l4156 i32) + (local $l4157 i32) + (local $l4158 i32) + (local $l4159 i32) + (local $l4160 i32) + (local $l4161 i32) + (local $l4162 i32) + (local $l4163 i32) + (local $l4164 i32) + (local $l4165 i32) + (local $l4166 i32) + (local $l4167 i32) + (local $l4168 i32) + (local $l4169 i32) + (local $l4170 i32) + (local $l4171 i32) + (local $l4172 i32) + (local $l4173 i32) + (local $l4174 i32) + (local $l4175 i32) + (local $l4176 i32) + (local $l4177 i32) + (local $l4178 i32) + (local $l4179 i32) + (local $l4180 i32) + (local $l4181 i32) + (local $l4182 i32) + (local $l4183 i32) + (local $l4184 i32) + (local $l4185 i32) + (local $l4186 i32) + (local $l4187 i32) + (local $l4188 i32) + (local $l4189 i32) + (local $l4190 i32) + (local $l4191 i32) + (local $l4192 i32) + (local $l4193 i32) + (local $l4194 i32) + (local $l4195 i32) + (local $l4196 i32) + (local $l4197 i32) + (local $l4198 i32) + (local $l4199 i32) + (local $l4200 i32) + (local $l4201 i32) + (local $l4202 i32) + (local $l4203 i32) + (local $l4204 i32) + (local $l4205 i32) + (local $l4206 i32) + (local $l4207 i32) + (local $l4208 i32) + (local $l4209 i32) + (local $l4210 i32) + (local $l4211 i32) + (local $l4212 i32) + (local $l4213 i32) + (local $l4214 i32) + (local $l4215 i32) + (local $l4216 i32) + (local $l4217 i32) + (local $l4218 i32) + (local $l4219 i32) + (local $l4220 i32) + (local $l4221 i32) + (local $l4222 i32) + (local $l4223 i32) + (local $l4224 i32) + (local $l4225 i32) + (local $l4226 i32) + (local $l4227 i32) + (local $l4228 i32) + (local $l4229 i32) + (local $l4230 i32) + (local $l4231 i32) + (local $l4232 i32) + (local $l4233 i32) + (local $l4234 i32) + (local $l4235 i32) + (local $l4236 i32) + (local $l4237 i32) + (local $l4238 i32) + (local $l4239 i32) + (local $l4240 i32) + (local $l4241 i32) + (local $l4242 i32) + (local $l4243 i32) + (local $l4244 i32) + (local $l4245 i32) + (local $l4246 i32) + (local $l4247 i32) + (local $l4248 i32) + (local $l4249 i32) + (local $l4250 i32) + (local $l4251 i32) + (local $l4252 i32) + (local $l4253 i32) + (local $l4254 i32) + (local $l4255 i32) + (local $l4256 i32) + (local $l4257 i32) + (local $l4258 i32) + (local $l4259 i32) + (local $l4260 i32) + (local $l4261 i32) + (local $l4262 i32) + (local $l4263 i32) + (local $l4264 i32) + (local $l4265 i32) + (local $l4266 i32) + (local $l4267 i32) + (local $l4268 i32) + (local $l4269 i32) + (local $l4270 i32) + (local $l4271 i32) + (local $l4272 i32) + (local $l4273 i32) + (local $l4274 i32) + (local $l4275 i32) + (local $l4276 i32) + (local $l4277 i32) + (local $l4278 i32) + (local $l4279 i32) + (local $l4280 i32) + (local $l4281 i32) + (local $l4282 i32) + (local $l4283 i32) + (local $l4284 i32) + (local $l4285 i32) + (local $l4286 i32) + (local $l4287 i32) + (local $l4288 i32) + (local $l4289 i32) + (local $l4290 i32) + (local $l4291 i32) + (local $l4292 i32) + (local $l4293 i32) + (local $l4294 i32) + (local $l4295 i32) + (local $l4296 i32) + (local $l4297 i32) + (local $l4298 i32) + (local $l4299 i32) + (local $l4300 i32) + (local $l4301 i32) + (local $l4302 i32) + (local $l4303 i32) + (local $l4304 i32) + (local $l4305 i32) + (local $l4306 i32) + (local $l4307 i32) + (local $l4308 i32) + (local $l4309 i32) + (local $l4310 i32) + (local $l4311 i32) + (local $l4312 i32) + (local $l4313 i32) + (local $l4314 i32) + (local $l4315 i32) + (local $l4316 i32) + (local $l4317 i32) + (local $l4318 i32) + (local $l4319 i32) + (local $l4320 i32) + (local $l4321 i32) + (local $l4322 i32) + (local $l4323 i32) + (local $l4324 i32) + (local $l4325 i32) + (local $l4326 i32) + (local $l4327 i32) + (local $l4328 i32) + (local $l4329 i32) + (local $l4330 i32) + (local $l4331 i32) + (local $l4332 i32) + (local $l4333 i32) + (local $l4334 i32) + (local $l4335 i32) + (local $l4336 i32) + (local $l4337 i32) + (local $l4338 i32) + (local $l4339 i32) + (local $l4340 i32) + (local $l4341 i32) + (local $l4342 i32) + (local $l4343 i32) + (local $l4344 i32) + (local $l4345 i32) + (local $l4346 i32) + (local $l4347 i32) + (local $l4348 i32) + (local $l4349 i32) + (local $l4350 i32) + (local $l4351 i32) + (local $l4352 i32) + (local $l4353 i32) + (local $l4354 i32) + (local $l4355 i32) + (local $l4356 i32) + (local $l4357 i32) + (local $l4358 i32) + (local $l4359 i32) + (local $l4360 i32) + (local $l4361 i32) + (local $l4362 i32) + (local $l4363 i32) + (local $l4364 i32) + (local $l4365 i32) + (local $l4366 i32) + (local $l4367 i32) + (local $l4368 i32) + (local $l4369 i32) + (local $l4370 i32) + (local $l4371 i32) + (local $l4372 i32) + (local $l4373 i32) + (local $l4374 i32) + (local $l4375 i32) + (local $l4376 i32) + (local $l4377 i32) + (local $l4378 i32) + (local $l4379 i32) + (local $l4380 i32) + (local $l4381 i32) + (local $l4382 i32) + (local $l4383 i32) + (local $l4384 i32) + (local $l4385 i32) + (local $l4386 i32) + (local $l4387 i32) + (local $l4388 i32) + (local $l4389 i32) + (local $l4390 i32) + (local $l4391 i32) + (local $l4392 i32) + (local $l4393 i32) + (local $l4394 i32) + (local $l4395 i32) + (local $l4396 i32) + (local $l4397 i32) + (local $l4398 i32) + (local $l4399 i32) + (local $l4400 i32) + (local $l4401 i32) + (local $l4402 i32) + (local $l4403 i32) + (local $l4404 i32) + (local $l4405 i32) + (local $l4406 i32) + (local $l4407 i32) + (local $l4408 i32) + (local $l4409 i32) + (local $l4410 i32) + (local $l4411 i32) + (local $l4412 i32) + (local $l4413 i32) + (local $l4414 i32) + (local $l4415 i32) + (local $l4416 i32) + (local $l4417 i32) + (local $l4418 i32) + (local $l4419 i32) + (local $l4420 i32) + (local $l4421 i32) + (local $l4422 i32) + (local $l4423 i32) + (local $l4424 i32) + (local $l4425 i32) + (local $l4426 i32) + (local $l4427 i32) + (local $l4428 i32) + (local $l4429 i32) + (local $l4430 i32) + (local $l4431 i32) + (local $l4432 i32) + (local $l4433 i32) + (local $l4434 i32) + (local $l4435 i32) + (local $l4436 i32) + (local $l4437 i32) + (local $l4438 i32) + (local $l4439 i32) + (local $l4440 i32) + (local $l4441 i32) + (local $l4442 i32) + (local $l4443 i32) + (local $l4444 i32) + (local $l4445 i32) + (local $l4446 i32) + (local $l4447 i32) + (local $l4448 i32) + (local $l4449 i32) + (local $l4450 i32) + (local $l4451 i32) + (local $l4452 i32) + (local $l4453 i32) + (local $l4454 i32) + (local $l4455 i32) + (local $l4456 i32) + (local $l4457 i32) + (local $l4458 i32) + (local $l4459 i32) + (local $l4460 i32) + (local $l4461 i32) + (local $l4462 i32) + (local $l4463 i32) + (local $l4464 i32) + (local $l4465 i32) + (local $l4466 i32) + (local $l4467 i32) + (local $l4468 i32) + (local $l4469 i32) + (local $l4470 i32) + (local $l4471 i32) + (local $l4472 i32) + (local $l4473 i32) + (local $l4474 i32) + (local $l4475 i32) + (local $l4476 i32) + (local $l4477 i32) + (local $l4478 i32) + (local $l4479 i32) + (local $l4480 i32) + (local $l4481 i32) + (local $l4482 i32) + (local $l4483 i32) + (local $l4484 i32) + (local $l4485 i32) + (local $l4486 i32) + (local $l4487 i32) + (local $l4488 i32) + (local $l4489 i32) + (local $l4490 i32) + (local $l4491 i32) + (local $l4492 i32) + (local $l4493 i32) + (local $l4494 i32) + (local $l4495 i32) + (local $l4496 i32) + (local $l4497 i32) + (local $l4498 i32) + (local $l4499 i32) + (local $l4500 i32) + (local $l4501 i32) + (local $l4502 i32) + (local $l4503 i32) + (local $l4504 i32) + (local $l4505 i32) + (local $l4506 i32) + (local $l4507 i32) + (local $l4508 i32) + (local $l4509 i32) + (local $l4510 i32) + (local $l4511 i32) + (local $l4512 i32) + (local $l4513 i32) + (local $l4514 i32) + (local $l4515 i32) + (local $l4516 i32) + (local $l4517 i32) + (local $l4518 i32) + (local $l4519 i32) + (local $l4520 i32) + (local $l4521 i32) + (local $l4522 i32) + (local $l4523 i32) + (local $l4524 i32) + (local $l4525 i32) + (local $l4526 i32) + (local $l4527 i32) + (local $l4528 i32) + (local $l4529 i32) + (local $l4530 i32) + (local $l4531 i32) + (local $l4532 i32) + (local $l4533 i32) + (local $l4534 i32) + (local $l4535 i32) + (local $l4536 i32) + (local $l4537 i32) + (local $l4538 i32) + (local $l4539 i32) + (local $l4540 i32) + (local $l4541 i32) + (local $l4542 i32) + (local $l4543 i32) + (local $l4544 i32) + (local $l4545 i32) + (local $l4546 i32) + (local $l4547 i32) + (local $l4548 i32) + (local $l4549 i32) + (local $l4550 i32) + (local $l4551 i32) + (local $l4552 i32) + (local $l4553 i32) + (local $l4554 i32) + (local $l4555 i32) + (local $l4556 i32) + (local $l4557 i32) + (local $l4558 i32) + (local $l4559 i32) + (local $l4560 i32) + (local $l4561 i32) + (local $l4562 i32) + (local $l4563 i32) + (local $l4564 i32) + (local $l4565 i32) + (local $l4566 i32) + (local $l4567 i32) + (local $l4568 i32) + (local $l4569 i32) + (local $l4570 i32) + (local $l4571 i32) + (local $l4572 i32) + (local $l4573 i32) + (local $l4574 i32) + (local $l4575 i32) + (local $l4576 i32) + (local $l4577 i32) + (local $l4578 i32) + (local $l4579 i32) + (local $l4580 i32) + (local $l4581 i32) + (local $l4582 i32) + (local $l4583 i32) + (local $l4584 i32) + (local $l4585 i32) + (local $l4586 i32) + (local $l4587 i32) + (local $l4588 i32) + (local $l4589 i32) + (local $l4590 i32) + (local $l4591 i32) + (local $l4592 i32) + (local $l4593 i32) + (local $l4594 i32) + (local $l4595 i32) + (local $l4596 i32) + (local $l4597 i32) + (local $l4598 i32) + (local $l4599 i32) + (local $l4600 i32) + (local $l4601 i32) + (local $l4602 i32) + (local $l4603 i32) + (local $l4604 i32) + (local $l4605 i32) + (local $l4606 i32) + (local $l4607 i32) + (local $l4608 i32) + (local $l4609 i32) + (local $l4610 i32) + (local $l4611 i32) + (local $l4612 i32) + (local $l4613 i32) + (local $l4614 i32) + (local $l4615 i32) + (local $l4616 i32) + (local $l4617 i32) + (local $l4618 i32) + (local $l4619 i32) + (local $l4620 i32) + (local $l4621 i32) + (local $l4622 i32) + (local $l4623 i32) + (local $l4624 i32) + (local $l4625 i32) + (local $l4626 i32) + (local $l4627 i32) + (local $l4628 i32) + (local $l4629 i32) + (local $l4630 i32) + (local $l4631 i32) + (local $l4632 i32) + (local $l4633 i32) + (local $l4634 i32) + (local $l4635 i32) + (local $l4636 i32) + (local $l4637 i32) + (local $l4638 i32) + (local $l4639 i32) + (local $l4640 i32) + (local $l4641 i32) + (local $l4642 i32) + (local $l4643 i32) + (local $l4644 i32) + (local $l4645 i32) + (local $l4646 i32) + (local $l4647 i32) + (local $l4648 i32) + (local $l4649 i32) + (local $l4650 i32) + (local $l4651 i32) + (local $l4652 i32) + (local $l4653 i32) + (local $l4654 i32) + (local $l4655 i32) + (local $l4656 i32) + (local $l4657 i32) + (local $l4658 i32) + (local $l4659 i32) + (local $l4660 i32) + (local $l4661 i32) + (local $l4662 i32) + (local $l4663 i32) + (local $l4664 i32) + (local $l4665 i32) + (local $l4666 i32) + (local $l4667 i32) + (local $l4668 i32) + (local $l4669 i32) + (local $l4670 i32) + (local $l4671 i32) + (local $l4672 i32) + (local $l4673 i32) + (local $l4674 i32) + (local $l4675 i32) + (local $l4676 i32) + (local $l4677 i32) + (local $l4678 i32) + (local $l4679 i32) + (local $l4680 i32) + (local $l4681 i32) + (local $l4682 i32) + (local $l4683 i32) + (local $l4684 i32) + (local $l4685 i32) + (local $l4686 i32) + (local $l4687 i32) + (local $l4688 i32) + (local $l4689 i32) + (local $l4690 i32) + (local $l4691 i32) + (local $l4692 i32) + (local $l4693 i32) + (local $l4694 i32) + (local $l4695 i32) + (local $l4696 i32) + (local $l4697 i32) + (local $l4698 i32) + (local $l4699 i32) + (local $l4700 i32) + (local $l4701 i32) + (local $l4702 i32) + (local $l4703 i32) + (local $l4704 i32) + (local $l4705 i32) + (local $l4706 i32) + (local $l4707 i32) + (local $l4708 i32) + (local $l4709 i32) + (local $l4710 i32) + (local $l4711 i32) + (local $l4712 i32) + (local $l4713 i32) + (local $l4714 i32) + (local $l4715 i32) + (local $l4716 i32) + (local $l4717 i32) + (local $l4718 i32) + (local $l4719 i32) + (local $l4720 i32) + (local $l4721 i32) + (local $l4722 i32) + (local $l4723 i32) + (local $l4724 i32) + (local $l4725 i32) + (local $l4726 i32) + (local $l4727 i32) + (local $l4728 i32) + (local $l4729 i32) + (local $l4730 i32) + (local $l4731 i32) + (local $l4732 i32) + (local $l4733 i32) + (local $l4734 i32) + (local $l4735 i32) + (local $l4736 i32) + (local $l4737 i32) + (local $l4738 i32) + (local $l4739 i32) + (local $l4740 i32) + (local $l4741 i32) + (local $l4742 i32) + (local $l4743 i32) + (local $l4744 i32) + (local $l4745 i32) + (local $l4746 i32) + (local $l4747 i32) + (local $l4748 i32) + (local $l4749 i32) + (local $l4750 i32) + (local $l4751 i32) + (local $l4752 i32) + (local $l4753 i32) + (local $l4754 i32) + (local $l4755 i32) + (local $l4756 i32) + (local $l4757 i32) + (local $l4758 i32) + (local $l4759 i32) + (local $l4760 i32) + (local $l4761 i32) + (local $l4762 i32) + (local $l4763 i32) + (local $l4764 i32) + (local $l4765 i32) + (local $l4766 i32) + (local $l4767 i32) + (local $l4768 i32) + (local $l4769 i32) + (local $l4770 i32) + (local $l4771 i32) + (local $l4772 i32) + (local $l4773 i32) + (local $l4774 i32) + (local $l4775 i32) + (local $l4776 i32) + (local $l4777 i32) + (local $l4778 i32) + (local $l4779 i32) + (local $l4780 i32) + (local $l4781 i32) + (local $l4782 i32) + (local $l4783 i32) + (local $l4784 i32) + (local $l4785 i32) + (local $l4786 i32) + (local $l4787 i32) + (local $l4788 i32) + (local $l4789 i32) + (local $l4790 i32) + (local $l4791 i32) + (local $l4792 i32) + (local $l4793 i32) + (local $l4794 i32) + (local $l4795 i32) + (local $l4796 i32) + (local $l4797 i32) + (local $l4798 i32) + (local $l4799 i32) + (local $l4800 i32) + (local $l4801 i32) + (local $l4802 i32) + (local $l4803 i32) + (local $l4804 i32) + (local $l4805 i32) + (local $l4806 i32) + (local $l4807 i32) + (local $l4808 i32) + (local $l4809 i32) + (local $l4810 i32) + (local $l4811 i32) + (local $l4812 i32) + (local $l4813 i32) + (local $l4814 i32) + (local $l4815 i32) + (local $l4816 i32) + (local $l4817 i32) + (local $l4818 i32) + (local $l4819 i32) + (local $l4820 i32) + (local $l4821 i32) + (local $l4822 i32) + (local $l4823 i32) + (local $l4824 i32) + (local $l4825 i32) + (local $l4826 i32) + (local $l4827 i32) + (local $l4828 i32) + (local $l4829 i32) + (local $l4830 i32) + (local $l4831 i32) + (local $l4832 i32) + (local $l4833 i32) + (local $l4834 i32) + (local $l4835 i32) + (local $l4836 i32) + (local $l4837 i32) + (local $l4838 i32) + (local $l4839 i32) + (local $l4840 i32) + (local $l4841 i32) + (local $l4842 i32) + (local $l4843 i32) + (local $l4844 i32) + (local $l4845 i32) + (local $l4846 i32) + (local $l4847 i32) + (local $l4848 i32) + (local $l4849 i32) + (local $l4850 i32) + (local $l4851 i32) + (local $l4852 i32) + (local $l4853 i32) + (local $l4854 i32) + (local $l4855 i32) + (local $l4856 i32) + (local $l4857 i32) + (local $l4858 i32) + (local $l4859 i32) + (local $l4860 i32) + (local $l4861 i32) + (local $l4862 i32) + (local $l4863 i32) + (local $l4864 i32) + (local $l4865 i32) + (local $l4866 i32) + (local $l4867 i32) + (local $l4868 i32) + (local $l4869 i32) + (local $l4870 i32) + (local $l4871 i32) + (local $l4872 i32) + (local $l4873 i32) + (local $l4874 i32) + (local $l4875 i32) + (local $l4876 i32) + (local $l4877 i32) + (local $l4878 i32) + (local $l4879 i32) + (local $l4880 i32) + (local $l4881 i32) + (local $l4882 i32) + (local $l4883 i32) + (local $l4884 i32) + (local $l4885 i32) + (local $l4886 i32) + (local $l4887 i32) + (local $l4888 i32) + (local $l4889 i32) + (local $l4890 i32) + (local $l4891 i32) + (local $l4892 i32) + (local $l4893 i32) + (local $l4894 i32) + (local $l4895 i32) + (local $l4896 i32) + (local $l4897 i32) + (local $l4898 i32) + (local $l4899 i32) + (local $l4900 i32) + (local $l4901 i32) + (local $l4902 i32) + (local $l4903 i32) + (local $l4904 i32) + (local $l4905 i32) + (local $l4906 i32) + (local $l4907 i32) + (local $l4908 i32) + (local $l4909 i32) + (local $l4910 i32) + (local $l4911 i32) + (local $l4912 i32) + (local $l4913 i32) + (local $l4914 i32) + (local $l4915 i32) + (local $l4916 i32) + (local $l4917 i32) + (local $l4918 i32) + (local $l4919 i32) + (local $l4920 i32) + (local $l4921 i32) + (local $l4922 i32) + (local $l4923 i32) + (local $l4924 i32) + (local $l4925 i32) + (local $l4926 i32) + (local $l4927 i32) + (local $l4928 i32) + (local $l4929 i32) + (local $l4930 i32) + (local $l4931 i32) + (local $l4932 i32) + (local $l4933 i32) + (local $l4934 i32) + (local $l4935 i32) + (local $l4936 i32) + (local $l4937 i32) + (local $l4938 i32) + (local $l4939 i32) + (local $l4940 i32) + (local $l4941 i32) + (local $l4942 i32) + (local $l4943 i32) + (local $l4944 i32) + (local $l4945 i32) + (local $l4946 i32) + (local $l4947 i32) + (local $l4948 i32) + (local $l4949 i32) + (local $l4950 i32) + (local $l4951 i32) + (local $l4952 i32) + (local $l4953 i32) + (local $l4954 i32) + (local $l4955 i32) + (local $l4956 i32) + (local $l4957 i32) + (local $l4958 i32) + (local $l4959 i32) + (local $l4960 i32) + (local $l4961 i32) + (local $l4962 i32) + (local $l4963 i32) + (local $l4964 i32) + (local $l4965 i32) + (local $l4966 i32) + (local $l4967 i32) + (local $l4968 i32) + (local $l4969 i32) + (local $l4970 i32) + (local $l4971 i32) + (local $l4972 i32) + (local $l4973 i32) + (local $l4974 i32) + (local $l4975 i32) + (local $l4976 i32) + (local $l4977 i32) + (local $l4978 i32) + (local $l4979 i32) + (local $l4980 i32) + (local $l4981 i32) + (local $l4982 i32) + (local $l4983 i32) + (local $l4984 i32) + (local $l4985 i32) + (local $l4986 i32) + (local $l4987 i32) + (local $l4988 i32) + (local $l4989 i32) + (local $l4990 i32) + (local $l4991 i32) + (local $l4992 i32) + (local $l4993 i32) + (local $l4994 i32) + (local $l4995 i32) + (local $l4996 i32) + (local $l4997 i32) + (local $l4998 i32) + (local $l4999 i32) + (local $l5000 i32) + (local $l5001 i32) + (local $l5002 i32) + (local $l5003 i32) + (local $l5004 i32) + (local $l5005 i32) + (local $l5006 i32) + (local $l5007 i32) + (local $l5008 i32) + (local $l5009 i32) + (local $l5010 i32) + (local $l5011 i32) + (local $l5012 i32) + (local $l5013 i32) + (local $l5014 i32) + (local $l5015 i32) + (local $l5016 i32) + (local $l5017 i32) + (local $l5018 i32) + (local $l5019 i32) + (local $l5020 i32) + (local $l5021 i32) + (local $l5022 i32) + (local $l5023 i32) + (local $l5024 i32) + (local $l5025 i32) + (local $l5026 i32) + (local $l5027 i32) + (local $l5028 i32) + (local $l5029 i32) + (local $l5030 i32) + (local $l5031 i32) + (local $l5032 i32) + (local $l5033 i32) + (local $l5034 i32) + (local $l5035 i32) + (local $l5036 i32) + (local $l5037 i32) + (local $l5038 i32) + (local $l5039 i32) + (local $l5040 i32) + (local $l5041 i32) + (local $l5042 i32) + (local $l5043 i32) + (local $l5044 i32) + (local $l5045 i32) + (local $l5046 i32) + (local $l5047 i32) + (local $l5048 i32) + (local $l5049 i32) + (local $l5050 i32) + (local $l5051 i32) + (local $l5052 i32) + (local $l5053 i32) + (local $l5054 i32) + (local $l5055 i32) + (local $l5056 i32) + (local $l5057 i32) + (local $l5058 i32) + (local $l5059 i32) + (local $l5060 i32) + (local $l5061 i32) + (local $l5062 i32) + (local $l5063 i32) + (local $l5064 i32) + (local $l5065 i32) + (local $l5066 i32) + (local $l5067 i32) + (local $l5068 i32) + (local $l5069 i32) + (local $l5070 i32) + (local $l5071 i32) + (local $l5072 i32) + (local $l5073 i32) + (local $l5074 i32) + (local $l5075 i32) + (local $l5076 i32) + (local $l5077 i32) + (local $l5078 i32) + (local $l5079 i32) + (local $l5080 i32) + (local $l5081 i32) + (local $l5082 i32) + (local $l5083 i32) + (local $l5084 i32) + (local $l5085 i32) + (local $l5086 i32) + (local $l5087 i32) + (local $l5088 i32) + (local $l5089 i32) + (local $l5090 i32) + (local $l5091 i32) + (local $l5092 i32) + (local $l5093 i32) + (local $l5094 i32) + (local $l5095 i32) + (local $l5096 i32) + (local $l5097 i32) + (local $l5098 i32) + (local $l5099 i32) + (local $l5100 i32) + (local $l5101 i32) + (local $l5102 i32) + (local $l5103 i32) + (local $l5104 i32) + (local $l5105 i32) + (local $l5106 i32) + (local $l5107 i32) + (local $l5108 i32) + (local $l5109 i32) + (local $l5110 i32) + (local $l5111 i32) + (local $l5112 i32) + (local $l5113 i32) + (local $l5114 i32) + (local $l5115 i32) + (local $l5116 i32) + (local $l5117 i32) + (local $l5118 i32) + (local $l5119 i32) + (local $l5120 i32) + (local $l5121 i32) + (local $l5122 i32) + (local $l5123 i32) + (local $l5124 i32) + (local $l5125 i32) + (local $l5126 i32) + (local $l5127 i32) + (local $l5128 i32) + (local $l5129 i32) + (local $l5130 i32) + (local $l5131 i32) + (local $l5132 i32) + (local $l5133 i32) + (local $l5134 i32) + (local $l5135 i32) + (local $l5136 i32) + (local $l5137 i32) + (local $l5138 i32) + (local $l5139 i32) + (local $l5140 i32) + (local $l5141 i32) + (local $l5142 i32) + (local $l5143 i32) + (local $l5144 i32) + (local $l5145 i32) + (local $l5146 i32) + (local $l5147 i32) + (local $l5148 i32) + (local $l5149 i32) + (local $l5150 i32) + (local $l5151 i32) + (local $l5152 i32) + (local $l5153 i32) + (local $l5154 i32) + (local $l5155 i32) + (local $l5156 i32) + (local $l5157 i32) + (local $l5158 i32) + (local $l5159 i32) + (local $l5160 i32) + (local $l5161 i32) + (local $l5162 i32) + (local $l5163 i32) + (local $l5164 i32) + (local $l5165 i32) + (local $l5166 i32) + (local $l5167 i32) + (local $l5168 i32) + (local $l5169 i32) + (local $l5170 i32) + (local $l5171 i32) + (local $l5172 i32) + (local $l5173 i32) + (local $l5174 i32) + (local $l5175 i32) + (local $l5176 i32) + (local $l5177 i32) + (local $l5178 i32) + (local $l5179 i32) + (local $l5180 i32) + (local $l5181 i32) + (local $l5182 i32) + (local $l5183 i32) + (local $l5184 i32) + (local $l5185 i32) + (local $l5186 i32) + (local $l5187 i32) + (local $l5188 i32) + (local $l5189 i32) + (local $l5190 i32) + (local $l5191 i32) + (local $l5192 i32) + (local $l5193 i32) + (local $l5194 i32) + (local $l5195 i32) + (local $l5196 i32) + (local $l5197 i32) + (local $l5198 i32) + (local $l5199 i32) + (local $l5200 i32) + (local $l5201 i32) + (local $l5202 i32) + (local $l5203 i32) + (local $l5204 i32) + (local $l5205 i32) + (local $l5206 i32) + (local $l5207 i32) + (local $l5208 i32) + (local $l5209 i32) + (local $l5210 i32) + (local $l5211 i32) + (local $l5212 i32) + (local $l5213 i32) + (local $l5214 i32) + (local $l5215 i32) + (local $l5216 i32) + (local $l5217 i32) + (local $l5218 i32) + (local $l5219 i32) + (local $l5220 i32) + (local $l5221 i32) + (local $l5222 i32) + (local $l5223 i32) + (local $l5224 i32) + (local $l5225 i32) + (local $l5226 i32) + (local $l5227 i32) + (local $l5228 i32) + (local $l5229 i32) + (local $l5230 i32) + (local $l5231 i32) + (local $l5232 i32) + (local $l5233 i32) + (local $l5234 i32) + (local $l5235 i32) + (local $l5236 i32) + (local $l5237 i32) + (local $l5238 i32) + (local $l5239 i32) + (local $l5240 i32) + (local $l5241 i32) + (local $l5242 i32) + (local $l5243 i32) + (local $l5244 i32) + (local $l5245 i32) + (local $l5246 i32) + (local $l5247 i32) + (local $l5248 i32) + (local $l5249 i32) + (local $l5250 i32) + (local $l5251 i32) + (local $l5252 i32) + (local $l5253 i32) + (local $l5254 i32) + (local $l5255 i32) + (local $l5256 i32) + (local $l5257 i32) + (local $l5258 i32) + (local $l5259 i32) + (local $l5260 i32) + (local $l5261 i32) + (local $l5262 i32) + (local $l5263 i32) + (local $l5264 i32) + (local $l5265 i32) + (local $l5266 i32) + (local $l5267 i32) + (local $l5268 i32) + (local $l5269 i32) + (local $l5270 i32) + (local $l5271 i32) + (local $l5272 i32) + (local $l5273 i32) + (local $l5274 i32) + (local $l5275 i32) + (local $l5276 i32) + (local $l5277 i32) + (local $l5278 i32) + (local $l5279 i32) + (local $l5280 i32) + (local $l5281 i32) + (local $l5282 i32) + (local $l5283 i32) + (local $l5284 i32) + (local $l5285 i32) + (local $l5286 i32) + (local $l5287 i32) + (local $l5288 i32) + (local $l5289 i32) + (local $l5290 i32) + (local $l5291 i32) + (local $l5292 i32) + (local $l5293 i32) + (local $l5294 i32) + (local $l5295 i32) + (local $l5296 i32) + (local $l5297 i32) + (local $l5298 i32) + (local $l5299 i32) + (local $l5300 i32) + (local $l5301 i32) + (local $l5302 i32) + (local $l5303 i32) + (local $l5304 i32) + (local $l5305 i32) + (local $l5306 i32) + (local $l5307 i32) + (local $l5308 i32) + (local $l5309 i32) + (local $l5310 i32) + (local $l5311 i32) + (local $l5312 i32) + (local $l5313 i32) + (local $l5314 i32) + (local $l5315 i32) + (local $l5316 i32) + (local $l5317 i32) + (local $l5318 i32) + (local $l5319 i32) + (local $l5320 i32) + (local $l5321 i32) + (local $l5322 i32) + (local $l5323 i32) + (local $l5324 i32) + (local $l5325 i32) + (local $l5326 i32) + (local $l5327 i32) + (local $l5328 i32) + (local $l5329 i32) + (local $l5330 i32) + (local $l5331 i32) + (local $l5332 i32) + (local $l5333 i32) + (local $l5334 i32) + (local $l5335 i32) + (local $l5336 i32) + (local $l5337 i32) + (local $l5338 i32) + (local $l5339 i32) + (local $l5340 i32) + (local $l5341 i32) + (local $l5342 i32) + (local $l5343 i32) + (local $l5344 i32) + (local $l5345 i32) + (local $l5346 i32) + (local $l5347 i32) + (local $l5348 i32) + (local $l5349 i32) + (local $l5350 i32) + (local $l5351 i32) + (local $l5352 i32) + (local $l5353 i32) + (local $l5354 i32) + (local $l5355 i32) + (local $l5356 i32) + (local $l5357 i32) + (local $l5358 i32) + (local $l5359 i32) + (local $l5360 i32) + (local $l5361 i32) + (local $l5362 i32) + (local $l5363 i32) + (local $l5364 i32) + (local $l5365 i32) + (local $l5366 i32) + (local $l5367 i32) + (local $l5368 i32) + (local $l5369 i32) + (local $l5370 i32) + (local $l5371 i32) + (local $l5372 i32) + (local $l5373 i32) + (local $l5374 i32) + (local $l5375 i32) + (local $l5376 i32) + (local $l5377 i32) + (local $l5378 i32) + (local $l5379 i32) + (local $l5380 i32) + (local $l5381 i32) + (local $l5382 i32) + (local $l5383 i32) + (local $l5384 i32) + (local $l5385 i32) + (local $l5386 i32) + (local $l5387 i32) + (local $l5388 i32) + (local $l5389 i32) + (local $l5390 i32) + (local $l5391 i32) + (local $l5392 i32) + (local $l5393 i32) + (local $l5394 i32) + (local $l5395 i32) + (local $l5396 i32) + (local $l5397 i32) + (local $l5398 i32) + (local $l5399 i32) + (local $l5400 i32) + (local $l5401 i32) + (local $l5402 i32) + (local $l5403 i32) + (local $l5404 i32) + (local $l5405 i32) + (local $l5406 i32) + (local $l5407 i32) + (local $l5408 i32) + (local $l5409 i32) + (local $l5410 i32) + (local $l5411 i32) + (local $l5412 i32) + (local $l5413 i32) + (local $l5414 i32) + (local $l5415 i32) + (local $l5416 i32) + (local $l5417 i32) + (local $l5418 i32) + (local $l5419 i32) + (local $l5420 i32) + (local $l5421 i32) + (local $l5422 i32) + (local $l5423 i32) + (local $l5424 i32) + (local $l5425 i32) + (local $l5426 i32) + (local $l5427 i32) + (local $l5428 i32) + (local $l5429 i32) + (local $l5430 i32) + (local $l5431 i32) + (local $l5432 i32) + (local $l5433 i32) + (local $l5434 i32) + (local $l5435 i32) + (local $l5436 i32) + (local $l5437 i32) + (local $l5438 i32) + (local $l5439 i32) + (local $l5440 i32) + (local $l5441 i32) + (local $l5442 i32) + (local $l5443 i32) + (local $l5444 i32) + (local $l5445 i32) + (local $l5446 i32) + (local $l5447 i32) + (local $l5448 i32) + (local $l5449 i32) + (local $l5450 i32) + (local $l5451 i32) + (local $l5452 i32) + (local $l5453 i32) + (local $l5454 i32) + (local $l5455 i32) + (local $l5456 i32) + (local $l5457 i32) + (local $l5458 i32) + (local $l5459 i32) + (local $l5460 i32) + (local $l5461 i32) + (local $l5462 i32) + (local $l5463 i32) + (local $l5464 i32) + (local $l5465 i32) + (local $l5466 i32) + (local $l5467 i32) + (local $l5468 i32) + (local $l5469 i32) + (local $l5470 i32) + (local $l5471 i32) + (local $l5472 i32) + (local $l5473 i32) + (local $l5474 i32) + (local $l5475 i32) + (local $l5476 i32) + (local $l5477 i32) + (local $l5478 i32) + (local $l5479 i32) + (local $l5480 i32) + (local $l5481 i32) + (local $l5482 i32) + (local $l5483 i32) + (local $l5484 i32) + (local $l5485 i32) + (local $l5486 i32) + (local $l5487 i32) + (local $l5488 i32) + (local $l5489 i32) + (local $l5490 i32) + (local $l5491 i32) + (local $l5492 i32) + (local $l5493 i32) + (local $l5494 i32) + (local $l5495 i32) + (local $l5496 i32) + (local $l5497 i32) + (local $l5498 i32) + (local $l5499 i32) + (local $l5500 i32) + (local $l5501 i32) + (local $l5502 i32) + (local $l5503 i32) + (local $l5504 i32) + (local $l5505 i32) + (local $l5506 i32) + (local $l5507 i32) + (local $l5508 i32) + (local $l5509 i32) + (local $l5510 i32) + (local $l5511 i32) + (local $l5512 i32) + (local $l5513 i32) + (local $l5514 i32) + (local $l5515 i32) + (local $l5516 i32) + (local $l5517 i32) + (local $l5518 i32) + (local $l5519 i32) + (local $l5520 i32) + (local $l5521 i32) + (local $l5522 i32) + (local $l5523 i32) + (local $l5524 i32) + (local $l5525 i32) + (local $l5526 i32) + (local $l5527 i32) + (local $l5528 i32) + (local $l5529 i32) + (local $l5530 i32) + (local $l5531 i32) + (local $l5532 i32) + (local $l5533 i32) + (local $l5534 i32) + (local $l5535 i32) + (local $l5536 i32) + (local $l5537 i32) + (local $l5538 i32) + (local $l5539 i32) + (local $l5540 i32) + (local $l5541 i32) + (local $l5542 i32) + (local $l5543 i32) + (local $l5544 i32) + (local $l5545 i32) + (local $l5546 i32) + (local $l5547 i32) + (local $l5548 i32) + (local $l5549 i32) + (local $l5550 i32) + (local $l5551 i32) + (local $l5552 i32) + (local $l5553 i32) + (local $l5554 i32) + (local $l5555 i32) + (local $l5556 i32) + (local $l5557 i32) + (local $l5558 i32) + (local $l5559 i32) + (local $l5560 i32) + (local $l5561 i32) + (local $l5562 i32) + (local $l5563 i32) + (local $l5564 i32) + (local $l5565 i32) + (local $l5566 i32) + (local $l5567 i32) + (local $l5568 i32) + (local $l5569 i32) + (local $l5570 i32) + (local $l5571 i32) + (local $l5572 i32) + (local $l5573 i32) + (local $l5574 i32) + (local $l5575 i32) + (local $l5576 i32) + (local $l5577 i32) + (local $l5578 i32) + (local $l5579 i32) + (local $l5580 i32) + (local $l5581 i32) + (local $l5582 i32) + (local $l5583 i32) + (local $l5584 i32) + (local $l5585 i32) + (local $l5586 i32) + (local $l5587 i32) + (local $l5588 i32) + (local $l5589 i32) + (local $l5590 i32) + (local $l5591 i32) + (local $l5592 i32) + (local $l5593 i32) + (local $l5594 i32) + (local $l5595 i32) + (local $l5596 i32) + (local $l5597 i32) + (local $l5598 i32) + (local $l5599 i32) + (local $l5600 i32) + (local $l5601 i32) + (local $l5602 i32) + (local $l5603 i32) + (local $l5604 i32) + (local $l5605 i32) + (local $l5606 i32) + (local $l5607 i32) + (local $l5608 i32) + (local $l5609 i32) + (local $l5610 i32) + (local $l5611 i32) + (local $l5612 i32) + (local $l5613 i32) + (local $l5614 i32) + (local $l5615 i32) + (local $l5616 i32) + (local $l5617 i32) + (local $l5618 i32) + (local $l5619 i32) + (local $l5620 i32) + (local $l5621 i32) + (local $l5622 i32) + (local $l5623 i32) + (local $l5624 i32) + (local $l5625 i32) + (local $l5626 i32) + (local $l5627 i32) + (local $l5628 i32) + (local $l5629 i32) + (local $l5630 i32) + (local $l5631 i32) + (local $l5632 i32) + (local $l5633 i32) + (local $l5634 i32) + (local $l5635 i32) + (local $l5636 i32) + (local $l5637 i32) + (local $l5638 i32) + (local $l5639 i32) + (local $l5640 i32) + (local $l5641 i32) + (local $l5642 i32) + (local $l5643 i32) + (local $l5644 i32) + (local $l5645 i32) + (local $l5646 i32) + (local $l5647 i32) + (local $l5648 i32) + (local $l5649 i32) + (local $l5650 i32) + (local $l5651 i32) + (local $l5652 i32) + (local $l5653 i32) + (local $l5654 i32) + (local $l5655 i32) + (local $l5656 i32) + (local $l5657 i32) + (local $l5658 i32) + (local $l5659 i32) + (local $l5660 i32) + (local $l5661 i32) + (local $l5662 i32) + (local $l5663 i32) + (local $l5664 i32) + (local $l5665 i32) + (local $l5666 i32) + (local $l5667 i32) + (local $l5668 i32) + (local $l5669 i32) + (local $l5670 i32) + (local $l5671 i32) + (local $l5672 i32) + (local $l5673 i32) + (local $l5674 i32) + (local $l5675 i32) + (local $l5676 i32) + (local $l5677 i32) + (local $l5678 i32) + (local $l5679 i32) + (local $l5680 i32) + (local $l5681 i32) + (local $l5682 i32) + (local $l5683 i32) + (local $l5684 i32) + (local $l5685 i32) + (local $l5686 i32) + (local $l5687 i32) + (local $l5688 i32) + (local $l5689 i32) + (local $l5690 i32) + (local $l5691 i32) + (local $l5692 i32) + (local $l5693 i32) + (local $l5694 i32) + (local $l5695 i32) + (local $l5696 i32) + (local $l5697 i32) + (local $l5698 i32) + (local $l5699 i32) + (local $l5700 i32) + (local $l5701 i32) + (local $l5702 i32) + (local $l5703 i32) + (local $l5704 i32) + (local $l5705 i32) + (local $l5706 i32) + (local $l5707 i32) + (local $l5708 i32) + (local $l5709 i32) + (local $l5710 i32) + (local $l5711 i32) + (local $l5712 i32) + (local $l5713 i32) + (local $l5714 i32) + (local $l5715 i32) + (local $l5716 i32) + (local $l5717 i32) + (local $l5718 i32) + (local $l5719 i32) + (local $l5720 i32) + (local $l5721 i32) + (local $l5722 i32) + (local $l5723 i32) + (local $l5724 i32) + (local $l5725 i32) + (local $l5726 i32) + (local $l5727 i32) + (local $l5728 i32) + (local $l5729 i32) + (local $l5730 i32) + (local $l5731 i32) + (local $l5732 i32) + (local $l5733 i32) + (local $l5734 i32) + (local $l5735 i32) + (local $l5736 i32) + (local $l5737 i32) + (local $l5738 i32) + (local $l5739 i32) + (local $l5740 i32) + (local $l5741 i32) + (local $l5742 i32) + (local $l5743 i32) + (local $l5744 i32) + (local $l5745 i32) + (local $l5746 i32) + (local $l5747 i32) + (local $l5748 i32) + (local $l5749 i32) + (local $l5750 i32) + (local $l5751 i32) + (local $l5752 i32) + (local $l5753 i32) + (local $l5754 i32) + (local $l5755 i32) + (local $l5756 i32) + (local $l5757 i32) + (local $l5758 i32) + (local $l5759 i32) + (local $l5760 i32) + (local $l5761 i32) + (local $l5762 i32) + (local $l5763 i32) + (local $l5764 i32) + (local $l5765 i32) + (local $l5766 i32) + (local $l5767 i32) + (local $l5768 i32) + (local $l5769 i32) + (local $l5770 i32) + (local $l5771 i32) + (local $l5772 i32) + (local $l5773 i32) + (local $l5774 i32) + (local $l5775 i32) + (local $l5776 i32) + (local $l5777 i32) + (local $l5778 i32) + (local $l5779 i32) + (local $l5780 i32) + (local $l5781 i32) + (local $l5782 i32) + (local $l5783 i32) + (local $l5784 i32) + (local $l5785 i32) + (local $l5786 i32) + (local $l5787 i32) + (local $l5788 i32) + (local $l5789 i32) + (local $l5790 i32) + (local $l5791 i32) + (local $l5792 i32) + (local $l5793 i32) + (local $l5794 i32) + (local $l5795 i32) + (local $l5796 i32) + (local $l5797 i32) + (local $l5798 i32) + (local $l5799 i32) + (local $l5800 i32) + (local $l5801 i32) + (local $l5802 i32) + (local $l5803 i32) + (local $l5804 i32) + (local $l5805 i32) + (local $l5806 i32) + (local $l5807 i32) + (local $l5808 i32) + (local $l5809 i32) + (local $l5810 i32) + (local $l5811 i32) + (local $l5812 i32) + (local $l5813 i32) + (local $l5814 i32) + (local $l5815 i32) + (local $l5816 i32) + (local $l5817 i32) + (local $l5818 i32) + (local $l5819 i32) + (local $l5820 i32) + (local $l5821 i32) + (local $l5822 i32) + (local $l5823 i32) + (local $l5824 i32) + (local $l5825 i32) + (local $l5826 i32) + (local $l5827 i32) + (local $l5828 i32) + (local $l5829 i32) + (local $l5830 i32) + (local $l5831 i32) + (local $l5832 i32) + (local $l5833 i32) + (local $l5834 i32) + (local $l5835 i32) + (local $l5836 i32) + (local $l5837 i32) + (local $l5838 i32) + (local $l5839 i32) + (local $l5840 i32) + (local $l5841 i32) + (local $l5842 i32) + (local $l5843 i32) + (local $l5844 i32) + (local $l5845 i32) + (local $l5846 i32) + (local $l5847 i32) + (local $l5848 i32) + (local $l5849 i32) + (local $l5850 i32) + (local $l5851 i32) + (local $l5852 i32) + (local $l5853 i32) + (local $l5854 i32) + (local $l5855 i32) + (local $l5856 i32) + (local $l5857 i32) + (local $l5858 i32) + (local $l5859 i32) + (local $l5860 i32) + (local $l5861 i32) + (local $l5862 i32) + (local $l5863 i32) + (local $l5864 i32) + (local $l5865 i32) + (local $l5866 i32) + (local $l5867 i32) + (local $l5868 i32) + (local $l5869 i32) + (local $l5870 i32) + (local $l5871 i32) + (local $l5872 i32) + (local $l5873 i32) + (local $l5874 i32) + (local $l5875 i32) + (local $l5876 i32) + (local $l5877 i32) + (local $l5878 i32) + (local $l5879 i32) + (local $l5880 i32) + (local $l5881 i32) + (local $l5882 i32) + (local $l5883 i32) + (local $l5884 i32) + (local $l5885 i32) + (local $l5886 i32) + (local $l5887 i32) + (local $l5888 i32) + (local $l5889 i32) + (local $l5890 i32) + (local $l5891 i32) + (local $l5892 i32) + (local $l5893 i32) + (local $l5894 i32) + (local $l5895 i32) + (local $l5896 i32) + (local $l5897 i32) + (local $l5898 i32) + (local $l5899 i32) + (local $l5900 i32) + (local $l5901 i32) + (local $l5902 i32) + (local $l5903 i32) + (local $l5904 i32) + (local $l5905 i32) + (local $l5906 i32) + (local $l5907 i32) + (local $l5908 i32) + (local $l5909 i32) + (local $l5910 i32) + (local $l5911 i32) + (local $l5912 i32) + (local $l5913 i32) + (local $l5914 i32) + (local $l5915 i32) + (local $l5916 i32) + (local $l5917 i32) + (local $l5918 i32) + (local $l5919 i32) + (local $l5920 i32) + (local $l5921 i32) + (local $l5922 i32) + (local $l5923 i32) + (local $l5924 i32) + (local $l5925 i32) + (local $l5926 i32) + (local $l5927 i32) + (local $l5928 i32) + (local $l5929 i32) + (local $l5930 i32) + (local $l5931 i32) + (local $l5932 i32) + (local $l5933 i32) + (local $l5934 i32) + (local $l5935 i32) + (local $l5936 i32) + (local $l5937 i32) + (local $l5938 i32) + (local $l5939 i32) + (local $l5940 i32) + (local $l5941 i32) + (local $l5942 i32) + (local $l5943 i32) + (local $l5944 i32) + (local $l5945 i32) + (local $l5946 i32) + (local $l5947 i32) + (local $l5948 i32) + (local $l5949 i32) + (local $l5950 i32) + (local $l5951 i32) + (local $l5952 i32) + (local $l5953 i32) + (local $l5954 i32) + (local $l5955 i32) + (local $l5956 i32) + (local $l5957 i32) + (local $l5958 i32) + (local $l5959 i32) + (local $l5960 i32) + (local $l5961 i32) + (local $l5962 i32) + (local $l5963 i32) + (local $l5964 i32) + (local $l5965 i32) + (local $l5966 i32) + (local $l5967 i32) + (local $l5968 i32) + (local $l5969 i32) + (local $l5970 i32) + (local $l5971 i32) + (local $l5972 i32) + (local $l5973 i32) + (local $l5974 i32) + (local $l5975 i32) + (local $l5976 i32) + (local $l5977 i32) + (local $l5978 i32) + (local $l5979 i32) + (local $l5980 i32) + (local $l5981 i32) + (local $l5982 i32) + (local $l5983 i32) + (local $l5984 i32) + (local $l5985 i32) + (local $l5986 i32) + (local $l5987 i32) + (local $l5988 i32) + (local $l5989 i32) + (local $l5990 i32) + (local $l5991 i32) + (local $l5992 i32) + (local $l5993 i32) + (local $l5994 i32) + (local $l5995 i32) + (local $l5996 i32) + (local $l5997 i32) + (local $l5998 i32) + (local $l5999 i32) + (local $l6000 i32) + (local $l6001 i32) + (local $l6002 i32) + (local $l6003 i32) + (local $l6004 i32) + (local $l6005 i32) + (local $l6006 i32) + (local $l6007 i32) + (local $l6008 i32) + (local $l6009 i32) + (local $l6010 i32) + (local $l6011 i32) + (local $l6012 i32) + (local $l6013 i32) + (local $l6014 i32) + (local $l6015 i32) + (local $l6016 i32) + (local $l6017 i32) + (local $l6018 i32) + (local $l6019 i32) + (local $l6020 i32) + (local $l6021 i32) + (local $l6022 i32) + (local $l6023 i32) + (local $l6024 i32) + (local $l6025 i32) + (local $l6026 i32) + (local $l6027 i32) + (local $l6028 i32) + (local $l6029 i32) + (local $l6030 i32) + (local $l6031 i32) + (local $l6032 i32) + (local $l6033 i32) + (local $l6034 i32) + (local $l6035 i32) + (local $l6036 i32) + (local $l6037 i32) + (local $l6038 i32) + (local $l6039 i32) + (local $l6040 i32) + (local $l6041 i32) + (local $l6042 i32) + (local $l6043 i32) + (local $l6044 i32) + (local $l6045 i32) + (local $l6046 i32) + (local $l6047 i32) + (local $l6048 i32) + (local $l6049 i32) + (local $l6050 i32) + (local $l6051 i32) + (local $l6052 i32) + (local $l6053 i32) + (local $l6054 i32) + (local $l6055 i32) + (local $l6056 i32) + (local $l6057 i32) + (local $l6058 i32) + (local $l6059 i32) + (local $l6060 i32) + (local $l6061 i32) + (local $l6062 i32) + (local $l6063 i32) + (local $l6064 i32) + (local $l6065 i32) + (local $l6066 i32) + (local $l6067 i32) + (local $l6068 i32) + (local $l6069 i32) + (local $l6070 i32) + (local $l6071 i32) + (local $l6072 i32) + (local $l6073 i32) + (local $l6074 i32) + (local $l6075 i32) + (local $l6076 i32) + (local $l6077 i32) + (local $l6078 i32) + (local $l6079 i32) + (local $l6080 i32) + (local $l6081 i32) + (local $l6082 i32) + (local $l6083 i32) + (local $l6084 i32) + (local $l6085 i32) + (local $l6086 i32) + (local $l6087 i32) + (local $l6088 i32) + (local $l6089 i32) + (local $l6090 i32) + (local $l6091 i32) + (local $l6092 i32) + (local $l6093 i32) + (local $l6094 i32) + (local $l6095 i32) + (local $l6096 i32) + (local $l6097 i32) + (local $l6098 i32) + (local $l6099 i32) + (local $l6100 i32) + (local $l6101 i32) + (local $l6102 i32) + (local $l6103 i32) + (local $l6104 i32) + (local $l6105 i32) + (local $l6106 i32) + (local $l6107 i32) + (local $l6108 i32) + (local $l6109 i32) + (local $l6110 i32) + (local $l6111 i32) + (local $l6112 i32) + (local $l6113 i32) + (local $l6114 i32) + (local $l6115 i32) + (local $l6116 i32) + (local $l6117 i32) + (local $l6118 i32) + (local $l6119 i32) + (local $l6120 i32) + (local $l6121 i32) + (local $l6122 i32) + (local $l6123 i32) + (local $l6124 i32) + (local $l6125 i32) + (local $l6126 i32) + (local $l6127 i32) + (local $l6128 i32) + (local $l6129 i32) + (local $l6130 i32) + (local $l6131 i32) + (local $l6132 i32) + (local $l6133 i32) + (local $l6134 i32) + (local $l6135 i32) + (local $l6136 i32) + (local $l6137 i32) + (local $l6138 i32) + (local $l6139 i32) + (local $l6140 i32) + (local $l6141 i32) + (local $l6142 i32) + (local $l6143 i32) + (local $l6144 i32) + (local $l6145 i32) + (local $l6146 i32) + (local $l6147 i32) + (local $l6148 i32) + (local $l6149 i32) + (local $l6150 i32) + (local $l6151 i32) + (local $l6152 i32) + (local $l6153 i32) + (local $l6154 i32) + (local $l6155 i32) + (local $l6156 i32) + (local $l6157 i32) + (local $l6158 i32) + (local $l6159 i32) + (local $l6160 i32) + (local $l6161 i32) + (local $l6162 i32) + (local $l6163 i32) + (local $l6164 i32) + (local $l6165 i32) + (local $l6166 i32) + (local $l6167 i32) + (local $l6168 i32) + (local $l6169 i32) + (local $l6170 i32) + (local $l6171 i32) + (local $l6172 i32) + (local $l6173 i32) + (local $l6174 i32) + (local $l6175 i32) + (local $l6176 i32) + (local $l6177 i32) + (local $l6178 i32) + (local $l6179 i32) + (local $l6180 i32) + (local $l6181 i32) + (local $l6182 i32) + (local $l6183 i32) + (local $l6184 i32) + (local $l6185 i32) + (local $l6186 i32) + (local $l6187 i32) + (local $l6188 i32) + (local $l6189 i32) + (local $l6190 i32) + (local $l6191 i32) + (local $l6192 i32) + (local $l6193 i32) + (local $l6194 i32) + (local $l6195 i32) + (local $l6196 i32) + (local $l6197 i32) + (local $l6198 i32) + (local $l6199 i32) + (local $l6200 i32) + (local $l6201 i32) + (local $l6202 i32) + (local $l6203 i32) + (local $l6204 i32) + (local $l6205 i32) + (local $l6206 i32) + (local $l6207 i32) + (local $l6208 i32) + (local $l6209 i32) + (local $l6210 i32) + (local $l6211 i32) + (local $l6212 i32) + (local $l6213 i32) + (local $l6214 i32) + (local $l6215 i32) + (local $l6216 i32) + (local $l6217 i32) + (local $l6218 i32) + (local $l6219 i32) + (local $l6220 i32) + (local $l6221 i32) + (local $l6222 i32) + (local $l6223 i32) + (local $l6224 i32) + (local $l6225 i32) + (local $l6226 i32) + (local $l6227 i32) + (local $l6228 i32) + (local $l6229 i32) + (local $l6230 i32) + (local $l6231 i32) + (local $l6232 i32) + (local $l6233 i32) + (local $l6234 i32) + (local $l6235 i32) + (local $l6236 i32) + (local $l6237 i32) + (local $l6238 i32) + (local $l6239 i32) + (local $l6240 i32) + (local $l6241 i32) + (local $l6242 i32) + (local $l6243 i32) + (local $l6244 i32) + (local $l6245 i32) + (local $l6246 i32) + (local $l6247 i32) + (local $l6248 i32) + (local $l6249 i32) + (local $l6250 i32) + (local $l6251 i32) + (local $l6252 i32) + (local $l6253 i32) + (local $l6254 i32) + (local $l6255 i32) + (local $l6256 i32) + (local $l6257 i32) + (local $l6258 i32) + (local $l6259 i32) + (local $l6260 i32) + (local $l6261 i32) + (local $l6262 i32) + (local $l6263 i32) + (local $l6264 i32) + (local $l6265 i32) + (local $l6266 i32) + (local $l6267 i32) + (local $l6268 i32) + (local $l6269 i32) + (local $l6270 i32) + (local $l6271 i32) + (local $l6272 i32) + (local $l6273 i32) + (local $l6274 i32) + (local $l6275 i32) + (local $l6276 i32) + (local $l6277 i32) + (local $l6278 i32) + (local $l6279 i32) + (local $l6280 i32) + (local $l6281 i32) + (local $l6282 i32) + (local $l6283 i32) + (local $l6284 i32) + (local $l6285 i32) + (local $l6286 i32) + (local $l6287 i32) + (local $l6288 i32) + (local $l6289 i32) + (local $l6290 i32) + (local $l6291 i32) + (local $l6292 i32) + (local $l6293 i32) + (local $l6294 i32) + (local $l6295 i32) + (local $l6296 i32) + (local $l6297 i32) + (local $l6298 i32) + (local $l6299 i32) + (local $l6300 i32) + (local $l6301 i32) + (local $l6302 i32) + (local $l6303 i32) + (local $l6304 i32) + (local $l6305 i32) + (local $l6306 i32) + (local $l6307 i32) + (local $l6308 i32) + (local $l6309 i32) + (local $l6310 i32) + (local $l6311 i32) + (local $l6312 i32) + (local $l6313 i32) + (local $l6314 i32) + (local $l6315 i32) + (local $l6316 i32) + (local $l6317 i32) + (local $l6318 i32) + (local $l6319 i32) + (local $l6320 i32) + (local $l6321 i32) + (local $l6322 i32) + (local $l6323 i32) + (local $l6324 i32) + (local $l6325 i32) + (local $l6326 i32) + (local $l6327 i32) + (local $l6328 i32) + (local $l6329 i32) + (local $l6330 i32) + (local $l6331 i32) + (local $l6332 i32) + (local $l6333 i32) + (local $l6334 i32) + (local $l6335 i32) + (local $l6336 i32) + (local $l6337 i32) + (local $l6338 i32) + (local $l6339 i32) + (local $l6340 i32) + (local $l6341 i32) + (local $l6342 i32) + (local $l6343 i32) + (local $l6344 i32) + (local $l6345 i32) + (local $l6346 i32) + (local $l6347 i32) + (local $l6348 i32) + (local $l6349 i32) + (local $l6350 i32) + (local $l6351 i32) + (local $l6352 i32) + (local $l6353 i32) + (local $l6354 i32) + (local $l6355 i32) + (local $l6356 i32) + (local $l6357 i32) + (local $l6358 i32) + (local $l6359 i32) + (local $l6360 i32) + (local $l6361 i32) + (local $l6362 i32) + (local $l6363 i32) + (local $l6364 i32) + (local $l6365 i32) + (local $l6366 i32) + (local $l6367 i32) + (local $l6368 i32) + (local $l6369 i32) + (local $l6370 i32) + (local $l6371 i32) + (local $l6372 i32) + (local $l6373 i32) + (local $l6374 i32) + (local $l6375 i32) + (local $l6376 i32) + (local $l6377 i32) + (local $l6378 i32) + (local $l6379 i32) + (local $l6380 i32) + (local $l6381 i32) + (local $l6382 i32) + (local $l6383 i32) + (local $l6384 i32) + (local $l6385 i32) + (local $l6386 i32) + (local $l6387 i32) + (local $l6388 i32) + (local $l6389 i32) + (local $l6390 i32) + (local $l6391 i32) + (local $l6392 i32) + (local $l6393 i32) + (local $l6394 i32) + (local $l6395 i32) + (local $l6396 i32) + (local $l6397 i32) + (local $l6398 i32) + (local $l6399 i32) + (local $l6400 i32) + (local $l6401 i32) + (local $l6402 i32) + (local $l6403 i32) + (local $l6404 i32) + (local $l6405 i32) + (local $l6406 i32) + (local $l6407 i32) + (local $l6408 i32) + (local $l6409 i32) + (local $l6410 i32) + (local $l6411 i32) + (local $l6412 i32) + (local $l6413 i32) + (local $l6414 i32) + (local $l6415 i32) + (local $l6416 i32) + (local $l6417 i32) + (local $l6418 i32) + (local $l6419 i32) + (local $l6420 i32) + (local $l6421 i32) + (local $l6422 i32) + (local $l6423 i32) + (local $l6424 i32) + (local $l6425 i32) + (local $l6426 i32) + (local $l6427 i32) + (local $l6428 i32) + (local $l6429 i32) + (local $l6430 i32) + (local $l6431 i32) + (local $l6432 i32) + (local $l6433 i32) + (local $l6434 i32) + (local $l6435 i32) + (local $l6436 i32) + (local $l6437 i32) + (local $l6438 i32) + (local $l6439 i32) + (local $l6440 i32) + (local $l6441 i32) + (local $l6442 i32) + (local $l6443 i32) + (local $l6444 i32) + (local $l6445 i32) + (local $l6446 i32) + (local $l6447 i32) + (local $l6448 i32) + (local $l6449 i32) + (local $l6450 i32) + (local $l6451 i32) + (local $l6452 i32) + (local $l6453 i32) + (local $l6454 i32) + (local $l6455 i32) + (local $l6456 i32) + (local $l6457 i32) + (local $l6458 i32) + (local $l6459 i32) + (local $l6460 i32) + (local $l6461 i32) + (local $l6462 i32) + (local $l6463 i32) + (local $l6464 i32) + (local $l6465 i32) + (local $l6466 i32) + (local $l6467 i32) + (local $l6468 i32) + (local $l6469 i32) + (local $l6470 i32) + (local $l6471 i32) + (local $l6472 i32) + (local $l6473 i32) + (local $l6474 i32) + (local $l6475 i32) + (local $l6476 i32) + (local $l6477 i32) + (local $l6478 i32) + (local $l6479 i32) + (local $l6480 i32) + (local $l6481 i32) + (local $l6482 i32) + (local $l6483 i32) + (local $l6484 i32) + (local $l6485 i32) + (local $l6486 i32) + (local $l6487 i32) + (local $l6488 i32) + (local $l6489 i32) + (local $l6490 i32) + (local $l6491 i32) + (local $l6492 i32) + (local $l6493 i32) + (local $l6494 i32) + (local $l6495 i32) + (local $l6496 i32) + (local $l6497 i32) + (local $l6498 i32) + (local $l6499 i32) + (local $l6500 i32) + (local $l6501 i32) + (local $l6502 i32) + (local $l6503 i32) + (local $l6504 i32) + (local $l6505 i32) + (local $l6506 i32) + (local $l6507 i32) + (local $l6508 i32) + (local $l6509 i32) + (local $l6510 i32) + (local $l6511 i32) + (local $l6512 i32) + (local $l6513 i32) + (local $l6514 i32) + (local $l6515 i32) + (local $l6516 i32) + (local $l6517 i32) + (local $l6518 i32) + (local $l6519 i32) + (local $l6520 i32) + (local $l6521 i32) + (local $l6522 i32) + (local $l6523 i32) + (local $l6524 i32) + (local $l6525 i32) + (local $l6526 i32) + (local $l6527 i32) + (local $l6528 i32) + (local $l6529 i32) + (local $l6530 i32) + (local $l6531 i32) + (local $l6532 i32) + (local $l6533 i32) + (local $l6534 i32) + (local $l6535 i32) + (local $l6536 i32) + (local $l6537 i32) + (local $l6538 i32) + (local $l6539 i32) + (local $l6540 i32) + (local $l6541 i32) + (local $l6542 i32) + (local $l6543 i32) + (local $l6544 i32) + (local $l6545 i32) + (local $l6546 i32) + (local $l6547 i32) + (local $l6548 i32) + (local $l6549 i32) + (local $l6550 i32) + (local $l6551 i32) + (local $l6552 i32) + (local $l6553 i32) + (local $l6554 i32) + (local $l6555 i32) + (local $l6556 i32) + (local $l6557 i32) + (local $l6558 i32) + (local $l6559 i32) + (local $l6560 i32) + (local $l6561 i32) + (local $l6562 i32) + (local $l6563 i32) + (local $l6564 i32) + (local $l6565 i32) + (local $l6566 i32) + (local $l6567 i32) + (local $l6568 i32) + (local $l6569 i32) + (local $l6570 i32) + (local $l6571 i32) + (local $l6572 i32) + (local $l6573 i32) + (local $l6574 i32) + (local $l6575 i32) + (local $l6576 i32) + (local $l6577 i32) + (local $l6578 i32) + (local $l6579 i32) + (local $l6580 i32) + (local $l6581 i32) + (local $l6582 i32) + (local $l6583 i32) + (local $l6584 i32) + (local $l6585 i32) + (local $l6586 i32) + (local $l6587 i32) + (local $l6588 i32) + (local $l6589 i32) + (local $l6590 i32) + (local $l6591 i32) + (local $l6592 i32) + (local $l6593 i32) + (local $l6594 i32) + (local $l6595 i32) + (local $l6596 i32) + (local $l6597 i32) + (local $l6598 i32) + (local $l6599 i32) + (local $l6600 i32) + (local $l6601 i32) + (local $l6602 i32) + (local $l6603 i32) + (local $l6604 i32) + (local $l6605 i32) + (local $l6606 i32) + (local $l6607 i32) + (local $l6608 i32) + (local $l6609 i32) + (local $l6610 i32) + (local $l6611 i32) + (local $l6612 i32) + (local $l6613 i32) + (local $l6614 i32) + (local $l6615 i32) + (local $l6616 i32) + (local $l6617 i32) + (local $l6618 i32) + (local $l6619 i32) + (local $l6620 i32) + (local $l6621 i32) + (local $l6622 i32) + (local $l6623 i32) + (local $l6624 i32) + (local $l6625 i32) + (local $l6626 i32) + (local $l6627 i32) + (local $l6628 i32) + (local $l6629 i32) + (local $l6630 i32) + (local $l6631 i32) + (local $l6632 i32) + (local $l6633 i32) + (local $l6634 i32) + (local $l6635 i32) + (local $l6636 i32) + (local $l6637 i32) + (local $l6638 i32) + (local $l6639 i32) + (local $l6640 i32) + (local $l6641 i32) + (local $l6642 i32) + (local $l6643 i32) + (local $l6644 i32) + (local $l6645 i32) + (local $l6646 i32) + (local $l6647 i32) + (local $l6648 i32) + (local $l6649 i32) + (local $l6650 i32) + (local $l6651 i32) + (local $l6652 i32) + (local $l6653 i32) + (local $l6654 i32) + (local $l6655 i32) + (local $l6656 i32) + (local $l6657 i32) + (local $l6658 i32) + (local $l6659 i32) + (local $l6660 i32) + (local $l6661 i32) + (local $l6662 i32) + (local $l6663 i32) + (local $l6664 i32) + (local $l6665 i32) + (local $l6666 i32) + (local $l6667 i32) + (local $l6668 i32) + (local $l6669 i32) + (local $l6670 i32) + (local $l6671 i32) + (local $l6672 i32) + (local $l6673 i32) + (local $l6674 i32) + (local $l6675 i32) + (local $l6676 i32) + (local $l6677 i32) + (local $l6678 i32) + (local $l6679 i32) + (local $l6680 i32) + (local $l6681 i32) + (local $l6682 i32) + (local $l6683 i32) + (local $l6684 i32) + (local $l6685 i32) + (local $l6686 i32) + (local $l6687 i32) + (local $l6688 i32) + (local $l6689 i32) + (local $l6690 i32) + (local $l6691 i32) + (local $l6692 i32) + (local $l6693 i32) + (local $l6694 i32) + (local $l6695 i32) + (local $l6696 i32) + (local $l6697 i32) + (local $l6698 i32) + (local $l6699 i32) + (local $l6700 i32) + (local $l6701 i32) + (local $l6702 i32) + (local $l6703 i32) + (local $l6704 i32) + (local $l6705 i32) + (local $l6706 i32) + (local $l6707 i32) + (local $l6708 i32) + (local $l6709 i32) + (local $l6710 i32) + (local $l6711 i32) + (local $l6712 i32) + (local $l6713 i32) + (local $l6714 i32) + (local $l6715 i32) + (local $l6716 i32) + (local $l6717 i32) + (local $l6718 i32) + (local $l6719 i32) + (local $l6720 i32) + (local $l6721 i32) + (local $l6722 i32) + (local $l6723 i32) + (local $l6724 i32) + (local $l6725 i32) + (local $l6726 i32) + (local $l6727 i32) + (local $l6728 i32) + (local $l6729 i32) + (local $l6730 i32) + (local $l6731 i32) + (local $l6732 i32) + (local $l6733 i32) + (local $l6734 i32) + (local $l6735 i32) + (local $l6736 i32) + (local $l6737 i32) + (local $l6738 i32) + (local $l6739 i32) + (local $l6740 i32) + (local $l6741 i32) + (local $l6742 i32) + (local $l6743 i32) + (local $l6744 i32) + (local $l6745 i32) + (local $l6746 i32) + (local $l6747 i32) + (local $l6748 i32) + (local $l6749 i32) + (local $l6750 i32) + (local $l6751 i32) + (local $l6752 i32) + (local $l6753 i32) + (local $l6754 i32) + (local $l6755 i32) + (local $l6756 i32) + (local $l6757 i32) + (local $l6758 i32) + (local $l6759 i32) + (local $l6760 i32) + (local $l6761 i32) + (local $l6762 i32) + (local $l6763 i32) + (local $l6764 i32) + (local $l6765 i32) + (local $l6766 i32) + (local $l6767 i32) + (local $l6768 i32) + (local $l6769 i32) + (local $l6770 i32) + (local $l6771 i32) + (local $l6772 i32) + (local $l6773 i32) + (local $l6774 i32) + (local $l6775 i32) + (local $l6776 i32) + (local $l6777 i32) + (local $l6778 i32) + (local $l6779 i32) + (local $l6780 i32) + (local $l6781 i32) + (local $l6782 i32) + (local $l6783 i32) + (local $l6784 i32) + (local $l6785 i32) + (local $l6786 i32) + (local $l6787 i32) + (local $l6788 i32) + (local $l6789 i32) + (local $l6790 i32) + (local $l6791 i32) + (local $l6792 i32) + (local $l6793 i32) + (local $l6794 i32) + (local $l6795 i32) + (local $l6796 i32) + (local $l6797 i32) + (local $l6798 i32) + (local $l6799 i32) + (local $l6800 i32) + (local $l6801 i32) + (local $l6802 i32) + (local $l6803 i32) + (local $l6804 i32) + (local $l6805 i32) + (local $l6806 i32) + (local $l6807 i32) + (local $l6808 i32) + (local $l6809 i32) + (local $l6810 i32) + (local $l6811 i32) + (local $l6812 i32) + (local $l6813 i32) + (local $l6814 i32) + (local $l6815 i32) + (local $l6816 i32) + (local $l6817 i32) + (local $l6818 i32) + (local $l6819 i32) + (local $l6820 i32) + (local $l6821 i32) + (local $l6822 i32) + (local $l6823 i32) + (local $l6824 i32) + (local $l6825 i32) + (local $l6826 i32) + (local $l6827 i32) + (local $l6828 i32) + (local $l6829 i32) + (local $l6830 i32) + (local $l6831 i32) + (local $l6832 i32) + (local $l6833 i32) + (local $l6834 i32) + (local $l6835 i32) + (local $l6836 i32) + (local $l6837 i32) + (local $l6838 i32) + (local $l6839 i32) + (local $l6840 i32) + (local $l6841 i32) + (local $l6842 i32) + (local $l6843 i32) + (local $l6844 i32) + (local $l6845 i32) + (local $l6846 i32) + (local $l6847 i32) + (local $l6848 i32) + (local $l6849 i32) + (local $l6850 i32) + (local $l6851 i32) + (local $l6852 i32) + (local $l6853 i32) + (local $l6854 i32) + (local $l6855 i32) + (local $l6856 i32) + (local $l6857 i32) + (local $l6858 i32) + (local $l6859 i32) + (local $l6860 i32) + (local $l6861 i32) + (local $l6862 i32) + (local $l6863 i32) + (local $l6864 i32) + (local $l6865 i32) + (local $l6866 i32) + (local $l6867 i32) + (local $l6868 i32) + (local $l6869 i32) + (local $l6870 i32) + (local $l6871 i32) + (local $l6872 i32) + (local $l6873 i32) + (local $l6874 i32) + (local $l6875 i32) + (local $l6876 i32) + (local $l6877 i32) + (local $l6878 i32) + (local $l6879 i32) + (local $l6880 i32) + (local $l6881 i32) + (local $l6882 i32) + (local $l6883 i32) + (local $l6884 i32) + (local $l6885 i32) + (local $l6886 i32) + (local $l6887 i32) + (local $l6888 i32) + (local $l6889 i32) + (local $l6890 i32) + (local $l6891 i32) + (local $l6892 i32) + (local $l6893 i32) + (local $l6894 i32) + (local $l6895 i32) + (local $l6896 i32) + (local $l6897 i32) + (local $l6898 i32) + (local $l6899 i32) + (local $l6900 i32) + (local $l6901 i32) + (local $l6902 i32) + (local $l6903 i32) + (local $l6904 i32) + (local $l6905 i32) + (local $l6906 i32) + (local $l6907 i32) + (local $l6908 i32) + (local $l6909 i32) + (local $l6910 i32) + (local $l6911 i32) + (local $l6912 i32) + (local $l6913 i32) + (local $l6914 i32) + (local $l6915 i32) + (local $l6916 i32) + (local $l6917 i32) + (local $l6918 i32) + (local $l6919 i32) + (local $l6920 i32) + (local $l6921 i32) + (local $l6922 i32) + (local $l6923 i32) + (local $l6924 i32) + (local $l6925 i32) + (local $l6926 i32) + (local $l6927 i32) + (local $l6928 i32) + (local $l6929 i32) + (local $l6930 i32) + (local $l6931 i32) + (local $l6932 i32) + (local $l6933 i32) + (local $l6934 i32) + (local $l6935 i32) + (local $l6936 i32) + (local $l6937 i32) + (local $l6938 i32) + (local $l6939 i32) + (local $l6940 i32) + (local $l6941 i32) + (local $l6942 i32) + (local $l6943 i32) + (local $l6944 i32) + (local $l6945 i32) + (local $l6946 i32) + (local $l6947 i32) + (local $l6948 i32) + (local $l6949 i32) + (local $l6950 i32) + (local $l6951 i32) + (local $l6952 i32) + (local $l6953 i32) + (local $l6954 i32) + (local $l6955 i32) + (local $l6956 i32) + (local $l6957 i32) + (local $l6958 i32) + (local $l6959 i32) + (local $l6960 i32) + (local $l6961 i32) + (local $l6962 i32) + (local $l6963 i32) + (local $l6964 i32) + (local $l6965 i32) + (local $l6966 i32) + (local $l6967 i32) + (local $l6968 i32) + (local $l6969 i32) + (local $l6970 i32) + (local $l6971 i32) + (local $l6972 i32) + (local $l6973 i32) + (local $l6974 i32) + (local $l6975 i32) + (local $l6976 i32) + (local $l6977 i32) + (local $l6978 i32) + (local $l6979 i32) + (local $l6980 i32) + (local $l6981 i32) + (local $l6982 i32) + (local $l6983 i32) + (local $l6984 i32) + (local $l6985 i32) + (local $l6986 i32) + (local $l6987 i32) + (local $l6988 i32) + (local $l6989 i32) + (local $l6990 i32) + (local $l6991 i32) + (local $l6992 i32) + (local $l6993 i32) + (local $l6994 i32) + (local $l6995 i32) + (local $l6996 i32) + (local $l6997 i32) + (local $l6998 i32) + (local $l6999 i32) + (local $l7000 i32) + (local $l7001 i32) + (local $l7002 i32) + (local $l7003 i32) + (local $l7004 i32) + (local $l7005 i32) + (local $l7006 i32) + (local $l7007 i32) + (local $l7008 i32) + (local $l7009 i32) + (local $l7010 i32) + (local $l7011 i32) + (local $l7012 i32) + (local $l7013 i32) + (local $l7014 i32) + (local $l7015 i32) + (local $l7016 i32) + (local $l7017 i32) + (local $l7018 i32) + (local $l7019 i32) + (local $l7020 i32) + (local $l7021 i32) + (local $l7022 i32) + (local $l7023 i32) + (local $l7024 i32) + (local $l7025 i32) + (local $l7026 i32) + (local $l7027 i32) + (local $l7028 i32) + (local $l7029 i32) + (local $l7030 i32) + (local $l7031 i32) + (local $l7032 i32) + (local $l7033 i32) + (local $l7034 i32) + (local $l7035 i32) + (local $l7036 i32) + (local $l7037 i32) + (local $l7038 i32) + (local $l7039 i32) + (local $l7040 i32) + (local $l7041 i32) + (local $l7042 i32) + (local $l7043 i32) + (local $l7044 i32) + (local $l7045 i32) + (local $l7046 i32) + (local $l7047 i32) + (local $l7048 i32) + (local $l7049 i32) + (local $l7050 i32) + (local $l7051 i32) + (local $l7052 i32) + (local $l7053 i32) + (local $l7054 i32) + (local $l7055 i32) + (local $l7056 i32) + (local $l7057 i32) + (local $l7058 i32) + (local $l7059 i32) + (local $l7060 i32) + (local $l7061 i32) + (local $l7062 i32) + (local $l7063 i32) + (local $l7064 i32) + (local $l7065 i32) + (local $l7066 i32) + (local $l7067 i32) + (local $l7068 i32) + (local $l7069 i32) + (local $l7070 i32) + (local $l7071 i32) + (local $l7072 i32) + (local $l7073 i32) + (local $l7074 i32) + (local $l7075 i32) + (local $l7076 i32) + (local $l7077 i32) + (local $l7078 i32) + (local $l7079 i32) + (local $l7080 i32) + (local $l7081 i32) + (local $l7082 i32) + (local $l7083 i32) + (local $l7084 i32) + (local $l7085 i32) + (local $l7086 i32) + (local $l7087 i32) + (local $l7088 i32) + (local $l7089 i32) + (local $l7090 i32) + (local $l7091 i32) + (local $l7092 i32) + (local $l7093 i32) + (local $l7094 i32) + (local $l7095 i32) + (local $l7096 i32) + (local $l7097 i32) + (local $l7098 i32) + (local $l7099 i32) + (local $l7100 i32) + (local $l7101 i32) + (local $l7102 i32) + (local $l7103 i32) + (local $l7104 i32) + (local $l7105 i32) + (local $l7106 i32) + (local $l7107 i32) + (local $l7108 i32) + (local $l7109 i32) + (local $l7110 i32) + (local $l7111 i32) + (local $l7112 i32) + (local $l7113 i32) + (local $l7114 i32) + (local $l7115 i32) + (local $l7116 i32) + (local $l7117 i32) + (local $l7118 i32) + (local $l7119 i32) + (local $l7120 i32) + (local $l7121 i32) + (local $l7122 i32) + (local $l7123 i32) + (local $l7124 i32) + (local $l7125 i32) + (local $l7126 i32) + (local $l7127 i32) + (local $l7128 i32) + (local $l7129 i32) + (local $l7130 i32) + (local $l7131 i32) + (local $l7132 i32) + (local $l7133 i32) + (local $l7134 i32) + (local $l7135 i32) + (local $l7136 i32) + (local $l7137 i32) + (local $l7138 i32) + (local $l7139 i32) + (local $l7140 i32) + (local $l7141 i32) + (local $l7142 i32) + (local $l7143 i32) + (local $l7144 i32) + (local $l7145 i32) + (local $l7146 i32) + (local $l7147 i32) + (local $l7148 i32) + (local $l7149 i32) + (local $l7150 i32) + (local $l7151 i32) + (local $l7152 i32) + (local $l7153 i32) + (local $l7154 i32) + (local $l7155 i32) + (local $l7156 i32) + (local $l7157 i32) + (local $l7158 i32) + (local $l7159 i32) + (local $l7160 i32) + (local $l7161 i32) + (local $l7162 i32) + (local $l7163 i32) + (local $l7164 i32) + (local $l7165 i32) + (local $l7166 i32) + (local $l7167 i32) + (local $l7168 i32) + (local $l7169 i32) + (local $l7170 i32) + (local $l7171 i32) + (local $l7172 i32) + (local $l7173 i32) + (local $l7174 i32) + (local $l7175 i32) + (local $l7176 i32) + (local $l7177 i32) + (local $l7178 i32) + (local $l7179 i32) + (local $l7180 i32) + (local $l7181 i32) + (local $l7182 i32) + (local $l7183 i32) + (local $l7184 i32) + (local $l7185 i32) + (local $l7186 i32) + (local $l7187 i32) + (local $l7188 i32) + (local $l7189 i32) + (local $l7190 i32) + (local $l7191 i32) + (local $l7192 i32) + (local $l7193 i32) + (local $l7194 i32) + (local $l7195 i32) + (local $l7196 i32) + (local $l7197 i32) + (local $l7198 i32) + (local $l7199 i32) + (local $l7200 i32) + (local $l7201 i32) + (local $l7202 i32) + (local $l7203 i32) + (local $l7204 i32) + (local $l7205 i32) + (local $l7206 i32) + (local $l7207 i32) + (local $l7208 i32) + (local $l7209 i32) + (local $l7210 i32) + (local $l7211 i32) + (local $l7212 i32) + (local $l7213 i32) + (local $l7214 i32) + (local $l7215 i32) + (local $l7216 i32) + (local $l7217 i32) + (local $l7218 i32) + (local $l7219 i32) + (local $l7220 i32) + (local $l7221 i32) + (local $l7222 i32) + (local $l7223 i32) + (local $l7224 i32) + (local $l7225 i32) + (local $l7226 i32) + (local $l7227 i32) + (local $l7228 i32) + (local $l7229 i32) + (local $l7230 i32) + (local $l7231 i32) + (local $l7232 i32) + (local $l7233 i32) + (local $l7234 i32) + (local $l7235 i32) + (local $l7236 i32) + (local $l7237 i32) + (local $l7238 i32) + (local $l7239 i32) + (local $l7240 i32) + (local $l7241 i32) + (local $l7242 i32) + (local $l7243 i32) + (local $l7244 i32) + (local $l7245 i32) + (local $l7246 i32) + (local $l7247 i32) + (local $l7248 i32) + (local $l7249 i32) + (local $l7250 i32) + (local $l7251 i32) + (local $l7252 i32) + (local $l7253 i32) + (local $l7254 i32) + (local $l7255 i32) + (local $l7256 i32) + (local $l7257 i32) + (local $l7258 i32) + (local $l7259 i32) + (local $l7260 i32) + (local $l7261 i32) + (local $l7262 i32) + (local $l7263 i32) + (local $l7264 i32) + (local $l7265 i32) + (local $l7266 i32) + (local $l7267 i32) + (local $l7268 i32) + (local $l7269 i32) + (local $l7270 i32) + (local $l7271 i32) + (local $l7272 i32) + (local $l7273 i32) + (local $l7274 i32) + (local $l7275 i32) + (local $l7276 i32) + (local $l7277 i32) + (local $l7278 i32) + (local $l7279 i32) + (local $l7280 i32) + (local $l7281 i32) + (local $l7282 i32) + (local $l7283 i32) + (local $l7284 i32) + (local $l7285 i32) + (local $l7286 i32) + (local $l7287 i32) + (local $l7288 i32) + (local $l7289 i32) + (local $l7290 i32) + (local $l7291 i32) + (local $l7292 i32) + (local $l7293 i32) + (local $l7294 i32) + (local $l7295 i32) + (local $l7296 i32) + (local $l7297 i32) + (local $l7298 i32) + (local $l7299 i32) + (local $l7300 i32) + (local $l7301 i32) + (local $l7302 i32) + (local $l7303 i32) + (local $l7304 i32) + (local $l7305 i32) + (local $l7306 i32) + (local $l7307 i32) + (local $l7308 i32) + (local $l7309 i32) + (local $l7310 i32) + (local $l7311 i32) + (local $l7312 i32) + (local $l7313 i32) + (local $l7314 i32) + (local $l7315 i32) + (local $l7316 i32) + (local $l7317 i32) + (local $l7318 i32) + (local $l7319 i32) + (local $l7320 i32) + (local $l7321 i32) + (local $l7322 i32) + (local $l7323 i32) + (local $l7324 i32) + (local $l7325 i32) + (local $l7326 i32) + (local $l7327 i32) + (local $l7328 i32) + (local $l7329 i32) + (local $l7330 i32) + (local $l7331 i32) + (local $l7332 i32) + (local $l7333 i32) + (local $l7334 i32) + (local $l7335 i32) + (local $l7336 i32) + (local $l7337 i32) + (local $l7338 i32) + (local $l7339 i32) + (local $l7340 i32) + (local $l7341 i32) + (local $l7342 i32) + (local $l7343 i32) + (local $l7344 i32) + (local $l7345 i32) + (local $l7346 i32) + (local $l7347 i32) + (local $l7348 i32) + (local $l7349 i32) + (local $l7350 i32) + (local $l7351 i32) + (local $l7352 i32) + (local $l7353 i32) + (local $l7354 i32) + (local $l7355 i32) + (local $l7356 i32) + (local $l7357 i32) + (local $l7358 i32) + (local $l7359 i32) + (local $l7360 i32) + (local $l7361 i32) + (local $l7362 i32) + (local $l7363 i32) + (local $l7364 i32) + (local $l7365 i32) + (local $l7366 i32) + (local $l7367 i32) + (local $l7368 i32) + (local $l7369 i32) + (local $l7370 i32) + (local $l7371 i32) + (local $l7372 i32) + (local $l7373 i32) + (local $l7374 i32) + (local $l7375 i32) + (local $l7376 i32) + (local $l7377 i32) + (local $l7378 i32) + (local $l7379 i32) + (local $l7380 i32) + (local $l7381 i32) + (local $l7382 i32) + (local $l7383 i32) + (local $l7384 i32) + (local $l7385 i32) + (local $l7386 i32) + (local $l7387 i32) + (local $l7388 i32) + (local $l7389 i32) + (local $l7390 i32) + (local $l7391 i32) + (local $l7392 i32) + (local $l7393 i32) + (local $l7394 i32) + (local $l7395 i32) + (local $l7396 i32) + (local $l7397 i32) + (local $l7398 i32) + (local $l7399 i32) + (local $l7400 i32) + (local $l7401 i32) + (local $l7402 i32) + (local $l7403 i32) + (local $l7404 i32) + (local $l7405 i32) + (local $l7406 i32) + (local $l7407 i32) + (local $l7408 i32) + (local $l7409 i32) + (local $l7410 i32) + (local $l7411 i32) + (local $l7412 i32) + (local $l7413 i32) + (local $l7414 i32) + (local $l7415 i32) + (local $l7416 i32) + (local $l7417 i32) + (local $l7418 i32) + (local $l7419 i32) + (local $l7420 i32) + (local $l7421 i32) + (local $l7422 i32) + (local $l7423 i32) + (local $l7424 i32) + (local $l7425 i32) + (local $l7426 i32) + (local $l7427 i32) + (local $l7428 i32) + (local $l7429 i32) + (local $l7430 i32) + (local $l7431 i32) + (local $l7432 i32) + (local $l7433 i32) + (local $l7434 i32) + (local $l7435 i32) + (local $l7436 i32) + (local $l7437 i32) + (local $l7438 i32) + (local $l7439 i32) + (local $l7440 i32) + (local $l7441 i32) + (local $l7442 i32) + (local $l7443 i32) + (local $l7444 i32) + (local $l7445 i32) + (local $l7446 i32) + (local $l7447 i32) + (local $l7448 i32) + (local $l7449 i32) + (local $l7450 i32) + (local $l7451 i32) + (local $l7452 i32) + (local $l7453 i32) + (local $l7454 i32) + (local $l7455 i32) + (local $l7456 i32) + (local $l7457 i32) + (local $l7458 i32) + (local $l7459 i32) + (local $l7460 i32) + (local $l7461 i32) + (local $l7462 i32) + (local $l7463 i32) + (local $l7464 i32) + (local $l7465 i32) + (local $l7466 i32) + (local $l7467 i32) + (local $l7468 i32) + (local $l7469 i32) + (local $l7470 i32) + (local $l7471 i32) + (local $l7472 i32) + (local $l7473 i32) + (local $l7474 i32) + (local $l7475 i32) + (local $l7476 i32) + (local $l7477 i32) + (local $l7478 i32) + (local $l7479 i32) + (local $l7480 i32) + (local $l7481 i32) + (local $l7482 i32) + (local $l7483 i32) + (local $l7484 i32) + (local $l7485 i32) + (local $l7486 i32) + (local $l7487 i32) + (local $l7488 i32) + (local $l7489 i32) + (local $l7490 i32) + (local $l7491 i32) + (local $l7492 i32) + (local $l7493 i32) + (local $l7494 i32) + (local $l7495 i32) + (local $l7496 i32) + (local $l7497 i32) + (local $l7498 i32) + (local $l7499 i32) + (local $l7500 i32) + (local $l7501 i32) + (local $l7502 i32) + (local $l7503 i32) + (local $l7504 i32) + (local $l7505 i32) + (local $l7506 i32) + (local $l7507 i32) + (local $l7508 i32) + (local $l7509 i32) + (local $l7510 i32) + (local $l7511 i32) + (local $l7512 i32) + (local $l7513 i32) + (local $l7514 i32) + (local $l7515 i32) + (local $l7516 i32) + (local $l7517 i32) + (local $l7518 i32) + (local $l7519 i32) + (local $l7520 i32) + (local $l7521 i32) + (local $l7522 i32) + (local $l7523 i32) + (local $l7524 i32) + (local $l7525 i32) + (local $l7526 i32) + (local $l7527 i32) + (local $l7528 i32) + (local $l7529 i32) + (local $l7530 i32) + (local $l7531 i32) + (local $l7532 i32) + (local $l7533 i32) + (local $l7534 i32) + (local $l7535 i32) + (local $l7536 i32) + (local $l7537 i32) + (local $l7538 i32) + (local $l7539 i32) + (local $l7540 i32) + (local $l7541 i32) + (local $l7542 i32) + (local $l7543 i32) + (local $l7544 i32) + (local $l7545 i32) + (local $l7546 i32) + (local $l7547 i32) + (local $l7548 i32) + (local $l7549 i32) + (local $l7550 i32) + (local $l7551 i32) + (local $l7552 i32) + (local $l7553 i32) + (local $l7554 i32) + (local $l7555 i32) + (local $l7556 i32) + (local $l7557 i32) + (local $l7558 i32) + (local $l7559 i32) + (local $l7560 i32) + (local $l7561 i32) + (local $l7562 i32) + (local $l7563 i32) + (local $l7564 i32) + (local $l7565 i32) + (local $l7566 i32) + (local $l7567 i32) + (local $l7568 i32) + (local $l7569 i32) + (local $l7570 i32) + (local $l7571 i32) + (local $l7572 i32) + (local $l7573 i32) + (local $l7574 i32) + (local $l7575 i32) + (local $l7576 i32) + (local $l7577 i32) + (local $l7578 i32) + (local $l7579 i32) + (local $l7580 i32) + (local $l7581 i32) + (local $l7582 i32) + (local $l7583 i32) + (local $l7584 i32) + (local $l7585 i32) + (local $l7586 i32) + (local $l7587 i32) + (local $l7588 i32) + (local $l7589 i32) + (local $l7590 i32) + (local $l7591 i32) + (local $l7592 i32) + (local $l7593 i32) + (local $l7594 i32) + (local $l7595 i32) + (local $l7596 i32) + (local $l7597 i32) + (local $l7598 i32) + (local $l7599 i32) + (local $l7600 i32) + (local $l7601 i32) + (local $l7602 i32) + (local $l7603 i32) + (local $l7604 i32) + (local $l7605 i32) + (local $l7606 i32) + (local $l7607 i32) + (local $l7608 i32) + (local $l7609 i32) + (local $l7610 i32) + (local $l7611 i32) + (local $l7612 i32) + (local $l7613 i32) + (local $l7614 i32) + (local $l7615 i32) + (local $l7616 i32) + (local $l7617 i32) + (local $l7618 i32) + (local $l7619 i32) + (local $l7620 i32) + (local $l7621 i32) + (local $l7622 i32) + (local $l7623 i32) + (local $l7624 i32) + (local $l7625 i32) + (local $l7626 i32) + (local $l7627 i32) + (local $l7628 i32) + (local $l7629 i32) + (local $l7630 i32) + (local $l7631 i32) + (local $l7632 i32) + (local $l7633 i32) + (local $l7634 i32) + (local $l7635 i32) + (local $l7636 i32) + (local $l7637 i32) + (local $l7638 i32) + (local $l7639 i32) + (local $l7640 i32) + (local $l7641 i32) + (local $l7642 i32) + (local $l7643 i32) + (local $l7644 i32) + (local $l7645 i32) + (local $l7646 i32) + (local $l7647 i32) + (local $l7648 i32) + (local $l7649 i32) + (local $l7650 i32) + (local $l7651 i32) + (local $l7652 i32) + (local $l7653 i32) + (local $l7654 i32) + (local $l7655 i32) + (local $l7656 i32) + (local $l7657 i32) + (local $l7658 i32) + (local $l7659 i32) + (local $l7660 i32) + (local $l7661 i32) + (local $l7662 i32) + (local $l7663 i32) + (local $l7664 i32) + (local $l7665 i32) + (local $l7666 i32) + (local $l7667 i32) + (local $l7668 i32) + (local $l7669 i32) + (local $l7670 i32) + (local $l7671 i32) + (local $l7672 i32) + (local $l7673 i32) + (local $l7674 i32) + (local $l7675 i32) + (local $l7676 i32) + (local $l7677 i32) + (local $l7678 i32) + (local $l7679 i32) + (local $l7680 i32) + (local $l7681 i32) + (local $l7682 i32) + (local $l7683 i32) + (local $l7684 i32) + (local $l7685 i32) + (local $l7686 i32) + (local $l7687 i32) + (local $l7688 i32) + (local $l7689 i32) + (local $l7690 i32) + (local $l7691 i32) + (local $l7692 i32) + (local $l7693 i32) + (local $l7694 i32) + (local $l7695 i32) + (local $l7696 i32) + (local $l7697 i32) + (local $l7698 i32) + (local $l7699 i32) + (local $l7700 i32) + (local $l7701 i32) + (local $l7702 i32) + (local $l7703 i32) + (local $l7704 i32) + (local $l7705 i32) + (local $l7706 i32) + (local $l7707 i32) + (local $l7708 i32) + (local $l7709 i32) + (local $l7710 i32) + (local $l7711 i32) + (local $l7712 i32) + (local $l7713 i32) + (local $l7714 i32) + (local $l7715 i32) + (local $l7716 i32) + (local $l7717 i32) + (local $l7718 i32) + (local $l7719 i32) + (local $l7720 i32) + (local $l7721 i32) + (local $l7722 i32) + (local $l7723 i32) + (local $l7724 i32) + (local $l7725 i32) + (local $l7726 i32) + (local $l7727 i32) + (local $l7728 i32) + (local $l7729 i32) + (local $l7730 i32) + (local $l7731 i32) + (local $l7732 i32) + (local $l7733 i32) + (local $l7734 i32) + (local $l7735 i32) + (local $l7736 i32) + (local $l7737 i32) + (local $l7738 i32) + (local $l7739 i32) + (local $l7740 i32) + (local $l7741 i32) + (local $l7742 i32) + (local $l7743 i32) + (local $l7744 i32) + (local $l7745 i32) + (local $l7746 i32) + (local $l7747 i32) + (local $l7748 i32) + (local $l7749 i32) + (local $l7750 i32) + (local $l7751 i32) + (local $l7752 i32) + (local $l7753 i32) + (local $l7754 i32) + (local $l7755 i32) + (local $l7756 i32) + (local $l7757 i32) + (local $l7758 i32) + (local $l7759 i32) + (local $l7760 i32) + (local $l7761 i32) + (local $l7762 i32) + (local $l7763 i32) + (local $l7764 i32) + (local $l7765 i32) + (local $l7766 i32) + (local $l7767 i32) + (local $l7768 i32) + (local $l7769 i32) + (local $l7770 i32) + (local $l7771 i32) + (local $l7772 i32) + (local $l7773 i32) + (local $l7774 i32) + (local $l7775 i32) + (local $l7776 i32) + (local $l7777 i32) + (local $l7778 i32) + (local $l7779 i32) + (local $l7780 i32) + (local $l7781 i32) + (local $l7782 i32) + (local $l7783 i32) + (local $l7784 i32) + (local $l7785 i32) + (local $l7786 i32) + (local $l7787 i32) + (local $l7788 i32) + (local $l7789 i32) + (local $l7790 i32) + (local $l7791 i32) + (local $l7792 i32) + (local $l7793 i32) + (local $l7794 i32) + (local $l7795 i32) + (local $l7796 i32) + (local $l7797 i32) + (local $l7798 i32) + (local $l7799 i32) + (local $l7800 i32) + (local $l7801 i32) + (local $l7802 i32) + (local $l7803 i32) + (local $l7804 i32) + (local $l7805 i32) + (local $l7806 i32) + (local $l7807 i32) + (local $l7808 i32) + (local $l7809 i32) + (local $l7810 i32) + (local $l7811 i32) + (local $l7812 i32) + (local $l7813 i32) + (local $l7814 i32) + (local $l7815 i32) + (local $l7816 i32) + (local $l7817 i32) + (local $l7818 i32) + (local $l7819 i32) + (local $l7820 i32) + (local $l7821 i32) + (local $l7822 i32) + (local $l7823 i32) + (local $l7824 i32) + (local $l7825 i32) + (local $l7826 i32) + (local $l7827 i32) + (local $l7828 i32) + (local $l7829 i32) + (local $l7830 i32) + (local $l7831 i32) + (local $l7832 i32) + (local $l7833 i32) + (local $l7834 i32) + (local $l7835 i32) + (local $l7836 i32) + (local $l7837 i32) + (local $l7838 i32) + (local $l7839 i32) + (local $l7840 i32) + (local $l7841 i32) + (local $l7842 i32) + (local $l7843 i32) + (local $l7844 i32) + (local $l7845 i32) + (local $l7846 i32) + (local $l7847 i32) + (local $l7848 i32) + (local $l7849 i32) + (local $l7850 i32) + (local $l7851 i32) + (local $l7852 i32) + (local $l7853 i32) + (local $l7854 i32) + (local $l7855 i32) + (local $l7856 i32) + (local $l7857 i32) + (local $l7858 i32) + (local $l7859 i32) + (local $l7860 i32) + (local $l7861 i32) + (local $l7862 i32) + (local $l7863 i32) + (local $l7864 i32) + (local $l7865 i32) + (local $l7866 i32) + (local $l7867 i32) + (local $l7868 i32) + (local $l7869 i32) + (local $l7870 i32) + (local $l7871 i32) + (local $l7872 i32) + (local $l7873 i32) + (local $l7874 i32) + (local $l7875 i32) + (local $l7876 i32) + (local $l7877 i32) + (local $l7878 i32) + (local $l7879 i32) + (local $l7880 i32) + (local $l7881 i32) + (local $l7882 i32) + (local $l7883 i32) + (local $l7884 i32) + (local $l7885 i32) + (local $l7886 i32) + (local $l7887 i32) + (local $l7888 i32) + (local $l7889 i32) + (local $l7890 i32) + (local $l7891 i32) + (local $l7892 i32) + (local $l7893 i32) + (local $l7894 i32) + (local $l7895 i32) + (local $l7896 i32) + (local $l7897 i32) + (local $l7898 i32) + (local $l7899 i32) + (local $l7900 i32) + (local $l7901 i32) + (local $l7902 i32) + (local $l7903 i32) + (local $l7904 i32) + (local $l7905 i32) + (local $l7906 i32) + (local $l7907 i32) + (local $l7908 i32) + (local $l7909 i32) + (local $l7910 i32) + (local $l7911 i32) + (local $l7912 i32) + (local $l7913 i32) + (local $l7914 i32) + (local $l7915 i32) + (local $l7916 i32) + (local $l7917 i32) + (local $l7918 i32) + (local $l7919 i32) + (local $l7920 i32) + (local $l7921 i32) + (local $l7922 i32) + (local $l7923 i32) + (local $l7924 i32) + (local $l7925 i32) + (local $l7926 i32) + (local $l7927 i32) + (local $l7928 i32) + (local $l7929 i32) + (local $l7930 i32) + (local $l7931 i32) + (local $l7932 i32) + (local $l7933 i32) + (local $l7934 i32) + (local $l7935 i32) + (local $l7936 i32) + (local $l7937 i32) + (local $l7938 i32) + (local $l7939 i32) + (local $l7940 i32) + (local $l7941 i32) + (local $l7942 i32) + (local $l7943 i32) + (local $l7944 i32) + (local $l7945 i32) + (local $l7946 i32) + (local $l7947 i32) + (local $l7948 i32) + (local $l7949 i32) + (local $l7950 i32) + (local $l7951 i32) + (local $l7952 i32) + (local $l7953 i32) + (local $l7954 i32) + (local $l7955 i32) + (local $l7956 i32) + (local $l7957 i32) + (local $l7958 i32) + (local $l7959 i32) + (local $l7960 i32) + (local $l7961 i32) + (local $l7962 i32) + (local $l7963 i32) + (local $l7964 i32) + (local $l7965 i32) + (local $l7966 i32) + (local $l7967 i32) + (local $l7968 i32) + (local $l7969 i32) + (local $l7970 i32) + (local $l7971 i32) + (local $l7972 i32) + (local $l7973 i32) + (local $l7974 i32) + (local $l7975 i32) + (local $l7976 i32) + (local $l7977 i32) + (local $l7978 i32) + (local $l7979 i32) + (local $l7980 i32) + (local $l7981 i32) + (local $l7982 i32) + (local $l7983 i32) + (local $l7984 i32) + (local $l7985 i32) + (local $l7986 i32) + (local $l7987 i32) + (local $l7988 i32) + (local $l7989 i32) + (local $l7990 i32) + (local $l7991 i32) + (local $l7992 i32) + (local $l7993 i32) + (local $l7994 i32) + (local $l7995 i32) + (local $l7996 i32) + (local $l7997 i32) + (local $l7998 i32) + (local $l7999 i32) + (local $l8000 i32) + (local $l8001 i32) + (local $l8002 i32) + (local $l8003 i32) + (local $l8004 i32) + (local $l8005 i32) + (local $l8006 i32) + (local $l8007 i32) + (local $l8008 i32) + (local $l8009 i32) + (local $l8010 i32) + (local $l8011 i32) + (local $l8012 i32) + (local $l8013 i32) + (local $l8014 i32) + (local $l8015 i32) + (local $l8016 i32) + (local $l8017 i32) + (local $l8018 i32) + (local $l8019 i32) + (local $l8020 i32) + (local $l8021 i32) + (local $l8022 i32) + (local $l8023 i32) + (local $l8024 i32) + (local $l8025 i32) + (local $l8026 i32) + (local $l8027 i32) + (local $l8028 i32) + (local $l8029 i32) + (local $l8030 i32) + (local $l8031 i32) + (local $l8032 i32) + (local $l8033 i32) + (local $l8034 i32) + (local $l8035 i32) + (local $l8036 i32) + (local $l8037 i32) + (local $l8038 i32) + (local $l8039 i32) + (local $l8040 i32) + (local $l8041 i32) + (local $l8042 i32) + (local $l8043 i32) + (local $l8044 i32) + (local $l8045 i32) + (local $l8046 i32) + (local $l8047 i32) + (local $l8048 i32) + (local $l8049 i32) + (local $l8050 i32) + (local $l8051 i32) + (local $l8052 i32) + (local $l8053 i32) + (local $l8054 i32) + (local $l8055 i32) + (local $l8056 i32) + (local $l8057 i32) + (local $l8058 i32) + (local $l8059 i32) + (local $l8060 i32) + (local $l8061 i32) + (local $l8062 i32) + (local $l8063 i32) + (local $l8064 i32) + (local $l8065 i32) + (local $l8066 i32) + (local $l8067 i32) + (local $l8068 i32) + (local $l8069 i32) + (local $l8070 i32) + (local $l8071 i32) + (local $l8072 i32) + (local $l8073 i32) + (local $l8074 i32) + (local $l8075 i32) + (local $l8076 i32) + (local $l8077 i32) + (local $l8078 i32) + (local $l8079 i32) + (local $l8080 i32) + (local $l8081 i32) + (local $l8082 i32) + (local $l8083 i32) + (local $l8084 i32) + (local $l8085 i32) + (local $l8086 i32) + (local $l8087 i32) + (local $l8088 i32) + (local $l8089 i32) + (local $l8090 i32) + (local $l8091 i32) + (local $l8092 i32) + (local $l8093 i32) + (local $l8094 i32) + (local $l8095 i32) + (local $l8096 i32) + (local $l8097 i32) + (local $l8098 i32) + (local $l8099 i32) + (local $l8100 i32) + (local $l8101 i32) + (local $l8102 i32) + (local $l8103 i32) + (local $l8104 i32) + (local $l8105 i32) + (local $l8106 i32) + (local $l8107 i32) + (local $l8108 i32) + (local $l8109 i32) + (local $l8110 i32) + (local $l8111 i32) + (local $l8112 i32) + (local $l8113 i32) + (local $l8114 i32) + (local $l8115 i32) + (local $l8116 i32) + (local $l8117 i32) + (local $l8118 i32) + (local $l8119 i32) + (local $l8120 i32) + (local $l8121 i32) + (local $l8122 i32) + (local $l8123 i32) + (local $l8124 i32) + (local $l8125 i32) + (local $l8126 i32) + (local $l8127 i32) + (local $l8128 i32) + (local $l8129 i32) + (local $l8130 i32) + (local $l8131 i32) + (local $l8132 i32) + (local $l8133 i32) + (local $l8134 i32) + (local $l8135 i32) + (local $l8136 i32) + (local $l8137 i32) + (local $l8138 i32) + (local $l8139 i32) + (local $l8140 i32) + (local $l8141 i32) + (local $l8142 i32) + (local $l8143 i32) + (local $l8144 i32) + (local $l8145 i32) + (local $l8146 i32) + (local $l8147 i32) + (local $l8148 i32) + (local $l8149 i32) + (local $l8150 i32) + (local $l8151 i32) + (local $l8152 i32) + (local $l8153 i32) + (local $l8154 i32) + (local $l8155 i32) + (local $l8156 i32) + (local $l8157 i32) + (local $l8158 i32) + (local $l8159 i32) + (local $l8160 i32) + (local $l8161 i32) + (local $l8162 i32) + (local $l8163 i32) + (local $l8164 i32) + (local $l8165 i32) + (local $l8166 i32) + (local $l8167 i32) + (local $l8168 i32) + (local $l8169 i32) + (local $l8170 i32) + (local $l8171 i32) + (local $l8172 i32) + (local $l8173 i32) + (local $l8174 i32) + (local $l8175 i32) + (local $l8176 i32) + (local $l8177 i32) + (local $l8178 i32) + (local $l8179 i32) + (local $l8180 i32) + (local $l8181 i32) + (local $l8182 i32) + (local $l8183 i32) + (local $l8184 i32) + (local $l8185 i32) + (local $l8186 i32) + (local $l8187 i32) + (local $l8188 i32) + (local $l8189 i32) + (local $l8190 i32) + (local $l8191 i32) + (local $l8192 i32) + (local $l8193 i32) + (local $l8194 i32) + (local $l8195 i32) + (local $l8196 i32) + (local $l8197 i32) + (local $l8198 i32) + (local $l8199 i32) + (local $l8200 i32) + (local $l8201 i32) + (local $l8202 i32) + (local $l8203 i32) + (local $l8204 i32) + (local $l8205 i32) + (local $l8206 i32) + (local $l8207 i32) + (local $l8208 i32) + (local $l8209 i32) + (local $l8210 i32) + (local $l8211 i32) + (local $l8212 i32) + (local $l8213 i32) + (local $l8214 i32) + (local $l8215 i32) + (local $l8216 i32) + (local $l8217 i32) + (local $l8218 i32) + (local $l8219 i32) + (local $l8220 i32) + (local $l8221 i32) + (local $l8222 i32) + (local $l8223 i32) + (local $l8224 i32) + (local $l8225 i32) + (local $l8226 i32) + (local $l8227 i32) + (local $l8228 i32) + (local $l8229 i32) + (local $l8230 i32) + (local $l8231 i32) + (local $l8232 i32) + (local $l8233 i32) + (local $l8234 i32) + (local $l8235 i32) + (local $l8236 i32) + (local $l8237 i32) + (local $l8238 i32) + (local $l8239 i32) + (local $l8240 i32) + (local $l8241 i32) + (local $l8242 i32) + (local $l8243 i32) + (local $l8244 i32) + (local $l8245 i32) + (local $l8246 i32) + (local $l8247 i32) + (local $l8248 i32) + (local $l8249 i32) + (local $l8250 i32) + (local $l8251 i32) + (local $l8252 i32) + (local $l8253 i32) + (local $l8254 i32) + (local $l8255 i32) + (local $l8256 i32) + (local $l8257 i32) + (local $l8258 i32) + (local $l8259 i32) + (local $l8260 i32) + (local $l8261 i32) + (local $l8262 i32) + (local $l8263 i32) + (local $l8264 i32) + (local $l8265 i32) + (local $l8266 i32) + (local $l8267 i32) + (local $l8268 i32) + (local $l8269 i32) + (local $l8270 i32) + (local $l8271 i32) + (local $l8272 i32) + (local $l8273 i32) + (local $l8274 i32) + (local $l8275 i32) + (local $l8276 i32) + (local $l8277 i32) + (local $l8278 i32) + (local $l8279 i32) + (local $l8280 i32) + (local $l8281 i32) + (local $l8282 i32) + (local $l8283 i32) + (local $l8284 i32) + (local $l8285 i32) + (local $l8286 i32) + (local $l8287 i32) + (local $l8288 i32) + (local $l8289 i32) + (local $l8290 i32) + (local $l8291 i32) + (local $l8292 i32) + (local $l8293 i32) + (local $l8294 i32) + (local $l8295 i32) + (local $l8296 i32) + (local $l8297 i32) + (local $l8298 i32) + (local $l8299 i32) + (local $l8300 i32) + (local $l8301 i32) + (local $l8302 i32) + (local $l8303 i32) + (local $l8304 i32) + (local $l8305 i32) + (local $l8306 i32) + (local $l8307 i32) + (local $l8308 i32) + (local $l8309 i32) + (local $l8310 i32) + (local $l8311 i32) + (local $l8312 i32) + (local $l8313 i32) + (local $l8314 i32) + (local $l8315 i32) + (local $l8316 i32) + (local $l8317 i32) + (local $l8318 i32) + (local $l8319 i32) + (local $l8320 i32) + (local $l8321 i32) + (local $l8322 i32) + (local $l8323 i32) + (local $l8324 i32) + (local $l8325 i32) + (local $l8326 i32) + (local $l8327 i32) + (local $l8328 i32) + (local $l8329 i32) + (local $l8330 i32) + (local $l8331 i32) + (local $l8332 i32) + (local $l8333 i32) + (local $l8334 i32) + (local $l8335 i32) + (local $l8336 i32) + (local $l8337 i32) + (local $l8338 i32) + (local $l8339 i32) + (local $l8340 i32) + (local $l8341 i32) + (local $l8342 i32) + (local $l8343 i32) + (local $l8344 i32) + (local $l8345 i32) + (local $l8346 i32) + (local $l8347 i32) + (local $l8348 i32) + (local $l8349 i32) + (local $l8350 i32) + (local $l8351 i32) + (local $l8352 i32) + (local $l8353 i32) + (local $l8354 i32) + (local $l8355 i32) + (local $l8356 i32) + (local $l8357 i32) + (local $l8358 i32) + (local $l8359 i32) + (local $l8360 i32) + (local $l8361 i32) + (local $l8362 i32) + (local $l8363 i32) + (local $l8364 i32) + (local $l8365 i32) + (local $l8366 i32) + (local $l8367 i32) + (local $l8368 i32) + (local $l8369 i32) + (local $l8370 i32) + (local $l8371 i32) + (local $l8372 i32) + (local $l8373 i32) + (local $l8374 i32) + (local $l8375 i32) + (local $l8376 i32) + (local $l8377 i32) + (local $l8378 i32) + (local $l8379 i32) + (local $l8380 i32) + (local $l8381 i32) + (local $l8382 i32) + (local $l8383 i32) + (local $l8384 i32) + (local $l8385 i32) + (local $l8386 i32) + (local $l8387 i32) + (local $l8388 i32) + (local $l8389 i32) + (local $l8390 i32) + (local $l8391 i32) + (local $l8392 i32) + (local $l8393 i32) + (local $l8394 i32) + (local $l8395 i32) + (local $l8396 i32) + (local $l8397 i32) + (local $l8398 i32) + (local $l8399 i32) + (local $l8400 i32) + (local $l8401 i32) + (local $l8402 i32) + (local $l8403 i32) + (local $l8404 i32) + (local $l8405 i32) + (local $l8406 i32) + (local $l8407 i32) + (local $l8408 i32) + (local $l8409 i32) + (local $l8410 i32) + (local $l8411 i32) + (local $l8412 i32) + (local $l8413 i32) + (local $l8414 i32) + (local $l8415 i32) + (local $l8416 i32) + (local $l8417 i32) + (local $l8418 i32) + (local $l8419 i32) + (local $l8420 i32) + (local $l8421 i32) + (local $l8422 i32) + (local $l8423 i32) + (local $l8424 i32) + (local $l8425 i32) + (local $l8426 i32) + (local $l8427 i32) + (local $l8428 i32) + (local $l8429 i32) + (local $l8430 i32) + (local $l8431 i32) + (local $l8432 i32) + (local $l8433 i32) + (local $l8434 i32) + (local $l8435 i32) + (local $l8436 i32) + (local $l8437 i32) + (local $l8438 i32) + (local $l8439 i32) + (local $l8440 i32) + (local $l8441 i32) + (local $l8442 i32) + (local $l8443 i32) + (local $l8444 i32) + (local $l8445 i32) + (local $l8446 i32) + (local $l8447 i32) + (local $l8448 i32) + (local $l8449 i32) + (local $l8450 i32) + (local $l8451 i32) + (local $l8452 i32) + (local $l8453 i32) + (local $l8454 i32) + (local $l8455 i32) + (local $l8456 i32) + (local $l8457 i32) + (local $l8458 i32) + (local $l8459 i32) + (local $l8460 i32) + (local $l8461 i32) + (local $l8462 i32) + (local $l8463 i32) + (local $l8464 i32) + (local $l8465 i32) + (local $l8466 i32) + (local $l8467 i32) + (local $l8468 i32) + (local $l8469 i32) + (local $l8470 i32) + (local $l8471 i32) + (local $l8472 i32) + (local $l8473 i32) + (local $l8474 i32) + (local $l8475 i32) + (local $l8476 i32) + (local $l8477 i32) + (local $l8478 i32) + (local $l8479 i32) + (local $l8480 i32) + (local $l8481 i32) + (local $l8482 i32) + (local $l8483 i32) + (local $l8484 i32) + (local $l8485 i32) + (local $l8486 i32) + (local $l8487 i32) + (local $l8488 i32) + (local $l8489 i32) + (local $l8490 i32) + (local $l8491 i32) + (local $l8492 i32) + (local $l8493 i32) + (local $l8494 i32) + (local $l8495 i32) + (local $l8496 i32) + (local $l8497 i32) + (local $l8498 i32) + (local $l8499 i32) + (local $l8500 i32) + (local $l8501 i32) + (local $l8502 i32) + (local $l8503 i32) + (local $l8504 i32) + (local $l8505 i32) + (local $l8506 i32) + (local $l8507 i32) + (local $l8508 i32) + (local $l8509 i32) + (local $l8510 i32) + (local $l8511 i32) + (local $l8512 i32) + (local $l8513 i32) + (local $l8514 i32) + (local $l8515 i32) + (local $l8516 i32) + (local $l8517 i32) + (local $l8518 i32) + (local $l8519 i32) + (local $l8520 i32) + (local $l8521 i32) + (local $l8522 i32) + (local $l8523 i32) + (local $l8524 i32) + (local $l8525 i32) + (local $l8526 i32) + (local $l8527 i32) + (local $l8528 i32) + (local $l8529 i32) + (local $l8530 i32) + (local $l8531 i32) + (local $l8532 i32) + (local $l8533 i32) + (local $l8534 i32) + (local $l8535 i32) + (local $l8536 i32) + (local $l8537 i32) + (local $l8538 i32) + (local $l8539 i32) + (local $l8540 i32) + (local $l8541 i32) + (local $l8542 i32) + (local $l8543 i32) + (local $l8544 i32) + (local $l8545 i32) + (local $l8546 i32) + (local $l8547 i32) + (local $l8548 i32) + (local $l8549 i32) + (local $l8550 i32) + (local $l8551 i32) + (local $l8552 i32) + (local $l8553 i32) + (local $l8554 i32) + (local $l8555 i32) + (local $l8556 i32) + (local $l8557 i32) + (local $l8558 i32) + (local $l8559 i32) + (local $l8560 i32) + (local $l8561 i32) + (local $l8562 i32) + (local $l8563 i32) + (local $l8564 i32) + (local $l8565 i32) + (local $l8566 i32) + (local $l8567 i32) + (local $l8568 i32) + (local $l8569 i32) + (local $l8570 i32) + (local $l8571 i32) + (local $l8572 i32) + (local $l8573 i32) + (local $l8574 i32) + (local $l8575 i32) + (local $l8576 i32) + (local $l8577 i32) + (local $l8578 i32) + (local $l8579 i32) + (local $l8580 i32) + (local $l8581 i32) + (local $l8582 i32) + (local $l8583 i32) + (local $l8584 i32) + (local $l8585 i32) + (local $l8586 i32) + (local $l8587 i32) + (local $l8588 i32) + (local $l8589 i32) + (local $l8590 i32) + (local $l8591 i32) + (local $l8592 i32) + (local $l8593 i32) + (local $l8594 i32) + (local $l8595 i32) + (local $l8596 i32) + (local $l8597 i32) + (local $l8598 i32) + (local $l8599 i32) + (local $l8600 i32) + (local $l8601 i32) + (local $l8602 i32) + (local $l8603 i32) + (local $l8604 i32) + (local $l8605 i32) + (local $l8606 i32) + (local $l8607 i32) + (local $l8608 i32) + (local $l8609 i32) + (local $l8610 i32) + (local $l8611 i32) + (local $l8612 i32) + (local $l8613 i32) + (local $l8614 i32) + (local $l8615 i32) + (local $l8616 i32) + (local $l8617 i32) + (local $l8618 i32) + (local $l8619 i32) + (local $l8620 i32) + (local $l8621 i32) + (local $l8622 i32) + (local $l8623 i32) + (local $l8624 i32) + (local $l8625 i32) + (local $l8626 i32) + (local $l8627 i32) + (local $l8628 i32) + (local $l8629 i32) + (local $l8630 i32) + (local $l8631 i32) + (local $l8632 i32) + (local $l8633 i32) + (local $l8634 i32) + (local $l8635 i32) + (local $l8636 i32) + (local $l8637 i32) + (local $l8638 i32) + (local $l8639 i32) + (local $l8640 i32) + (local $l8641 i32) + (local $l8642 i32) + (local $l8643 i32) + (local $l8644 i32) + (local $l8645 i32) + (local $l8646 i32) + (local $l8647 i32) + (local $l8648 i32) + (local $l8649 i32) + (local $l8650 i32) + (local $l8651 i32) + (local $l8652 i32) + (local $l8653 i32) + (local $l8654 i32) + (local $l8655 i32) + (local $l8656 i32) + (local $l8657 i32) + (local $l8658 i32) + (local $l8659 i32) + (local $l8660 i32) + (local $l8661 i32) + (local $l8662 i32) + (local $l8663 i32) + (local $l8664 i32) + (local $l8665 i32) + (local $l8666 i32) + (local $l8667 i32) + (local $l8668 i32) + (local $l8669 i32) + (local $l8670 i32) + (local $l8671 i32) + (local $l8672 i32) + (local $l8673 i32) + (local $l8674 i32) + (local $l8675 i32) + (local $l8676 i32) + (local $l8677 i32) + (local $l8678 i32) + (local $l8679 i32) + (local $l8680 i32) + (local $l8681 i32) + (local $l8682 i32) + (local $l8683 i32) + (local $l8684 i32) + (local $l8685 i32) + (local $l8686 i32) + (local $l8687 i32) + (local $l8688 i32) + (local $l8689 i32) + (local $l8690 i32) + (local $l8691 i32) + (local $l8692 i32) + (local $l8693 i32) + (local $l8694 i32) + (local $l8695 i32) + (local $l8696 i32) + (local $l8697 i32) + (local $l8698 i32) + (local $l8699 i32) + (local $l8700 i32) + (local $l8701 i32) + (local $l8702 i32) + (local $l8703 i32) + (local $l8704 i32) + (local $l8705 i32) + (local $l8706 i32) + (local $l8707 i32) + (local $l8708 i32) + (local $l8709 i32) + (local $l8710 i32) + (local $l8711 i32) + (local $l8712 i32) + (local $l8713 i32) + (local $l8714 i32) + (local $l8715 i32) + (local $l8716 i32) + (local $l8717 i32) + (local $l8718 i32) + (local $l8719 i32) + (local $l8720 i32) + (local $l8721 i32) + (local $l8722 i32) + (local $l8723 i32) + (local $l8724 i32) + (local $l8725 i32) + (local $l8726 i32) + (local $l8727 i32) + (local $l8728 i32) + (local $l8729 i32) + (local $l8730 i32) + (local $l8731 i32) + (local $l8732 i32) + (local $l8733 i32) + (local $l8734 i32) + (local $l8735 i32) + (local $l8736 i32) + (local $l8737 i32) + (local $l8738 i32) + (local $l8739 i32) + (local $l8740 i32) + (local $l8741 i32) + (local $l8742 i32) + (local $l8743 i32) + (local $l8744 i32) + (local $l8745 i32) + (local $l8746 i32) + (local $l8747 i32) + (local $l8748 i32) + (local $l8749 i32) + (local $l8750 i32) + (local $l8751 i32) + (local $l8752 i32) + (local $l8753 i32) + (local $l8754 i32) + (local $l8755 i32) + (local $l8756 i32) + (local $l8757 i32) + (local $l8758 i32) + (local $l8759 i32) + (local $l8760 i32) + (local $l8761 i32) + (local $l8762 i32) + (local $l8763 i32) + (local $l8764 i32) + (local $l8765 i32) + (local $l8766 i32) + (local $l8767 i32) + (local $l8768 i32) + (local $l8769 i32) + (local $l8770 i32) + (local $l8771 i32) + (local $l8772 i32) + (local $l8773 i32) + (local $l8774 i32) + (local $l8775 i32) + (local $l8776 i32) + (local $l8777 i32) + (local $l8778 i32) + (local $l8779 i32) + (local $l8780 i32) + (local $l8781 i32) + (local $l8782 i32) + (local $l8783 i32) + (local $l8784 i32) + (local $l8785 i32) + (local $l8786 i32) + (local $l8787 i32) + (local $l8788 i32) + (local $l8789 i32) + (local $l8790 i32) + (local $l8791 i32) + (local $l8792 i32) + (local $l8793 i32) + (local $l8794 i32) + (local $l8795 i32) + (local $l8796 i32) + (local $l8797 i32) + (local $l8798 i32) + (local $l8799 i32) + (local $l8800 i32) + (local $l8801 i32) + (local $l8802 i32) + (local $l8803 i32) + (local $l8804 i32) + (local $l8805 i32) + (local $l8806 i32) + (local $l8807 i32) + (local $l8808 i32) + (local $l8809 i32) + (local $l8810 i32) + (local $l8811 i32) + (local $l8812 i32) + (local $l8813 i32) + (local $l8814 i32) + (local $l8815 i32) + (local $l8816 i32) + (local $l8817 i32) + (local $l8818 i32) + (local $l8819 i32) + (local $l8820 i32) + (local $l8821 i32) + (local $l8822 i32) + (local $l8823 i32) + (local $l8824 i32) + (local $l8825 i32) + (local $l8826 i32) + (local $l8827 i32) + (local $l8828 i32) + (local $l8829 i32) + (local $l8830 i32) + (local $l8831 i32) + (local $l8832 i32) + (local $l8833 i32) + (local $l8834 i32) + (local $l8835 i32) + (local $l8836 i32) + (local $l8837 i32) + (local $l8838 i32) + (local $l8839 i32) + (local $l8840 i32) + (local $l8841 i32) + (local $l8842 i32) + (local $l8843 i32) + (local $l8844 i32) + (local $l8845 i32) + (local $l8846 i32) + (local $l8847 i32) + (local $l8848 i32) + (local $l8849 i32) + (local $l8850 i32) + (local $l8851 i32) + (local $l8852 i32) + (local $l8853 i32) + (local $l8854 i32) + (local $l8855 i32) + (local $l8856 i32) + (local $l8857 i32) + (local $l8858 i32) + (local $l8859 i32) + (local $l8860 i32) + (local $l8861 i32) + (local $l8862 i32) + (local $l8863 i32) + (local $l8864 i32) + (local $l8865 i32) + (local $l8866 i32) + (local $l8867 i32) + (local $l8868 i32) + (local $l8869 i32) + (local $l8870 i32) + (local $l8871 i32) + (local $l8872 i32) + (local $l8873 i32) + (local $l8874 i32) + (local $l8875 i32) + (local $l8876 i32) + (local $l8877 i32) + (local $l8878 i32) + (local $l8879 i32) + (local $l8880 i32) + (local $l8881 i32) + (local $l8882 i32) + (local $l8883 i32) + (local $l8884 i32) + (local $l8885 i32) + (local $l8886 i32) + (local $l8887 i32) + (local $l8888 i32) + (local $l8889 i32) + (local $l8890 i32) + (local $l8891 i32) + (local $l8892 i32) + (local $l8893 i32) + (local $l8894 i32) + (local $l8895 i32) + (local $l8896 i32) + (local $l8897 i32) + (local $l8898 i32) + (local $l8899 i32) + (local $l8900 i32) + (local $l8901 i32) + (local $l8902 i32) + (local $l8903 i32) + (local $l8904 i32) + (local $l8905 i32) + (local $l8906 i32) + (local $l8907 i32) + (local $l8908 i32) + (local $l8909 i32) + (local $l8910 i32) + (local $l8911 i32) + (local $l8912 i32) + (local $l8913 i32) + (local $l8914 i32) + (local $l8915 i32) + (local $l8916 i32) + (local $l8917 i32) + (local $l8918 i32) + (local $l8919 i32) + (local $l8920 i32) + (local $l8921 i32) + (local $l8922 i32) + (local $l8923 i32) + (local $l8924 i32) + (local $l8925 i32) + (local $l8926 i32) + (local $l8927 i32) + (local $l8928 i32) + (local $l8929 i32) + (local $l8930 i32) + (local $l8931 i32) + (local $l8932 i32) + (local $l8933 i32) + (local $l8934 i32) + (local $l8935 i32) + (local $l8936 i32) + (local $l8937 i32) + (local $l8938 i32) + (local $l8939 i32) + (local $l8940 i32) + (local $l8941 i32) + (local $l8942 i32) + (local $l8943 i32) + (local $l8944 i32) + (local $l8945 i32) + (local $l8946 i32) + (local $l8947 i32) + (local $l8948 i32) + (local $l8949 i32) + (local $l8950 i32) + (local $l8951 i32) + (local $l8952 i32) + (local $l8953 i32) + (local $l8954 i32) + (local $l8955 i32) + (local $l8956 i32) + (local $l8957 i32) + (local $l8958 i32) + (local $l8959 i32) + (local $l8960 i32) + (local $l8961 i32) + (local $l8962 i32) + (local $l8963 i32) + (local $l8964 i32) + (local $l8965 i32) + (local $l8966 i32) + (local $l8967 i32) + (local $l8968 i32) + (local $l8969 i32) + (local $l8970 i32) + (local $l8971 i32) + (local $l8972 i32) + (local $l8973 i32) + (local $l8974 i32) + (local $l8975 i32) + (local $l8976 i32) + (local $l8977 i32) + (local $l8978 i32) + (local $l8979 i32) + (local $l8980 i32) + (local $l8981 i32) + (local $l8982 i32) + (local $l8983 i32) + (local $l8984 i32) + (local $l8985 i32) + (local $l8986 i32) + (local $l8987 i32) + (local $l8988 i32) + (local $l8989 i32) + (local $l8990 i32) + (local $l8991 i32) + (local $l8992 i32) + (local $l8993 i32) + (local $l8994 i32) + (local $l8995 i32) + (local $l8996 i32) + (local $l8997 i32) + (local $l8998 i32) + (local $l8999 i32) + (local $l9000 i32) + (local $l9001 i32) + (local $l9002 i32) + (local $l9003 i32) + (local $l9004 i32) + (local $l9005 i32) + (local $l9006 i32) + (local $l9007 i32) + (local $l9008 i32) + (local $l9009 i32) + (local $l9010 i32) + (local $l9011 i32) + (local $l9012 i32) + (local $l9013 i32) + (local $l9014 i32) + (local $l9015 i32) + (local $l9016 i32) + (local $l9017 i32) + (local $l9018 i32) + (local $l9019 i32) + (local $l9020 i32) + (local $l9021 i32) + (local $l9022 i32) + (local $l9023 i32) + (local $l9024 i32) + (local $l9025 i32) + (local $l9026 i32) + (local $l9027 i32) + (local $l9028 i32) + (local $l9029 i32) + (local $l9030 i32) + (local $l9031 i32) + (local $l9032 i32) + (local $l9033 i32) + (local $l9034 i32) + (local $l9035 i32) + (local $l9036 i32) + (local $l9037 i32) + (local $l9038 i32) + (local $l9039 i32) + (local $l9040 i32) + (local $l9041 i32) + (local $l9042 i32) + (local $l9043 i32) + (local $l9044 i32) + (local $l9045 i32) + (local $l9046 i32) + (local $l9047 i32) + (local $l9048 i32) + (local $l9049 i32) + (local $l9050 i32) + (local $l9051 i32) + (local $l9052 i32) + (local $l9053 i32) + (local $l9054 i32) + (local $l9055 i32) + (local $l9056 i32) + (local $l9057 i32) + (local $l9058 i32) + (local $l9059 i32) + (local $l9060 i32) + (local $l9061 i32) + (local $l9062 i32) + (local $l9063 i32) + (local $l9064 i32) + (local $l9065 i32) + (local $l9066 i32) + (local $l9067 i32) + (local $l9068 i32) + (local $l9069 i32) + (local $l9070 i32) + (local $l9071 i32) + (local $l9072 i32) + (local $l9073 i32) + (local $l9074 i32) + (local $l9075 i32) + (local $l9076 i32) + (local $l9077 i32) + (local $l9078 i32) + (local $l9079 i32) + (local $l9080 i32) + (local $l9081 i32) + (local $l9082 i32) + (local $l9083 i32) + (local $l9084 i32) + (local $l9085 i32) + (local $l9086 i32) + (local $l9087 i32) + (local $l9088 i32) + (local $l9089 i32) + (local $l9090 i32) + (local $l9091 i32) + (local $l9092 i32) + (local $l9093 i32) + (local $l9094 i32) + (local $l9095 i32) + (local $l9096 i32) + (local $l9097 i32) + (local $l9098 i32) + (local $l9099 i32) + (local $l9100 i32) + (local $l9101 i32) + (local $l9102 i32) + (local $l9103 i32) + (local $l9104 i32) + (local $l9105 i32) + (local $l9106 i32) + (local $l9107 i32) + (local $l9108 i32) + (local $l9109 i32) + (local $l9110 i32) + (local $l9111 i32) + (local $l9112 i32) + (local $l9113 i32) + (local $l9114 i32) + (local $l9115 i32) + (local $l9116 i32) + (local $l9117 i32) + (local $l9118 i32) + (local $l9119 i32) + (local $l9120 i32) + (local $l9121 i32) + (local $l9122 i32) + (local $l9123 i32) + (local $l9124 i32) + (local $l9125 i32) + (local $l9126 i32) + (local $l9127 i32) + (local $l9128 i32) + (local $l9129 i32) + (local $l9130 i32) + (local $l9131 i32) + (local $l9132 i32) + (local $l9133 i32) + (local $l9134 i32) + (local $l9135 i32) + (local $l9136 i32) + (local $l9137 i32) + (local $l9138 i32) + (local $l9139 i32) + (local $l9140 i32) + (local $l9141 i32) + (local $l9142 i32) + (local $l9143 i32) + (local $l9144 i32) + (local $l9145 i32) + (local $l9146 i32) + (local $l9147 i32) + (local $l9148 i32) + (local $l9149 i32) + (local $l9150 i32) + (local $l9151 i32) + (local $l9152 i32) + (local $l9153 i32) + (local $l9154 i32) + (local $l9155 i32) + (local $l9156 i32) + (local $l9157 i32) + (local $l9158 i32) + (local $l9159 i32) + (local $l9160 i32) + (local $l9161 i32) + (local $l9162 i32) + (local $l9163 i32) + (local $l9164 i32) + (local $l9165 i32) + (local $l9166 i32) + (local $l9167 i32) + (local $l9168 i32) + (local $l9169 i32) + (local $l9170 i32) + (local $l9171 i32) + (local $l9172 i32) + (local $l9173 i32) + (local $l9174 i32) + (local $l9175 i32) + (local $l9176 i32) + (local $l9177 i32) + (local $l9178 i32) + (local $l9179 i32) + (local $l9180 i32) + (local $l9181 i32) + (local $l9182 i32) + (local $l9183 i32) + (local $l9184 i32) + (local $l9185 i32) + (local $l9186 i32) + (local $l9187 i32) + (local $l9188 i32) + (local $l9189 i32) + (local $l9190 i32) + (local $l9191 i32) + (local $l9192 i32) + (local $l9193 i32) + (local $l9194 i32) + (local $l9195 i32) + (local $l9196 i32) + (local $l9197 i32) + (local $l9198 i32) + (local $l9199 i32) + (local $l9200 i32) + (local $l9201 i32) + (local $l9202 i32) + (local $l9203 i32) + (local $l9204 i32) + (local $l9205 i32) + (local $l9206 i32) + (local $l9207 i32) + (local $l9208 i32) + (local $l9209 i32) + (local $l9210 i32) + (local $l9211 i32) + (local $l9212 i32) + (local $l9213 i32) + (local $l9214 i32) + (local $l9215 i32) + (local $l9216 i32) + (local $l9217 i32) + (local $l9218 i32) + (local $l9219 i32) + (local $l9220 i32) + (local $l9221 i32) + (local $l9222 i32) + (local $l9223 i32) + (local $l9224 i32) + (local $l9225 i32) + (local $l9226 i32) + (local $l9227 i32) + (local $l9228 i32) + (local $l9229 i32) + (local $l9230 i32) + (local $l9231 i32) + (local $l9232 i32) + (local $l9233 i32) + (local $l9234 i32) + (local $l9235 i32) + (local $l9236 i32) + (local $l9237 i32) + (local $l9238 i32) + (local $l9239 i32) + (local $l9240 i32) + (local $l9241 i32) + (local $l9242 i32) + (local $l9243 i32) + (local $l9244 i32) + (local $l9245 i32) + (local $l9246 i32) + (local $l9247 i32) + (local $l9248 i32) + (local $l9249 i32) + (local $l9250 i32) + (local $l9251 i32) + (local $l9252 i32) + (local $l9253 i32) + (local $l9254 i32) + (local $l9255 i32) + (local $l9256 i32) + (local $l9257 i32) + (local $l9258 i32) + (local $l9259 i32) + (local $l9260 i32) + (local $l9261 i32) + (local $l9262 i32) + (local $l9263 i32) + (local $l9264 i32) + (local $l9265 i32) + (local $l9266 i32) + (local $l9267 i32) + (local $l9268 i32) + (local $l9269 i32) + (local $l9270 i32) + (local $l9271 i32) + (local $l9272 i32) + (local $l9273 i32) + (local $l9274 i32) + (local $l9275 i32) + (local $l9276 i32) + (local $l9277 i32) + (local $l9278 i32) + (local $l9279 i32) + (local $l9280 i32) + (local $l9281 i32) + (local $l9282 i32) + (local $l9283 i32) + (local $l9284 i32) + (local $l9285 i32) + (local $l9286 i32) + (local $l9287 i32) + (local $l9288 i32) + (local $l9289 i32) + (local $l9290 i32) + (local $l9291 i32) + (local $l9292 i32) + (local $l9293 i32) + (local $l9294 i32) + (local $l9295 i32) + (local $l9296 i32) + (local $l9297 i32) + (local $l9298 i32) + (local $l9299 i32) + (local $l9300 i32) + (local $l9301 i32) + (local $l9302 i32) + (local $l9303 i32) + (local $l9304 i32) + (local $l9305 i32) + (local $l9306 i32) + (local $l9307 i32) + (local $l9308 i32) + (local $l9309 i32) + (local $l9310 i32) + (local $l9311 i32) + (local $l9312 i32) + (local $l9313 i32) + (local $l9314 i32) + (local $l9315 i32) + (local $l9316 i32) + (local $l9317 i32) + (local $l9318 i32) + (local $l9319 i32) + (local $l9320 i32) + (local $l9321 i32) + (local $l9322 i32) + (local $l9323 i32) + (local $l9324 i32) + (local $l9325 i32) + (local $l9326 i32) + (local $l9327 i32) + (local $l9328 i32) + (local $l9329 i32) + (local $l9330 i32) + (local $l9331 i32) + (local $l9332 i32) + (local $l9333 i32) + (local $l9334 i32) + (local $l9335 i32) + (local $l9336 i32) + (local $l9337 i32) + (local $l9338 i32) + (local $l9339 i32) + (local $l9340 i32) + (local $l9341 i32) + (local $l9342 i32) + (local $l9343 i32) + (local $l9344 i32) + (local $l9345 i32) + (local $l9346 i32) + (local $l9347 i32) + (local $l9348 i32) + (local $l9349 i32) + (local $l9350 i32) + (local $l9351 i32) + (local $l9352 i32) + (local $l9353 i32) + (local $l9354 i32) + (local $l9355 i32) + (local $l9356 i32) + (local $l9357 i32) + (local $l9358 i32) + (local $l9359 i32) + (local $l9360 i32) + (local $l9361 i32) + (local $l9362 i32) + (local $l9363 i32) + (local $l9364 i32) + (local $l9365 i32) + (local $l9366 i32) + (local $l9367 i32) + (local $l9368 i32) + (local $l9369 i32) + (local $l9370 i32) + (local $l9371 i32) + (local $l9372 i32) + (local $l9373 i32) + (local $l9374 i32) + (local $l9375 i32) + (local $l9376 i32) + (local $l9377 i32) + (local $l9378 i32) + (local $l9379 i32) + (local $l9380 i32) + (local $l9381 i32) + (local $l9382 i32) + (local $l9383 i32) + (local $l9384 i32) + (local $l9385 i32) + (local $l9386 i32) + (local $l9387 i32) + (local $l9388 i32) + (local $l9389 i32) + (local $l9390 i32) + (local $l9391 i32) + (local $l9392 i32) + (local $l9393 i32) + (local $l9394 i32) + (local $l9395 i32) + (local $l9396 i32) + (local $l9397 i32) + (local $l9398 i32) + (local $l9399 i32) + (local $l9400 i32) + (local $l9401 i32) + (local $l9402 i32) + (local $l9403 i32) + (local $l9404 i32) + (local $l9405 i32) + (local $l9406 i32) + (local $l9407 i32) + (local $l9408 i32) + (local $l9409 i32) + (local $l9410 i32) + (local $l9411 i32) + (local $l9412 i32) + (local $l9413 i32) + (local $l9414 i32) + (local $l9415 i32) + (local $l9416 i32) + (local $l9417 i32) + (local $l9418 i32) + (local $l9419 i32) + (local $l9420 i32) + (local $l9421 i32) + (local $l9422 i32) + (local $l9423 i32) + (local $l9424 i32) + (local $l9425 i32) + (local $l9426 i32) + (local $l9427 i32) + (local $l9428 i32) + (local $l9429 i32) + (local $l9430 i32) + (local $l9431 i32) + (local $l9432 i32) + (local $l9433 i32) + (local $l9434 i32) + (local $l9435 i32) + (local $l9436 i32) + (local $l9437 i32) + (local $l9438 i32) + (local $l9439 i32) + (local $l9440 i32) + (local $l9441 i32) + (local $l9442 i32) + (local $l9443 i32) + (local $l9444 i32) + (local $l9445 i32) + (local $l9446 i32) + (local $l9447 i32) + (local $l9448 i32) + (local $l9449 i32) + (local $l9450 i32) + (local $l9451 i32) + (local $l9452 i32) + (local $l9453 i32) + (local $l9454 i32) + (local $l9455 i32) + (local $l9456 i32) + (local $l9457 i32) + (local $l9458 i32) + (local $l9459 i32) + (local $l9460 i32) + (local $l9461 i32) + (local $l9462 i32) + (local $l9463 i32) + (local $l9464 i32) + (local $l9465 i32) + (local $l9466 i32) + (local $l9467 i32) + (local $l9468 i32) + (local $l9469 i32) + (local $l9470 i32) + (local $l9471 i32) + (local $l9472 i32) + (local $l9473 i32) + (local $l9474 i32) + (local $l9475 i32) + (local $l9476 i32) + (local $l9477 i32) + (local $l9478 i32) + (local $l9479 i32) + (local $l9480 i32) + (local $l9481 i32) + (local $l9482 i32) + (local $l9483 i32) + (local $l9484 i32) + (local $l9485 i32) + (local $l9486 i32) + (local $l9487 i32) + (local $l9488 i32) + (local $l9489 i32) + (local $l9490 i32) + (local $l9491 i32) + (local $l9492 i32) + (local $l9493 i32) + (local $l9494 i32) + (local $l9495 i32) + (local $l9496 i32) + (local $l9497 i32) + (local $l9498 i32) + (local $l9499 i32) + (local $l9500 i32) + (local $l9501 i32) + (local $l9502 i32) + (local $l9503 i32) + (local $l9504 i32) + (local $l9505 i32) + (local $l9506 i32) + (local $l9507 i32) + (local $l9508 i32) + (local $l9509 i32) + (local $l9510 i32) + (local $l9511 i32) + (local $l9512 i32) + (local $l9513 i32) + (local $l9514 i32) + (local $l9515 i32) + (local $l9516 i32) + (local $l9517 i32) + (local $l9518 i32) + (local $l9519 i32) + (local $l9520 i32) + (local $l9521 i32) + (local $l9522 i32) + (local $l9523 i32) + (local $l9524 i32) + (local $l9525 i32) + (local $l9526 i32) + (local $l9527 i32) + (local $l9528 i32) + (local $l9529 i32) + (local $l9530 i32) + (local $l9531 i32) + (local $l9532 i32) + (local $l9533 i32) + (local $l9534 i32) + (local $l9535 i32) + (local $l9536 i32) + (local $l9537 i32) + (local $l9538 i32) + (local $l9539 i32) + (local $l9540 i32) + (local $l9541 i32) + (local $l9542 i32) + (local $l9543 i32) + (local $l9544 i32) + (local $l9545 i32) + (local $l9546 i32) + (local $l9547 i32) + (local $l9548 i32) + (local $l9549 i32) + (local $l9550 i32) + (local $l9551 i32) + (local $l9552 i32) + (local $l9553 i32) + (local $l9554 i32) + (local $l9555 i32) + (local $l9556 i32) + (local $l9557 i32) + (local $l9558 i32) + (local $l9559 i32) + (local $l9560 i32) + (local $l9561 i32) + (local $l9562 i32) + (local $l9563 i32) + (local $l9564 i32) + (local $l9565 i32) + (local $l9566 i32) + (local $l9567 i32) + (local $l9568 i32) + (local $l9569 i32) + (local $l9570 i32) + (local $l9571 i32) + (local $l9572 i32) + (local $l9573 i32) + (local $l9574 i32) + (local $l9575 i32) + (local $l9576 i32) + (local $l9577 i32) + (local $l9578 i32) + (local $l9579 i32) + (local $l9580 i32) + (local $l9581 i32) + (local $l9582 i32) + (local $l9583 i32) + (local $l9584 i32) + (local $l9585 i32) + (local $l9586 i32) + (local $l9587 i32) + (local $l9588 i32) + (local $l9589 i32) + (local $l9590 i32) + (local $l9591 i32) + (local $l9592 i32) + (local $l9593 i32) + (local $l9594 i32) + (local $l9595 i32) + (local $l9596 i32) + (local $l9597 i32) + (local $l9598 i32) + (local $l9599 i32) + (local $l9600 i32) + (local $l9601 i32) + (local $l9602 i32) + (local $l9603 i32) + (local $l9604 i32) + (local $l9605 i32) + (local $l9606 i32) + (local $l9607 i32) + (local $l9608 i32) + (local $l9609 i32) + (local $l9610 i32) + (local $l9611 i32) + (local $l9612 i32) + (local $l9613 i32) + (local $l9614 i32) + (local $l9615 i32) + (local $l9616 i32) + (local $l9617 i32) + (local $l9618 i32) + (local $l9619 i32) + (local $l9620 i32) + (local $l9621 i32) + (local $l9622 i32) + (local $l9623 i32) + (local $l9624 i32) + (local $l9625 i32) + (local $l9626 i32) + (local $l9627 i32) + (local $l9628 i32) + (local $l9629 i32) + (local $l9630 i32) + (local $l9631 i32) + (local $l9632 i32) + (local $l9633 i32) + (local $l9634 i32) + (local $l9635 i32) + (local $l9636 i32) + (local $l9637 i32) + (local $l9638 i32) + (local $l9639 i32) + (local $l9640 i32) + (local $l9641 i32) + (local $l9642 i32) + (local $l9643 i32) + (local $l9644 i32) + (local $l9645 i32) + (local $l9646 i32) + (local $l9647 i32) + (local $l9648 i32) + (local $l9649 i32) + (local $l9650 i32) + (local $l9651 i32) + (local $l9652 i32) + (local $l9653 i32) + (local $l9654 i32) + (local $l9655 i32) + (local $l9656 i32) + (local $l9657 i32) + (local $l9658 i32) + (local $l9659 i32) + (local $l9660 i32) + (local $l9661 i32) + (local $l9662 i32) + (local $l9663 i32) + (local $l9664 i32) + (local $l9665 i32) + (local $l9666 i32) + (local $l9667 i32) + (local $l9668 i32) + (local $l9669 i32) + (local $l9670 i32) + (local $l9671 i32) + (local $l9672 i32) + (local $l9673 i32) + (local $l9674 i32) + (local $l9675 i32) + (local $l9676 i32) + (local $l9677 i32) + (local $l9678 i32) + (local $l9679 i32) + (local $l9680 i32) + (local $l9681 i32) + (local $l9682 i32) + (local $l9683 i32) + (local $l9684 i32) + (local $l9685 i32) + (local $l9686 i32) + (local $l9687 i32) + (local $l9688 i32) + (local $l9689 i32) + (local $l9690 i32) + (local $l9691 i32) + (local $l9692 i32) + (local $l9693 i32) + (local $l9694 i32) + (local $l9695 i32) + (local $l9696 i32) + (local $l9697 i32) + (local $l9698 i32) + (local $l9699 i32) + (local $l9700 i32) + (local $l9701 i32) + (local $l9702 i32) + (local $l9703 i32) + (local $l9704 i32) + (local $l9705 i32) + (local $l9706 i32) + (local $l9707 i32) + (local $l9708 i32) + (local $l9709 i32) + (local $l9710 i32) + (local $l9711 i32) + (local $l9712 i32) + (local $l9713 i32) + (local $l9714 i32) + (local $l9715 i32) + (local $l9716 i32) + (local $l9717 i32) + (local $l9718 i32) + (local $l9719 i32) + (local $l9720 i32) + (local $l9721 i32) + (local $l9722 i32) + (local $l9723 i32) + (local $l9724 i32) + (local $l9725 i32) + (local $l9726 i32) + (local $l9727 i32) + (local $l9728 i32) + (local $l9729 i32) + (local $l9730 i32) + (local $l9731 i32) + (local $l9732 i32) + (local $l9733 i32) + (local $l9734 i32) + (local $l9735 i32) + (local $l9736 i32) + (local $l9737 i32) + (local $l9738 i32) + (local $l9739 i32) + (local $l9740 i32) + (local $l9741 i32) + (local $l9742 i32) + (local $l9743 i32) + (local $l9744 i32) + (local $l9745 i32) + (local $l9746 i32) + (local $l9747 i32) + (local $l9748 i32) + (local $l9749 i32) + (local $l9750 i32) + (local $l9751 i32) + (local $l9752 i32) + (local $l9753 i32) + (local $l9754 i32) + (local $l9755 i32) + (local $l9756 i32) + (local $l9757 i32) + (local $l9758 i32) + (local $l9759 i32) + (local $l9760 i32) + (local $l9761 i32) + (local $l9762 i32) + (local $l9763 i32) + (local $l9764 i32) + (local $l9765 i32) + (local $l9766 i32) + (local $l9767 i32) + (local $l9768 i32) + (local $l9769 i32) + (local $l9770 i32) + (local $l9771 i32) + (local $l9772 i32) + (local $l9773 i32) + (local $l9774 i32) + (local $l9775 i32) + (local $l9776 i32) + (local $l9777 i32) + (local $l9778 i32) + (local $l9779 i32) + (local $l9780 i32) + (local $l9781 i32) + (local $l9782 i32) + (local $l9783 i32) + (local $l9784 i32) + (local $l9785 i32) + (local $l9786 i32) + (local $l9787 i32) + (local $l9788 i32) + (local $l9789 i32) + (local $l9790 i32) + (local $l9791 i32) + (local $l9792 i32) + (local $l9793 i32) + (local $l9794 i32) + (local $l9795 i32) + (local $l9796 i32) + (local $l9797 i32) + (local $l9798 i32) + (local $l9799 i32) + (local $l9800 i32) + (local $l9801 i32) + (local $l9802 i32) + (local $l9803 i32) + (local $l9804 i32) + (local $l9805 i32) + (local $l9806 i32) + (local $l9807 i32) + (local $l9808 i32) + (local $l9809 i32) + (local $l9810 i32) + (local $l9811 i32) + (local $l9812 i32) + (local $l9813 i32) + (local $l9814 i32) + (local $l9815 i32) + (local $l9816 i32) + (local $l9817 i32) + (local $l9818 i32) + (local $l9819 i32) + (local $l9820 i32) + (local $l9821 i32) + (local $l9822 i32) + (local $l9823 i32) + (local $l9824 i32) + (local $l9825 i32) + (local $l9826 i32) + (local $l9827 i32) + (local $l9828 i32) + (local $l9829 i32) + (local $l9830 i32) + (local $l9831 i32) + (local $l9832 i32) + (local $l9833 i32) + (local $l9834 i32) + (local $l9835 i32) + (local $l9836 i32) + (local $l9837 i32) + (local $l9838 i32) + (local $l9839 i32) + (local $l9840 i32) + (local $l9841 i32) + (local $l9842 i32) + (local $l9843 i32) + (local $l9844 i32) + (local $l9845 i32) + (local $l9846 i32) + (local $l9847 i32) + (local $l9848 i32) + (local $l9849 i32) + (local $l9850 i32) + (local $l9851 i32) + (local $l9852 i32) + (local $l9853 i32) + (local $l9854 i32) + (local $l9855 i32) + (local $l9856 i32) + (local $l9857 i32) + (local $l9858 i32) + (local $l9859 i32) + (local $l9860 i32) + (local $l9861 i32) + (local $l9862 i32) + (local $l9863 i32) + (local $l9864 i32) + (local $l9865 i32) + (local $l9866 i32) + (local $l9867 i32) + (local $l9868 i32) + (local $l9869 i32) + (local $l9870 i32) + (local $l9871 i32) + (local $l9872 i32) + (local $l9873 i32) + (local $l9874 i32) + (local $l9875 i32) + (local $l9876 i32) + (local $l9877 i32) + (local $l9878 i32) + (local $l9879 i32) + (local $l9880 i32) + (local $l9881 i32) + (local $l9882 i32) + (local $l9883 i32) + (local $l9884 i32) + (local $l9885 i32) + (local $l9886 i32) + (local $l9887 i32) + (local $l9888 i32) + (local $l9889 i32) + (local $l9890 i32) + (local $l9891 i32) + (local $l9892 i32) + (local $l9893 i32) + (local $l9894 i32) + (local $l9895 i32) + (local $l9896 i32) + (local $l9897 i32) + (local $l9898 i32) + (local $l9899 i32) + (local $l9900 i32) + (local $l9901 i32) + (local $l9902 i32) + (local $l9903 i32) + (local $l9904 i32) + (local $l9905 i32) + (local $l9906 i32) + (local $l9907 i32) + (local $l9908 i32) + (local $l9909 i32) + (local $l9910 i32) + (local $l9911 i32) + (local $l9912 i32) + (local $l9913 i32) + (local $l9914 i32) + (local $l9915 i32) + (local $l9916 i32) + (local $l9917 i32) + (local $l9918 i32) + (local $l9919 i32) + (local $l9920 i32) + (local $l9921 i32) + (local $l9922 i32) + (local $l9923 i32) + (local $l9924 i32) + (local $l9925 i32) + (local $l9926 i32) + (local $l9927 i32) + (local $l9928 i32) + (local $l9929 i32) + (local $l9930 i32) + (local $l9931 i32) + (local $l9932 i32) + (local $l9933 i32) + (local $l9934 i32) + (local $l9935 i32) + (local $l9936 i32) + (local $l9937 i32) + (local $l9938 i32) + (local $l9939 i32) + (local $l9940 i32) + (local $l9941 i32) + (local $l9942 i32) + (local $l9943 i32) + (local $l9944 i32) + (local $l9945 i32) + (local $l9946 i32) + (local $l9947 i32) + (local $l9948 i32) + (local $l9949 i32) + (local $l9950 i32) + (local $l9951 i32) + (local $l9952 i32) + (local $l9953 i32) + (local $l9954 i32) + (local $l9955 i32) + (local $l9956 i32) + (local $l9957 i32) + (local $l9958 i32) + (local $l9959 i32) + (local $l9960 i32) + (local $l9961 i32) + (local $l9962 i32) + (local $l9963 i32) + (local $l9964 i32) + (local $l9965 i32) + (local $l9966 i32) + (local $l9967 i32) + (local $l9968 i32) + (local $l9969 i32) + (local $l9970 i32) + (local $l9971 i32) + (local $l9972 i32) + (local $l9973 i32) + (local $l9974 i32) + (local $l9975 i32) + (local $l9976 i32) + (local $l9977 i32) + (local $l9978 i32) + (local $l9979 i32) + (local $l9980 i32) + (local $l9981 i32) + (local $l9982 i32) + (local $l9983 i32) + (local $l9984 i32) + (local $l9985 i32) + (local $l9986 i32) + (local $l9987 i32) + (local $l9988 i32) + (local $l9989 i32) + (local $l9990 i32) + (local $l9991 i32) + (local $l9992 i32) + (local $l9993 i32) + (local $l9994 i32) + (local $l9995 i32) + (local $l9996 i32) + (local $l9997 i32) + (local $l9998 i32) + (local $l9999 i32) + ;; Initialize locals as sum of previous two + local.get $p0 + local.get $p1 + i32.add + local.set $l2 + local.get $p1 + local.get $l2 + i32.add + local.set $l3 + local.get $l2 + local.get $l3 + i32.add + local.set $l4 + local.get $l3 + local.get $l4 + i32.add + local.set $l5 + local.get $l4 + local.get $l5 + i32.add + local.set $l6 + local.get $l5 + local.get $l6 + i32.add + local.set $l7 + local.get $l6 + local.get $l7 + i32.add + local.set $l8 + local.get $l7 + local.get $l8 + i32.add + local.set $l9 + local.get $l8 + local.get $l9 + i32.add + local.set $l10 + local.get $l9 + local.get $l10 + i32.add + local.set $l11 + local.get $l10 + local.get $l11 + i32.add + local.set $l12 + local.get $l11 + local.get $l12 + i32.add + local.set $l13 + local.get $l12 + local.get $l13 + i32.add + local.set $l14 + local.get $l13 + local.get $l14 + i32.add + local.set $l15 + local.get $l14 + local.get $l15 + i32.add + local.set $l16 + local.get $l15 + local.get $l16 + i32.add + local.set $l17 + local.get $l16 + local.get $l17 + i32.add + local.set $l18 + local.get $l17 + local.get $l18 + i32.add + local.set $l19 + local.get $l18 + local.get $l19 + i32.add + local.set $l20 + local.get $l19 + local.get $l20 + i32.add + local.set $l21 + local.get $l20 + local.get $l21 + i32.add + local.set $l22 + local.get $l21 + local.get $l22 + i32.add + local.set $l23 + local.get $l22 + local.get $l23 + i32.add + local.set $l24 + local.get $l23 + local.get $l24 + i32.add + local.set $l25 + local.get $l24 + local.get $l25 + i32.add + local.set $l26 + local.get $l25 + local.get $l26 + i32.add + local.set $l27 + local.get $l26 + local.get $l27 + i32.add + local.set $l28 + local.get $l27 + local.get $l28 + i32.add + local.set $l29 + local.get $l28 + local.get $l29 + i32.add + local.set $l30 + local.get $l29 + local.get $l30 + i32.add + local.set $l31 + local.get $l30 + local.get $l31 + i32.add + local.set $l32 + local.get $l31 + local.get $l32 + i32.add + local.set $l33 + local.get $l32 + local.get $l33 + i32.add + local.set $l34 + local.get $l33 + local.get $l34 + i32.add + local.set $l35 + local.get $l34 + local.get $l35 + i32.add + local.set $l36 + local.get $l35 + local.get $l36 + i32.add + local.set $l37 + local.get $l36 + local.get $l37 + i32.add + local.set $l38 + local.get $l37 + local.get $l38 + i32.add + local.set $l39 + local.get $l38 + local.get $l39 + i32.add + local.set $l40 + local.get $l39 + local.get $l40 + i32.add + local.set $l41 + local.get $l40 + local.get $l41 + i32.add + local.set $l42 + local.get $l41 + local.get $l42 + i32.add + local.set $l43 + local.get $l42 + local.get $l43 + i32.add + local.set $l44 + local.get $l43 + local.get $l44 + i32.add + local.set $l45 + local.get $l44 + local.get $l45 + i32.add + local.set $l46 + local.get $l45 + local.get $l46 + i32.add + local.set $l47 + local.get $l46 + local.get $l47 + i32.add + local.set $l48 + local.get $l47 + local.get $l48 + i32.add + local.set $l49 + local.get $l48 + local.get $l49 + i32.add + local.set $l50 + local.get $l49 + local.get $l50 + i32.add + local.set $l51 + local.get $l50 + local.get $l51 + i32.add + local.set $l52 + local.get $l51 + local.get $l52 + i32.add + local.set $l53 + local.get $l52 + local.get $l53 + i32.add + local.set $l54 + local.get $l53 + local.get $l54 + i32.add + local.set $l55 + local.get $l54 + local.get $l55 + i32.add + local.set $l56 + local.get $l55 + local.get $l56 + i32.add + local.set $l57 + local.get $l56 + local.get $l57 + i32.add + local.set $l58 + local.get $l57 + local.get $l58 + i32.add + local.set $l59 + local.get $l58 + local.get $l59 + i32.add + local.set $l60 + local.get $l59 + local.get $l60 + i32.add + local.set $l61 + local.get $l60 + local.get $l61 + i32.add + local.set $l62 + local.get $l61 + local.get $l62 + i32.add + local.set $l63 + local.get $l62 + local.get $l63 + i32.add + local.set $l64 + local.get $l63 + local.get $l64 + i32.add + local.set $l65 + local.get $l64 + local.get $l65 + i32.add + local.set $l66 + local.get $l65 + local.get $l66 + i32.add + local.set $l67 + local.get $l66 + local.get $l67 + i32.add + local.set $l68 + local.get $l67 + local.get $l68 + i32.add + local.set $l69 + local.get $l68 + local.get $l69 + i32.add + local.set $l70 + local.get $l69 + local.get $l70 + i32.add + local.set $l71 + local.get $l70 + local.get $l71 + i32.add + local.set $l72 + local.get $l71 + local.get $l72 + i32.add + local.set $l73 + local.get $l72 + local.get $l73 + i32.add + local.set $l74 + local.get $l73 + local.get $l74 + i32.add + local.set $l75 + local.get $l74 + local.get $l75 + i32.add + local.set $l76 + local.get $l75 + local.get $l76 + i32.add + local.set $l77 + local.get $l76 + local.get $l77 + i32.add + local.set $l78 + local.get $l77 + local.get $l78 + i32.add + local.set $l79 + local.get $l78 + local.get $l79 + i32.add + local.set $l80 + local.get $l79 + local.get $l80 + i32.add + local.set $l81 + local.get $l80 + local.get $l81 + i32.add + local.set $l82 + local.get $l81 + local.get $l82 + i32.add + local.set $l83 + local.get $l82 + local.get $l83 + i32.add + local.set $l84 + local.get $l83 + local.get $l84 + i32.add + local.set $l85 + local.get $l84 + local.get $l85 + i32.add + local.set $l86 + local.get $l85 + local.get $l86 + i32.add + local.set $l87 + local.get $l86 + local.get $l87 + i32.add + local.set $l88 + local.get $l87 + local.get $l88 + i32.add + local.set $l89 + local.get $l88 + local.get $l89 + i32.add + local.set $l90 + local.get $l89 + local.get $l90 + i32.add + local.set $l91 + local.get $l90 + local.get $l91 + i32.add + local.set $l92 + local.get $l91 + local.get $l92 + i32.add + local.set $l93 + local.get $l92 + local.get $l93 + i32.add + local.set $l94 + local.get $l93 + local.get $l94 + i32.add + local.set $l95 + local.get $l94 + local.get $l95 + i32.add + local.set $l96 + local.get $l95 + local.get $l96 + i32.add + local.set $l97 + local.get $l96 + local.get $l97 + i32.add + local.set $l98 + local.get $l97 + local.get $l98 + i32.add + local.set $l99 + local.get $l98 + local.get $l99 + i32.add + local.set $l100 + local.get $l99 + local.get $l100 + i32.add + local.set $l101 + local.get $l100 + local.get $l101 + i32.add + local.set $l102 + local.get $l101 + local.get $l102 + i32.add + local.set $l103 + local.get $l102 + local.get $l103 + i32.add + local.set $l104 + local.get $l103 + local.get $l104 + i32.add + local.set $l105 + local.get $l104 + local.get $l105 + i32.add + local.set $l106 + local.get $l105 + local.get $l106 + i32.add + local.set $l107 + local.get $l106 + local.get $l107 + i32.add + local.set $l108 + local.get $l107 + local.get $l108 + i32.add + local.set $l109 + local.get $l108 + local.get $l109 + i32.add + local.set $l110 + local.get $l109 + local.get $l110 + i32.add + local.set $l111 + local.get $l110 + local.get $l111 + i32.add + local.set $l112 + local.get $l111 + local.get $l112 + i32.add + local.set $l113 + local.get $l112 + local.get $l113 + i32.add + local.set $l114 + local.get $l113 + local.get $l114 + i32.add + local.set $l115 + local.get $l114 + local.get $l115 + i32.add + local.set $l116 + local.get $l115 + local.get $l116 + i32.add + local.set $l117 + local.get $l116 + local.get $l117 + i32.add + local.set $l118 + local.get $l117 + local.get $l118 + i32.add + local.set $l119 + local.get $l118 + local.get $l119 + i32.add + local.set $l120 + local.get $l119 + local.get $l120 + i32.add + local.set $l121 + local.get $l120 + local.get $l121 + i32.add + local.set $l122 + local.get $l121 + local.get $l122 + i32.add + local.set $l123 + local.get $l122 + local.get $l123 + i32.add + local.set $l124 + local.get $l123 + local.get $l124 + i32.add + local.set $l125 + local.get $l124 + local.get $l125 + i32.add + local.set $l126 + local.get $l125 + local.get $l126 + i32.add + local.set $l127 + local.get $l126 + local.get $l127 + i32.add + local.set $l128 + local.get $l127 + local.get $l128 + i32.add + local.set $l129 + local.get $l128 + local.get $l129 + i32.add + local.set $l130 + local.get $l129 + local.get $l130 + i32.add + local.set $l131 + local.get $l130 + local.get $l131 + i32.add + local.set $l132 + local.get $l131 + local.get $l132 + i32.add + local.set $l133 + local.get $l132 + local.get $l133 + i32.add + local.set $l134 + local.get $l133 + local.get $l134 + i32.add + local.set $l135 + local.get $l134 + local.get $l135 + i32.add + local.set $l136 + local.get $l135 + local.get $l136 + i32.add + local.set $l137 + local.get $l136 + local.get $l137 + i32.add + local.set $l138 + local.get $l137 + local.get $l138 + i32.add + local.set $l139 + local.get $l138 + local.get $l139 + i32.add + local.set $l140 + local.get $l139 + local.get $l140 + i32.add + local.set $l141 + local.get $l140 + local.get $l141 + i32.add + local.set $l142 + local.get $l141 + local.get $l142 + i32.add + local.set $l143 + local.get $l142 + local.get $l143 + i32.add + local.set $l144 + local.get $l143 + local.get $l144 + i32.add + local.set $l145 + local.get $l144 + local.get $l145 + i32.add + local.set $l146 + local.get $l145 + local.get $l146 + i32.add + local.set $l147 + local.get $l146 + local.get $l147 + i32.add + local.set $l148 + local.get $l147 + local.get $l148 + i32.add + local.set $l149 + local.get $l148 + local.get $l149 + i32.add + local.set $l150 + local.get $l149 + local.get $l150 + i32.add + local.set $l151 + local.get $l150 + local.get $l151 + i32.add + local.set $l152 + local.get $l151 + local.get $l152 + i32.add + local.set $l153 + local.get $l152 + local.get $l153 + i32.add + local.set $l154 + local.get $l153 + local.get $l154 + i32.add + local.set $l155 + local.get $l154 + local.get $l155 + i32.add + local.set $l156 + local.get $l155 + local.get $l156 + i32.add + local.set $l157 + local.get $l156 + local.get $l157 + i32.add + local.set $l158 + local.get $l157 + local.get $l158 + i32.add + local.set $l159 + local.get $l158 + local.get $l159 + i32.add + local.set $l160 + local.get $l159 + local.get $l160 + i32.add + local.set $l161 + local.get $l160 + local.get $l161 + i32.add + local.set $l162 + local.get $l161 + local.get $l162 + i32.add + local.set $l163 + local.get $l162 + local.get $l163 + i32.add + local.set $l164 + local.get $l163 + local.get $l164 + i32.add + local.set $l165 + local.get $l164 + local.get $l165 + i32.add + local.set $l166 + local.get $l165 + local.get $l166 + i32.add + local.set $l167 + local.get $l166 + local.get $l167 + i32.add + local.set $l168 + local.get $l167 + local.get $l168 + i32.add + local.set $l169 + local.get $l168 + local.get $l169 + i32.add + local.set $l170 + local.get $l169 + local.get $l170 + i32.add + local.set $l171 + local.get $l170 + local.get $l171 + i32.add + local.set $l172 + local.get $l171 + local.get $l172 + i32.add + local.set $l173 + local.get $l172 + local.get $l173 + i32.add + local.set $l174 + local.get $l173 + local.get $l174 + i32.add + local.set $l175 + local.get $l174 + local.get $l175 + i32.add + local.set $l176 + local.get $l175 + local.get $l176 + i32.add + local.set $l177 + local.get $l176 + local.get $l177 + i32.add + local.set $l178 + local.get $l177 + local.get $l178 + i32.add + local.set $l179 + local.get $l178 + local.get $l179 + i32.add + local.set $l180 + local.get $l179 + local.get $l180 + i32.add + local.set $l181 + local.get $l180 + local.get $l181 + i32.add + local.set $l182 + local.get $l181 + local.get $l182 + i32.add + local.set $l183 + local.get $l182 + local.get $l183 + i32.add + local.set $l184 + local.get $l183 + local.get $l184 + i32.add + local.set $l185 + local.get $l184 + local.get $l185 + i32.add + local.set $l186 + local.get $l185 + local.get $l186 + i32.add + local.set $l187 + local.get $l186 + local.get $l187 + i32.add + local.set $l188 + local.get $l187 + local.get $l188 + i32.add + local.set $l189 + local.get $l188 + local.get $l189 + i32.add + local.set $l190 + local.get $l189 + local.get $l190 + i32.add + local.set $l191 + local.get $l190 + local.get $l191 + i32.add + local.set $l192 + local.get $l191 + local.get $l192 + i32.add + local.set $l193 + local.get $l192 + local.get $l193 + i32.add + local.set $l194 + local.get $l193 + local.get $l194 + i32.add + local.set $l195 + local.get $l194 + local.get $l195 + i32.add + local.set $l196 + local.get $l195 + local.get $l196 + i32.add + local.set $l197 + local.get $l196 + local.get $l197 + i32.add + local.set $l198 + local.get $l197 + local.get $l198 + i32.add + local.set $l199 + local.get $l198 + local.get $l199 + i32.add + local.set $l200 + local.get $l199 + local.get $l200 + i32.add + local.set $l201 + local.get $l200 + local.get $l201 + i32.add + local.set $l202 + local.get $l201 + local.get $l202 + i32.add + local.set $l203 + local.get $l202 + local.get $l203 + i32.add + local.set $l204 + local.get $l203 + local.get $l204 + i32.add + local.set $l205 + local.get $l204 + local.get $l205 + i32.add + local.set $l206 + local.get $l205 + local.get $l206 + i32.add + local.set $l207 + local.get $l206 + local.get $l207 + i32.add + local.set $l208 + local.get $l207 + local.get $l208 + i32.add + local.set $l209 + local.get $l208 + local.get $l209 + i32.add + local.set $l210 + local.get $l209 + local.get $l210 + i32.add + local.set $l211 + local.get $l210 + local.get $l211 + i32.add + local.set $l212 + local.get $l211 + local.get $l212 + i32.add + local.set $l213 + local.get $l212 + local.get $l213 + i32.add + local.set $l214 + local.get $l213 + local.get $l214 + i32.add + local.set $l215 + local.get $l214 + local.get $l215 + i32.add + local.set $l216 + local.get $l215 + local.get $l216 + i32.add + local.set $l217 + local.get $l216 + local.get $l217 + i32.add + local.set $l218 + local.get $l217 + local.get $l218 + i32.add + local.set $l219 + local.get $l218 + local.get $l219 + i32.add + local.set $l220 + local.get $l219 + local.get $l220 + i32.add + local.set $l221 + local.get $l220 + local.get $l221 + i32.add + local.set $l222 + local.get $l221 + local.get $l222 + i32.add + local.set $l223 + local.get $l222 + local.get $l223 + i32.add + local.set $l224 + local.get $l223 + local.get $l224 + i32.add + local.set $l225 + local.get $l224 + local.get $l225 + i32.add + local.set $l226 + local.get $l225 + local.get $l226 + i32.add + local.set $l227 + local.get $l226 + local.get $l227 + i32.add + local.set $l228 + local.get $l227 + local.get $l228 + i32.add + local.set $l229 + local.get $l228 + local.get $l229 + i32.add + local.set $l230 + local.get $l229 + local.get $l230 + i32.add + local.set $l231 + local.get $l230 + local.get $l231 + i32.add + local.set $l232 + local.get $l231 + local.get $l232 + i32.add + local.set $l233 + local.get $l232 + local.get $l233 + i32.add + local.set $l234 + local.get $l233 + local.get $l234 + i32.add + local.set $l235 + local.get $l234 + local.get $l235 + i32.add + local.set $l236 + local.get $l235 + local.get $l236 + i32.add + local.set $l237 + local.get $l236 + local.get $l237 + i32.add + local.set $l238 + local.get $l237 + local.get $l238 + i32.add + local.set $l239 + local.get $l238 + local.get $l239 + i32.add + local.set $l240 + local.get $l239 + local.get $l240 + i32.add + local.set $l241 + local.get $l240 + local.get $l241 + i32.add + local.set $l242 + local.get $l241 + local.get $l242 + i32.add + local.set $l243 + local.get $l242 + local.get $l243 + i32.add + local.set $l244 + local.get $l243 + local.get $l244 + i32.add + local.set $l245 + local.get $l244 + local.get $l245 + i32.add + local.set $l246 + local.get $l245 + local.get $l246 + i32.add + local.set $l247 + local.get $l246 + local.get $l247 + i32.add + local.set $l248 + local.get $l247 + local.get $l248 + i32.add + local.set $l249 + local.get $l248 + local.get $l249 + i32.add + local.set $l250 + local.get $l249 + local.get $l250 + i32.add + local.set $l251 + local.get $l250 + local.get $l251 + i32.add + local.set $l252 + local.get $l251 + local.get $l252 + i32.add + local.set $l253 + local.get $l252 + local.get $l253 + i32.add + local.set $l254 + local.get $l253 + local.get $l254 + i32.add + local.set $l255 + local.get $l254 + local.get $l255 + i32.add + local.set $l256 + local.get $l255 + local.get $l256 + i32.add + local.set $l257 + local.get $l256 + local.get $l257 + i32.add + local.set $l258 + local.get $l257 + local.get $l258 + i32.add + local.set $l259 + local.get $l258 + local.get $l259 + i32.add + local.set $l260 + local.get $l259 + local.get $l260 + i32.add + local.set $l261 + local.get $l260 + local.get $l261 + i32.add + local.set $l262 + local.get $l261 + local.get $l262 + i32.add + local.set $l263 + local.get $l262 + local.get $l263 + i32.add + local.set $l264 + local.get $l263 + local.get $l264 + i32.add + local.set $l265 + local.get $l264 + local.get $l265 + i32.add + local.set $l266 + local.get $l265 + local.get $l266 + i32.add + local.set $l267 + local.get $l266 + local.get $l267 + i32.add + local.set $l268 + local.get $l267 + local.get $l268 + i32.add + local.set $l269 + local.get $l268 + local.get $l269 + i32.add + local.set $l270 + local.get $l269 + local.get $l270 + i32.add + local.set $l271 + local.get $l270 + local.get $l271 + i32.add + local.set $l272 + local.get $l271 + local.get $l272 + i32.add + local.set $l273 + local.get $l272 + local.get $l273 + i32.add + local.set $l274 + local.get $l273 + local.get $l274 + i32.add + local.set $l275 + local.get $l274 + local.get $l275 + i32.add + local.set $l276 + local.get $l275 + local.get $l276 + i32.add + local.set $l277 + local.get $l276 + local.get $l277 + i32.add + local.set $l278 + local.get $l277 + local.get $l278 + i32.add + local.set $l279 + local.get $l278 + local.get $l279 + i32.add + local.set $l280 + local.get $l279 + local.get $l280 + i32.add + local.set $l281 + local.get $l280 + local.get $l281 + i32.add + local.set $l282 + local.get $l281 + local.get $l282 + i32.add + local.set $l283 + local.get $l282 + local.get $l283 + i32.add + local.set $l284 + local.get $l283 + local.get $l284 + i32.add + local.set $l285 + local.get $l284 + local.get $l285 + i32.add + local.set $l286 + local.get $l285 + local.get $l286 + i32.add + local.set $l287 + local.get $l286 + local.get $l287 + i32.add + local.set $l288 + local.get $l287 + local.get $l288 + i32.add + local.set $l289 + local.get $l288 + local.get $l289 + i32.add + local.set $l290 + local.get $l289 + local.get $l290 + i32.add + local.set $l291 + local.get $l290 + local.get $l291 + i32.add + local.set $l292 + local.get $l291 + local.get $l292 + i32.add + local.set $l293 + local.get $l292 + local.get $l293 + i32.add + local.set $l294 + local.get $l293 + local.get $l294 + i32.add + local.set $l295 + local.get $l294 + local.get $l295 + i32.add + local.set $l296 + local.get $l295 + local.get $l296 + i32.add + local.set $l297 + local.get $l296 + local.get $l297 + i32.add + local.set $l298 + local.get $l297 + local.get $l298 + i32.add + local.set $l299 + local.get $l298 + local.get $l299 + i32.add + local.set $l300 + local.get $l299 + local.get $l300 + i32.add + local.set $l301 + local.get $l300 + local.get $l301 + i32.add + local.set $l302 + local.get $l301 + local.get $l302 + i32.add + local.set $l303 + local.get $l302 + local.get $l303 + i32.add + local.set $l304 + local.get $l303 + local.get $l304 + i32.add + local.set $l305 + local.get $l304 + local.get $l305 + i32.add + local.set $l306 + local.get $l305 + local.get $l306 + i32.add + local.set $l307 + local.get $l306 + local.get $l307 + i32.add + local.set $l308 + local.get $l307 + local.get $l308 + i32.add + local.set $l309 + local.get $l308 + local.get $l309 + i32.add + local.set $l310 + local.get $l309 + local.get $l310 + i32.add + local.set $l311 + local.get $l310 + local.get $l311 + i32.add + local.set $l312 + local.get $l311 + local.get $l312 + i32.add + local.set $l313 + local.get $l312 + local.get $l313 + i32.add + local.set $l314 + local.get $l313 + local.get $l314 + i32.add + local.set $l315 + local.get $l314 + local.get $l315 + i32.add + local.set $l316 + local.get $l315 + local.get $l316 + i32.add + local.set $l317 + local.get $l316 + local.get $l317 + i32.add + local.set $l318 + local.get $l317 + local.get $l318 + i32.add + local.set $l319 + local.get $l318 + local.get $l319 + i32.add + local.set $l320 + local.get $l319 + local.get $l320 + i32.add + local.set $l321 + local.get $l320 + local.get $l321 + i32.add + local.set $l322 + local.get $l321 + local.get $l322 + i32.add + local.set $l323 + local.get $l322 + local.get $l323 + i32.add + local.set $l324 + local.get $l323 + local.get $l324 + i32.add + local.set $l325 + local.get $l324 + local.get $l325 + i32.add + local.set $l326 + local.get $l325 + local.get $l326 + i32.add + local.set $l327 + local.get $l326 + local.get $l327 + i32.add + local.set $l328 + local.get $l327 + local.get $l328 + i32.add + local.set $l329 + local.get $l328 + local.get $l329 + i32.add + local.set $l330 + local.get $l329 + local.get $l330 + i32.add + local.set $l331 + local.get $l330 + local.get $l331 + i32.add + local.set $l332 + local.get $l331 + local.get $l332 + i32.add + local.set $l333 + local.get $l332 + local.get $l333 + i32.add + local.set $l334 + local.get $l333 + local.get $l334 + i32.add + local.set $l335 + local.get $l334 + local.get $l335 + i32.add + local.set $l336 + local.get $l335 + local.get $l336 + i32.add + local.set $l337 + local.get $l336 + local.get $l337 + i32.add + local.set $l338 + local.get $l337 + local.get $l338 + i32.add + local.set $l339 + local.get $l338 + local.get $l339 + i32.add + local.set $l340 + local.get $l339 + local.get $l340 + i32.add + local.set $l341 + local.get $l340 + local.get $l341 + i32.add + local.set $l342 + local.get $l341 + local.get $l342 + i32.add + local.set $l343 + local.get $l342 + local.get $l343 + i32.add + local.set $l344 + local.get $l343 + local.get $l344 + i32.add + local.set $l345 + local.get $l344 + local.get $l345 + i32.add + local.set $l346 + local.get $l345 + local.get $l346 + i32.add + local.set $l347 + local.get $l346 + local.get $l347 + i32.add + local.set $l348 + local.get $l347 + local.get $l348 + i32.add + local.set $l349 + local.get $l348 + local.get $l349 + i32.add + local.set $l350 + local.get $l349 + local.get $l350 + i32.add + local.set $l351 + local.get $l350 + local.get $l351 + i32.add + local.set $l352 + local.get $l351 + local.get $l352 + i32.add + local.set $l353 + local.get $l352 + local.get $l353 + i32.add + local.set $l354 + local.get $l353 + local.get $l354 + i32.add + local.set $l355 + local.get $l354 + local.get $l355 + i32.add + local.set $l356 + local.get $l355 + local.get $l356 + i32.add + local.set $l357 + local.get $l356 + local.get $l357 + i32.add + local.set $l358 + local.get $l357 + local.get $l358 + i32.add + local.set $l359 + local.get $l358 + local.get $l359 + i32.add + local.set $l360 + local.get $l359 + local.get $l360 + i32.add + local.set $l361 + local.get $l360 + local.get $l361 + i32.add + local.set $l362 + local.get $l361 + local.get $l362 + i32.add + local.set $l363 + local.get $l362 + local.get $l363 + i32.add + local.set $l364 + local.get $l363 + local.get $l364 + i32.add + local.set $l365 + local.get $l364 + local.get $l365 + i32.add + local.set $l366 + local.get $l365 + local.get $l366 + i32.add + local.set $l367 + local.get $l366 + local.get $l367 + i32.add + local.set $l368 + local.get $l367 + local.get $l368 + i32.add + local.set $l369 + local.get $l368 + local.get $l369 + i32.add + local.set $l370 + local.get $l369 + local.get $l370 + i32.add + local.set $l371 + local.get $l370 + local.get $l371 + i32.add + local.set $l372 + local.get $l371 + local.get $l372 + i32.add + local.set $l373 + local.get $l372 + local.get $l373 + i32.add + local.set $l374 + local.get $l373 + local.get $l374 + i32.add + local.set $l375 + local.get $l374 + local.get $l375 + i32.add + local.set $l376 + local.get $l375 + local.get $l376 + i32.add + local.set $l377 + local.get $l376 + local.get $l377 + i32.add + local.set $l378 + local.get $l377 + local.get $l378 + i32.add + local.set $l379 + local.get $l378 + local.get $l379 + i32.add + local.set $l380 + local.get $l379 + local.get $l380 + i32.add + local.set $l381 + local.get $l380 + local.get $l381 + i32.add + local.set $l382 + local.get $l381 + local.get $l382 + i32.add + local.set $l383 + local.get $l382 + local.get $l383 + i32.add + local.set $l384 + local.get $l383 + local.get $l384 + i32.add + local.set $l385 + local.get $l384 + local.get $l385 + i32.add + local.set $l386 + local.get $l385 + local.get $l386 + i32.add + local.set $l387 + local.get $l386 + local.get $l387 + i32.add + local.set $l388 + local.get $l387 + local.get $l388 + i32.add + local.set $l389 + local.get $l388 + local.get $l389 + i32.add + local.set $l390 + local.get $l389 + local.get $l390 + i32.add + local.set $l391 + local.get $l390 + local.get $l391 + i32.add + local.set $l392 + local.get $l391 + local.get $l392 + i32.add + local.set $l393 + local.get $l392 + local.get $l393 + i32.add + local.set $l394 + local.get $l393 + local.get $l394 + i32.add + local.set $l395 + local.get $l394 + local.get $l395 + i32.add + local.set $l396 + local.get $l395 + local.get $l396 + i32.add + local.set $l397 + local.get $l396 + local.get $l397 + i32.add + local.set $l398 + local.get $l397 + local.get $l398 + i32.add + local.set $l399 + local.get $l398 + local.get $l399 + i32.add + local.set $l400 + local.get $l399 + local.get $l400 + i32.add + local.set $l401 + local.get $l400 + local.get $l401 + i32.add + local.set $l402 + local.get $l401 + local.get $l402 + i32.add + local.set $l403 + local.get $l402 + local.get $l403 + i32.add + local.set $l404 + local.get $l403 + local.get $l404 + i32.add + local.set $l405 + local.get $l404 + local.get $l405 + i32.add + local.set $l406 + local.get $l405 + local.get $l406 + i32.add + local.set $l407 + local.get $l406 + local.get $l407 + i32.add + local.set $l408 + local.get $l407 + local.get $l408 + i32.add + local.set $l409 + local.get $l408 + local.get $l409 + i32.add + local.set $l410 + local.get $l409 + local.get $l410 + i32.add + local.set $l411 + local.get $l410 + local.get $l411 + i32.add + local.set $l412 + local.get $l411 + local.get $l412 + i32.add + local.set $l413 + local.get $l412 + local.get $l413 + i32.add + local.set $l414 + local.get $l413 + local.get $l414 + i32.add + local.set $l415 + local.get $l414 + local.get $l415 + i32.add + local.set $l416 + local.get $l415 + local.get $l416 + i32.add + local.set $l417 + local.get $l416 + local.get $l417 + i32.add + local.set $l418 + local.get $l417 + local.get $l418 + i32.add + local.set $l419 + local.get $l418 + local.get $l419 + i32.add + local.set $l420 + local.get $l419 + local.get $l420 + i32.add + local.set $l421 + local.get $l420 + local.get $l421 + i32.add + local.set $l422 + local.get $l421 + local.get $l422 + i32.add + local.set $l423 + local.get $l422 + local.get $l423 + i32.add + local.set $l424 + local.get $l423 + local.get $l424 + i32.add + local.set $l425 + local.get $l424 + local.get $l425 + i32.add + local.set $l426 + local.get $l425 + local.get $l426 + i32.add + local.set $l427 + local.get $l426 + local.get $l427 + i32.add + local.set $l428 + local.get $l427 + local.get $l428 + i32.add + local.set $l429 + local.get $l428 + local.get $l429 + i32.add + local.set $l430 + local.get $l429 + local.get $l430 + i32.add + local.set $l431 + local.get $l430 + local.get $l431 + i32.add + local.set $l432 + local.get $l431 + local.get $l432 + i32.add + local.set $l433 + local.get $l432 + local.get $l433 + i32.add + local.set $l434 + local.get $l433 + local.get $l434 + i32.add + local.set $l435 + local.get $l434 + local.get $l435 + i32.add + local.set $l436 + local.get $l435 + local.get $l436 + i32.add + local.set $l437 + local.get $l436 + local.get $l437 + i32.add + local.set $l438 + local.get $l437 + local.get $l438 + i32.add + local.set $l439 + local.get $l438 + local.get $l439 + i32.add + local.set $l440 + local.get $l439 + local.get $l440 + i32.add + local.set $l441 + local.get $l440 + local.get $l441 + i32.add + local.set $l442 + local.get $l441 + local.get $l442 + i32.add + local.set $l443 + local.get $l442 + local.get $l443 + i32.add + local.set $l444 + local.get $l443 + local.get $l444 + i32.add + local.set $l445 + local.get $l444 + local.get $l445 + i32.add + local.set $l446 + local.get $l445 + local.get $l446 + i32.add + local.set $l447 + local.get $l446 + local.get $l447 + i32.add + local.set $l448 + local.get $l447 + local.get $l448 + i32.add + local.set $l449 + local.get $l448 + local.get $l449 + i32.add + local.set $l450 + local.get $l449 + local.get $l450 + i32.add + local.set $l451 + local.get $l450 + local.get $l451 + i32.add + local.set $l452 + local.get $l451 + local.get $l452 + i32.add + local.set $l453 + local.get $l452 + local.get $l453 + i32.add + local.set $l454 + local.get $l453 + local.get $l454 + i32.add + local.set $l455 + local.get $l454 + local.get $l455 + i32.add + local.set $l456 + local.get $l455 + local.get $l456 + i32.add + local.set $l457 + local.get $l456 + local.get $l457 + i32.add + local.set $l458 + local.get $l457 + local.get $l458 + i32.add + local.set $l459 + local.get $l458 + local.get $l459 + i32.add + local.set $l460 + local.get $l459 + local.get $l460 + i32.add + local.set $l461 + local.get $l460 + local.get $l461 + i32.add + local.set $l462 + local.get $l461 + local.get $l462 + i32.add + local.set $l463 + local.get $l462 + local.get $l463 + i32.add + local.set $l464 + local.get $l463 + local.get $l464 + i32.add + local.set $l465 + local.get $l464 + local.get $l465 + i32.add + local.set $l466 + local.get $l465 + local.get $l466 + i32.add + local.set $l467 + local.get $l466 + local.get $l467 + i32.add + local.set $l468 + local.get $l467 + local.get $l468 + i32.add + local.set $l469 + local.get $l468 + local.get $l469 + i32.add + local.set $l470 + local.get $l469 + local.get $l470 + i32.add + local.set $l471 + local.get $l470 + local.get $l471 + i32.add + local.set $l472 + local.get $l471 + local.get $l472 + i32.add + local.set $l473 + local.get $l472 + local.get $l473 + i32.add + local.set $l474 + local.get $l473 + local.get $l474 + i32.add + local.set $l475 + local.get $l474 + local.get $l475 + i32.add + local.set $l476 + local.get $l475 + local.get $l476 + i32.add + local.set $l477 + local.get $l476 + local.get $l477 + i32.add + local.set $l478 + local.get $l477 + local.get $l478 + i32.add + local.set $l479 + local.get $l478 + local.get $l479 + i32.add + local.set $l480 + local.get $l479 + local.get $l480 + i32.add + local.set $l481 + local.get $l480 + local.get $l481 + i32.add + local.set $l482 + local.get $l481 + local.get $l482 + i32.add + local.set $l483 + local.get $l482 + local.get $l483 + i32.add + local.set $l484 + local.get $l483 + local.get $l484 + i32.add + local.set $l485 + local.get $l484 + local.get $l485 + i32.add + local.set $l486 + local.get $l485 + local.get $l486 + i32.add + local.set $l487 + local.get $l486 + local.get $l487 + i32.add + local.set $l488 + local.get $l487 + local.get $l488 + i32.add + local.set $l489 + local.get $l488 + local.get $l489 + i32.add + local.set $l490 + local.get $l489 + local.get $l490 + i32.add + local.set $l491 + local.get $l490 + local.get $l491 + i32.add + local.set $l492 + local.get $l491 + local.get $l492 + i32.add + local.set $l493 + local.get $l492 + local.get $l493 + i32.add + local.set $l494 + local.get $l493 + local.get $l494 + i32.add + local.set $l495 + local.get $l494 + local.get $l495 + i32.add + local.set $l496 + local.get $l495 + local.get $l496 + i32.add + local.set $l497 + local.get $l496 + local.get $l497 + i32.add + local.set $l498 + local.get $l497 + local.get $l498 + i32.add + local.set $l499 + local.get $l498 + local.get $l499 + i32.add + local.set $l500 + local.get $l499 + local.get $l500 + i32.add + local.set $l501 + local.get $l500 + local.get $l501 + i32.add + local.set $l502 + local.get $l501 + local.get $l502 + i32.add + local.set $l503 + local.get $l502 + local.get $l503 + i32.add + local.set $l504 + local.get $l503 + local.get $l504 + i32.add + local.set $l505 + local.get $l504 + local.get $l505 + i32.add + local.set $l506 + local.get $l505 + local.get $l506 + i32.add + local.set $l507 + local.get $l506 + local.get $l507 + i32.add + local.set $l508 + local.get $l507 + local.get $l508 + i32.add + local.set $l509 + local.get $l508 + local.get $l509 + i32.add + local.set $l510 + local.get $l509 + local.get $l510 + i32.add + local.set $l511 + local.get $l510 + local.get $l511 + i32.add + local.set $l512 + local.get $l511 + local.get $l512 + i32.add + local.set $l513 + local.get $l512 + local.get $l513 + i32.add + local.set $l514 + local.get $l513 + local.get $l514 + i32.add + local.set $l515 + local.get $l514 + local.get $l515 + i32.add + local.set $l516 + local.get $l515 + local.get $l516 + i32.add + local.set $l517 + local.get $l516 + local.get $l517 + i32.add + local.set $l518 + local.get $l517 + local.get $l518 + i32.add + local.set $l519 + local.get $l518 + local.get $l519 + i32.add + local.set $l520 + local.get $l519 + local.get $l520 + i32.add + local.set $l521 + local.get $l520 + local.get $l521 + i32.add + local.set $l522 + local.get $l521 + local.get $l522 + i32.add + local.set $l523 + local.get $l522 + local.get $l523 + i32.add + local.set $l524 + local.get $l523 + local.get $l524 + i32.add + local.set $l525 + local.get $l524 + local.get $l525 + i32.add + local.set $l526 + local.get $l525 + local.get $l526 + i32.add + local.set $l527 + local.get $l526 + local.get $l527 + i32.add + local.set $l528 + local.get $l527 + local.get $l528 + i32.add + local.set $l529 + local.get $l528 + local.get $l529 + i32.add + local.set $l530 + local.get $l529 + local.get $l530 + i32.add + local.set $l531 + local.get $l530 + local.get $l531 + i32.add + local.set $l532 + local.get $l531 + local.get $l532 + i32.add + local.set $l533 + local.get $l532 + local.get $l533 + i32.add + local.set $l534 + local.get $l533 + local.get $l534 + i32.add + local.set $l535 + local.get $l534 + local.get $l535 + i32.add + local.set $l536 + local.get $l535 + local.get $l536 + i32.add + local.set $l537 + local.get $l536 + local.get $l537 + i32.add + local.set $l538 + local.get $l537 + local.get $l538 + i32.add + local.set $l539 + local.get $l538 + local.get $l539 + i32.add + local.set $l540 + local.get $l539 + local.get $l540 + i32.add + local.set $l541 + local.get $l540 + local.get $l541 + i32.add + local.set $l542 + local.get $l541 + local.get $l542 + i32.add + local.set $l543 + local.get $l542 + local.get $l543 + i32.add + local.set $l544 + local.get $l543 + local.get $l544 + i32.add + local.set $l545 + local.get $l544 + local.get $l545 + i32.add + local.set $l546 + local.get $l545 + local.get $l546 + i32.add + local.set $l547 + local.get $l546 + local.get $l547 + i32.add + local.set $l548 + local.get $l547 + local.get $l548 + i32.add + local.set $l549 + local.get $l548 + local.get $l549 + i32.add + local.set $l550 + local.get $l549 + local.get $l550 + i32.add + local.set $l551 + local.get $l550 + local.get $l551 + i32.add + local.set $l552 + local.get $l551 + local.get $l552 + i32.add + local.set $l553 + local.get $l552 + local.get $l553 + i32.add + local.set $l554 + local.get $l553 + local.get $l554 + i32.add + local.set $l555 + local.get $l554 + local.get $l555 + i32.add + local.set $l556 + local.get $l555 + local.get $l556 + i32.add + local.set $l557 + local.get $l556 + local.get $l557 + i32.add + local.set $l558 + local.get $l557 + local.get $l558 + i32.add + local.set $l559 + local.get $l558 + local.get $l559 + i32.add + local.set $l560 + local.get $l559 + local.get $l560 + i32.add + local.set $l561 + local.get $l560 + local.get $l561 + i32.add + local.set $l562 + local.get $l561 + local.get $l562 + i32.add + local.set $l563 + local.get $l562 + local.get $l563 + i32.add + local.set $l564 + local.get $l563 + local.get $l564 + i32.add + local.set $l565 + local.get $l564 + local.get $l565 + i32.add + local.set $l566 + local.get $l565 + local.get $l566 + i32.add + local.set $l567 + local.get $l566 + local.get $l567 + i32.add + local.set $l568 + local.get $l567 + local.get $l568 + i32.add + local.set $l569 + local.get $l568 + local.get $l569 + i32.add + local.set $l570 + local.get $l569 + local.get $l570 + i32.add + local.set $l571 + local.get $l570 + local.get $l571 + i32.add + local.set $l572 + local.get $l571 + local.get $l572 + i32.add + local.set $l573 + local.get $l572 + local.get $l573 + i32.add + local.set $l574 + local.get $l573 + local.get $l574 + i32.add + local.set $l575 + local.get $l574 + local.get $l575 + i32.add + local.set $l576 + local.get $l575 + local.get $l576 + i32.add + local.set $l577 + local.get $l576 + local.get $l577 + i32.add + local.set $l578 + local.get $l577 + local.get $l578 + i32.add + local.set $l579 + local.get $l578 + local.get $l579 + i32.add + local.set $l580 + local.get $l579 + local.get $l580 + i32.add + local.set $l581 + local.get $l580 + local.get $l581 + i32.add + local.set $l582 + local.get $l581 + local.get $l582 + i32.add + local.set $l583 + local.get $l582 + local.get $l583 + i32.add + local.set $l584 + local.get $l583 + local.get $l584 + i32.add + local.set $l585 + local.get $l584 + local.get $l585 + i32.add + local.set $l586 + local.get $l585 + local.get $l586 + i32.add + local.set $l587 + local.get $l586 + local.get $l587 + i32.add + local.set $l588 + local.get $l587 + local.get $l588 + i32.add + local.set $l589 + local.get $l588 + local.get $l589 + i32.add + local.set $l590 + local.get $l589 + local.get $l590 + i32.add + local.set $l591 + local.get $l590 + local.get $l591 + i32.add + local.set $l592 + local.get $l591 + local.get $l592 + i32.add + local.set $l593 + local.get $l592 + local.get $l593 + i32.add + local.set $l594 + local.get $l593 + local.get $l594 + i32.add + local.set $l595 + local.get $l594 + local.get $l595 + i32.add + local.set $l596 + local.get $l595 + local.get $l596 + i32.add + local.set $l597 + local.get $l596 + local.get $l597 + i32.add + local.set $l598 + local.get $l597 + local.get $l598 + i32.add + local.set $l599 + local.get $l598 + local.get $l599 + i32.add + local.set $l600 + local.get $l599 + local.get $l600 + i32.add + local.set $l601 + local.get $l600 + local.get $l601 + i32.add + local.set $l602 + local.get $l601 + local.get $l602 + i32.add + local.set $l603 + local.get $l602 + local.get $l603 + i32.add + local.set $l604 + local.get $l603 + local.get $l604 + i32.add + local.set $l605 + local.get $l604 + local.get $l605 + i32.add + local.set $l606 + local.get $l605 + local.get $l606 + i32.add + local.set $l607 + local.get $l606 + local.get $l607 + i32.add + local.set $l608 + local.get $l607 + local.get $l608 + i32.add + local.set $l609 + local.get $l608 + local.get $l609 + i32.add + local.set $l610 + local.get $l609 + local.get $l610 + i32.add + local.set $l611 + local.get $l610 + local.get $l611 + i32.add + local.set $l612 + local.get $l611 + local.get $l612 + i32.add + local.set $l613 + local.get $l612 + local.get $l613 + i32.add + local.set $l614 + local.get $l613 + local.get $l614 + i32.add + local.set $l615 + local.get $l614 + local.get $l615 + i32.add + local.set $l616 + local.get $l615 + local.get $l616 + i32.add + local.set $l617 + local.get $l616 + local.get $l617 + i32.add + local.set $l618 + local.get $l617 + local.get $l618 + i32.add + local.set $l619 + local.get $l618 + local.get $l619 + i32.add + local.set $l620 + local.get $l619 + local.get $l620 + i32.add + local.set $l621 + local.get $l620 + local.get $l621 + i32.add + local.set $l622 + local.get $l621 + local.get $l622 + i32.add + local.set $l623 + local.get $l622 + local.get $l623 + i32.add + local.set $l624 + local.get $l623 + local.get $l624 + i32.add + local.set $l625 + local.get $l624 + local.get $l625 + i32.add + local.set $l626 + local.get $l625 + local.get $l626 + i32.add + local.set $l627 + local.get $l626 + local.get $l627 + i32.add + local.set $l628 + local.get $l627 + local.get $l628 + i32.add + local.set $l629 + local.get $l628 + local.get $l629 + i32.add + local.set $l630 + local.get $l629 + local.get $l630 + i32.add + local.set $l631 + local.get $l630 + local.get $l631 + i32.add + local.set $l632 + local.get $l631 + local.get $l632 + i32.add + local.set $l633 + local.get $l632 + local.get $l633 + i32.add + local.set $l634 + local.get $l633 + local.get $l634 + i32.add + local.set $l635 + local.get $l634 + local.get $l635 + i32.add + local.set $l636 + local.get $l635 + local.get $l636 + i32.add + local.set $l637 + local.get $l636 + local.get $l637 + i32.add + local.set $l638 + local.get $l637 + local.get $l638 + i32.add + local.set $l639 + local.get $l638 + local.get $l639 + i32.add + local.set $l640 + local.get $l639 + local.get $l640 + i32.add + local.set $l641 + local.get $l640 + local.get $l641 + i32.add + local.set $l642 + local.get $l641 + local.get $l642 + i32.add + local.set $l643 + local.get $l642 + local.get $l643 + i32.add + local.set $l644 + local.get $l643 + local.get $l644 + i32.add + local.set $l645 + local.get $l644 + local.get $l645 + i32.add + local.set $l646 + local.get $l645 + local.get $l646 + i32.add + local.set $l647 + local.get $l646 + local.get $l647 + i32.add + local.set $l648 + local.get $l647 + local.get $l648 + i32.add + local.set $l649 + local.get $l648 + local.get $l649 + i32.add + local.set $l650 + local.get $l649 + local.get $l650 + i32.add + local.set $l651 + local.get $l650 + local.get $l651 + i32.add + local.set $l652 + local.get $l651 + local.get $l652 + i32.add + local.set $l653 + local.get $l652 + local.get $l653 + i32.add + local.set $l654 + local.get $l653 + local.get $l654 + i32.add + local.set $l655 + local.get $l654 + local.get $l655 + i32.add + local.set $l656 + local.get $l655 + local.get $l656 + i32.add + local.set $l657 + local.get $l656 + local.get $l657 + i32.add + local.set $l658 + local.get $l657 + local.get $l658 + i32.add + local.set $l659 + local.get $l658 + local.get $l659 + i32.add + local.set $l660 + local.get $l659 + local.get $l660 + i32.add + local.set $l661 + local.get $l660 + local.get $l661 + i32.add + local.set $l662 + local.get $l661 + local.get $l662 + i32.add + local.set $l663 + local.get $l662 + local.get $l663 + i32.add + local.set $l664 + local.get $l663 + local.get $l664 + i32.add + local.set $l665 + local.get $l664 + local.get $l665 + i32.add + local.set $l666 + local.get $l665 + local.get $l666 + i32.add + local.set $l667 + local.get $l666 + local.get $l667 + i32.add + local.set $l668 + local.get $l667 + local.get $l668 + i32.add + local.set $l669 + local.get $l668 + local.get $l669 + i32.add + local.set $l670 + local.get $l669 + local.get $l670 + i32.add + local.set $l671 + local.get $l670 + local.get $l671 + i32.add + local.set $l672 + local.get $l671 + local.get $l672 + i32.add + local.set $l673 + local.get $l672 + local.get $l673 + i32.add + local.set $l674 + local.get $l673 + local.get $l674 + i32.add + local.set $l675 + local.get $l674 + local.get $l675 + i32.add + local.set $l676 + local.get $l675 + local.get $l676 + i32.add + local.set $l677 + local.get $l676 + local.get $l677 + i32.add + local.set $l678 + local.get $l677 + local.get $l678 + i32.add + local.set $l679 + local.get $l678 + local.get $l679 + i32.add + local.set $l680 + local.get $l679 + local.get $l680 + i32.add + local.set $l681 + local.get $l680 + local.get $l681 + i32.add + local.set $l682 + local.get $l681 + local.get $l682 + i32.add + local.set $l683 + local.get $l682 + local.get $l683 + i32.add + local.set $l684 + local.get $l683 + local.get $l684 + i32.add + local.set $l685 + local.get $l684 + local.get $l685 + i32.add + local.set $l686 + local.get $l685 + local.get $l686 + i32.add + local.set $l687 + local.get $l686 + local.get $l687 + i32.add + local.set $l688 + local.get $l687 + local.get $l688 + i32.add + local.set $l689 + local.get $l688 + local.get $l689 + i32.add + local.set $l690 + local.get $l689 + local.get $l690 + i32.add + local.set $l691 + local.get $l690 + local.get $l691 + i32.add + local.set $l692 + local.get $l691 + local.get $l692 + i32.add + local.set $l693 + local.get $l692 + local.get $l693 + i32.add + local.set $l694 + local.get $l693 + local.get $l694 + i32.add + local.set $l695 + local.get $l694 + local.get $l695 + i32.add + local.set $l696 + local.get $l695 + local.get $l696 + i32.add + local.set $l697 + local.get $l696 + local.get $l697 + i32.add + local.set $l698 + local.get $l697 + local.get $l698 + i32.add + local.set $l699 + local.get $l698 + local.get $l699 + i32.add + local.set $l700 + local.get $l699 + local.get $l700 + i32.add + local.set $l701 + local.get $l700 + local.get $l701 + i32.add + local.set $l702 + local.get $l701 + local.get $l702 + i32.add + local.set $l703 + local.get $l702 + local.get $l703 + i32.add + local.set $l704 + local.get $l703 + local.get $l704 + i32.add + local.set $l705 + local.get $l704 + local.get $l705 + i32.add + local.set $l706 + local.get $l705 + local.get $l706 + i32.add + local.set $l707 + local.get $l706 + local.get $l707 + i32.add + local.set $l708 + local.get $l707 + local.get $l708 + i32.add + local.set $l709 + local.get $l708 + local.get $l709 + i32.add + local.set $l710 + local.get $l709 + local.get $l710 + i32.add + local.set $l711 + local.get $l710 + local.get $l711 + i32.add + local.set $l712 + local.get $l711 + local.get $l712 + i32.add + local.set $l713 + local.get $l712 + local.get $l713 + i32.add + local.set $l714 + local.get $l713 + local.get $l714 + i32.add + local.set $l715 + local.get $l714 + local.get $l715 + i32.add + local.set $l716 + local.get $l715 + local.get $l716 + i32.add + local.set $l717 + local.get $l716 + local.get $l717 + i32.add + local.set $l718 + local.get $l717 + local.get $l718 + i32.add + local.set $l719 + local.get $l718 + local.get $l719 + i32.add + local.set $l720 + local.get $l719 + local.get $l720 + i32.add + local.set $l721 + local.get $l720 + local.get $l721 + i32.add + local.set $l722 + local.get $l721 + local.get $l722 + i32.add + local.set $l723 + local.get $l722 + local.get $l723 + i32.add + local.set $l724 + local.get $l723 + local.get $l724 + i32.add + local.set $l725 + local.get $l724 + local.get $l725 + i32.add + local.set $l726 + local.get $l725 + local.get $l726 + i32.add + local.set $l727 + local.get $l726 + local.get $l727 + i32.add + local.set $l728 + local.get $l727 + local.get $l728 + i32.add + local.set $l729 + local.get $l728 + local.get $l729 + i32.add + local.set $l730 + local.get $l729 + local.get $l730 + i32.add + local.set $l731 + local.get $l730 + local.get $l731 + i32.add + local.set $l732 + local.get $l731 + local.get $l732 + i32.add + local.set $l733 + local.get $l732 + local.get $l733 + i32.add + local.set $l734 + local.get $l733 + local.get $l734 + i32.add + local.set $l735 + local.get $l734 + local.get $l735 + i32.add + local.set $l736 + local.get $l735 + local.get $l736 + i32.add + local.set $l737 + local.get $l736 + local.get $l737 + i32.add + local.set $l738 + local.get $l737 + local.get $l738 + i32.add + local.set $l739 + local.get $l738 + local.get $l739 + i32.add + local.set $l740 + local.get $l739 + local.get $l740 + i32.add + local.set $l741 + local.get $l740 + local.get $l741 + i32.add + local.set $l742 + local.get $l741 + local.get $l742 + i32.add + local.set $l743 + local.get $l742 + local.get $l743 + i32.add + local.set $l744 + local.get $l743 + local.get $l744 + i32.add + local.set $l745 + local.get $l744 + local.get $l745 + i32.add + local.set $l746 + local.get $l745 + local.get $l746 + i32.add + local.set $l747 + local.get $l746 + local.get $l747 + i32.add + local.set $l748 + local.get $l747 + local.get $l748 + i32.add + local.set $l749 + local.get $l748 + local.get $l749 + i32.add + local.set $l750 + local.get $l749 + local.get $l750 + i32.add + local.set $l751 + local.get $l750 + local.get $l751 + i32.add + local.set $l752 + local.get $l751 + local.get $l752 + i32.add + local.set $l753 + local.get $l752 + local.get $l753 + i32.add + local.set $l754 + local.get $l753 + local.get $l754 + i32.add + local.set $l755 + local.get $l754 + local.get $l755 + i32.add + local.set $l756 + local.get $l755 + local.get $l756 + i32.add + local.set $l757 + local.get $l756 + local.get $l757 + i32.add + local.set $l758 + local.get $l757 + local.get $l758 + i32.add + local.set $l759 + local.get $l758 + local.get $l759 + i32.add + local.set $l760 + local.get $l759 + local.get $l760 + i32.add + local.set $l761 + local.get $l760 + local.get $l761 + i32.add + local.set $l762 + local.get $l761 + local.get $l762 + i32.add + local.set $l763 + local.get $l762 + local.get $l763 + i32.add + local.set $l764 + local.get $l763 + local.get $l764 + i32.add + local.set $l765 + local.get $l764 + local.get $l765 + i32.add + local.set $l766 + local.get $l765 + local.get $l766 + i32.add + local.set $l767 + local.get $l766 + local.get $l767 + i32.add + local.set $l768 + local.get $l767 + local.get $l768 + i32.add + local.set $l769 + local.get $l768 + local.get $l769 + i32.add + local.set $l770 + local.get $l769 + local.get $l770 + i32.add + local.set $l771 + local.get $l770 + local.get $l771 + i32.add + local.set $l772 + local.get $l771 + local.get $l772 + i32.add + local.set $l773 + local.get $l772 + local.get $l773 + i32.add + local.set $l774 + local.get $l773 + local.get $l774 + i32.add + local.set $l775 + local.get $l774 + local.get $l775 + i32.add + local.set $l776 + local.get $l775 + local.get $l776 + i32.add + local.set $l777 + local.get $l776 + local.get $l777 + i32.add + local.set $l778 + local.get $l777 + local.get $l778 + i32.add + local.set $l779 + local.get $l778 + local.get $l779 + i32.add + local.set $l780 + local.get $l779 + local.get $l780 + i32.add + local.set $l781 + local.get $l780 + local.get $l781 + i32.add + local.set $l782 + local.get $l781 + local.get $l782 + i32.add + local.set $l783 + local.get $l782 + local.get $l783 + i32.add + local.set $l784 + local.get $l783 + local.get $l784 + i32.add + local.set $l785 + local.get $l784 + local.get $l785 + i32.add + local.set $l786 + local.get $l785 + local.get $l786 + i32.add + local.set $l787 + local.get $l786 + local.get $l787 + i32.add + local.set $l788 + local.get $l787 + local.get $l788 + i32.add + local.set $l789 + local.get $l788 + local.get $l789 + i32.add + local.set $l790 + local.get $l789 + local.get $l790 + i32.add + local.set $l791 + local.get $l790 + local.get $l791 + i32.add + local.set $l792 + local.get $l791 + local.get $l792 + i32.add + local.set $l793 + local.get $l792 + local.get $l793 + i32.add + local.set $l794 + local.get $l793 + local.get $l794 + i32.add + local.set $l795 + local.get $l794 + local.get $l795 + i32.add + local.set $l796 + local.get $l795 + local.get $l796 + i32.add + local.set $l797 + local.get $l796 + local.get $l797 + i32.add + local.set $l798 + local.get $l797 + local.get $l798 + i32.add + local.set $l799 + local.get $l798 + local.get $l799 + i32.add + local.set $l800 + local.get $l799 + local.get $l800 + i32.add + local.set $l801 + local.get $l800 + local.get $l801 + i32.add + local.set $l802 + local.get $l801 + local.get $l802 + i32.add + local.set $l803 + local.get $l802 + local.get $l803 + i32.add + local.set $l804 + local.get $l803 + local.get $l804 + i32.add + local.set $l805 + local.get $l804 + local.get $l805 + i32.add + local.set $l806 + local.get $l805 + local.get $l806 + i32.add + local.set $l807 + local.get $l806 + local.get $l807 + i32.add + local.set $l808 + local.get $l807 + local.get $l808 + i32.add + local.set $l809 + local.get $l808 + local.get $l809 + i32.add + local.set $l810 + local.get $l809 + local.get $l810 + i32.add + local.set $l811 + local.get $l810 + local.get $l811 + i32.add + local.set $l812 + local.get $l811 + local.get $l812 + i32.add + local.set $l813 + local.get $l812 + local.get $l813 + i32.add + local.set $l814 + local.get $l813 + local.get $l814 + i32.add + local.set $l815 + local.get $l814 + local.get $l815 + i32.add + local.set $l816 + local.get $l815 + local.get $l816 + i32.add + local.set $l817 + local.get $l816 + local.get $l817 + i32.add + local.set $l818 + local.get $l817 + local.get $l818 + i32.add + local.set $l819 + local.get $l818 + local.get $l819 + i32.add + local.set $l820 + local.get $l819 + local.get $l820 + i32.add + local.set $l821 + local.get $l820 + local.get $l821 + i32.add + local.set $l822 + local.get $l821 + local.get $l822 + i32.add + local.set $l823 + local.get $l822 + local.get $l823 + i32.add + local.set $l824 + local.get $l823 + local.get $l824 + i32.add + local.set $l825 + local.get $l824 + local.get $l825 + i32.add + local.set $l826 + local.get $l825 + local.get $l826 + i32.add + local.set $l827 + local.get $l826 + local.get $l827 + i32.add + local.set $l828 + local.get $l827 + local.get $l828 + i32.add + local.set $l829 + local.get $l828 + local.get $l829 + i32.add + local.set $l830 + local.get $l829 + local.get $l830 + i32.add + local.set $l831 + local.get $l830 + local.get $l831 + i32.add + local.set $l832 + local.get $l831 + local.get $l832 + i32.add + local.set $l833 + local.get $l832 + local.get $l833 + i32.add + local.set $l834 + local.get $l833 + local.get $l834 + i32.add + local.set $l835 + local.get $l834 + local.get $l835 + i32.add + local.set $l836 + local.get $l835 + local.get $l836 + i32.add + local.set $l837 + local.get $l836 + local.get $l837 + i32.add + local.set $l838 + local.get $l837 + local.get $l838 + i32.add + local.set $l839 + local.get $l838 + local.get $l839 + i32.add + local.set $l840 + local.get $l839 + local.get $l840 + i32.add + local.set $l841 + local.get $l840 + local.get $l841 + i32.add + local.set $l842 + local.get $l841 + local.get $l842 + i32.add + local.set $l843 + local.get $l842 + local.get $l843 + i32.add + local.set $l844 + local.get $l843 + local.get $l844 + i32.add + local.set $l845 + local.get $l844 + local.get $l845 + i32.add + local.set $l846 + local.get $l845 + local.get $l846 + i32.add + local.set $l847 + local.get $l846 + local.get $l847 + i32.add + local.set $l848 + local.get $l847 + local.get $l848 + i32.add + local.set $l849 + local.get $l848 + local.get $l849 + i32.add + local.set $l850 + local.get $l849 + local.get $l850 + i32.add + local.set $l851 + local.get $l850 + local.get $l851 + i32.add + local.set $l852 + local.get $l851 + local.get $l852 + i32.add + local.set $l853 + local.get $l852 + local.get $l853 + i32.add + local.set $l854 + local.get $l853 + local.get $l854 + i32.add + local.set $l855 + local.get $l854 + local.get $l855 + i32.add + local.set $l856 + local.get $l855 + local.get $l856 + i32.add + local.set $l857 + local.get $l856 + local.get $l857 + i32.add + local.set $l858 + local.get $l857 + local.get $l858 + i32.add + local.set $l859 + local.get $l858 + local.get $l859 + i32.add + local.set $l860 + local.get $l859 + local.get $l860 + i32.add + local.set $l861 + local.get $l860 + local.get $l861 + i32.add + local.set $l862 + local.get $l861 + local.get $l862 + i32.add + local.set $l863 + local.get $l862 + local.get $l863 + i32.add + local.set $l864 + local.get $l863 + local.get $l864 + i32.add + local.set $l865 + local.get $l864 + local.get $l865 + i32.add + local.set $l866 + local.get $l865 + local.get $l866 + i32.add + local.set $l867 + local.get $l866 + local.get $l867 + i32.add + local.set $l868 + local.get $l867 + local.get $l868 + i32.add + local.set $l869 + local.get $l868 + local.get $l869 + i32.add + local.set $l870 + local.get $l869 + local.get $l870 + i32.add + local.set $l871 + local.get $l870 + local.get $l871 + i32.add + local.set $l872 + local.get $l871 + local.get $l872 + i32.add + local.set $l873 + local.get $l872 + local.get $l873 + i32.add + local.set $l874 + local.get $l873 + local.get $l874 + i32.add + local.set $l875 + local.get $l874 + local.get $l875 + i32.add + local.set $l876 + local.get $l875 + local.get $l876 + i32.add + local.set $l877 + local.get $l876 + local.get $l877 + i32.add + local.set $l878 + local.get $l877 + local.get $l878 + i32.add + local.set $l879 + local.get $l878 + local.get $l879 + i32.add + local.set $l880 + local.get $l879 + local.get $l880 + i32.add + local.set $l881 + local.get $l880 + local.get $l881 + i32.add + local.set $l882 + local.get $l881 + local.get $l882 + i32.add + local.set $l883 + local.get $l882 + local.get $l883 + i32.add + local.set $l884 + local.get $l883 + local.get $l884 + i32.add + local.set $l885 + local.get $l884 + local.get $l885 + i32.add + local.set $l886 + local.get $l885 + local.get $l886 + i32.add + local.set $l887 + local.get $l886 + local.get $l887 + i32.add + local.set $l888 + local.get $l887 + local.get $l888 + i32.add + local.set $l889 + local.get $l888 + local.get $l889 + i32.add + local.set $l890 + local.get $l889 + local.get $l890 + i32.add + local.set $l891 + local.get $l890 + local.get $l891 + i32.add + local.set $l892 + local.get $l891 + local.get $l892 + i32.add + local.set $l893 + local.get $l892 + local.get $l893 + i32.add + local.set $l894 + local.get $l893 + local.get $l894 + i32.add + local.set $l895 + local.get $l894 + local.get $l895 + i32.add + local.set $l896 + local.get $l895 + local.get $l896 + i32.add + local.set $l897 + local.get $l896 + local.get $l897 + i32.add + local.set $l898 + local.get $l897 + local.get $l898 + i32.add + local.set $l899 + local.get $l898 + local.get $l899 + i32.add + local.set $l900 + local.get $l899 + local.get $l900 + i32.add + local.set $l901 + local.get $l900 + local.get $l901 + i32.add + local.set $l902 + local.get $l901 + local.get $l902 + i32.add + local.set $l903 + local.get $l902 + local.get $l903 + i32.add + local.set $l904 + local.get $l903 + local.get $l904 + i32.add + local.set $l905 + local.get $l904 + local.get $l905 + i32.add + local.set $l906 + local.get $l905 + local.get $l906 + i32.add + local.set $l907 + local.get $l906 + local.get $l907 + i32.add + local.set $l908 + local.get $l907 + local.get $l908 + i32.add + local.set $l909 + local.get $l908 + local.get $l909 + i32.add + local.set $l910 + local.get $l909 + local.get $l910 + i32.add + local.set $l911 + local.get $l910 + local.get $l911 + i32.add + local.set $l912 + local.get $l911 + local.get $l912 + i32.add + local.set $l913 + local.get $l912 + local.get $l913 + i32.add + local.set $l914 + local.get $l913 + local.get $l914 + i32.add + local.set $l915 + local.get $l914 + local.get $l915 + i32.add + local.set $l916 + local.get $l915 + local.get $l916 + i32.add + local.set $l917 + local.get $l916 + local.get $l917 + i32.add + local.set $l918 + local.get $l917 + local.get $l918 + i32.add + local.set $l919 + local.get $l918 + local.get $l919 + i32.add + local.set $l920 + local.get $l919 + local.get $l920 + i32.add + local.set $l921 + local.get $l920 + local.get $l921 + i32.add + local.set $l922 + local.get $l921 + local.get $l922 + i32.add + local.set $l923 + local.get $l922 + local.get $l923 + i32.add + local.set $l924 + local.get $l923 + local.get $l924 + i32.add + local.set $l925 + local.get $l924 + local.get $l925 + i32.add + local.set $l926 + local.get $l925 + local.get $l926 + i32.add + local.set $l927 + local.get $l926 + local.get $l927 + i32.add + local.set $l928 + local.get $l927 + local.get $l928 + i32.add + local.set $l929 + local.get $l928 + local.get $l929 + i32.add + local.set $l930 + local.get $l929 + local.get $l930 + i32.add + local.set $l931 + local.get $l930 + local.get $l931 + i32.add + local.set $l932 + local.get $l931 + local.get $l932 + i32.add + local.set $l933 + local.get $l932 + local.get $l933 + i32.add + local.set $l934 + local.get $l933 + local.get $l934 + i32.add + local.set $l935 + local.get $l934 + local.get $l935 + i32.add + local.set $l936 + local.get $l935 + local.get $l936 + i32.add + local.set $l937 + local.get $l936 + local.get $l937 + i32.add + local.set $l938 + local.get $l937 + local.get $l938 + i32.add + local.set $l939 + local.get $l938 + local.get $l939 + i32.add + local.set $l940 + local.get $l939 + local.get $l940 + i32.add + local.set $l941 + local.get $l940 + local.get $l941 + i32.add + local.set $l942 + local.get $l941 + local.get $l942 + i32.add + local.set $l943 + local.get $l942 + local.get $l943 + i32.add + local.set $l944 + local.get $l943 + local.get $l944 + i32.add + local.set $l945 + local.get $l944 + local.get $l945 + i32.add + local.set $l946 + local.get $l945 + local.get $l946 + i32.add + local.set $l947 + local.get $l946 + local.get $l947 + i32.add + local.set $l948 + local.get $l947 + local.get $l948 + i32.add + local.set $l949 + local.get $l948 + local.get $l949 + i32.add + local.set $l950 + local.get $l949 + local.get $l950 + i32.add + local.set $l951 + local.get $l950 + local.get $l951 + i32.add + local.set $l952 + local.get $l951 + local.get $l952 + i32.add + local.set $l953 + local.get $l952 + local.get $l953 + i32.add + local.set $l954 + local.get $l953 + local.get $l954 + i32.add + local.set $l955 + local.get $l954 + local.get $l955 + i32.add + local.set $l956 + local.get $l955 + local.get $l956 + i32.add + local.set $l957 + local.get $l956 + local.get $l957 + i32.add + local.set $l958 + local.get $l957 + local.get $l958 + i32.add + local.set $l959 + local.get $l958 + local.get $l959 + i32.add + local.set $l960 + local.get $l959 + local.get $l960 + i32.add + local.set $l961 + local.get $l960 + local.get $l961 + i32.add + local.set $l962 + local.get $l961 + local.get $l962 + i32.add + local.set $l963 + local.get $l962 + local.get $l963 + i32.add + local.set $l964 + local.get $l963 + local.get $l964 + i32.add + local.set $l965 + local.get $l964 + local.get $l965 + i32.add + local.set $l966 + local.get $l965 + local.get $l966 + i32.add + local.set $l967 + local.get $l966 + local.get $l967 + i32.add + local.set $l968 + local.get $l967 + local.get $l968 + i32.add + local.set $l969 + local.get $l968 + local.get $l969 + i32.add + local.set $l970 + local.get $l969 + local.get $l970 + i32.add + local.set $l971 + local.get $l970 + local.get $l971 + i32.add + local.set $l972 + local.get $l971 + local.get $l972 + i32.add + local.set $l973 + local.get $l972 + local.get $l973 + i32.add + local.set $l974 + local.get $l973 + local.get $l974 + i32.add + local.set $l975 + local.get $l974 + local.get $l975 + i32.add + local.set $l976 + local.get $l975 + local.get $l976 + i32.add + local.set $l977 + local.get $l976 + local.get $l977 + i32.add + local.set $l978 + local.get $l977 + local.get $l978 + i32.add + local.set $l979 + local.get $l978 + local.get $l979 + i32.add + local.set $l980 + local.get $l979 + local.get $l980 + i32.add + local.set $l981 + local.get $l980 + local.get $l981 + i32.add + local.set $l982 + local.get $l981 + local.get $l982 + i32.add + local.set $l983 + local.get $l982 + local.get $l983 + i32.add + local.set $l984 + local.get $l983 + local.get $l984 + i32.add + local.set $l985 + local.get $l984 + local.get $l985 + i32.add + local.set $l986 + local.get $l985 + local.get $l986 + i32.add + local.set $l987 + local.get $l986 + local.get $l987 + i32.add + local.set $l988 + local.get $l987 + local.get $l988 + i32.add + local.set $l989 + local.get $l988 + local.get $l989 + i32.add + local.set $l990 + local.get $l989 + local.get $l990 + i32.add + local.set $l991 + local.get $l990 + local.get $l991 + i32.add + local.set $l992 + local.get $l991 + local.get $l992 + i32.add + local.set $l993 + local.get $l992 + local.get $l993 + i32.add + local.set $l994 + local.get $l993 + local.get $l994 + i32.add + local.set $l995 + local.get $l994 + local.get $l995 + i32.add + local.set $l996 + local.get $l995 + local.get $l996 + i32.add + local.set $l997 + local.get $l996 + local.get $l997 + i32.add + local.set $l998 + local.get $l997 + local.get $l998 + i32.add + local.set $l999 + local.get $l998 + local.get $l999 + i32.add + local.set $l1000 + local.get $l999 + local.get $l1000 + i32.add + local.set $l1001 + local.get $l1000 + local.get $l1001 + i32.add + local.set $l1002 + local.get $l1001 + local.get $l1002 + i32.add + local.set $l1003 + local.get $l1002 + local.get $l1003 + i32.add + local.set $l1004 + local.get $l1003 + local.get $l1004 + i32.add + local.set $l1005 + local.get $l1004 + local.get $l1005 + i32.add + local.set $l1006 + local.get $l1005 + local.get $l1006 + i32.add + local.set $l1007 + local.get $l1006 + local.get $l1007 + i32.add + local.set $l1008 + local.get $l1007 + local.get $l1008 + i32.add + local.set $l1009 + local.get $l1008 + local.get $l1009 + i32.add + local.set $l1010 + local.get $l1009 + local.get $l1010 + i32.add + local.set $l1011 + local.get $l1010 + local.get $l1011 + i32.add + local.set $l1012 + local.get $l1011 + local.get $l1012 + i32.add + local.set $l1013 + local.get $l1012 + local.get $l1013 + i32.add + local.set $l1014 + local.get $l1013 + local.get $l1014 + i32.add + local.set $l1015 + local.get $l1014 + local.get $l1015 + i32.add + local.set $l1016 + local.get $l1015 + local.get $l1016 + i32.add + local.set $l1017 + local.get $l1016 + local.get $l1017 + i32.add + local.set $l1018 + local.get $l1017 + local.get $l1018 + i32.add + local.set $l1019 + local.get $l1018 + local.get $l1019 + i32.add + local.set $l1020 + local.get $l1019 + local.get $l1020 + i32.add + local.set $l1021 + local.get $l1020 + local.get $l1021 + i32.add + local.set $l1022 + local.get $l1021 + local.get $l1022 + i32.add + local.set $l1023 + local.get $l1022 + local.get $l1023 + i32.add + local.set $l1024 + local.get $l1023 + local.get $l1024 + i32.add + local.set $l1025 + local.get $l1024 + local.get $l1025 + i32.add + local.set $l1026 + local.get $l1025 + local.get $l1026 + i32.add + local.set $l1027 + local.get $l1026 + local.get $l1027 + i32.add + local.set $l1028 + local.get $l1027 + local.get $l1028 + i32.add + local.set $l1029 + local.get $l1028 + local.get $l1029 + i32.add + local.set $l1030 + local.get $l1029 + local.get $l1030 + i32.add + local.set $l1031 + local.get $l1030 + local.get $l1031 + i32.add + local.set $l1032 + local.get $l1031 + local.get $l1032 + i32.add + local.set $l1033 + local.get $l1032 + local.get $l1033 + i32.add + local.set $l1034 + local.get $l1033 + local.get $l1034 + i32.add + local.set $l1035 + local.get $l1034 + local.get $l1035 + i32.add + local.set $l1036 + local.get $l1035 + local.get $l1036 + i32.add + local.set $l1037 + local.get $l1036 + local.get $l1037 + i32.add + local.set $l1038 + local.get $l1037 + local.get $l1038 + i32.add + local.set $l1039 + local.get $l1038 + local.get $l1039 + i32.add + local.set $l1040 + local.get $l1039 + local.get $l1040 + i32.add + local.set $l1041 + local.get $l1040 + local.get $l1041 + i32.add + local.set $l1042 + local.get $l1041 + local.get $l1042 + i32.add + local.set $l1043 + local.get $l1042 + local.get $l1043 + i32.add + local.set $l1044 + local.get $l1043 + local.get $l1044 + i32.add + local.set $l1045 + local.get $l1044 + local.get $l1045 + i32.add + local.set $l1046 + local.get $l1045 + local.get $l1046 + i32.add + local.set $l1047 + local.get $l1046 + local.get $l1047 + i32.add + local.set $l1048 + local.get $l1047 + local.get $l1048 + i32.add + local.set $l1049 + local.get $l1048 + local.get $l1049 + i32.add + local.set $l1050 + local.get $l1049 + local.get $l1050 + i32.add + local.set $l1051 + local.get $l1050 + local.get $l1051 + i32.add + local.set $l1052 + local.get $l1051 + local.get $l1052 + i32.add + local.set $l1053 + local.get $l1052 + local.get $l1053 + i32.add + local.set $l1054 + local.get $l1053 + local.get $l1054 + i32.add + local.set $l1055 + local.get $l1054 + local.get $l1055 + i32.add + local.set $l1056 + local.get $l1055 + local.get $l1056 + i32.add + local.set $l1057 + local.get $l1056 + local.get $l1057 + i32.add + local.set $l1058 + local.get $l1057 + local.get $l1058 + i32.add + local.set $l1059 + local.get $l1058 + local.get $l1059 + i32.add + local.set $l1060 + local.get $l1059 + local.get $l1060 + i32.add + local.set $l1061 + local.get $l1060 + local.get $l1061 + i32.add + local.set $l1062 + local.get $l1061 + local.get $l1062 + i32.add + local.set $l1063 + local.get $l1062 + local.get $l1063 + i32.add + local.set $l1064 + local.get $l1063 + local.get $l1064 + i32.add + local.set $l1065 + local.get $l1064 + local.get $l1065 + i32.add + local.set $l1066 + local.get $l1065 + local.get $l1066 + i32.add + local.set $l1067 + local.get $l1066 + local.get $l1067 + i32.add + local.set $l1068 + local.get $l1067 + local.get $l1068 + i32.add + local.set $l1069 + local.get $l1068 + local.get $l1069 + i32.add + local.set $l1070 + local.get $l1069 + local.get $l1070 + i32.add + local.set $l1071 + local.get $l1070 + local.get $l1071 + i32.add + local.set $l1072 + local.get $l1071 + local.get $l1072 + i32.add + local.set $l1073 + local.get $l1072 + local.get $l1073 + i32.add + local.set $l1074 + local.get $l1073 + local.get $l1074 + i32.add + local.set $l1075 + local.get $l1074 + local.get $l1075 + i32.add + local.set $l1076 + local.get $l1075 + local.get $l1076 + i32.add + local.set $l1077 + local.get $l1076 + local.get $l1077 + i32.add + local.set $l1078 + local.get $l1077 + local.get $l1078 + i32.add + local.set $l1079 + local.get $l1078 + local.get $l1079 + i32.add + local.set $l1080 + local.get $l1079 + local.get $l1080 + i32.add + local.set $l1081 + local.get $l1080 + local.get $l1081 + i32.add + local.set $l1082 + local.get $l1081 + local.get $l1082 + i32.add + local.set $l1083 + local.get $l1082 + local.get $l1083 + i32.add + local.set $l1084 + local.get $l1083 + local.get $l1084 + i32.add + local.set $l1085 + local.get $l1084 + local.get $l1085 + i32.add + local.set $l1086 + local.get $l1085 + local.get $l1086 + i32.add + local.set $l1087 + local.get $l1086 + local.get $l1087 + i32.add + local.set $l1088 + local.get $l1087 + local.get $l1088 + i32.add + local.set $l1089 + local.get $l1088 + local.get $l1089 + i32.add + local.set $l1090 + local.get $l1089 + local.get $l1090 + i32.add + local.set $l1091 + local.get $l1090 + local.get $l1091 + i32.add + local.set $l1092 + local.get $l1091 + local.get $l1092 + i32.add + local.set $l1093 + local.get $l1092 + local.get $l1093 + i32.add + local.set $l1094 + local.get $l1093 + local.get $l1094 + i32.add + local.set $l1095 + local.get $l1094 + local.get $l1095 + i32.add + local.set $l1096 + local.get $l1095 + local.get $l1096 + i32.add + local.set $l1097 + local.get $l1096 + local.get $l1097 + i32.add + local.set $l1098 + local.get $l1097 + local.get $l1098 + i32.add + local.set $l1099 + local.get $l1098 + local.get $l1099 + i32.add + local.set $l1100 + local.get $l1099 + local.get $l1100 + i32.add + local.set $l1101 + local.get $l1100 + local.get $l1101 + i32.add + local.set $l1102 + local.get $l1101 + local.get $l1102 + i32.add + local.set $l1103 + local.get $l1102 + local.get $l1103 + i32.add + local.set $l1104 + local.get $l1103 + local.get $l1104 + i32.add + local.set $l1105 + local.get $l1104 + local.get $l1105 + i32.add + local.set $l1106 + local.get $l1105 + local.get $l1106 + i32.add + local.set $l1107 + local.get $l1106 + local.get $l1107 + i32.add + local.set $l1108 + local.get $l1107 + local.get $l1108 + i32.add + local.set $l1109 + local.get $l1108 + local.get $l1109 + i32.add + local.set $l1110 + local.get $l1109 + local.get $l1110 + i32.add + local.set $l1111 + local.get $l1110 + local.get $l1111 + i32.add + local.set $l1112 + local.get $l1111 + local.get $l1112 + i32.add + local.set $l1113 + local.get $l1112 + local.get $l1113 + i32.add + local.set $l1114 + local.get $l1113 + local.get $l1114 + i32.add + local.set $l1115 + local.get $l1114 + local.get $l1115 + i32.add + local.set $l1116 + local.get $l1115 + local.get $l1116 + i32.add + local.set $l1117 + local.get $l1116 + local.get $l1117 + i32.add + local.set $l1118 + local.get $l1117 + local.get $l1118 + i32.add + local.set $l1119 + local.get $l1118 + local.get $l1119 + i32.add + local.set $l1120 + local.get $l1119 + local.get $l1120 + i32.add + local.set $l1121 + local.get $l1120 + local.get $l1121 + i32.add + local.set $l1122 + local.get $l1121 + local.get $l1122 + i32.add + local.set $l1123 + local.get $l1122 + local.get $l1123 + i32.add + local.set $l1124 + local.get $l1123 + local.get $l1124 + i32.add + local.set $l1125 + local.get $l1124 + local.get $l1125 + i32.add + local.set $l1126 + local.get $l1125 + local.get $l1126 + i32.add + local.set $l1127 + local.get $l1126 + local.get $l1127 + i32.add + local.set $l1128 + local.get $l1127 + local.get $l1128 + i32.add + local.set $l1129 + local.get $l1128 + local.get $l1129 + i32.add + local.set $l1130 + local.get $l1129 + local.get $l1130 + i32.add + local.set $l1131 + local.get $l1130 + local.get $l1131 + i32.add + local.set $l1132 + local.get $l1131 + local.get $l1132 + i32.add + local.set $l1133 + local.get $l1132 + local.get $l1133 + i32.add + local.set $l1134 + local.get $l1133 + local.get $l1134 + i32.add + local.set $l1135 + local.get $l1134 + local.get $l1135 + i32.add + local.set $l1136 + local.get $l1135 + local.get $l1136 + i32.add + local.set $l1137 + local.get $l1136 + local.get $l1137 + i32.add + local.set $l1138 + local.get $l1137 + local.get $l1138 + i32.add + local.set $l1139 + local.get $l1138 + local.get $l1139 + i32.add + local.set $l1140 + local.get $l1139 + local.get $l1140 + i32.add + local.set $l1141 + local.get $l1140 + local.get $l1141 + i32.add + local.set $l1142 + local.get $l1141 + local.get $l1142 + i32.add + local.set $l1143 + local.get $l1142 + local.get $l1143 + i32.add + local.set $l1144 + local.get $l1143 + local.get $l1144 + i32.add + local.set $l1145 + local.get $l1144 + local.get $l1145 + i32.add + local.set $l1146 + local.get $l1145 + local.get $l1146 + i32.add + local.set $l1147 + local.get $l1146 + local.get $l1147 + i32.add + local.set $l1148 + local.get $l1147 + local.get $l1148 + i32.add + local.set $l1149 + local.get $l1148 + local.get $l1149 + i32.add + local.set $l1150 + local.get $l1149 + local.get $l1150 + i32.add + local.set $l1151 + local.get $l1150 + local.get $l1151 + i32.add + local.set $l1152 + local.get $l1151 + local.get $l1152 + i32.add + local.set $l1153 + local.get $l1152 + local.get $l1153 + i32.add + local.set $l1154 + local.get $l1153 + local.get $l1154 + i32.add + local.set $l1155 + local.get $l1154 + local.get $l1155 + i32.add + local.set $l1156 + local.get $l1155 + local.get $l1156 + i32.add + local.set $l1157 + local.get $l1156 + local.get $l1157 + i32.add + local.set $l1158 + local.get $l1157 + local.get $l1158 + i32.add + local.set $l1159 + local.get $l1158 + local.get $l1159 + i32.add + local.set $l1160 + local.get $l1159 + local.get $l1160 + i32.add + local.set $l1161 + local.get $l1160 + local.get $l1161 + i32.add + local.set $l1162 + local.get $l1161 + local.get $l1162 + i32.add + local.set $l1163 + local.get $l1162 + local.get $l1163 + i32.add + local.set $l1164 + local.get $l1163 + local.get $l1164 + i32.add + local.set $l1165 + local.get $l1164 + local.get $l1165 + i32.add + local.set $l1166 + local.get $l1165 + local.get $l1166 + i32.add + local.set $l1167 + local.get $l1166 + local.get $l1167 + i32.add + local.set $l1168 + local.get $l1167 + local.get $l1168 + i32.add + local.set $l1169 + local.get $l1168 + local.get $l1169 + i32.add + local.set $l1170 + local.get $l1169 + local.get $l1170 + i32.add + local.set $l1171 + local.get $l1170 + local.get $l1171 + i32.add + local.set $l1172 + local.get $l1171 + local.get $l1172 + i32.add + local.set $l1173 + local.get $l1172 + local.get $l1173 + i32.add + local.set $l1174 + local.get $l1173 + local.get $l1174 + i32.add + local.set $l1175 + local.get $l1174 + local.get $l1175 + i32.add + local.set $l1176 + local.get $l1175 + local.get $l1176 + i32.add + local.set $l1177 + local.get $l1176 + local.get $l1177 + i32.add + local.set $l1178 + local.get $l1177 + local.get $l1178 + i32.add + local.set $l1179 + local.get $l1178 + local.get $l1179 + i32.add + local.set $l1180 + local.get $l1179 + local.get $l1180 + i32.add + local.set $l1181 + local.get $l1180 + local.get $l1181 + i32.add + local.set $l1182 + local.get $l1181 + local.get $l1182 + i32.add + local.set $l1183 + local.get $l1182 + local.get $l1183 + i32.add + local.set $l1184 + local.get $l1183 + local.get $l1184 + i32.add + local.set $l1185 + local.get $l1184 + local.get $l1185 + i32.add + local.set $l1186 + local.get $l1185 + local.get $l1186 + i32.add + local.set $l1187 + local.get $l1186 + local.get $l1187 + i32.add + local.set $l1188 + local.get $l1187 + local.get $l1188 + i32.add + local.set $l1189 + local.get $l1188 + local.get $l1189 + i32.add + local.set $l1190 + local.get $l1189 + local.get $l1190 + i32.add + local.set $l1191 + local.get $l1190 + local.get $l1191 + i32.add + local.set $l1192 + local.get $l1191 + local.get $l1192 + i32.add + local.set $l1193 + local.get $l1192 + local.get $l1193 + i32.add + local.set $l1194 + local.get $l1193 + local.get $l1194 + i32.add + local.set $l1195 + local.get $l1194 + local.get $l1195 + i32.add + local.set $l1196 + local.get $l1195 + local.get $l1196 + i32.add + local.set $l1197 + local.get $l1196 + local.get $l1197 + i32.add + local.set $l1198 + local.get $l1197 + local.get $l1198 + i32.add + local.set $l1199 + local.get $l1198 + local.get $l1199 + i32.add + local.set $l1200 + local.get $l1199 + local.get $l1200 + i32.add + local.set $l1201 + local.get $l1200 + local.get $l1201 + i32.add + local.set $l1202 + local.get $l1201 + local.get $l1202 + i32.add + local.set $l1203 + local.get $l1202 + local.get $l1203 + i32.add + local.set $l1204 + local.get $l1203 + local.get $l1204 + i32.add + local.set $l1205 + local.get $l1204 + local.get $l1205 + i32.add + local.set $l1206 + local.get $l1205 + local.get $l1206 + i32.add + local.set $l1207 + local.get $l1206 + local.get $l1207 + i32.add + local.set $l1208 + local.get $l1207 + local.get $l1208 + i32.add + local.set $l1209 + local.get $l1208 + local.get $l1209 + i32.add + local.set $l1210 + local.get $l1209 + local.get $l1210 + i32.add + local.set $l1211 + local.get $l1210 + local.get $l1211 + i32.add + local.set $l1212 + local.get $l1211 + local.get $l1212 + i32.add + local.set $l1213 + local.get $l1212 + local.get $l1213 + i32.add + local.set $l1214 + local.get $l1213 + local.get $l1214 + i32.add + local.set $l1215 + local.get $l1214 + local.get $l1215 + i32.add + local.set $l1216 + local.get $l1215 + local.get $l1216 + i32.add + local.set $l1217 + local.get $l1216 + local.get $l1217 + i32.add + local.set $l1218 + local.get $l1217 + local.get $l1218 + i32.add + local.set $l1219 + local.get $l1218 + local.get $l1219 + i32.add + local.set $l1220 + local.get $l1219 + local.get $l1220 + i32.add + local.set $l1221 + local.get $l1220 + local.get $l1221 + i32.add + local.set $l1222 + local.get $l1221 + local.get $l1222 + i32.add + local.set $l1223 + local.get $l1222 + local.get $l1223 + i32.add + local.set $l1224 + local.get $l1223 + local.get $l1224 + i32.add + local.set $l1225 + local.get $l1224 + local.get $l1225 + i32.add + local.set $l1226 + local.get $l1225 + local.get $l1226 + i32.add + local.set $l1227 + local.get $l1226 + local.get $l1227 + i32.add + local.set $l1228 + local.get $l1227 + local.get $l1228 + i32.add + local.set $l1229 + local.get $l1228 + local.get $l1229 + i32.add + local.set $l1230 + local.get $l1229 + local.get $l1230 + i32.add + local.set $l1231 + local.get $l1230 + local.get $l1231 + i32.add + local.set $l1232 + local.get $l1231 + local.get $l1232 + i32.add + local.set $l1233 + local.get $l1232 + local.get $l1233 + i32.add + local.set $l1234 + local.get $l1233 + local.get $l1234 + i32.add + local.set $l1235 + local.get $l1234 + local.get $l1235 + i32.add + local.set $l1236 + local.get $l1235 + local.get $l1236 + i32.add + local.set $l1237 + local.get $l1236 + local.get $l1237 + i32.add + local.set $l1238 + local.get $l1237 + local.get $l1238 + i32.add + local.set $l1239 + local.get $l1238 + local.get $l1239 + i32.add + local.set $l1240 + local.get $l1239 + local.get $l1240 + i32.add + local.set $l1241 + local.get $l1240 + local.get $l1241 + i32.add + local.set $l1242 + local.get $l1241 + local.get $l1242 + i32.add + local.set $l1243 + local.get $l1242 + local.get $l1243 + i32.add + local.set $l1244 + local.get $l1243 + local.get $l1244 + i32.add + local.set $l1245 + local.get $l1244 + local.get $l1245 + i32.add + local.set $l1246 + local.get $l1245 + local.get $l1246 + i32.add + local.set $l1247 + local.get $l1246 + local.get $l1247 + i32.add + local.set $l1248 + local.get $l1247 + local.get $l1248 + i32.add + local.set $l1249 + local.get $l1248 + local.get $l1249 + i32.add + local.set $l1250 + local.get $l1249 + local.get $l1250 + i32.add + local.set $l1251 + local.get $l1250 + local.get $l1251 + i32.add + local.set $l1252 + local.get $l1251 + local.get $l1252 + i32.add + local.set $l1253 + local.get $l1252 + local.get $l1253 + i32.add + local.set $l1254 + local.get $l1253 + local.get $l1254 + i32.add + local.set $l1255 + local.get $l1254 + local.get $l1255 + i32.add + local.set $l1256 + local.get $l1255 + local.get $l1256 + i32.add + local.set $l1257 + local.get $l1256 + local.get $l1257 + i32.add + local.set $l1258 + local.get $l1257 + local.get $l1258 + i32.add + local.set $l1259 + local.get $l1258 + local.get $l1259 + i32.add + local.set $l1260 + local.get $l1259 + local.get $l1260 + i32.add + local.set $l1261 + local.get $l1260 + local.get $l1261 + i32.add + local.set $l1262 + local.get $l1261 + local.get $l1262 + i32.add + local.set $l1263 + local.get $l1262 + local.get $l1263 + i32.add + local.set $l1264 + local.get $l1263 + local.get $l1264 + i32.add + local.set $l1265 + local.get $l1264 + local.get $l1265 + i32.add + local.set $l1266 + local.get $l1265 + local.get $l1266 + i32.add + local.set $l1267 + local.get $l1266 + local.get $l1267 + i32.add + local.set $l1268 + local.get $l1267 + local.get $l1268 + i32.add + local.set $l1269 + local.get $l1268 + local.get $l1269 + i32.add + local.set $l1270 + local.get $l1269 + local.get $l1270 + i32.add + local.set $l1271 + local.get $l1270 + local.get $l1271 + i32.add + local.set $l1272 + local.get $l1271 + local.get $l1272 + i32.add + local.set $l1273 + local.get $l1272 + local.get $l1273 + i32.add + local.set $l1274 + local.get $l1273 + local.get $l1274 + i32.add + local.set $l1275 + local.get $l1274 + local.get $l1275 + i32.add + local.set $l1276 + local.get $l1275 + local.get $l1276 + i32.add + local.set $l1277 + local.get $l1276 + local.get $l1277 + i32.add + local.set $l1278 + local.get $l1277 + local.get $l1278 + i32.add + local.set $l1279 + local.get $l1278 + local.get $l1279 + i32.add + local.set $l1280 + local.get $l1279 + local.get $l1280 + i32.add + local.set $l1281 + local.get $l1280 + local.get $l1281 + i32.add + local.set $l1282 + local.get $l1281 + local.get $l1282 + i32.add + local.set $l1283 + local.get $l1282 + local.get $l1283 + i32.add + local.set $l1284 + local.get $l1283 + local.get $l1284 + i32.add + local.set $l1285 + local.get $l1284 + local.get $l1285 + i32.add + local.set $l1286 + local.get $l1285 + local.get $l1286 + i32.add + local.set $l1287 + local.get $l1286 + local.get $l1287 + i32.add + local.set $l1288 + local.get $l1287 + local.get $l1288 + i32.add + local.set $l1289 + local.get $l1288 + local.get $l1289 + i32.add + local.set $l1290 + local.get $l1289 + local.get $l1290 + i32.add + local.set $l1291 + local.get $l1290 + local.get $l1291 + i32.add + local.set $l1292 + local.get $l1291 + local.get $l1292 + i32.add + local.set $l1293 + local.get $l1292 + local.get $l1293 + i32.add + local.set $l1294 + local.get $l1293 + local.get $l1294 + i32.add + local.set $l1295 + local.get $l1294 + local.get $l1295 + i32.add + local.set $l1296 + local.get $l1295 + local.get $l1296 + i32.add + local.set $l1297 + local.get $l1296 + local.get $l1297 + i32.add + local.set $l1298 + local.get $l1297 + local.get $l1298 + i32.add + local.set $l1299 + local.get $l1298 + local.get $l1299 + i32.add + local.set $l1300 + local.get $l1299 + local.get $l1300 + i32.add + local.set $l1301 + local.get $l1300 + local.get $l1301 + i32.add + local.set $l1302 + local.get $l1301 + local.get $l1302 + i32.add + local.set $l1303 + local.get $l1302 + local.get $l1303 + i32.add + local.set $l1304 + local.get $l1303 + local.get $l1304 + i32.add + local.set $l1305 + local.get $l1304 + local.get $l1305 + i32.add + local.set $l1306 + local.get $l1305 + local.get $l1306 + i32.add + local.set $l1307 + local.get $l1306 + local.get $l1307 + i32.add + local.set $l1308 + local.get $l1307 + local.get $l1308 + i32.add + local.set $l1309 + local.get $l1308 + local.get $l1309 + i32.add + local.set $l1310 + local.get $l1309 + local.get $l1310 + i32.add + local.set $l1311 + local.get $l1310 + local.get $l1311 + i32.add + local.set $l1312 + local.get $l1311 + local.get $l1312 + i32.add + local.set $l1313 + local.get $l1312 + local.get $l1313 + i32.add + local.set $l1314 + local.get $l1313 + local.get $l1314 + i32.add + local.set $l1315 + local.get $l1314 + local.get $l1315 + i32.add + local.set $l1316 + local.get $l1315 + local.get $l1316 + i32.add + local.set $l1317 + local.get $l1316 + local.get $l1317 + i32.add + local.set $l1318 + local.get $l1317 + local.get $l1318 + i32.add + local.set $l1319 + local.get $l1318 + local.get $l1319 + i32.add + local.set $l1320 + local.get $l1319 + local.get $l1320 + i32.add + local.set $l1321 + local.get $l1320 + local.get $l1321 + i32.add + local.set $l1322 + local.get $l1321 + local.get $l1322 + i32.add + local.set $l1323 + local.get $l1322 + local.get $l1323 + i32.add + local.set $l1324 + local.get $l1323 + local.get $l1324 + i32.add + local.set $l1325 + local.get $l1324 + local.get $l1325 + i32.add + local.set $l1326 + local.get $l1325 + local.get $l1326 + i32.add + local.set $l1327 + local.get $l1326 + local.get $l1327 + i32.add + local.set $l1328 + local.get $l1327 + local.get $l1328 + i32.add + local.set $l1329 + local.get $l1328 + local.get $l1329 + i32.add + local.set $l1330 + local.get $l1329 + local.get $l1330 + i32.add + local.set $l1331 + local.get $l1330 + local.get $l1331 + i32.add + local.set $l1332 + local.get $l1331 + local.get $l1332 + i32.add + local.set $l1333 + local.get $l1332 + local.get $l1333 + i32.add + local.set $l1334 + local.get $l1333 + local.get $l1334 + i32.add + local.set $l1335 + local.get $l1334 + local.get $l1335 + i32.add + local.set $l1336 + local.get $l1335 + local.get $l1336 + i32.add + local.set $l1337 + local.get $l1336 + local.get $l1337 + i32.add + local.set $l1338 + local.get $l1337 + local.get $l1338 + i32.add + local.set $l1339 + local.get $l1338 + local.get $l1339 + i32.add + local.set $l1340 + local.get $l1339 + local.get $l1340 + i32.add + local.set $l1341 + local.get $l1340 + local.get $l1341 + i32.add + local.set $l1342 + local.get $l1341 + local.get $l1342 + i32.add + local.set $l1343 + local.get $l1342 + local.get $l1343 + i32.add + local.set $l1344 + local.get $l1343 + local.get $l1344 + i32.add + local.set $l1345 + local.get $l1344 + local.get $l1345 + i32.add + local.set $l1346 + local.get $l1345 + local.get $l1346 + i32.add + local.set $l1347 + local.get $l1346 + local.get $l1347 + i32.add + local.set $l1348 + local.get $l1347 + local.get $l1348 + i32.add + local.set $l1349 + local.get $l1348 + local.get $l1349 + i32.add + local.set $l1350 + local.get $l1349 + local.get $l1350 + i32.add + local.set $l1351 + local.get $l1350 + local.get $l1351 + i32.add + local.set $l1352 + local.get $l1351 + local.get $l1352 + i32.add + local.set $l1353 + local.get $l1352 + local.get $l1353 + i32.add + local.set $l1354 + local.get $l1353 + local.get $l1354 + i32.add + local.set $l1355 + local.get $l1354 + local.get $l1355 + i32.add + local.set $l1356 + local.get $l1355 + local.get $l1356 + i32.add + local.set $l1357 + local.get $l1356 + local.get $l1357 + i32.add + local.set $l1358 + local.get $l1357 + local.get $l1358 + i32.add + local.set $l1359 + local.get $l1358 + local.get $l1359 + i32.add + local.set $l1360 + local.get $l1359 + local.get $l1360 + i32.add + local.set $l1361 + local.get $l1360 + local.get $l1361 + i32.add + local.set $l1362 + local.get $l1361 + local.get $l1362 + i32.add + local.set $l1363 + local.get $l1362 + local.get $l1363 + i32.add + local.set $l1364 + local.get $l1363 + local.get $l1364 + i32.add + local.set $l1365 + local.get $l1364 + local.get $l1365 + i32.add + local.set $l1366 + local.get $l1365 + local.get $l1366 + i32.add + local.set $l1367 + local.get $l1366 + local.get $l1367 + i32.add + local.set $l1368 + local.get $l1367 + local.get $l1368 + i32.add + local.set $l1369 + local.get $l1368 + local.get $l1369 + i32.add + local.set $l1370 + local.get $l1369 + local.get $l1370 + i32.add + local.set $l1371 + local.get $l1370 + local.get $l1371 + i32.add + local.set $l1372 + local.get $l1371 + local.get $l1372 + i32.add + local.set $l1373 + local.get $l1372 + local.get $l1373 + i32.add + local.set $l1374 + local.get $l1373 + local.get $l1374 + i32.add + local.set $l1375 + local.get $l1374 + local.get $l1375 + i32.add + local.set $l1376 + local.get $l1375 + local.get $l1376 + i32.add + local.set $l1377 + local.get $l1376 + local.get $l1377 + i32.add + local.set $l1378 + local.get $l1377 + local.get $l1378 + i32.add + local.set $l1379 + local.get $l1378 + local.get $l1379 + i32.add + local.set $l1380 + local.get $l1379 + local.get $l1380 + i32.add + local.set $l1381 + local.get $l1380 + local.get $l1381 + i32.add + local.set $l1382 + local.get $l1381 + local.get $l1382 + i32.add + local.set $l1383 + local.get $l1382 + local.get $l1383 + i32.add + local.set $l1384 + local.get $l1383 + local.get $l1384 + i32.add + local.set $l1385 + local.get $l1384 + local.get $l1385 + i32.add + local.set $l1386 + local.get $l1385 + local.get $l1386 + i32.add + local.set $l1387 + local.get $l1386 + local.get $l1387 + i32.add + local.set $l1388 + local.get $l1387 + local.get $l1388 + i32.add + local.set $l1389 + local.get $l1388 + local.get $l1389 + i32.add + local.set $l1390 + local.get $l1389 + local.get $l1390 + i32.add + local.set $l1391 + local.get $l1390 + local.get $l1391 + i32.add + local.set $l1392 + local.get $l1391 + local.get $l1392 + i32.add + local.set $l1393 + local.get $l1392 + local.get $l1393 + i32.add + local.set $l1394 + local.get $l1393 + local.get $l1394 + i32.add + local.set $l1395 + local.get $l1394 + local.get $l1395 + i32.add + local.set $l1396 + local.get $l1395 + local.get $l1396 + i32.add + local.set $l1397 + local.get $l1396 + local.get $l1397 + i32.add + local.set $l1398 + local.get $l1397 + local.get $l1398 + i32.add + local.set $l1399 + local.get $l1398 + local.get $l1399 + i32.add + local.set $l1400 + local.get $l1399 + local.get $l1400 + i32.add + local.set $l1401 + local.get $l1400 + local.get $l1401 + i32.add + local.set $l1402 + local.get $l1401 + local.get $l1402 + i32.add + local.set $l1403 + local.get $l1402 + local.get $l1403 + i32.add + local.set $l1404 + local.get $l1403 + local.get $l1404 + i32.add + local.set $l1405 + local.get $l1404 + local.get $l1405 + i32.add + local.set $l1406 + local.get $l1405 + local.get $l1406 + i32.add + local.set $l1407 + local.get $l1406 + local.get $l1407 + i32.add + local.set $l1408 + local.get $l1407 + local.get $l1408 + i32.add + local.set $l1409 + local.get $l1408 + local.get $l1409 + i32.add + local.set $l1410 + local.get $l1409 + local.get $l1410 + i32.add + local.set $l1411 + local.get $l1410 + local.get $l1411 + i32.add + local.set $l1412 + local.get $l1411 + local.get $l1412 + i32.add + local.set $l1413 + local.get $l1412 + local.get $l1413 + i32.add + local.set $l1414 + local.get $l1413 + local.get $l1414 + i32.add + local.set $l1415 + local.get $l1414 + local.get $l1415 + i32.add + local.set $l1416 + local.get $l1415 + local.get $l1416 + i32.add + local.set $l1417 + local.get $l1416 + local.get $l1417 + i32.add + local.set $l1418 + local.get $l1417 + local.get $l1418 + i32.add + local.set $l1419 + local.get $l1418 + local.get $l1419 + i32.add + local.set $l1420 + local.get $l1419 + local.get $l1420 + i32.add + local.set $l1421 + local.get $l1420 + local.get $l1421 + i32.add + local.set $l1422 + local.get $l1421 + local.get $l1422 + i32.add + local.set $l1423 + local.get $l1422 + local.get $l1423 + i32.add + local.set $l1424 + local.get $l1423 + local.get $l1424 + i32.add + local.set $l1425 + local.get $l1424 + local.get $l1425 + i32.add + local.set $l1426 + local.get $l1425 + local.get $l1426 + i32.add + local.set $l1427 + local.get $l1426 + local.get $l1427 + i32.add + local.set $l1428 + local.get $l1427 + local.get $l1428 + i32.add + local.set $l1429 + local.get $l1428 + local.get $l1429 + i32.add + local.set $l1430 + local.get $l1429 + local.get $l1430 + i32.add + local.set $l1431 + local.get $l1430 + local.get $l1431 + i32.add + local.set $l1432 + local.get $l1431 + local.get $l1432 + i32.add + local.set $l1433 + local.get $l1432 + local.get $l1433 + i32.add + local.set $l1434 + local.get $l1433 + local.get $l1434 + i32.add + local.set $l1435 + local.get $l1434 + local.get $l1435 + i32.add + local.set $l1436 + local.get $l1435 + local.get $l1436 + i32.add + local.set $l1437 + local.get $l1436 + local.get $l1437 + i32.add + local.set $l1438 + local.get $l1437 + local.get $l1438 + i32.add + local.set $l1439 + local.get $l1438 + local.get $l1439 + i32.add + local.set $l1440 + local.get $l1439 + local.get $l1440 + i32.add + local.set $l1441 + local.get $l1440 + local.get $l1441 + i32.add + local.set $l1442 + local.get $l1441 + local.get $l1442 + i32.add + local.set $l1443 + local.get $l1442 + local.get $l1443 + i32.add + local.set $l1444 + local.get $l1443 + local.get $l1444 + i32.add + local.set $l1445 + local.get $l1444 + local.get $l1445 + i32.add + local.set $l1446 + local.get $l1445 + local.get $l1446 + i32.add + local.set $l1447 + local.get $l1446 + local.get $l1447 + i32.add + local.set $l1448 + local.get $l1447 + local.get $l1448 + i32.add + local.set $l1449 + local.get $l1448 + local.get $l1449 + i32.add + local.set $l1450 + local.get $l1449 + local.get $l1450 + i32.add + local.set $l1451 + local.get $l1450 + local.get $l1451 + i32.add + local.set $l1452 + local.get $l1451 + local.get $l1452 + i32.add + local.set $l1453 + local.get $l1452 + local.get $l1453 + i32.add + local.set $l1454 + local.get $l1453 + local.get $l1454 + i32.add + local.set $l1455 + local.get $l1454 + local.get $l1455 + i32.add + local.set $l1456 + local.get $l1455 + local.get $l1456 + i32.add + local.set $l1457 + local.get $l1456 + local.get $l1457 + i32.add + local.set $l1458 + local.get $l1457 + local.get $l1458 + i32.add + local.set $l1459 + local.get $l1458 + local.get $l1459 + i32.add + local.set $l1460 + local.get $l1459 + local.get $l1460 + i32.add + local.set $l1461 + local.get $l1460 + local.get $l1461 + i32.add + local.set $l1462 + local.get $l1461 + local.get $l1462 + i32.add + local.set $l1463 + local.get $l1462 + local.get $l1463 + i32.add + local.set $l1464 + local.get $l1463 + local.get $l1464 + i32.add + local.set $l1465 + local.get $l1464 + local.get $l1465 + i32.add + local.set $l1466 + local.get $l1465 + local.get $l1466 + i32.add + local.set $l1467 + local.get $l1466 + local.get $l1467 + i32.add + local.set $l1468 + local.get $l1467 + local.get $l1468 + i32.add + local.set $l1469 + local.get $l1468 + local.get $l1469 + i32.add + local.set $l1470 + local.get $l1469 + local.get $l1470 + i32.add + local.set $l1471 + local.get $l1470 + local.get $l1471 + i32.add + local.set $l1472 + local.get $l1471 + local.get $l1472 + i32.add + local.set $l1473 + local.get $l1472 + local.get $l1473 + i32.add + local.set $l1474 + local.get $l1473 + local.get $l1474 + i32.add + local.set $l1475 + local.get $l1474 + local.get $l1475 + i32.add + local.set $l1476 + local.get $l1475 + local.get $l1476 + i32.add + local.set $l1477 + local.get $l1476 + local.get $l1477 + i32.add + local.set $l1478 + local.get $l1477 + local.get $l1478 + i32.add + local.set $l1479 + local.get $l1478 + local.get $l1479 + i32.add + local.set $l1480 + local.get $l1479 + local.get $l1480 + i32.add + local.set $l1481 + local.get $l1480 + local.get $l1481 + i32.add + local.set $l1482 + local.get $l1481 + local.get $l1482 + i32.add + local.set $l1483 + local.get $l1482 + local.get $l1483 + i32.add + local.set $l1484 + local.get $l1483 + local.get $l1484 + i32.add + local.set $l1485 + local.get $l1484 + local.get $l1485 + i32.add + local.set $l1486 + local.get $l1485 + local.get $l1486 + i32.add + local.set $l1487 + local.get $l1486 + local.get $l1487 + i32.add + local.set $l1488 + local.get $l1487 + local.get $l1488 + i32.add + local.set $l1489 + local.get $l1488 + local.get $l1489 + i32.add + local.set $l1490 + local.get $l1489 + local.get $l1490 + i32.add + local.set $l1491 + local.get $l1490 + local.get $l1491 + i32.add + local.set $l1492 + local.get $l1491 + local.get $l1492 + i32.add + local.set $l1493 + local.get $l1492 + local.get $l1493 + i32.add + local.set $l1494 + local.get $l1493 + local.get $l1494 + i32.add + local.set $l1495 + local.get $l1494 + local.get $l1495 + i32.add + local.set $l1496 + local.get $l1495 + local.get $l1496 + i32.add + local.set $l1497 + local.get $l1496 + local.get $l1497 + i32.add + local.set $l1498 + local.get $l1497 + local.get $l1498 + i32.add + local.set $l1499 + local.get $l1498 + local.get $l1499 + i32.add + local.set $l1500 + local.get $l1499 + local.get $l1500 + i32.add + local.set $l1501 + local.get $l1500 + local.get $l1501 + i32.add + local.set $l1502 + local.get $l1501 + local.get $l1502 + i32.add + local.set $l1503 + local.get $l1502 + local.get $l1503 + i32.add + local.set $l1504 + local.get $l1503 + local.get $l1504 + i32.add + local.set $l1505 + local.get $l1504 + local.get $l1505 + i32.add + local.set $l1506 + local.get $l1505 + local.get $l1506 + i32.add + local.set $l1507 + local.get $l1506 + local.get $l1507 + i32.add + local.set $l1508 + local.get $l1507 + local.get $l1508 + i32.add + local.set $l1509 + local.get $l1508 + local.get $l1509 + i32.add + local.set $l1510 + local.get $l1509 + local.get $l1510 + i32.add + local.set $l1511 + local.get $l1510 + local.get $l1511 + i32.add + local.set $l1512 + local.get $l1511 + local.get $l1512 + i32.add + local.set $l1513 + local.get $l1512 + local.get $l1513 + i32.add + local.set $l1514 + local.get $l1513 + local.get $l1514 + i32.add + local.set $l1515 + local.get $l1514 + local.get $l1515 + i32.add + local.set $l1516 + local.get $l1515 + local.get $l1516 + i32.add + local.set $l1517 + local.get $l1516 + local.get $l1517 + i32.add + local.set $l1518 + local.get $l1517 + local.get $l1518 + i32.add + local.set $l1519 + local.get $l1518 + local.get $l1519 + i32.add + local.set $l1520 + local.get $l1519 + local.get $l1520 + i32.add + local.set $l1521 + local.get $l1520 + local.get $l1521 + i32.add + local.set $l1522 + local.get $l1521 + local.get $l1522 + i32.add + local.set $l1523 + local.get $l1522 + local.get $l1523 + i32.add + local.set $l1524 + local.get $l1523 + local.get $l1524 + i32.add + local.set $l1525 + local.get $l1524 + local.get $l1525 + i32.add + local.set $l1526 + local.get $l1525 + local.get $l1526 + i32.add + local.set $l1527 + local.get $l1526 + local.get $l1527 + i32.add + local.set $l1528 + local.get $l1527 + local.get $l1528 + i32.add + local.set $l1529 + local.get $l1528 + local.get $l1529 + i32.add + local.set $l1530 + local.get $l1529 + local.get $l1530 + i32.add + local.set $l1531 + local.get $l1530 + local.get $l1531 + i32.add + local.set $l1532 + local.get $l1531 + local.get $l1532 + i32.add + local.set $l1533 + local.get $l1532 + local.get $l1533 + i32.add + local.set $l1534 + local.get $l1533 + local.get $l1534 + i32.add + local.set $l1535 + local.get $l1534 + local.get $l1535 + i32.add + local.set $l1536 + local.get $l1535 + local.get $l1536 + i32.add + local.set $l1537 + local.get $l1536 + local.get $l1537 + i32.add + local.set $l1538 + local.get $l1537 + local.get $l1538 + i32.add + local.set $l1539 + local.get $l1538 + local.get $l1539 + i32.add + local.set $l1540 + local.get $l1539 + local.get $l1540 + i32.add + local.set $l1541 + local.get $l1540 + local.get $l1541 + i32.add + local.set $l1542 + local.get $l1541 + local.get $l1542 + i32.add + local.set $l1543 + local.get $l1542 + local.get $l1543 + i32.add + local.set $l1544 + local.get $l1543 + local.get $l1544 + i32.add + local.set $l1545 + local.get $l1544 + local.get $l1545 + i32.add + local.set $l1546 + local.get $l1545 + local.get $l1546 + i32.add + local.set $l1547 + local.get $l1546 + local.get $l1547 + i32.add + local.set $l1548 + local.get $l1547 + local.get $l1548 + i32.add + local.set $l1549 + local.get $l1548 + local.get $l1549 + i32.add + local.set $l1550 + local.get $l1549 + local.get $l1550 + i32.add + local.set $l1551 + local.get $l1550 + local.get $l1551 + i32.add + local.set $l1552 + local.get $l1551 + local.get $l1552 + i32.add + local.set $l1553 + local.get $l1552 + local.get $l1553 + i32.add + local.set $l1554 + local.get $l1553 + local.get $l1554 + i32.add + local.set $l1555 + local.get $l1554 + local.get $l1555 + i32.add + local.set $l1556 + local.get $l1555 + local.get $l1556 + i32.add + local.set $l1557 + local.get $l1556 + local.get $l1557 + i32.add + local.set $l1558 + local.get $l1557 + local.get $l1558 + i32.add + local.set $l1559 + local.get $l1558 + local.get $l1559 + i32.add + local.set $l1560 + local.get $l1559 + local.get $l1560 + i32.add + local.set $l1561 + local.get $l1560 + local.get $l1561 + i32.add + local.set $l1562 + local.get $l1561 + local.get $l1562 + i32.add + local.set $l1563 + local.get $l1562 + local.get $l1563 + i32.add + local.set $l1564 + local.get $l1563 + local.get $l1564 + i32.add + local.set $l1565 + local.get $l1564 + local.get $l1565 + i32.add + local.set $l1566 + local.get $l1565 + local.get $l1566 + i32.add + local.set $l1567 + local.get $l1566 + local.get $l1567 + i32.add + local.set $l1568 + local.get $l1567 + local.get $l1568 + i32.add + local.set $l1569 + local.get $l1568 + local.get $l1569 + i32.add + local.set $l1570 + local.get $l1569 + local.get $l1570 + i32.add + local.set $l1571 + local.get $l1570 + local.get $l1571 + i32.add + local.set $l1572 + local.get $l1571 + local.get $l1572 + i32.add + local.set $l1573 + local.get $l1572 + local.get $l1573 + i32.add + local.set $l1574 + local.get $l1573 + local.get $l1574 + i32.add + local.set $l1575 + local.get $l1574 + local.get $l1575 + i32.add + local.set $l1576 + local.get $l1575 + local.get $l1576 + i32.add + local.set $l1577 + local.get $l1576 + local.get $l1577 + i32.add + local.set $l1578 + local.get $l1577 + local.get $l1578 + i32.add + local.set $l1579 + local.get $l1578 + local.get $l1579 + i32.add + local.set $l1580 + local.get $l1579 + local.get $l1580 + i32.add + local.set $l1581 + local.get $l1580 + local.get $l1581 + i32.add + local.set $l1582 + local.get $l1581 + local.get $l1582 + i32.add + local.set $l1583 + local.get $l1582 + local.get $l1583 + i32.add + local.set $l1584 + local.get $l1583 + local.get $l1584 + i32.add + local.set $l1585 + local.get $l1584 + local.get $l1585 + i32.add + local.set $l1586 + local.get $l1585 + local.get $l1586 + i32.add + local.set $l1587 + local.get $l1586 + local.get $l1587 + i32.add + local.set $l1588 + local.get $l1587 + local.get $l1588 + i32.add + local.set $l1589 + local.get $l1588 + local.get $l1589 + i32.add + local.set $l1590 + local.get $l1589 + local.get $l1590 + i32.add + local.set $l1591 + local.get $l1590 + local.get $l1591 + i32.add + local.set $l1592 + local.get $l1591 + local.get $l1592 + i32.add + local.set $l1593 + local.get $l1592 + local.get $l1593 + i32.add + local.set $l1594 + local.get $l1593 + local.get $l1594 + i32.add + local.set $l1595 + local.get $l1594 + local.get $l1595 + i32.add + local.set $l1596 + local.get $l1595 + local.get $l1596 + i32.add + local.set $l1597 + local.get $l1596 + local.get $l1597 + i32.add + local.set $l1598 + local.get $l1597 + local.get $l1598 + i32.add + local.set $l1599 + local.get $l1598 + local.get $l1599 + i32.add + local.set $l1600 + local.get $l1599 + local.get $l1600 + i32.add + local.set $l1601 + local.get $l1600 + local.get $l1601 + i32.add + local.set $l1602 + local.get $l1601 + local.get $l1602 + i32.add + local.set $l1603 + local.get $l1602 + local.get $l1603 + i32.add + local.set $l1604 + local.get $l1603 + local.get $l1604 + i32.add + local.set $l1605 + local.get $l1604 + local.get $l1605 + i32.add + local.set $l1606 + local.get $l1605 + local.get $l1606 + i32.add + local.set $l1607 + local.get $l1606 + local.get $l1607 + i32.add + local.set $l1608 + local.get $l1607 + local.get $l1608 + i32.add + local.set $l1609 + local.get $l1608 + local.get $l1609 + i32.add + local.set $l1610 + local.get $l1609 + local.get $l1610 + i32.add + local.set $l1611 + local.get $l1610 + local.get $l1611 + i32.add + local.set $l1612 + local.get $l1611 + local.get $l1612 + i32.add + local.set $l1613 + local.get $l1612 + local.get $l1613 + i32.add + local.set $l1614 + local.get $l1613 + local.get $l1614 + i32.add + local.set $l1615 + local.get $l1614 + local.get $l1615 + i32.add + local.set $l1616 + local.get $l1615 + local.get $l1616 + i32.add + local.set $l1617 + local.get $l1616 + local.get $l1617 + i32.add + local.set $l1618 + local.get $l1617 + local.get $l1618 + i32.add + local.set $l1619 + local.get $l1618 + local.get $l1619 + i32.add + local.set $l1620 + local.get $l1619 + local.get $l1620 + i32.add + local.set $l1621 + local.get $l1620 + local.get $l1621 + i32.add + local.set $l1622 + local.get $l1621 + local.get $l1622 + i32.add + local.set $l1623 + local.get $l1622 + local.get $l1623 + i32.add + local.set $l1624 + local.get $l1623 + local.get $l1624 + i32.add + local.set $l1625 + local.get $l1624 + local.get $l1625 + i32.add + local.set $l1626 + local.get $l1625 + local.get $l1626 + i32.add + local.set $l1627 + local.get $l1626 + local.get $l1627 + i32.add + local.set $l1628 + local.get $l1627 + local.get $l1628 + i32.add + local.set $l1629 + local.get $l1628 + local.get $l1629 + i32.add + local.set $l1630 + local.get $l1629 + local.get $l1630 + i32.add + local.set $l1631 + local.get $l1630 + local.get $l1631 + i32.add + local.set $l1632 + local.get $l1631 + local.get $l1632 + i32.add + local.set $l1633 + local.get $l1632 + local.get $l1633 + i32.add + local.set $l1634 + local.get $l1633 + local.get $l1634 + i32.add + local.set $l1635 + local.get $l1634 + local.get $l1635 + i32.add + local.set $l1636 + local.get $l1635 + local.get $l1636 + i32.add + local.set $l1637 + local.get $l1636 + local.get $l1637 + i32.add + local.set $l1638 + local.get $l1637 + local.get $l1638 + i32.add + local.set $l1639 + local.get $l1638 + local.get $l1639 + i32.add + local.set $l1640 + local.get $l1639 + local.get $l1640 + i32.add + local.set $l1641 + local.get $l1640 + local.get $l1641 + i32.add + local.set $l1642 + local.get $l1641 + local.get $l1642 + i32.add + local.set $l1643 + local.get $l1642 + local.get $l1643 + i32.add + local.set $l1644 + local.get $l1643 + local.get $l1644 + i32.add + local.set $l1645 + local.get $l1644 + local.get $l1645 + i32.add + local.set $l1646 + local.get $l1645 + local.get $l1646 + i32.add + local.set $l1647 + local.get $l1646 + local.get $l1647 + i32.add + local.set $l1648 + local.get $l1647 + local.get $l1648 + i32.add + local.set $l1649 + local.get $l1648 + local.get $l1649 + i32.add + local.set $l1650 + local.get $l1649 + local.get $l1650 + i32.add + local.set $l1651 + local.get $l1650 + local.get $l1651 + i32.add + local.set $l1652 + local.get $l1651 + local.get $l1652 + i32.add + local.set $l1653 + local.get $l1652 + local.get $l1653 + i32.add + local.set $l1654 + local.get $l1653 + local.get $l1654 + i32.add + local.set $l1655 + local.get $l1654 + local.get $l1655 + i32.add + local.set $l1656 + local.get $l1655 + local.get $l1656 + i32.add + local.set $l1657 + local.get $l1656 + local.get $l1657 + i32.add + local.set $l1658 + local.get $l1657 + local.get $l1658 + i32.add + local.set $l1659 + local.get $l1658 + local.get $l1659 + i32.add + local.set $l1660 + local.get $l1659 + local.get $l1660 + i32.add + local.set $l1661 + local.get $l1660 + local.get $l1661 + i32.add + local.set $l1662 + local.get $l1661 + local.get $l1662 + i32.add + local.set $l1663 + local.get $l1662 + local.get $l1663 + i32.add + local.set $l1664 + local.get $l1663 + local.get $l1664 + i32.add + local.set $l1665 + local.get $l1664 + local.get $l1665 + i32.add + local.set $l1666 + local.get $l1665 + local.get $l1666 + i32.add + local.set $l1667 + local.get $l1666 + local.get $l1667 + i32.add + local.set $l1668 + local.get $l1667 + local.get $l1668 + i32.add + local.set $l1669 + local.get $l1668 + local.get $l1669 + i32.add + local.set $l1670 + local.get $l1669 + local.get $l1670 + i32.add + local.set $l1671 + local.get $l1670 + local.get $l1671 + i32.add + local.set $l1672 + local.get $l1671 + local.get $l1672 + i32.add + local.set $l1673 + local.get $l1672 + local.get $l1673 + i32.add + local.set $l1674 + local.get $l1673 + local.get $l1674 + i32.add + local.set $l1675 + local.get $l1674 + local.get $l1675 + i32.add + local.set $l1676 + local.get $l1675 + local.get $l1676 + i32.add + local.set $l1677 + local.get $l1676 + local.get $l1677 + i32.add + local.set $l1678 + local.get $l1677 + local.get $l1678 + i32.add + local.set $l1679 + local.get $l1678 + local.get $l1679 + i32.add + local.set $l1680 + local.get $l1679 + local.get $l1680 + i32.add + local.set $l1681 + local.get $l1680 + local.get $l1681 + i32.add + local.set $l1682 + local.get $l1681 + local.get $l1682 + i32.add + local.set $l1683 + local.get $l1682 + local.get $l1683 + i32.add + local.set $l1684 + local.get $l1683 + local.get $l1684 + i32.add + local.set $l1685 + local.get $l1684 + local.get $l1685 + i32.add + local.set $l1686 + local.get $l1685 + local.get $l1686 + i32.add + local.set $l1687 + local.get $l1686 + local.get $l1687 + i32.add + local.set $l1688 + local.get $l1687 + local.get $l1688 + i32.add + local.set $l1689 + local.get $l1688 + local.get $l1689 + i32.add + local.set $l1690 + local.get $l1689 + local.get $l1690 + i32.add + local.set $l1691 + local.get $l1690 + local.get $l1691 + i32.add + local.set $l1692 + local.get $l1691 + local.get $l1692 + i32.add + local.set $l1693 + local.get $l1692 + local.get $l1693 + i32.add + local.set $l1694 + local.get $l1693 + local.get $l1694 + i32.add + local.set $l1695 + local.get $l1694 + local.get $l1695 + i32.add + local.set $l1696 + local.get $l1695 + local.get $l1696 + i32.add + local.set $l1697 + local.get $l1696 + local.get $l1697 + i32.add + local.set $l1698 + local.get $l1697 + local.get $l1698 + i32.add + local.set $l1699 + local.get $l1698 + local.get $l1699 + i32.add + local.set $l1700 + local.get $l1699 + local.get $l1700 + i32.add + local.set $l1701 + local.get $l1700 + local.get $l1701 + i32.add + local.set $l1702 + local.get $l1701 + local.get $l1702 + i32.add + local.set $l1703 + local.get $l1702 + local.get $l1703 + i32.add + local.set $l1704 + local.get $l1703 + local.get $l1704 + i32.add + local.set $l1705 + local.get $l1704 + local.get $l1705 + i32.add + local.set $l1706 + local.get $l1705 + local.get $l1706 + i32.add + local.set $l1707 + local.get $l1706 + local.get $l1707 + i32.add + local.set $l1708 + local.get $l1707 + local.get $l1708 + i32.add + local.set $l1709 + local.get $l1708 + local.get $l1709 + i32.add + local.set $l1710 + local.get $l1709 + local.get $l1710 + i32.add + local.set $l1711 + local.get $l1710 + local.get $l1711 + i32.add + local.set $l1712 + local.get $l1711 + local.get $l1712 + i32.add + local.set $l1713 + local.get $l1712 + local.get $l1713 + i32.add + local.set $l1714 + local.get $l1713 + local.get $l1714 + i32.add + local.set $l1715 + local.get $l1714 + local.get $l1715 + i32.add + local.set $l1716 + local.get $l1715 + local.get $l1716 + i32.add + local.set $l1717 + local.get $l1716 + local.get $l1717 + i32.add + local.set $l1718 + local.get $l1717 + local.get $l1718 + i32.add + local.set $l1719 + local.get $l1718 + local.get $l1719 + i32.add + local.set $l1720 + local.get $l1719 + local.get $l1720 + i32.add + local.set $l1721 + local.get $l1720 + local.get $l1721 + i32.add + local.set $l1722 + local.get $l1721 + local.get $l1722 + i32.add + local.set $l1723 + local.get $l1722 + local.get $l1723 + i32.add + local.set $l1724 + local.get $l1723 + local.get $l1724 + i32.add + local.set $l1725 + local.get $l1724 + local.get $l1725 + i32.add + local.set $l1726 + local.get $l1725 + local.get $l1726 + i32.add + local.set $l1727 + local.get $l1726 + local.get $l1727 + i32.add + local.set $l1728 + local.get $l1727 + local.get $l1728 + i32.add + local.set $l1729 + local.get $l1728 + local.get $l1729 + i32.add + local.set $l1730 + local.get $l1729 + local.get $l1730 + i32.add + local.set $l1731 + local.get $l1730 + local.get $l1731 + i32.add + local.set $l1732 + local.get $l1731 + local.get $l1732 + i32.add + local.set $l1733 + local.get $l1732 + local.get $l1733 + i32.add + local.set $l1734 + local.get $l1733 + local.get $l1734 + i32.add + local.set $l1735 + local.get $l1734 + local.get $l1735 + i32.add + local.set $l1736 + local.get $l1735 + local.get $l1736 + i32.add + local.set $l1737 + local.get $l1736 + local.get $l1737 + i32.add + local.set $l1738 + local.get $l1737 + local.get $l1738 + i32.add + local.set $l1739 + local.get $l1738 + local.get $l1739 + i32.add + local.set $l1740 + local.get $l1739 + local.get $l1740 + i32.add + local.set $l1741 + local.get $l1740 + local.get $l1741 + i32.add + local.set $l1742 + local.get $l1741 + local.get $l1742 + i32.add + local.set $l1743 + local.get $l1742 + local.get $l1743 + i32.add + local.set $l1744 + local.get $l1743 + local.get $l1744 + i32.add + local.set $l1745 + local.get $l1744 + local.get $l1745 + i32.add + local.set $l1746 + local.get $l1745 + local.get $l1746 + i32.add + local.set $l1747 + local.get $l1746 + local.get $l1747 + i32.add + local.set $l1748 + local.get $l1747 + local.get $l1748 + i32.add + local.set $l1749 + local.get $l1748 + local.get $l1749 + i32.add + local.set $l1750 + local.get $l1749 + local.get $l1750 + i32.add + local.set $l1751 + local.get $l1750 + local.get $l1751 + i32.add + local.set $l1752 + local.get $l1751 + local.get $l1752 + i32.add + local.set $l1753 + local.get $l1752 + local.get $l1753 + i32.add + local.set $l1754 + local.get $l1753 + local.get $l1754 + i32.add + local.set $l1755 + local.get $l1754 + local.get $l1755 + i32.add + local.set $l1756 + local.get $l1755 + local.get $l1756 + i32.add + local.set $l1757 + local.get $l1756 + local.get $l1757 + i32.add + local.set $l1758 + local.get $l1757 + local.get $l1758 + i32.add + local.set $l1759 + local.get $l1758 + local.get $l1759 + i32.add + local.set $l1760 + local.get $l1759 + local.get $l1760 + i32.add + local.set $l1761 + local.get $l1760 + local.get $l1761 + i32.add + local.set $l1762 + local.get $l1761 + local.get $l1762 + i32.add + local.set $l1763 + local.get $l1762 + local.get $l1763 + i32.add + local.set $l1764 + local.get $l1763 + local.get $l1764 + i32.add + local.set $l1765 + local.get $l1764 + local.get $l1765 + i32.add + local.set $l1766 + local.get $l1765 + local.get $l1766 + i32.add + local.set $l1767 + local.get $l1766 + local.get $l1767 + i32.add + local.set $l1768 + local.get $l1767 + local.get $l1768 + i32.add + local.set $l1769 + local.get $l1768 + local.get $l1769 + i32.add + local.set $l1770 + local.get $l1769 + local.get $l1770 + i32.add + local.set $l1771 + local.get $l1770 + local.get $l1771 + i32.add + local.set $l1772 + local.get $l1771 + local.get $l1772 + i32.add + local.set $l1773 + local.get $l1772 + local.get $l1773 + i32.add + local.set $l1774 + local.get $l1773 + local.get $l1774 + i32.add + local.set $l1775 + local.get $l1774 + local.get $l1775 + i32.add + local.set $l1776 + local.get $l1775 + local.get $l1776 + i32.add + local.set $l1777 + local.get $l1776 + local.get $l1777 + i32.add + local.set $l1778 + local.get $l1777 + local.get $l1778 + i32.add + local.set $l1779 + local.get $l1778 + local.get $l1779 + i32.add + local.set $l1780 + local.get $l1779 + local.get $l1780 + i32.add + local.set $l1781 + local.get $l1780 + local.get $l1781 + i32.add + local.set $l1782 + local.get $l1781 + local.get $l1782 + i32.add + local.set $l1783 + local.get $l1782 + local.get $l1783 + i32.add + local.set $l1784 + local.get $l1783 + local.get $l1784 + i32.add + local.set $l1785 + local.get $l1784 + local.get $l1785 + i32.add + local.set $l1786 + local.get $l1785 + local.get $l1786 + i32.add + local.set $l1787 + local.get $l1786 + local.get $l1787 + i32.add + local.set $l1788 + local.get $l1787 + local.get $l1788 + i32.add + local.set $l1789 + local.get $l1788 + local.get $l1789 + i32.add + local.set $l1790 + local.get $l1789 + local.get $l1790 + i32.add + local.set $l1791 + local.get $l1790 + local.get $l1791 + i32.add + local.set $l1792 + local.get $l1791 + local.get $l1792 + i32.add + local.set $l1793 + local.get $l1792 + local.get $l1793 + i32.add + local.set $l1794 + local.get $l1793 + local.get $l1794 + i32.add + local.set $l1795 + local.get $l1794 + local.get $l1795 + i32.add + local.set $l1796 + local.get $l1795 + local.get $l1796 + i32.add + local.set $l1797 + local.get $l1796 + local.get $l1797 + i32.add + local.set $l1798 + local.get $l1797 + local.get $l1798 + i32.add + local.set $l1799 + local.get $l1798 + local.get $l1799 + i32.add + local.set $l1800 + local.get $l1799 + local.get $l1800 + i32.add + local.set $l1801 + local.get $l1800 + local.get $l1801 + i32.add + local.set $l1802 + local.get $l1801 + local.get $l1802 + i32.add + local.set $l1803 + local.get $l1802 + local.get $l1803 + i32.add + local.set $l1804 + local.get $l1803 + local.get $l1804 + i32.add + local.set $l1805 + local.get $l1804 + local.get $l1805 + i32.add + local.set $l1806 + local.get $l1805 + local.get $l1806 + i32.add + local.set $l1807 + local.get $l1806 + local.get $l1807 + i32.add + local.set $l1808 + local.get $l1807 + local.get $l1808 + i32.add + local.set $l1809 + local.get $l1808 + local.get $l1809 + i32.add + local.set $l1810 + local.get $l1809 + local.get $l1810 + i32.add + local.set $l1811 + local.get $l1810 + local.get $l1811 + i32.add + local.set $l1812 + local.get $l1811 + local.get $l1812 + i32.add + local.set $l1813 + local.get $l1812 + local.get $l1813 + i32.add + local.set $l1814 + local.get $l1813 + local.get $l1814 + i32.add + local.set $l1815 + local.get $l1814 + local.get $l1815 + i32.add + local.set $l1816 + local.get $l1815 + local.get $l1816 + i32.add + local.set $l1817 + local.get $l1816 + local.get $l1817 + i32.add + local.set $l1818 + local.get $l1817 + local.get $l1818 + i32.add + local.set $l1819 + local.get $l1818 + local.get $l1819 + i32.add + local.set $l1820 + local.get $l1819 + local.get $l1820 + i32.add + local.set $l1821 + local.get $l1820 + local.get $l1821 + i32.add + local.set $l1822 + local.get $l1821 + local.get $l1822 + i32.add + local.set $l1823 + local.get $l1822 + local.get $l1823 + i32.add + local.set $l1824 + local.get $l1823 + local.get $l1824 + i32.add + local.set $l1825 + local.get $l1824 + local.get $l1825 + i32.add + local.set $l1826 + local.get $l1825 + local.get $l1826 + i32.add + local.set $l1827 + local.get $l1826 + local.get $l1827 + i32.add + local.set $l1828 + local.get $l1827 + local.get $l1828 + i32.add + local.set $l1829 + local.get $l1828 + local.get $l1829 + i32.add + local.set $l1830 + local.get $l1829 + local.get $l1830 + i32.add + local.set $l1831 + local.get $l1830 + local.get $l1831 + i32.add + local.set $l1832 + local.get $l1831 + local.get $l1832 + i32.add + local.set $l1833 + local.get $l1832 + local.get $l1833 + i32.add + local.set $l1834 + local.get $l1833 + local.get $l1834 + i32.add + local.set $l1835 + local.get $l1834 + local.get $l1835 + i32.add + local.set $l1836 + local.get $l1835 + local.get $l1836 + i32.add + local.set $l1837 + local.get $l1836 + local.get $l1837 + i32.add + local.set $l1838 + local.get $l1837 + local.get $l1838 + i32.add + local.set $l1839 + local.get $l1838 + local.get $l1839 + i32.add + local.set $l1840 + local.get $l1839 + local.get $l1840 + i32.add + local.set $l1841 + local.get $l1840 + local.get $l1841 + i32.add + local.set $l1842 + local.get $l1841 + local.get $l1842 + i32.add + local.set $l1843 + local.get $l1842 + local.get $l1843 + i32.add + local.set $l1844 + local.get $l1843 + local.get $l1844 + i32.add + local.set $l1845 + local.get $l1844 + local.get $l1845 + i32.add + local.set $l1846 + local.get $l1845 + local.get $l1846 + i32.add + local.set $l1847 + local.get $l1846 + local.get $l1847 + i32.add + local.set $l1848 + local.get $l1847 + local.get $l1848 + i32.add + local.set $l1849 + local.get $l1848 + local.get $l1849 + i32.add + local.set $l1850 + local.get $l1849 + local.get $l1850 + i32.add + local.set $l1851 + local.get $l1850 + local.get $l1851 + i32.add + local.set $l1852 + local.get $l1851 + local.get $l1852 + i32.add + local.set $l1853 + local.get $l1852 + local.get $l1853 + i32.add + local.set $l1854 + local.get $l1853 + local.get $l1854 + i32.add + local.set $l1855 + local.get $l1854 + local.get $l1855 + i32.add + local.set $l1856 + local.get $l1855 + local.get $l1856 + i32.add + local.set $l1857 + local.get $l1856 + local.get $l1857 + i32.add + local.set $l1858 + local.get $l1857 + local.get $l1858 + i32.add + local.set $l1859 + local.get $l1858 + local.get $l1859 + i32.add + local.set $l1860 + local.get $l1859 + local.get $l1860 + i32.add + local.set $l1861 + local.get $l1860 + local.get $l1861 + i32.add + local.set $l1862 + local.get $l1861 + local.get $l1862 + i32.add + local.set $l1863 + local.get $l1862 + local.get $l1863 + i32.add + local.set $l1864 + local.get $l1863 + local.get $l1864 + i32.add + local.set $l1865 + local.get $l1864 + local.get $l1865 + i32.add + local.set $l1866 + local.get $l1865 + local.get $l1866 + i32.add + local.set $l1867 + local.get $l1866 + local.get $l1867 + i32.add + local.set $l1868 + local.get $l1867 + local.get $l1868 + i32.add + local.set $l1869 + local.get $l1868 + local.get $l1869 + i32.add + local.set $l1870 + local.get $l1869 + local.get $l1870 + i32.add + local.set $l1871 + local.get $l1870 + local.get $l1871 + i32.add + local.set $l1872 + local.get $l1871 + local.get $l1872 + i32.add + local.set $l1873 + local.get $l1872 + local.get $l1873 + i32.add + local.set $l1874 + local.get $l1873 + local.get $l1874 + i32.add + local.set $l1875 + local.get $l1874 + local.get $l1875 + i32.add + local.set $l1876 + local.get $l1875 + local.get $l1876 + i32.add + local.set $l1877 + local.get $l1876 + local.get $l1877 + i32.add + local.set $l1878 + local.get $l1877 + local.get $l1878 + i32.add + local.set $l1879 + local.get $l1878 + local.get $l1879 + i32.add + local.set $l1880 + local.get $l1879 + local.get $l1880 + i32.add + local.set $l1881 + local.get $l1880 + local.get $l1881 + i32.add + local.set $l1882 + local.get $l1881 + local.get $l1882 + i32.add + local.set $l1883 + local.get $l1882 + local.get $l1883 + i32.add + local.set $l1884 + local.get $l1883 + local.get $l1884 + i32.add + local.set $l1885 + local.get $l1884 + local.get $l1885 + i32.add + local.set $l1886 + local.get $l1885 + local.get $l1886 + i32.add + local.set $l1887 + local.get $l1886 + local.get $l1887 + i32.add + local.set $l1888 + local.get $l1887 + local.get $l1888 + i32.add + local.set $l1889 + local.get $l1888 + local.get $l1889 + i32.add + local.set $l1890 + local.get $l1889 + local.get $l1890 + i32.add + local.set $l1891 + local.get $l1890 + local.get $l1891 + i32.add + local.set $l1892 + local.get $l1891 + local.get $l1892 + i32.add + local.set $l1893 + local.get $l1892 + local.get $l1893 + i32.add + local.set $l1894 + local.get $l1893 + local.get $l1894 + i32.add + local.set $l1895 + local.get $l1894 + local.get $l1895 + i32.add + local.set $l1896 + local.get $l1895 + local.get $l1896 + i32.add + local.set $l1897 + local.get $l1896 + local.get $l1897 + i32.add + local.set $l1898 + local.get $l1897 + local.get $l1898 + i32.add + local.set $l1899 + local.get $l1898 + local.get $l1899 + i32.add + local.set $l1900 + local.get $l1899 + local.get $l1900 + i32.add + local.set $l1901 + local.get $l1900 + local.get $l1901 + i32.add + local.set $l1902 + local.get $l1901 + local.get $l1902 + i32.add + local.set $l1903 + local.get $l1902 + local.get $l1903 + i32.add + local.set $l1904 + local.get $l1903 + local.get $l1904 + i32.add + local.set $l1905 + local.get $l1904 + local.get $l1905 + i32.add + local.set $l1906 + local.get $l1905 + local.get $l1906 + i32.add + local.set $l1907 + local.get $l1906 + local.get $l1907 + i32.add + local.set $l1908 + local.get $l1907 + local.get $l1908 + i32.add + local.set $l1909 + local.get $l1908 + local.get $l1909 + i32.add + local.set $l1910 + local.get $l1909 + local.get $l1910 + i32.add + local.set $l1911 + local.get $l1910 + local.get $l1911 + i32.add + local.set $l1912 + local.get $l1911 + local.get $l1912 + i32.add + local.set $l1913 + local.get $l1912 + local.get $l1913 + i32.add + local.set $l1914 + local.get $l1913 + local.get $l1914 + i32.add + local.set $l1915 + local.get $l1914 + local.get $l1915 + i32.add + local.set $l1916 + local.get $l1915 + local.get $l1916 + i32.add + local.set $l1917 + local.get $l1916 + local.get $l1917 + i32.add + local.set $l1918 + local.get $l1917 + local.get $l1918 + i32.add + local.set $l1919 + local.get $l1918 + local.get $l1919 + i32.add + local.set $l1920 + local.get $l1919 + local.get $l1920 + i32.add + local.set $l1921 + local.get $l1920 + local.get $l1921 + i32.add + local.set $l1922 + local.get $l1921 + local.get $l1922 + i32.add + local.set $l1923 + local.get $l1922 + local.get $l1923 + i32.add + local.set $l1924 + local.get $l1923 + local.get $l1924 + i32.add + local.set $l1925 + local.get $l1924 + local.get $l1925 + i32.add + local.set $l1926 + local.get $l1925 + local.get $l1926 + i32.add + local.set $l1927 + local.get $l1926 + local.get $l1927 + i32.add + local.set $l1928 + local.get $l1927 + local.get $l1928 + i32.add + local.set $l1929 + local.get $l1928 + local.get $l1929 + i32.add + local.set $l1930 + local.get $l1929 + local.get $l1930 + i32.add + local.set $l1931 + local.get $l1930 + local.get $l1931 + i32.add + local.set $l1932 + local.get $l1931 + local.get $l1932 + i32.add + local.set $l1933 + local.get $l1932 + local.get $l1933 + i32.add + local.set $l1934 + local.get $l1933 + local.get $l1934 + i32.add + local.set $l1935 + local.get $l1934 + local.get $l1935 + i32.add + local.set $l1936 + local.get $l1935 + local.get $l1936 + i32.add + local.set $l1937 + local.get $l1936 + local.get $l1937 + i32.add + local.set $l1938 + local.get $l1937 + local.get $l1938 + i32.add + local.set $l1939 + local.get $l1938 + local.get $l1939 + i32.add + local.set $l1940 + local.get $l1939 + local.get $l1940 + i32.add + local.set $l1941 + local.get $l1940 + local.get $l1941 + i32.add + local.set $l1942 + local.get $l1941 + local.get $l1942 + i32.add + local.set $l1943 + local.get $l1942 + local.get $l1943 + i32.add + local.set $l1944 + local.get $l1943 + local.get $l1944 + i32.add + local.set $l1945 + local.get $l1944 + local.get $l1945 + i32.add + local.set $l1946 + local.get $l1945 + local.get $l1946 + i32.add + local.set $l1947 + local.get $l1946 + local.get $l1947 + i32.add + local.set $l1948 + local.get $l1947 + local.get $l1948 + i32.add + local.set $l1949 + local.get $l1948 + local.get $l1949 + i32.add + local.set $l1950 + local.get $l1949 + local.get $l1950 + i32.add + local.set $l1951 + local.get $l1950 + local.get $l1951 + i32.add + local.set $l1952 + local.get $l1951 + local.get $l1952 + i32.add + local.set $l1953 + local.get $l1952 + local.get $l1953 + i32.add + local.set $l1954 + local.get $l1953 + local.get $l1954 + i32.add + local.set $l1955 + local.get $l1954 + local.get $l1955 + i32.add + local.set $l1956 + local.get $l1955 + local.get $l1956 + i32.add + local.set $l1957 + local.get $l1956 + local.get $l1957 + i32.add + local.set $l1958 + local.get $l1957 + local.get $l1958 + i32.add + local.set $l1959 + local.get $l1958 + local.get $l1959 + i32.add + local.set $l1960 + local.get $l1959 + local.get $l1960 + i32.add + local.set $l1961 + local.get $l1960 + local.get $l1961 + i32.add + local.set $l1962 + local.get $l1961 + local.get $l1962 + i32.add + local.set $l1963 + local.get $l1962 + local.get $l1963 + i32.add + local.set $l1964 + local.get $l1963 + local.get $l1964 + i32.add + local.set $l1965 + local.get $l1964 + local.get $l1965 + i32.add + local.set $l1966 + local.get $l1965 + local.get $l1966 + i32.add + local.set $l1967 + local.get $l1966 + local.get $l1967 + i32.add + local.set $l1968 + local.get $l1967 + local.get $l1968 + i32.add + local.set $l1969 + local.get $l1968 + local.get $l1969 + i32.add + local.set $l1970 + local.get $l1969 + local.get $l1970 + i32.add + local.set $l1971 + local.get $l1970 + local.get $l1971 + i32.add + local.set $l1972 + local.get $l1971 + local.get $l1972 + i32.add + local.set $l1973 + local.get $l1972 + local.get $l1973 + i32.add + local.set $l1974 + local.get $l1973 + local.get $l1974 + i32.add + local.set $l1975 + local.get $l1974 + local.get $l1975 + i32.add + local.set $l1976 + local.get $l1975 + local.get $l1976 + i32.add + local.set $l1977 + local.get $l1976 + local.get $l1977 + i32.add + local.set $l1978 + local.get $l1977 + local.get $l1978 + i32.add + local.set $l1979 + local.get $l1978 + local.get $l1979 + i32.add + local.set $l1980 + local.get $l1979 + local.get $l1980 + i32.add + local.set $l1981 + local.get $l1980 + local.get $l1981 + i32.add + local.set $l1982 + local.get $l1981 + local.get $l1982 + i32.add + local.set $l1983 + local.get $l1982 + local.get $l1983 + i32.add + local.set $l1984 + local.get $l1983 + local.get $l1984 + i32.add + local.set $l1985 + local.get $l1984 + local.get $l1985 + i32.add + local.set $l1986 + local.get $l1985 + local.get $l1986 + i32.add + local.set $l1987 + local.get $l1986 + local.get $l1987 + i32.add + local.set $l1988 + local.get $l1987 + local.get $l1988 + i32.add + local.set $l1989 + local.get $l1988 + local.get $l1989 + i32.add + local.set $l1990 + local.get $l1989 + local.get $l1990 + i32.add + local.set $l1991 + local.get $l1990 + local.get $l1991 + i32.add + local.set $l1992 + local.get $l1991 + local.get $l1992 + i32.add + local.set $l1993 + local.get $l1992 + local.get $l1993 + i32.add + local.set $l1994 + local.get $l1993 + local.get $l1994 + i32.add + local.set $l1995 + local.get $l1994 + local.get $l1995 + i32.add + local.set $l1996 + local.get $l1995 + local.get $l1996 + i32.add + local.set $l1997 + local.get $l1996 + local.get $l1997 + i32.add + local.set $l1998 + local.get $l1997 + local.get $l1998 + i32.add + local.set $l1999 + local.get $l1998 + local.get $l1999 + i32.add + local.set $l2000 + local.get $l1999 + local.get $l2000 + i32.add + local.set $l2001 + local.get $l2000 + local.get $l2001 + i32.add + local.set $l2002 + local.get $l2001 + local.get $l2002 + i32.add + local.set $l2003 + local.get $l2002 + local.get $l2003 + i32.add + local.set $l2004 + local.get $l2003 + local.get $l2004 + i32.add + local.set $l2005 + local.get $l2004 + local.get $l2005 + i32.add + local.set $l2006 + local.get $l2005 + local.get $l2006 + i32.add + local.set $l2007 + local.get $l2006 + local.get $l2007 + i32.add + local.set $l2008 + local.get $l2007 + local.get $l2008 + i32.add + local.set $l2009 + local.get $l2008 + local.get $l2009 + i32.add + local.set $l2010 + local.get $l2009 + local.get $l2010 + i32.add + local.set $l2011 + local.get $l2010 + local.get $l2011 + i32.add + local.set $l2012 + local.get $l2011 + local.get $l2012 + i32.add + local.set $l2013 + local.get $l2012 + local.get $l2013 + i32.add + local.set $l2014 + local.get $l2013 + local.get $l2014 + i32.add + local.set $l2015 + local.get $l2014 + local.get $l2015 + i32.add + local.set $l2016 + local.get $l2015 + local.get $l2016 + i32.add + local.set $l2017 + local.get $l2016 + local.get $l2017 + i32.add + local.set $l2018 + local.get $l2017 + local.get $l2018 + i32.add + local.set $l2019 + local.get $l2018 + local.get $l2019 + i32.add + local.set $l2020 + local.get $l2019 + local.get $l2020 + i32.add + local.set $l2021 + local.get $l2020 + local.get $l2021 + i32.add + local.set $l2022 + local.get $l2021 + local.get $l2022 + i32.add + local.set $l2023 + local.get $l2022 + local.get $l2023 + i32.add + local.set $l2024 + local.get $l2023 + local.get $l2024 + i32.add + local.set $l2025 + local.get $l2024 + local.get $l2025 + i32.add + local.set $l2026 + local.get $l2025 + local.get $l2026 + i32.add + local.set $l2027 + local.get $l2026 + local.get $l2027 + i32.add + local.set $l2028 + local.get $l2027 + local.get $l2028 + i32.add + local.set $l2029 + local.get $l2028 + local.get $l2029 + i32.add + local.set $l2030 + local.get $l2029 + local.get $l2030 + i32.add + local.set $l2031 + local.get $l2030 + local.get $l2031 + i32.add + local.set $l2032 + local.get $l2031 + local.get $l2032 + i32.add + local.set $l2033 + local.get $l2032 + local.get $l2033 + i32.add + local.set $l2034 + local.get $l2033 + local.get $l2034 + i32.add + local.set $l2035 + local.get $l2034 + local.get $l2035 + i32.add + local.set $l2036 + local.get $l2035 + local.get $l2036 + i32.add + local.set $l2037 + local.get $l2036 + local.get $l2037 + i32.add + local.set $l2038 + local.get $l2037 + local.get $l2038 + i32.add + local.set $l2039 + local.get $l2038 + local.get $l2039 + i32.add + local.set $l2040 + local.get $l2039 + local.get $l2040 + i32.add + local.set $l2041 + local.get $l2040 + local.get $l2041 + i32.add + local.set $l2042 + local.get $l2041 + local.get $l2042 + i32.add + local.set $l2043 + local.get $l2042 + local.get $l2043 + i32.add + local.set $l2044 + local.get $l2043 + local.get $l2044 + i32.add + local.set $l2045 + local.get $l2044 + local.get $l2045 + i32.add + local.set $l2046 + local.get $l2045 + local.get $l2046 + i32.add + local.set $l2047 + local.get $l2046 + local.get $l2047 + i32.add + local.set $l2048 + local.get $l2047 + local.get $l2048 + i32.add + local.set $l2049 + local.get $l2048 + local.get $l2049 + i32.add + local.set $l2050 + local.get $l2049 + local.get $l2050 + i32.add + local.set $l2051 + local.get $l2050 + local.get $l2051 + i32.add + local.set $l2052 + local.get $l2051 + local.get $l2052 + i32.add + local.set $l2053 + local.get $l2052 + local.get $l2053 + i32.add + local.set $l2054 + local.get $l2053 + local.get $l2054 + i32.add + local.set $l2055 + local.get $l2054 + local.get $l2055 + i32.add + local.set $l2056 + local.get $l2055 + local.get $l2056 + i32.add + local.set $l2057 + local.get $l2056 + local.get $l2057 + i32.add + local.set $l2058 + local.get $l2057 + local.get $l2058 + i32.add + local.set $l2059 + local.get $l2058 + local.get $l2059 + i32.add + local.set $l2060 + local.get $l2059 + local.get $l2060 + i32.add + local.set $l2061 + local.get $l2060 + local.get $l2061 + i32.add + local.set $l2062 + local.get $l2061 + local.get $l2062 + i32.add + local.set $l2063 + local.get $l2062 + local.get $l2063 + i32.add + local.set $l2064 + local.get $l2063 + local.get $l2064 + i32.add + local.set $l2065 + local.get $l2064 + local.get $l2065 + i32.add + local.set $l2066 + local.get $l2065 + local.get $l2066 + i32.add + local.set $l2067 + local.get $l2066 + local.get $l2067 + i32.add + local.set $l2068 + local.get $l2067 + local.get $l2068 + i32.add + local.set $l2069 + local.get $l2068 + local.get $l2069 + i32.add + local.set $l2070 + local.get $l2069 + local.get $l2070 + i32.add + local.set $l2071 + local.get $l2070 + local.get $l2071 + i32.add + local.set $l2072 + local.get $l2071 + local.get $l2072 + i32.add + local.set $l2073 + local.get $l2072 + local.get $l2073 + i32.add + local.set $l2074 + local.get $l2073 + local.get $l2074 + i32.add + local.set $l2075 + local.get $l2074 + local.get $l2075 + i32.add + local.set $l2076 + local.get $l2075 + local.get $l2076 + i32.add + local.set $l2077 + local.get $l2076 + local.get $l2077 + i32.add + local.set $l2078 + local.get $l2077 + local.get $l2078 + i32.add + local.set $l2079 + local.get $l2078 + local.get $l2079 + i32.add + local.set $l2080 + local.get $l2079 + local.get $l2080 + i32.add + local.set $l2081 + local.get $l2080 + local.get $l2081 + i32.add + local.set $l2082 + local.get $l2081 + local.get $l2082 + i32.add + local.set $l2083 + local.get $l2082 + local.get $l2083 + i32.add + local.set $l2084 + local.get $l2083 + local.get $l2084 + i32.add + local.set $l2085 + local.get $l2084 + local.get $l2085 + i32.add + local.set $l2086 + local.get $l2085 + local.get $l2086 + i32.add + local.set $l2087 + local.get $l2086 + local.get $l2087 + i32.add + local.set $l2088 + local.get $l2087 + local.get $l2088 + i32.add + local.set $l2089 + local.get $l2088 + local.get $l2089 + i32.add + local.set $l2090 + local.get $l2089 + local.get $l2090 + i32.add + local.set $l2091 + local.get $l2090 + local.get $l2091 + i32.add + local.set $l2092 + local.get $l2091 + local.get $l2092 + i32.add + local.set $l2093 + local.get $l2092 + local.get $l2093 + i32.add + local.set $l2094 + local.get $l2093 + local.get $l2094 + i32.add + local.set $l2095 + local.get $l2094 + local.get $l2095 + i32.add + local.set $l2096 + local.get $l2095 + local.get $l2096 + i32.add + local.set $l2097 + local.get $l2096 + local.get $l2097 + i32.add + local.set $l2098 + local.get $l2097 + local.get $l2098 + i32.add + local.set $l2099 + local.get $l2098 + local.get $l2099 + i32.add + local.set $l2100 + local.get $l2099 + local.get $l2100 + i32.add + local.set $l2101 + local.get $l2100 + local.get $l2101 + i32.add + local.set $l2102 + local.get $l2101 + local.get $l2102 + i32.add + local.set $l2103 + local.get $l2102 + local.get $l2103 + i32.add + local.set $l2104 + local.get $l2103 + local.get $l2104 + i32.add + local.set $l2105 + local.get $l2104 + local.get $l2105 + i32.add + local.set $l2106 + local.get $l2105 + local.get $l2106 + i32.add + local.set $l2107 + local.get $l2106 + local.get $l2107 + i32.add + local.set $l2108 + local.get $l2107 + local.get $l2108 + i32.add + local.set $l2109 + local.get $l2108 + local.get $l2109 + i32.add + local.set $l2110 + local.get $l2109 + local.get $l2110 + i32.add + local.set $l2111 + local.get $l2110 + local.get $l2111 + i32.add + local.set $l2112 + local.get $l2111 + local.get $l2112 + i32.add + local.set $l2113 + local.get $l2112 + local.get $l2113 + i32.add + local.set $l2114 + local.get $l2113 + local.get $l2114 + i32.add + local.set $l2115 + local.get $l2114 + local.get $l2115 + i32.add + local.set $l2116 + local.get $l2115 + local.get $l2116 + i32.add + local.set $l2117 + local.get $l2116 + local.get $l2117 + i32.add + local.set $l2118 + local.get $l2117 + local.get $l2118 + i32.add + local.set $l2119 + local.get $l2118 + local.get $l2119 + i32.add + local.set $l2120 + local.get $l2119 + local.get $l2120 + i32.add + local.set $l2121 + local.get $l2120 + local.get $l2121 + i32.add + local.set $l2122 + local.get $l2121 + local.get $l2122 + i32.add + local.set $l2123 + local.get $l2122 + local.get $l2123 + i32.add + local.set $l2124 + local.get $l2123 + local.get $l2124 + i32.add + local.set $l2125 + local.get $l2124 + local.get $l2125 + i32.add + local.set $l2126 + local.get $l2125 + local.get $l2126 + i32.add + local.set $l2127 + local.get $l2126 + local.get $l2127 + i32.add + local.set $l2128 + local.get $l2127 + local.get $l2128 + i32.add + local.set $l2129 + local.get $l2128 + local.get $l2129 + i32.add + local.set $l2130 + local.get $l2129 + local.get $l2130 + i32.add + local.set $l2131 + local.get $l2130 + local.get $l2131 + i32.add + local.set $l2132 + local.get $l2131 + local.get $l2132 + i32.add + local.set $l2133 + local.get $l2132 + local.get $l2133 + i32.add + local.set $l2134 + local.get $l2133 + local.get $l2134 + i32.add + local.set $l2135 + local.get $l2134 + local.get $l2135 + i32.add + local.set $l2136 + local.get $l2135 + local.get $l2136 + i32.add + local.set $l2137 + local.get $l2136 + local.get $l2137 + i32.add + local.set $l2138 + local.get $l2137 + local.get $l2138 + i32.add + local.set $l2139 + local.get $l2138 + local.get $l2139 + i32.add + local.set $l2140 + local.get $l2139 + local.get $l2140 + i32.add + local.set $l2141 + local.get $l2140 + local.get $l2141 + i32.add + local.set $l2142 + local.get $l2141 + local.get $l2142 + i32.add + local.set $l2143 + local.get $l2142 + local.get $l2143 + i32.add + local.set $l2144 + local.get $l2143 + local.get $l2144 + i32.add + local.set $l2145 + local.get $l2144 + local.get $l2145 + i32.add + local.set $l2146 + local.get $l2145 + local.get $l2146 + i32.add + local.set $l2147 + local.get $l2146 + local.get $l2147 + i32.add + local.set $l2148 + local.get $l2147 + local.get $l2148 + i32.add + local.set $l2149 + local.get $l2148 + local.get $l2149 + i32.add + local.set $l2150 + local.get $l2149 + local.get $l2150 + i32.add + local.set $l2151 + local.get $l2150 + local.get $l2151 + i32.add + local.set $l2152 + local.get $l2151 + local.get $l2152 + i32.add + local.set $l2153 + local.get $l2152 + local.get $l2153 + i32.add + local.set $l2154 + local.get $l2153 + local.get $l2154 + i32.add + local.set $l2155 + local.get $l2154 + local.get $l2155 + i32.add + local.set $l2156 + local.get $l2155 + local.get $l2156 + i32.add + local.set $l2157 + local.get $l2156 + local.get $l2157 + i32.add + local.set $l2158 + local.get $l2157 + local.get $l2158 + i32.add + local.set $l2159 + local.get $l2158 + local.get $l2159 + i32.add + local.set $l2160 + local.get $l2159 + local.get $l2160 + i32.add + local.set $l2161 + local.get $l2160 + local.get $l2161 + i32.add + local.set $l2162 + local.get $l2161 + local.get $l2162 + i32.add + local.set $l2163 + local.get $l2162 + local.get $l2163 + i32.add + local.set $l2164 + local.get $l2163 + local.get $l2164 + i32.add + local.set $l2165 + local.get $l2164 + local.get $l2165 + i32.add + local.set $l2166 + local.get $l2165 + local.get $l2166 + i32.add + local.set $l2167 + local.get $l2166 + local.get $l2167 + i32.add + local.set $l2168 + local.get $l2167 + local.get $l2168 + i32.add + local.set $l2169 + local.get $l2168 + local.get $l2169 + i32.add + local.set $l2170 + local.get $l2169 + local.get $l2170 + i32.add + local.set $l2171 + local.get $l2170 + local.get $l2171 + i32.add + local.set $l2172 + local.get $l2171 + local.get $l2172 + i32.add + local.set $l2173 + local.get $l2172 + local.get $l2173 + i32.add + local.set $l2174 + local.get $l2173 + local.get $l2174 + i32.add + local.set $l2175 + local.get $l2174 + local.get $l2175 + i32.add + local.set $l2176 + local.get $l2175 + local.get $l2176 + i32.add + local.set $l2177 + local.get $l2176 + local.get $l2177 + i32.add + local.set $l2178 + local.get $l2177 + local.get $l2178 + i32.add + local.set $l2179 + local.get $l2178 + local.get $l2179 + i32.add + local.set $l2180 + local.get $l2179 + local.get $l2180 + i32.add + local.set $l2181 + local.get $l2180 + local.get $l2181 + i32.add + local.set $l2182 + local.get $l2181 + local.get $l2182 + i32.add + local.set $l2183 + local.get $l2182 + local.get $l2183 + i32.add + local.set $l2184 + local.get $l2183 + local.get $l2184 + i32.add + local.set $l2185 + local.get $l2184 + local.get $l2185 + i32.add + local.set $l2186 + local.get $l2185 + local.get $l2186 + i32.add + local.set $l2187 + local.get $l2186 + local.get $l2187 + i32.add + local.set $l2188 + local.get $l2187 + local.get $l2188 + i32.add + local.set $l2189 + local.get $l2188 + local.get $l2189 + i32.add + local.set $l2190 + local.get $l2189 + local.get $l2190 + i32.add + local.set $l2191 + local.get $l2190 + local.get $l2191 + i32.add + local.set $l2192 + local.get $l2191 + local.get $l2192 + i32.add + local.set $l2193 + local.get $l2192 + local.get $l2193 + i32.add + local.set $l2194 + local.get $l2193 + local.get $l2194 + i32.add + local.set $l2195 + local.get $l2194 + local.get $l2195 + i32.add + local.set $l2196 + local.get $l2195 + local.get $l2196 + i32.add + local.set $l2197 + local.get $l2196 + local.get $l2197 + i32.add + local.set $l2198 + local.get $l2197 + local.get $l2198 + i32.add + local.set $l2199 + local.get $l2198 + local.get $l2199 + i32.add + local.set $l2200 + local.get $l2199 + local.get $l2200 + i32.add + local.set $l2201 + local.get $l2200 + local.get $l2201 + i32.add + local.set $l2202 + local.get $l2201 + local.get $l2202 + i32.add + local.set $l2203 + local.get $l2202 + local.get $l2203 + i32.add + local.set $l2204 + local.get $l2203 + local.get $l2204 + i32.add + local.set $l2205 + local.get $l2204 + local.get $l2205 + i32.add + local.set $l2206 + local.get $l2205 + local.get $l2206 + i32.add + local.set $l2207 + local.get $l2206 + local.get $l2207 + i32.add + local.set $l2208 + local.get $l2207 + local.get $l2208 + i32.add + local.set $l2209 + local.get $l2208 + local.get $l2209 + i32.add + local.set $l2210 + local.get $l2209 + local.get $l2210 + i32.add + local.set $l2211 + local.get $l2210 + local.get $l2211 + i32.add + local.set $l2212 + local.get $l2211 + local.get $l2212 + i32.add + local.set $l2213 + local.get $l2212 + local.get $l2213 + i32.add + local.set $l2214 + local.get $l2213 + local.get $l2214 + i32.add + local.set $l2215 + local.get $l2214 + local.get $l2215 + i32.add + local.set $l2216 + local.get $l2215 + local.get $l2216 + i32.add + local.set $l2217 + local.get $l2216 + local.get $l2217 + i32.add + local.set $l2218 + local.get $l2217 + local.get $l2218 + i32.add + local.set $l2219 + local.get $l2218 + local.get $l2219 + i32.add + local.set $l2220 + local.get $l2219 + local.get $l2220 + i32.add + local.set $l2221 + local.get $l2220 + local.get $l2221 + i32.add + local.set $l2222 + local.get $l2221 + local.get $l2222 + i32.add + local.set $l2223 + local.get $l2222 + local.get $l2223 + i32.add + local.set $l2224 + local.get $l2223 + local.get $l2224 + i32.add + local.set $l2225 + local.get $l2224 + local.get $l2225 + i32.add + local.set $l2226 + local.get $l2225 + local.get $l2226 + i32.add + local.set $l2227 + local.get $l2226 + local.get $l2227 + i32.add + local.set $l2228 + local.get $l2227 + local.get $l2228 + i32.add + local.set $l2229 + local.get $l2228 + local.get $l2229 + i32.add + local.set $l2230 + local.get $l2229 + local.get $l2230 + i32.add + local.set $l2231 + local.get $l2230 + local.get $l2231 + i32.add + local.set $l2232 + local.get $l2231 + local.get $l2232 + i32.add + local.set $l2233 + local.get $l2232 + local.get $l2233 + i32.add + local.set $l2234 + local.get $l2233 + local.get $l2234 + i32.add + local.set $l2235 + local.get $l2234 + local.get $l2235 + i32.add + local.set $l2236 + local.get $l2235 + local.get $l2236 + i32.add + local.set $l2237 + local.get $l2236 + local.get $l2237 + i32.add + local.set $l2238 + local.get $l2237 + local.get $l2238 + i32.add + local.set $l2239 + local.get $l2238 + local.get $l2239 + i32.add + local.set $l2240 + local.get $l2239 + local.get $l2240 + i32.add + local.set $l2241 + local.get $l2240 + local.get $l2241 + i32.add + local.set $l2242 + local.get $l2241 + local.get $l2242 + i32.add + local.set $l2243 + local.get $l2242 + local.get $l2243 + i32.add + local.set $l2244 + local.get $l2243 + local.get $l2244 + i32.add + local.set $l2245 + local.get $l2244 + local.get $l2245 + i32.add + local.set $l2246 + local.get $l2245 + local.get $l2246 + i32.add + local.set $l2247 + local.get $l2246 + local.get $l2247 + i32.add + local.set $l2248 + local.get $l2247 + local.get $l2248 + i32.add + local.set $l2249 + local.get $l2248 + local.get $l2249 + i32.add + local.set $l2250 + local.get $l2249 + local.get $l2250 + i32.add + local.set $l2251 + local.get $l2250 + local.get $l2251 + i32.add + local.set $l2252 + local.get $l2251 + local.get $l2252 + i32.add + local.set $l2253 + local.get $l2252 + local.get $l2253 + i32.add + local.set $l2254 + local.get $l2253 + local.get $l2254 + i32.add + local.set $l2255 + local.get $l2254 + local.get $l2255 + i32.add + local.set $l2256 + local.get $l2255 + local.get $l2256 + i32.add + local.set $l2257 + local.get $l2256 + local.get $l2257 + i32.add + local.set $l2258 + local.get $l2257 + local.get $l2258 + i32.add + local.set $l2259 + local.get $l2258 + local.get $l2259 + i32.add + local.set $l2260 + local.get $l2259 + local.get $l2260 + i32.add + local.set $l2261 + local.get $l2260 + local.get $l2261 + i32.add + local.set $l2262 + local.get $l2261 + local.get $l2262 + i32.add + local.set $l2263 + local.get $l2262 + local.get $l2263 + i32.add + local.set $l2264 + local.get $l2263 + local.get $l2264 + i32.add + local.set $l2265 + local.get $l2264 + local.get $l2265 + i32.add + local.set $l2266 + local.get $l2265 + local.get $l2266 + i32.add + local.set $l2267 + local.get $l2266 + local.get $l2267 + i32.add + local.set $l2268 + local.get $l2267 + local.get $l2268 + i32.add + local.set $l2269 + local.get $l2268 + local.get $l2269 + i32.add + local.set $l2270 + local.get $l2269 + local.get $l2270 + i32.add + local.set $l2271 + local.get $l2270 + local.get $l2271 + i32.add + local.set $l2272 + local.get $l2271 + local.get $l2272 + i32.add + local.set $l2273 + local.get $l2272 + local.get $l2273 + i32.add + local.set $l2274 + local.get $l2273 + local.get $l2274 + i32.add + local.set $l2275 + local.get $l2274 + local.get $l2275 + i32.add + local.set $l2276 + local.get $l2275 + local.get $l2276 + i32.add + local.set $l2277 + local.get $l2276 + local.get $l2277 + i32.add + local.set $l2278 + local.get $l2277 + local.get $l2278 + i32.add + local.set $l2279 + local.get $l2278 + local.get $l2279 + i32.add + local.set $l2280 + local.get $l2279 + local.get $l2280 + i32.add + local.set $l2281 + local.get $l2280 + local.get $l2281 + i32.add + local.set $l2282 + local.get $l2281 + local.get $l2282 + i32.add + local.set $l2283 + local.get $l2282 + local.get $l2283 + i32.add + local.set $l2284 + local.get $l2283 + local.get $l2284 + i32.add + local.set $l2285 + local.get $l2284 + local.get $l2285 + i32.add + local.set $l2286 + local.get $l2285 + local.get $l2286 + i32.add + local.set $l2287 + local.get $l2286 + local.get $l2287 + i32.add + local.set $l2288 + local.get $l2287 + local.get $l2288 + i32.add + local.set $l2289 + local.get $l2288 + local.get $l2289 + i32.add + local.set $l2290 + local.get $l2289 + local.get $l2290 + i32.add + local.set $l2291 + local.get $l2290 + local.get $l2291 + i32.add + local.set $l2292 + local.get $l2291 + local.get $l2292 + i32.add + local.set $l2293 + local.get $l2292 + local.get $l2293 + i32.add + local.set $l2294 + local.get $l2293 + local.get $l2294 + i32.add + local.set $l2295 + local.get $l2294 + local.get $l2295 + i32.add + local.set $l2296 + local.get $l2295 + local.get $l2296 + i32.add + local.set $l2297 + local.get $l2296 + local.get $l2297 + i32.add + local.set $l2298 + local.get $l2297 + local.get $l2298 + i32.add + local.set $l2299 + local.get $l2298 + local.get $l2299 + i32.add + local.set $l2300 + local.get $l2299 + local.get $l2300 + i32.add + local.set $l2301 + local.get $l2300 + local.get $l2301 + i32.add + local.set $l2302 + local.get $l2301 + local.get $l2302 + i32.add + local.set $l2303 + local.get $l2302 + local.get $l2303 + i32.add + local.set $l2304 + local.get $l2303 + local.get $l2304 + i32.add + local.set $l2305 + local.get $l2304 + local.get $l2305 + i32.add + local.set $l2306 + local.get $l2305 + local.get $l2306 + i32.add + local.set $l2307 + local.get $l2306 + local.get $l2307 + i32.add + local.set $l2308 + local.get $l2307 + local.get $l2308 + i32.add + local.set $l2309 + local.get $l2308 + local.get $l2309 + i32.add + local.set $l2310 + local.get $l2309 + local.get $l2310 + i32.add + local.set $l2311 + local.get $l2310 + local.get $l2311 + i32.add + local.set $l2312 + local.get $l2311 + local.get $l2312 + i32.add + local.set $l2313 + local.get $l2312 + local.get $l2313 + i32.add + local.set $l2314 + local.get $l2313 + local.get $l2314 + i32.add + local.set $l2315 + local.get $l2314 + local.get $l2315 + i32.add + local.set $l2316 + local.get $l2315 + local.get $l2316 + i32.add + local.set $l2317 + local.get $l2316 + local.get $l2317 + i32.add + local.set $l2318 + local.get $l2317 + local.get $l2318 + i32.add + local.set $l2319 + local.get $l2318 + local.get $l2319 + i32.add + local.set $l2320 + local.get $l2319 + local.get $l2320 + i32.add + local.set $l2321 + local.get $l2320 + local.get $l2321 + i32.add + local.set $l2322 + local.get $l2321 + local.get $l2322 + i32.add + local.set $l2323 + local.get $l2322 + local.get $l2323 + i32.add + local.set $l2324 + local.get $l2323 + local.get $l2324 + i32.add + local.set $l2325 + local.get $l2324 + local.get $l2325 + i32.add + local.set $l2326 + local.get $l2325 + local.get $l2326 + i32.add + local.set $l2327 + local.get $l2326 + local.get $l2327 + i32.add + local.set $l2328 + local.get $l2327 + local.get $l2328 + i32.add + local.set $l2329 + local.get $l2328 + local.get $l2329 + i32.add + local.set $l2330 + local.get $l2329 + local.get $l2330 + i32.add + local.set $l2331 + local.get $l2330 + local.get $l2331 + i32.add + local.set $l2332 + local.get $l2331 + local.get $l2332 + i32.add + local.set $l2333 + local.get $l2332 + local.get $l2333 + i32.add + local.set $l2334 + local.get $l2333 + local.get $l2334 + i32.add + local.set $l2335 + local.get $l2334 + local.get $l2335 + i32.add + local.set $l2336 + local.get $l2335 + local.get $l2336 + i32.add + local.set $l2337 + local.get $l2336 + local.get $l2337 + i32.add + local.set $l2338 + local.get $l2337 + local.get $l2338 + i32.add + local.set $l2339 + local.get $l2338 + local.get $l2339 + i32.add + local.set $l2340 + local.get $l2339 + local.get $l2340 + i32.add + local.set $l2341 + local.get $l2340 + local.get $l2341 + i32.add + local.set $l2342 + local.get $l2341 + local.get $l2342 + i32.add + local.set $l2343 + local.get $l2342 + local.get $l2343 + i32.add + local.set $l2344 + local.get $l2343 + local.get $l2344 + i32.add + local.set $l2345 + local.get $l2344 + local.get $l2345 + i32.add + local.set $l2346 + local.get $l2345 + local.get $l2346 + i32.add + local.set $l2347 + local.get $l2346 + local.get $l2347 + i32.add + local.set $l2348 + local.get $l2347 + local.get $l2348 + i32.add + local.set $l2349 + local.get $l2348 + local.get $l2349 + i32.add + local.set $l2350 + local.get $l2349 + local.get $l2350 + i32.add + local.set $l2351 + local.get $l2350 + local.get $l2351 + i32.add + local.set $l2352 + local.get $l2351 + local.get $l2352 + i32.add + local.set $l2353 + local.get $l2352 + local.get $l2353 + i32.add + local.set $l2354 + local.get $l2353 + local.get $l2354 + i32.add + local.set $l2355 + local.get $l2354 + local.get $l2355 + i32.add + local.set $l2356 + local.get $l2355 + local.get $l2356 + i32.add + local.set $l2357 + local.get $l2356 + local.get $l2357 + i32.add + local.set $l2358 + local.get $l2357 + local.get $l2358 + i32.add + local.set $l2359 + local.get $l2358 + local.get $l2359 + i32.add + local.set $l2360 + local.get $l2359 + local.get $l2360 + i32.add + local.set $l2361 + local.get $l2360 + local.get $l2361 + i32.add + local.set $l2362 + local.get $l2361 + local.get $l2362 + i32.add + local.set $l2363 + local.get $l2362 + local.get $l2363 + i32.add + local.set $l2364 + local.get $l2363 + local.get $l2364 + i32.add + local.set $l2365 + local.get $l2364 + local.get $l2365 + i32.add + local.set $l2366 + local.get $l2365 + local.get $l2366 + i32.add + local.set $l2367 + local.get $l2366 + local.get $l2367 + i32.add + local.set $l2368 + local.get $l2367 + local.get $l2368 + i32.add + local.set $l2369 + local.get $l2368 + local.get $l2369 + i32.add + local.set $l2370 + local.get $l2369 + local.get $l2370 + i32.add + local.set $l2371 + local.get $l2370 + local.get $l2371 + i32.add + local.set $l2372 + local.get $l2371 + local.get $l2372 + i32.add + local.set $l2373 + local.get $l2372 + local.get $l2373 + i32.add + local.set $l2374 + local.get $l2373 + local.get $l2374 + i32.add + local.set $l2375 + local.get $l2374 + local.get $l2375 + i32.add + local.set $l2376 + local.get $l2375 + local.get $l2376 + i32.add + local.set $l2377 + local.get $l2376 + local.get $l2377 + i32.add + local.set $l2378 + local.get $l2377 + local.get $l2378 + i32.add + local.set $l2379 + local.get $l2378 + local.get $l2379 + i32.add + local.set $l2380 + local.get $l2379 + local.get $l2380 + i32.add + local.set $l2381 + local.get $l2380 + local.get $l2381 + i32.add + local.set $l2382 + local.get $l2381 + local.get $l2382 + i32.add + local.set $l2383 + local.get $l2382 + local.get $l2383 + i32.add + local.set $l2384 + local.get $l2383 + local.get $l2384 + i32.add + local.set $l2385 + local.get $l2384 + local.get $l2385 + i32.add + local.set $l2386 + local.get $l2385 + local.get $l2386 + i32.add + local.set $l2387 + local.get $l2386 + local.get $l2387 + i32.add + local.set $l2388 + local.get $l2387 + local.get $l2388 + i32.add + local.set $l2389 + local.get $l2388 + local.get $l2389 + i32.add + local.set $l2390 + local.get $l2389 + local.get $l2390 + i32.add + local.set $l2391 + local.get $l2390 + local.get $l2391 + i32.add + local.set $l2392 + local.get $l2391 + local.get $l2392 + i32.add + local.set $l2393 + local.get $l2392 + local.get $l2393 + i32.add + local.set $l2394 + local.get $l2393 + local.get $l2394 + i32.add + local.set $l2395 + local.get $l2394 + local.get $l2395 + i32.add + local.set $l2396 + local.get $l2395 + local.get $l2396 + i32.add + local.set $l2397 + local.get $l2396 + local.get $l2397 + i32.add + local.set $l2398 + local.get $l2397 + local.get $l2398 + i32.add + local.set $l2399 + local.get $l2398 + local.get $l2399 + i32.add + local.set $l2400 + local.get $l2399 + local.get $l2400 + i32.add + local.set $l2401 + local.get $l2400 + local.get $l2401 + i32.add + local.set $l2402 + local.get $l2401 + local.get $l2402 + i32.add + local.set $l2403 + local.get $l2402 + local.get $l2403 + i32.add + local.set $l2404 + local.get $l2403 + local.get $l2404 + i32.add + local.set $l2405 + local.get $l2404 + local.get $l2405 + i32.add + local.set $l2406 + local.get $l2405 + local.get $l2406 + i32.add + local.set $l2407 + local.get $l2406 + local.get $l2407 + i32.add + local.set $l2408 + local.get $l2407 + local.get $l2408 + i32.add + local.set $l2409 + local.get $l2408 + local.get $l2409 + i32.add + local.set $l2410 + local.get $l2409 + local.get $l2410 + i32.add + local.set $l2411 + local.get $l2410 + local.get $l2411 + i32.add + local.set $l2412 + local.get $l2411 + local.get $l2412 + i32.add + local.set $l2413 + local.get $l2412 + local.get $l2413 + i32.add + local.set $l2414 + local.get $l2413 + local.get $l2414 + i32.add + local.set $l2415 + local.get $l2414 + local.get $l2415 + i32.add + local.set $l2416 + local.get $l2415 + local.get $l2416 + i32.add + local.set $l2417 + local.get $l2416 + local.get $l2417 + i32.add + local.set $l2418 + local.get $l2417 + local.get $l2418 + i32.add + local.set $l2419 + local.get $l2418 + local.get $l2419 + i32.add + local.set $l2420 + local.get $l2419 + local.get $l2420 + i32.add + local.set $l2421 + local.get $l2420 + local.get $l2421 + i32.add + local.set $l2422 + local.get $l2421 + local.get $l2422 + i32.add + local.set $l2423 + local.get $l2422 + local.get $l2423 + i32.add + local.set $l2424 + local.get $l2423 + local.get $l2424 + i32.add + local.set $l2425 + local.get $l2424 + local.get $l2425 + i32.add + local.set $l2426 + local.get $l2425 + local.get $l2426 + i32.add + local.set $l2427 + local.get $l2426 + local.get $l2427 + i32.add + local.set $l2428 + local.get $l2427 + local.get $l2428 + i32.add + local.set $l2429 + local.get $l2428 + local.get $l2429 + i32.add + local.set $l2430 + local.get $l2429 + local.get $l2430 + i32.add + local.set $l2431 + local.get $l2430 + local.get $l2431 + i32.add + local.set $l2432 + local.get $l2431 + local.get $l2432 + i32.add + local.set $l2433 + local.get $l2432 + local.get $l2433 + i32.add + local.set $l2434 + local.get $l2433 + local.get $l2434 + i32.add + local.set $l2435 + local.get $l2434 + local.get $l2435 + i32.add + local.set $l2436 + local.get $l2435 + local.get $l2436 + i32.add + local.set $l2437 + local.get $l2436 + local.get $l2437 + i32.add + local.set $l2438 + local.get $l2437 + local.get $l2438 + i32.add + local.set $l2439 + local.get $l2438 + local.get $l2439 + i32.add + local.set $l2440 + local.get $l2439 + local.get $l2440 + i32.add + local.set $l2441 + local.get $l2440 + local.get $l2441 + i32.add + local.set $l2442 + local.get $l2441 + local.get $l2442 + i32.add + local.set $l2443 + local.get $l2442 + local.get $l2443 + i32.add + local.set $l2444 + local.get $l2443 + local.get $l2444 + i32.add + local.set $l2445 + local.get $l2444 + local.get $l2445 + i32.add + local.set $l2446 + local.get $l2445 + local.get $l2446 + i32.add + local.set $l2447 + local.get $l2446 + local.get $l2447 + i32.add + local.set $l2448 + local.get $l2447 + local.get $l2448 + i32.add + local.set $l2449 + local.get $l2448 + local.get $l2449 + i32.add + local.set $l2450 + local.get $l2449 + local.get $l2450 + i32.add + local.set $l2451 + local.get $l2450 + local.get $l2451 + i32.add + local.set $l2452 + local.get $l2451 + local.get $l2452 + i32.add + local.set $l2453 + local.get $l2452 + local.get $l2453 + i32.add + local.set $l2454 + local.get $l2453 + local.get $l2454 + i32.add + local.set $l2455 + local.get $l2454 + local.get $l2455 + i32.add + local.set $l2456 + local.get $l2455 + local.get $l2456 + i32.add + local.set $l2457 + local.get $l2456 + local.get $l2457 + i32.add + local.set $l2458 + local.get $l2457 + local.get $l2458 + i32.add + local.set $l2459 + local.get $l2458 + local.get $l2459 + i32.add + local.set $l2460 + local.get $l2459 + local.get $l2460 + i32.add + local.set $l2461 + local.get $l2460 + local.get $l2461 + i32.add + local.set $l2462 + local.get $l2461 + local.get $l2462 + i32.add + local.set $l2463 + local.get $l2462 + local.get $l2463 + i32.add + local.set $l2464 + local.get $l2463 + local.get $l2464 + i32.add + local.set $l2465 + local.get $l2464 + local.get $l2465 + i32.add + local.set $l2466 + local.get $l2465 + local.get $l2466 + i32.add + local.set $l2467 + local.get $l2466 + local.get $l2467 + i32.add + local.set $l2468 + local.get $l2467 + local.get $l2468 + i32.add + local.set $l2469 + local.get $l2468 + local.get $l2469 + i32.add + local.set $l2470 + local.get $l2469 + local.get $l2470 + i32.add + local.set $l2471 + local.get $l2470 + local.get $l2471 + i32.add + local.set $l2472 + local.get $l2471 + local.get $l2472 + i32.add + local.set $l2473 + local.get $l2472 + local.get $l2473 + i32.add + local.set $l2474 + local.get $l2473 + local.get $l2474 + i32.add + local.set $l2475 + local.get $l2474 + local.get $l2475 + i32.add + local.set $l2476 + local.get $l2475 + local.get $l2476 + i32.add + local.set $l2477 + local.get $l2476 + local.get $l2477 + i32.add + local.set $l2478 + local.get $l2477 + local.get $l2478 + i32.add + local.set $l2479 + local.get $l2478 + local.get $l2479 + i32.add + local.set $l2480 + local.get $l2479 + local.get $l2480 + i32.add + local.set $l2481 + local.get $l2480 + local.get $l2481 + i32.add + local.set $l2482 + local.get $l2481 + local.get $l2482 + i32.add + local.set $l2483 + local.get $l2482 + local.get $l2483 + i32.add + local.set $l2484 + local.get $l2483 + local.get $l2484 + i32.add + local.set $l2485 + local.get $l2484 + local.get $l2485 + i32.add + local.set $l2486 + local.get $l2485 + local.get $l2486 + i32.add + local.set $l2487 + local.get $l2486 + local.get $l2487 + i32.add + local.set $l2488 + local.get $l2487 + local.get $l2488 + i32.add + local.set $l2489 + local.get $l2488 + local.get $l2489 + i32.add + local.set $l2490 + local.get $l2489 + local.get $l2490 + i32.add + local.set $l2491 + local.get $l2490 + local.get $l2491 + i32.add + local.set $l2492 + local.get $l2491 + local.get $l2492 + i32.add + local.set $l2493 + local.get $l2492 + local.get $l2493 + i32.add + local.set $l2494 + local.get $l2493 + local.get $l2494 + i32.add + local.set $l2495 + local.get $l2494 + local.get $l2495 + i32.add + local.set $l2496 + local.get $l2495 + local.get $l2496 + i32.add + local.set $l2497 + local.get $l2496 + local.get $l2497 + i32.add + local.set $l2498 + local.get $l2497 + local.get $l2498 + i32.add + local.set $l2499 + local.get $l2498 + local.get $l2499 + i32.add + local.set $l2500 + local.get $l2499 + local.get $l2500 + i32.add + local.set $l2501 + local.get $l2500 + local.get $l2501 + i32.add + local.set $l2502 + local.get $l2501 + local.get $l2502 + i32.add + local.set $l2503 + local.get $l2502 + local.get $l2503 + i32.add + local.set $l2504 + local.get $l2503 + local.get $l2504 + i32.add + local.set $l2505 + local.get $l2504 + local.get $l2505 + i32.add + local.set $l2506 + local.get $l2505 + local.get $l2506 + i32.add + local.set $l2507 + local.get $l2506 + local.get $l2507 + i32.add + local.set $l2508 + local.get $l2507 + local.get $l2508 + i32.add + local.set $l2509 + local.get $l2508 + local.get $l2509 + i32.add + local.set $l2510 + local.get $l2509 + local.get $l2510 + i32.add + local.set $l2511 + local.get $l2510 + local.get $l2511 + i32.add + local.set $l2512 + local.get $l2511 + local.get $l2512 + i32.add + local.set $l2513 + local.get $l2512 + local.get $l2513 + i32.add + local.set $l2514 + local.get $l2513 + local.get $l2514 + i32.add + local.set $l2515 + local.get $l2514 + local.get $l2515 + i32.add + local.set $l2516 + local.get $l2515 + local.get $l2516 + i32.add + local.set $l2517 + local.get $l2516 + local.get $l2517 + i32.add + local.set $l2518 + local.get $l2517 + local.get $l2518 + i32.add + local.set $l2519 + local.get $l2518 + local.get $l2519 + i32.add + local.set $l2520 + local.get $l2519 + local.get $l2520 + i32.add + local.set $l2521 + local.get $l2520 + local.get $l2521 + i32.add + local.set $l2522 + local.get $l2521 + local.get $l2522 + i32.add + local.set $l2523 + local.get $l2522 + local.get $l2523 + i32.add + local.set $l2524 + local.get $l2523 + local.get $l2524 + i32.add + local.set $l2525 + local.get $l2524 + local.get $l2525 + i32.add + local.set $l2526 + local.get $l2525 + local.get $l2526 + i32.add + local.set $l2527 + local.get $l2526 + local.get $l2527 + i32.add + local.set $l2528 + local.get $l2527 + local.get $l2528 + i32.add + local.set $l2529 + local.get $l2528 + local.get $l2529 + i32.add + local.set $l2530 + local.get $l2529 + local.get $l2530 + i32.add + local.set $l2531 + local.get $l2530 + local.get $l2531 + i32.add + local.set $l2532 + local.get $l2531 + local.get $l2532 + i32.add + local.set $l2533 + local.get $l2532 + local.get $l2533 + i32.add + local.set $l2534 + local.get $l2533 + local.get $l2534 + i32.add + local.set $l2535 + local.get $l2534 + local.get $l2535 + i32.add + local.set $l2536 + local.get $l2535 + local.get $l2536 + i32.add + local.set $l2537 + local.get $l2536 + local.get $l2537 + i32.add + local.set $l2538 + local.get $l2537 + local.get $l2538 + i32.add + local.set $l2539 + local.get $l2538 + local.get $l2539 + i32.add + local.set $l2540 + local.get $l2539 + local.get $l2540 + i32.add + local.set $l2541 + local.get $l2540 + local.get $l2541 + i32.add + local.set $l2542 + local.get $l2541 + local.get $l2542 + i32.add + local.set $l2543 + local.get $l2542 + local.get $l2543 + i32.add + local.set $l2544 + local.get $l2543 + local.get $l2544 + i32.add + local.set $l2545 + local.get $l2544 + local.get $l2545 + i32.add + local.set $l2546 + local.get $l2545 + local.get $l2546 + i32.add + local.set $l2547 + local.get $l2546 + local.get $l2547 + i32.add + local.set $l2548 + local.get $l2547 + local.get $l2548 + i32.add + local.set $l2549 + local.get $l2548 + local.get $l2549 + i32.add + local.set $l2550 + local.get $l2549 + local.get $l2550 + i32.add + local.set $l2551 + local.get $l2550 + local.get $l2551 + i32.add + local.set $l2552 + local.get $l2551 + local.get $l2552 + i32.add + local.set $l2553 + local.get $l2552 + local.get $l2553 + i32.add + local.set $l2554 + local.get $l2553 + local.get $l2554 + i32.add + local.set $l2555 + local.get $l2554 + local.get $l2555 + i32.add + local.set $l2556 + local.get $l2555 + local.get $l2556 + i32.add + local.set $l2557 + local.get $l2556 + local.get $l2557 + i32.add + local.set $l2558 + local.get $l2557 + local.get $l2558 + i32.add + local.set $l2559 + local.get $l2558 + local.get $l2559 + i32.add + local.set $l2560 + local.get $l2559 + local.get $l2560 + i32.add + local.set $l2561 + local.get $l2560 + local.get $l2561 + i32.add + local.set $l2562 + local.get $l2561 + local.get $l2562 + i32.add + local.set $l2563 + local.get $l2562 + local.get $l2563 + i32.add + local.set $l2564 + local.get $l2563 + local.get $l2564 + i32.add + local.set $l2565 + local.get $l2564 + local.get $l2565 + i32.add + local.set $l2566 + local.get $l2565 + local.get $l2566 + i32.add + local.set $l2567 + local.get $l2566 + local.get $l2567 + i32.add + local.set $l2568 + local.get $l2567 + local.get $l2568 + i32.add + local.set $l2569 + local.get $l2568 + local.get $l2569 + i32.add + local.set $l2570 + local.get $l2569 + local.get $l2570 + i32.add + local.set $l2571 + local.get $l2570 + local.get $l2571 + i32.add + local.set $l2572 + local.get $l2571 + local.get $l2572 + i32.add + local.set $l2573 + local.get $l2572 + local.get $l2573 + i32.add + local.set $l2574 + local.get $l2573 + local.get $l2574 + i32.add + local.set $l2575 + local.get $l2574 + local.get $l2575 + i32.add + local.set $l2576 + local.get $l2575 + local.get $l2576 + i32.add + local.set $l2577 + local.get $l2576 + local.get $l2577 + i32.add + local.set $l2578 + local.get $l2577 + local.get $l2578 + i32.add + local.set $l2579 + local.get $l2578 + local.get $l2579 + i32.add + local.set $l2580 + local.get $l2579 + local.get $l2580 + i32.add + local.set $l2581 + local.get $l2580 + local.get $l2581 + i32.add + local.set $l2582 + local.get $l2581 + local.get $l2582 + i32.add + local.set $l2583 + local.get $l2582 + local.get $l2583 + i32.add + local.set $l2584 + local.get $l2583 + local.get $l2584 + i32.add + local.set $l2585 + local.get $l2584 + local.get $l2585 + i32.add + local.set $l2586 + local.get $l2585 + local.get $l2586 + i32.add + local.set $l2587 + local.get $l2586 + local.get $l2587 + i32.add + local.set $l2588 + local.get $l2587 + local.get $l2588 + i32.add + local.set $l2589 + local.get $l2588 + local.get $l2589 + i32.add + local.set $l2590 + local.get $l2589 + local.get $l2590 + i32.add + local.set $l2591 + local.get $l2590 + local.get $l2591 + i32.add + local.set $l2592 + local.get $l2591 + local.get $l2592 + i32.add + local.set $l2593 + local.get $l2592 + local.get $l2593 + i32.add + local.set $l2594 + local.get $l2593 + local.get $l2594 + i32.add + local.set $l2595 + local.get $l2594 + local.get $l2595 + i32.add + local.set $l2596 + local.get $l2595 + local.get $l2596 + i32.add + local.set $l2597 + local.get $l2596 + local.get $l2597 + i32.add + local.set $l2598 + local.get $l2597 + local.get $l2598 + i32.add + local.set $l2599 + local.get $l2598 + local.get $l2599 + i32.add + local.set $l2600 + local.get $l2599 + local.get $l2600 + i32.add + local.set $l2601 + local.get $l2600 + local.get $l2601 + i32.add + local.set $l2602 + local.get $l2601 + local.get $l2602 + i32.add + local.set $l2603 + local.get $l2602 + local.get $l2603 + i32.add + local.set $l2604 + local.get $l2603 + local.get $l2604 + i32.add + local.set $l2605 + local.get $l2604 + local.get $l2605 + i32.add + local.set $l2606 + local.get $l2605 + local.get $l2606 + i32.add + local.set $l2607 + local.get $l2606 + local.get $l2607 + i32.add + local.set $l2608 + local.get $l2607 + local.get $l2608 + i32.add + local.set $l2609 + local.get $l2608 + local.get $l2609 + i32.add + local.set $l2610 + local.get $l2609 + local.get $l2610 + i32.add + local.set $l2611 + local.get $l2610 + local.get $l2611 + i32.add + local.set $l2612 + local.get $l2611 + local.get $l2612 + i32.add + local.set $l2613 + local.get $l2612 + local.get $l2613 + i32.add + local.set $l2614 + local.get $l2613 + local.get $l2614 + i32.add + local.set $l2615 + local.get $l2614 + local.get $l2615 + i32.add + local.set $l2616 + local.get $l2615 + local.get $l2616 + i32.add + local.set $l2617 + local.get $l2616 + local.get $l2617 + i32.add + local.set $l2618 + local.get $l2617 + local.get $l2618 + i32.add + local.set $l2619 + local.get $l2618 + local.get $l2619 + i32.add + local.set $l2620 + local.get $l2619 + local.get $l2620 + i32.add + local.set $l2621 + local.get $l2620 + local.get $l2621 + i32.add + local.set $l2622 + local.get $l2621 + local.get $l2622 + i32.add + local.set $l2623 + local.get $l2622 + local.get $l2623 + i32.add + local.set $l2624 + local.get $l2623 + local.get $l2624 + i32.add + local.set $l2625 + local.get $l2624 + local.get $l2625 + i32.add + local.set $l2626 + local.get $l2625 + local.get $l2626 + i32.add + local.set $l2627 + local.get $l2626 + local.get $l2627 + i32.add + local.set $l2628 + local.get $l2627 + local.get $l2628 + i32.add + local.set $l2629 + local.get $l2628 + local.get $l2629 + i32.add + local.set $l2630 + local.get $l2629 + local.get $l2630 + i32.add + local.set $l2631 + local.get $l2630 + local.get $l2631 + i32.add + local.set $l2632 + local.get $l2631 + local.get $l2632 + i32.add + local.set $l2633 + local.get $l2632 + local.get $l2633 + i32.add + local.set $l2634 + local.get $l2633 + local.get $l2634 + i32.add + local.set $l2635 + local.get $l2634 + local.get $l2635 + i32.add + local.set $l2636 + local.get $l2635 + local.get $l2636 + i32.add + local.set $l2637 + local.get $l2636 + local.get $l2637 + i32.add + local.set $l2638 + local.get $l2637 + local.get $l2638 + i32.add + local.set $l2639 + local.get $l2638 + local.get $l2639 + i32.add + local.set $l2640 + local.get $l2639 + local.get $l2640 + i32.add + local.set $l2641 + local.get $l2640 + local.get $l2641 + i32.add + local.set $l2642 + local.get $l2641 + local.get $l2642 + i32.add + local.set $l2643 + local.get $l2642 + local.get $l2643 + i32.add + local.set $l2644 + local.get $l2643 + local.get $l2644 + i32.add + local.set $l2645 + local.get $l2644 + local.get $l2645 + i32.add + local.set $l2646 + local.get $l2645 + local.get $l2646 + i32.add + local.set $l2647 + local.get $l2646 + local.get $l2647 + i32.add + local.set $l2648 + local.get $l2647 + local.get $l2648 + i32.add + local.set $l2649 + local.get $l2648 + local.get $l2649 + i32.add + local.set $l2650 + local.get $l2649 + local.get $l2650 + i32.add + local.set $l2651 + local.get $l2650 + local.get $l2651 + i32.add + local.set $l2652 + local.get $l2651 + local.get $l2652 + i32.add + local.set $l2653 + local.get $l2652 + local.get $l2653 + i32.add + local.set $l2654 + local.get $l2653 + local.get $l2654 + i32.add + local.set $l2655 + local.get $l2654 + local.get $l2655 + i32.add + local.set $l2656 + local.get $l2655 + local.get $l2656 + i32.add + local.set $l2657 + local.get $l2656 + local.get $l2657 + i32.add + local.set $l2658 + local.get $l2657 + local.get $l2658 + i32.add + local.set $l2659 + local.get $l2658 + local.get $l2659 + i32.add + local.set $l2660 + local.get $l2659 + local.get $l2660 + i32.add + local.set $l2661 + local.get $l2660 + local.get $l2661 + i32.add + local.set $l2662 + local.get $l2661 + local.get $l2662 + i32.add + local.set $l2663 + local.get $l2662 + local.get $l2663 + i32.add + local.set $l2664 + local.get $l2663 + local.get $l2664 + i32.add + local.set $l2665 + local.get $l2664 + local.get $l2665 + i32.add + local.set $l2666 + local.get $l2665 + local.get $l2666 + i32.add + local.set $l2667 + local.get $l2666 + local.get $l2667 + i32.add + local.set $l2668 + local.get $l2667 + local.get $l2668 + i32.add + local.set $l2669 + local.get $l2668 + local.get $l2669 + i32.add + local.set $l2670 + local.get $l2669 + local.get $l2670 + i32.add + local.set $l2671 + local.get $l2670 + local.get $l2671 + i32.add + local.set $l2672 + local.get $l2671 + local.get $l2672 + i32.add + local.set $l2673 + local.get $l2672 + local.get $l2673 + i32.add + local.set $l2674 + local.get $l2673 + local.get $l2674 + i32.add + local.set $l2675 + local.get $l2674 + local.get $l2675 + i32.add + local.set $l2676 + local.get $l2675 + local.get $l2676 + i32.add + local.set $l2677 + local.get $l2676 + local.get $l2677 + i32.add + local.set $l2678 + local.get $l2677 + local.get $l2678 + i32.add + local.set $l2679 + local.get $l2678 + local.get $l2679 + i32.add + local.set $l2680 + local.get $l2679 + local.get $l2680 + i32.add + local.set $l2681 + local.get $l2680 + local.get $l2681 + i32.add + local.set $l2682 + local.get $l2681 + local.get $l2682 + i32.add + local.set $l2683 + local.get $l2682 + local.get $l2683 + i32.add + local.set $l2684 + local.get $l2683 + local.get $l2684 + i32.add + local.set $l2685 + local.get $l2684 + local.get $l2685 + i32.add + local.set $l2686 + local.get $l2685 + local.get $l2686 + i32.add + local.set $l2687 + local.get $l2686 + local.get $l2687 + i32.add + local.set $l2688 + local.get $l2687 + local.get $l2688 + i32.add + local.set $l2689 + local.get $l2688 + local.get $l2689 + i32.add + local.set $l2690 + local.get $l2689 + local.get $l2690 + i32.add + local.set $l2691 + local.get $l2690 + local.get $l2691 + i32.add + local.set $l2692 + local.get $l2691 + local.get $l2692 + i32.add + local.set $l2693 + local.get $l2692 + local.get $l2693 + i32.add + local.set $l2694 + local.get $l2693 + local.get $l2694 + i32.add + local.set $l2695 + local.get $l2694 + local.get $l2695 + i32.add + local.set $l2696 + local.get $l2695 + local.get $l2696 + i32.add + local.set $l2697 + local.get $l2696 + local.get $l2697 + i32.add + local.set $l2698 + local.get $l2697 + local.get $l2698 + i32.add + local.set $l2699 + local.get $l2698 + local.get $l2699 + i32.add + local.set $l2700 + local.get $l2699 + local.get $l2700 + i32.add + local.set $l2701 + local.get $l2700 + local.get $l2701 + i32.add + local.set $l2702 + local.get $l2701 + local.get $l2702 + i32.add + local.set $l2703 + local.get $l2702 + local.get $l2703 + i32.add + local.set $l2704 + local.get $l2703 + local.get $l2704 + i32.add + local.set $l2705 + local.get $l2704 + local.get $l2705 + i32.add + local.set $l2706 + local.get $l2705 + local.get $l2706 + i32.add + local.set $l2707 + local.get $l2706 + local.get $l2707 + i32.add + local.set $l2708 + local.get $l2707 + local.get $l2708 + i32.add + local.set $l2709 + local.get $l2708 + local.get $l2709 + i32.add + local.set $l2710 + local.get $l2709 + local.get $l2710 + i32.add + local.set $l2711 + local.get $l2710 + local.get $l2711 + i32.add + local.set $l2712 + local.get $l2711 + local.get $l2712 + i32.add + local.set $l2713 + local.get $l2712 + local.get $l2713 + i32.add + local.set $l2714 + local.get $l2713 + local.get $l2714 + i32.add + local.set $l2715 + local.get $l2714 + local.get $l2715 + i32.add + local.set $l2716 + local.get $l2715 + local.get $l2716 + i32.add + local.set $l2717 + local.get $l2716 + local.get $l2717 + i32.add + local.set $l2718 + local.get $l2717 + local.get $l2718 + i32.add + local.set $l2719 + local.get $l2718 + local.get $l2719 + i32.add + local.set $l2720 + local.get $l2719 + local.get $l2720 + i32.add + local.set $l2721 + local.get $l2720 + local.get $l2721 + i32.add + local.set $l2722 + local.get $l2721 + local.get $l2722 + i32.add + local.set $l2723 + local.get $l2722 + local.get $l2723 + i32.add + local.set $l2724 + local.get $l2723 + local.get $l2724 + i32.add + local.set $l2725 + local.get $l2724 + local.get $l2725 + i32.add + local.set $l2726 + local.get $l2725 + local.get $l2726 + i32.add + local.set $l2727 + local.get $l2726 + local.get $l2727 + i32.add + local.set $l2728 + local.get $l2727 + local.get $l2728 + i32.add + local.set $l2729 + local.get $l2728 + local.get $l2729 + i32.add + local.set $l2730 + local.get $l2729 + local.get $l2730 + i32.add + local.set $l2731 + local.get $l2730 + local.get $l2731 + i32.add + local.set $l2732 + local.get $l2731 + local.get $l2732 + i32.add + local.set $l2733 + local.get $l2732 + local.get $l2733 + i32.add + local.set $l2734 + local.get $l2733 + local.get $l2734 + i32.add + local.set $l2735 + local.get $l2734 + local.get $l2735 + i32.add + local.set $l2736 + local.get $l2735 + local.get $l2736 + i32.add + local.set $l2737 + local.get $l2736 + local.get $l2737 + i32.add + local.set $l2738 + local.get $l2737 + local.get $l2738 + i32.add + local.set $l2739 + local.get $l2738 + local.get $l2739 + i32.add + local.set $l2740 + local.get $l2739 + local.get $l2740 + i32.add + local.set $l2741 + local.get $l2740 + local.get $l2741 + i32.add + local.set $l2742 + local.get $l2741 + local.get $l2742 + i32.add + local.set $l2743 + local.get $l2742 + local.get $l2743 + i32.add + local.set $l2744 + local.get $l2743 + local.get $l2744 + i32.add + local.set $l2745 + local.get $l2744 + local.get $l2745 + i32.add + local.set $l2746 + local.get $l2745 + local.get $l2746 + i32.add + local.set $l2747 + local.get $l2746 + local.get $l2747 + i32.add + local.set $l2748 + local.get $l2747 + local.get $l2748 + i32.add + local.set $l2749 + local.get $l2748 + local.get $l2749 + i32.add + local.set $l2750 + local.get $l2749 + local.get $l2750 + i32.add + local.set $l2751 + local.get $l2750 + local.get $l2751 + i32.add + local.set $l2752 + local.get $l2751 + local.get $l2752 + i32.add + local.set $l2753 + local.get $l2752 + local.get $l2753 + i32.add + local.set $l2754 + local.get $l2753 + local.get $l2754 + i32.add + local.set $l2755 + local.get $l2754 + local.get $l2755 + i32.add + local.set $l2756 + local.get $l2755 + local.get $l2756 + i32.add + local.set $l2757 + local.get $l2756 + local.get $l2757 + i32.add + local.set $l2758 + local.get $l2757 + local.get $l2758 + i32.add + local.set $l2759 + local.get $l2758 + local.get $l2759 + i32.add + local.set $l2760 + local.get $l2759 + local.get $l2760 + i32.add + local.set $l2761 + local.get $l2760 + local.get $l2761 + i32.add + local.set $l2762 + local.get $l2761 + local.get $l2762 + i32.add + local.set $l2763 + local.get $l2762 + local.get $l2763 + i32.add + local.set $l2764 + local.get $l2763 + local.get $l2764 + i32.add + local.set $l2765 + local.get $l2764 + local.get $l2765 + i32.add + local.set $l2766 + local.get $l2765 + local.get $l2766 + i32.add + local.set $l2767 + local.get $l2766 + local.get $l2767 + i32.add + local.set $l2768 + local.get $l2767 + local.get $l2768 + i32.add + local.set $l2769 + local.get $l2768 + local.get $l2769 + i32.add + local.set $l2770 + local.get $l2769 + local.get $l2770 + i32.add + local.set $l2771 + local.get $l2770 + local.get $l2771 + i32.add + local.set $l2772 + local.get $l2771 + local.get $l2772 + i32.add + local.set $l2773 + local.get $l2772 + local.get $l2773 + i32.add + local.set $l2774 + local.get $l2773 + local.get $l2774 + i32.add + local.set $l2775 + local.get $l2774 + local.get $l2775 + i32.add + local.set $l2776 + local.get $l2775 + local.get $l2776 + i32.add + local.set $l2777 + local.get $l2776 + local.get $l2777 + i32.add + local.set $l2778 + local.get $l2777 + local.get $l2778 + i32.add + local.set $l2779 + local.get $l2778 + local.get $l2779 + i32.add + local.set $l2780 + local.get $l2779 + local.get $l2780 + i32.add + local.set $l2781 + local.get $l2780 + local.get $l2781 + i32.add + local.set $l2782 + local.get $l2781 + local.get $l2782 + i32.add + local.set $l2783 + local.get $l2782 + local.get $l2783 + i32.add + local.set $l2784 + local.get $l2783 + local.get $l2784 + i32.add + local.set $l2785 + local.get $l2784 + local.get $l2785 + i32.add + local.set $l2786 + local.get $l2785 + local.get $l2786 + i32.add + local.set $l2787 + local.get $l2786 + local.get $l2787 + i32.add + local.set $l2788 + local.get $l2787 + local.get $l2788 + i32.add + local.set $l2789 + local.get $l2788 + local.get $l2789 + i32.add + local.set $l2790 + local.get $l2789 + local.get $l2790 + i32.add + local.set $l2791 + local.get $l2790 + local.get $l2791 + i32.add + local.set $l2792 + local.get $l2791 + local.get $l2792 + i32.add + local.set $l2793 + local.get $l2792 + local.get $l2793 + i32.add + local.set $l2794 + local.get $l2793 + local.get $l2794 + i32.add + local.set $l2795 + local.get $l2794 + local.get $l2795 + i32.add + local.set $l2796 + local.get $l2795 + local.get $l2796 + i32.add + local.set $l2797 + local.get $l2796 + local.get $l2797 + i32.add + local.set $l2798 + local.get $l2797 + local.get $l2798 + i32.add + local.set $l2799 + local.get $l2798 + local.get $l2799 + i32.add + local.set $l2800 + local.get $l2799 + local.get $l2800 + i32.add + local.set $l2801 + local.get $l2800 + local.get $l2801 + i32.add + local.set $l2802 + local.get $l2801 + local.get $l2802 + i32.add + local.set $l2803 + local.get $l2802 + local.get $l2803 + i32.add + local.set $l2804 + local.get $l2803 + local.get $l2804 + i32.add + local.set $l2805 + local.get $l2804 + local.get $l2805 + i32.add + local.set $l2806 + local.get $l2805 + local.get $l2806 + i32.add + local.set $l2807 + local.get $l2806 + local.get $l2807 + i32.add + local.set $l2808 + local.get $l2807 + local.get $l2808 + i32.add + local.set $l2809 + local.get $l2808 + local.get $l2809 + i32.add + local.set $l2810 + local.get $l2809 + local.get $l2810 + i32.add + local.set $l2811 + local.get $l2810 + local.get $l2811 + i32.add + local.set $l2812 + local.get $l2811 + local.get $l2812 + i32.add + local.set $l2813 + local.get $l2812 + local.get $l2813 + i32.add + local.set $l2814 + local.get $l2813 + local.get $l2814 + i32.add + local.set $l2815 + local.get $l2814 + local.get $l2815 + i32.add + local.set $l2816 + local.get $l2815 + local.get $l2816 + i32.add + local.set $l2817 + local.get $l2816 + local.get $l2817 + i32.add + local.set $l2818 + local.get $l2817 + local.get $l2818 + i32.add + local.set $l2819 + local.get $l2818 + local.get $l2819 + i32.add + local.set $l2820 + local.get $l2819 + local.get $l2820 + i32.add + local.set $l2821 + local.get $l2820 + local.get $l2821 + i32.add + local.set $l2822 + local.get $l2821 + local.get $l2822 + i32.add + local.set $l2823 + local.get $l2822 + local.get $l2823 + i32.add + local.set $l2824 + local.get $l2823 + local.get $l2824 + i32.add + local.set $l2825 + local.get $l2824 + local.get $l2825 + i32.add + local.set $l2826 + local.get $l2825 + local.get $l2826 + i32.add + local.set $l2827 + local.get $l2826 + local.get $l2827 + i32.add + local.set $l2828 + local.get $l2827 + local.get $l2828 + i32.add + local.set $l2829 + local.get $l2828 + local.get $l2829 + i32.add + local.set $l2830 + local.get $l2829 + local.get $l2830 + i32.add + local.set $l2831 + local.get $l2830 + local.get $l2831 + i32.add + local.set $l2832 + local.get $l2831 + local.get $l2832 + i32.add + local.set $l2833 + local.get $l2832 + local.get $l2833 + i32.add + local.set $l2834 + local.get $l2833 + local.get $l2834 + i32.add + local.set $l2835 + local.get $l2834 + local.get $l2835 + i32.add + local.set $l2836 + local.get $l2835 + local.get $l2836 + i32.add + local.set $l2837 + local.get $l2836 + local.get $l2837 + i32.add + local.set $l2838 + local.get $l2837 + local.get $l2838 + i32.add + local.set $l2839 + local.get $l2838 + local.get $l2839 + i32.add + local.set $l2840 + local.get $l2839 + local.get $l2840 + i32.add + local.set $l2841 + local.get $l2840 + local.get $l2841 + i32.add + local.set $l2842 + local.get $l2841 + local.get $l2842 + i32.add + local.set $l2843 + local.get $l2842 + local.get $l2843 + i32.add + local.set $l2844 + local.get $l2843 + local.get $l2844 + i32.add + local.set $l2845 + local.get $l2844 + local.get $l2845 + i32.add + local.set $l2846 + local.get $l2845 + local.get $l2846 + i32.add + local.set $l2847 + local.get $l2846 + local.get $l2847 + i32.add + local.set $l2848 + local.get $l2847 + local.get $l2848 + i32.add + local.set $l2849 + local.get $l2848 + local.get $l2849 + i32.add + local.set $l2850 + local.get $l2849 + local.get $l2850 + i32.add + local.set $l2851 + local.get $l2850 + local.get $l2851 + i32.add + local.set $l2852 + local.get $l2851 + local.get $l2852 + i32.add + local.set $l2853 + local.get $l2852 + local.get $l2853 + i32.add + local.set $l2854 + local.get $l2853 + local.get $l2854 + i32.add + local.set $l2855 + local.get $l2854 + local.get $l2855 + i32.add + local.set $l2856 + local.get $l2855 + local.get $l2856 + i32.add + local.set $l2857 + local.get $l2856 + local.get $l2857 + i32.add + local.set $l2858 + local.get $l2857 + local.get $l2858 + i32.add + local.set $l2859 + local.get $l2858 + local.get $l2859 + i32.add + local.set $l2860 + local.get $l2859 + local.get $l2860 + i32.add + local.set $l2861 + local.get $l2860 + local.get $l2861 + i32.add + local.set $l2862 + local.get $l2861 + local.get $l2862 + i32.add + local.set $l2863 + local.get $l2862 + local.get $l2863 + i32.add + local.set $l2864 + local.get $l2863 + local.get $l2864 + i32.add + local.set $l2865 + local.get $l2864 + local.get $l2865 + i32.add + local.set $l2866 + local.get $l2865 + local.get $l2866 + i32.add + local.set $l2867 + local.get $l2866 + local.get $l2867 + i32.add + local.set $l2868 + local.get $l2867 + local.get $l2868 + i32.add + local.set $l2869 + local.get $l2868 + local.get $l2869 + i32.add + local.set $l2870 + local.get $l2869 + local.get $l2870 + i32.add + local.set $l2871 + local.get $l2870 + local.get $l2871 + i32.add + local.set $l2872 + local.get $l2871 + local.get $l2872 + i32.add + local.set $l2873 + local.get $l2872 + local.get $l2873 + i32.add + local.set $l2874 + local.get $l2873 + local.get $l2874 + i32.add + local.set $l2875 + local.get $l2874 + local.get $l2875 + i32.add + local.set $l2876 + local.get $l2875 + local.get $l2876 + i32.add + local.set $l2877 + local.get $l2876 + local.get $l2877 + i32.add + local.set $l2878 + local.get $l2877 + local.get $l2878 + i32.add + local.set $l2879 + local.get $l2878 + local.get $l2879 + i32.add + local.set $l2880 + local.get $l2879 + local.get $l2880 + i32.add + local.set $l2881 + local.get $l2880 + local.get $l2881 + i32.add + local.set $l2882 + local.get $l2881 + local.get $l2882 + i32.add + local.set $l2883 + local.get $l2882 + local.get $l2883 + i32.add + local.set $l2884 + local.get $l2883 + local.get $l2884 + i32.add + local.set $l2885 + local.get $l2884 + local.get $l2885 + i32.add + local.set $l2886 + local.get $l2885 + local.get $l2886 + i32.add + local.set $l2887 + local.get $l2886 + local.get $l2887 + i32.add + local.set $l2888 + local.get $l2887 + local.get $l2888 + i32.add + local.set $l2889 + local.get $l2888 + local.get $l2889 + i32.add + local.set $l2890 + local.get $l2889 + local.get $l2890 + i32.add + local.set $l2891 + local.get $l2890 + local.get $l2891 + i32.add + local.set $l2892 + local.get $l2891 + local.get $l2892 + i32.add + local.set $l2893 + local.get $l2892 + local.get $l2893 + i32.add + local.set $l2894 + local.get $l2893 + local.get $l2894 + i32.add + local.set $l2895 + local.get $l2894 + local.get $l2895 + i32.add + local.set $l2896 + local.get $l2895 + local.get $l2896 + i32.add + local.set $l2897 + local.get $l2896 + local.get $l2897 + i32.add + local.set $l2898 + local.get $l2897 + local.get $l2898 + i32.add + local.set $l2899 + local.get $l2898 + local.get $l2899 + i32.add + local.set $l2900 + local.get $l2899 + local.get $l2900 + i32.add + local.set $l2901 + local.get $l2900 + local.get $l2901 + i32.add + local.set $l2902 + local.get $l2901 + local.get $l2902 + i32.add + local.set $l2903 + local.get $l2902 + local.get $l2903 + i32.add + local.set $l2904 + local.get $l2903 + local.get $l2904 + i32.add + local.set $l2905 + local.get $l2904 + local.get $l2905 + i32.add + local.set $l2906 + local.get $l2905 + local.get $l2906 + i32.add + local.set $l2907 + local.get $l2906 + local.get $l2907 + i32.add + local.set $l2908 + local.get $l2907 + local.get $l2908 + i32.add + local.set $l2909 + local.get $l2908 + local.get $l2909 + i32.add + local.set $l2910 + local.get $l2909 + local.get $l2910 + i32.add + local.set $l2911 + local.get $l2910 + local.get $l2911 + i32.add + local.set $l2912 + local.get $l2911 + local.get $l2912 + i32.add + local.set $l2913 + local.get $l2912 + local.get $l2913 + i32.add + local.set $l2914 + local.get $l2913 + local.get $l2914 + i32.add + local.set $l2915 + local.get $l2914 + local.get $l2915 + i32.add + local.set $l2916 + local.get $l2915 + local.get $l2916 + i32.add + local.set $l2917 + local.get $l2916 + local.get $l2917 + i32.add + local.set $l2918 + local.get $l2917 + local.get $l2918 + i32.add + local.set $l2919 + local.get $l2918 + local.get $l2919 + i32.add + local.set $l2920 + local.get $l2919 + local.get $l2920 + i32.add + local.set $l2921 + local.get $l2920 + local.get $l2921 + i32.add + local.set $l2922 + local.get $l2921 + local.get $l2922 + i32.add + local.set $l2923 + local.get $l2922 + local.get $l2923 + i32.add + local.set $l2924 + local.get $l2923 + local.get $l2924 + i32.add + local.set $l2925 + local.get $l2924 + local.get $l2925 + i32.add + local.set $l2926 + local.get $l2925 + local.get $l2926 + i32.add + local.set $l2927 + local.get $l2926 + local.get $l2927 + i32.add + local.set $l2928 + local.get $l2927 + local.get $l2928 + i32.add + local.set $l2929 + local.get $l2928 + local.get $l2929 + i32.add + local.set $l2930 + local.get $l2929 + local.get $l2930 + i32.add + local.set $l2931 + local.get $l2930 + local.get $l2931 + i32.add + local.set $l2932 + local.get $l2931 + local.get $l2932 + i32.add + local.set $l2933 + local.get $l2932 + local.get $l2933 + i32.add + local.set $l2934 + local.get $l2933 + local.get $l2934 + i32.add + local.set $l2935 + local.get $l2934 + local.get $l2935 + i32.add + local.set $l2936 + local.get $l2935 + local.get $l2936 + i32.add + local.set $l2937 + local.get $l2936 + local.get $l2937 + i32.add + local.set $l2938 + local.get $l2937 + local.get $l2938 + i32.add + local.set $l2939 + local.get $l2938 + local.get $l2939 + i32.add + local.set $l2940 + local.get $l2939 + local.get $l2940 + i32.add + local.set $l2941 + local.get $l2940 + local.get $l2941 + i32.add + local.set $l2942 + local.get $l2941 + local.get $l2942 + i32.add + local.set $l2943 + local.get $l2942 + local.get $l2943 + i32.add + local.set $l2944 + local.get $l2943 + local.get $l2944 + i32.add + local.set $l2945 + local.get $l2944 + local.get $l2945 + i32.add + local.set $l2946 + local.get $l2945 + local.get $l2946 + i32.add + local.set $l2947 + local.get $l2946 + local.get $l2947 + i32.add + local.set $l2948 + local.get $l2947 + local.get $l2948 + i32.add + local.set $l2949 + local.get $l2948 + local.get $l2949 + i32.add + local.set $l2950 + local.get $l2949 + local.get $l2950 + i32.add + local.set $l2951 + local.get $l2950 + local.get $l2951 + i32.add + local.set $l2952 + local.get $l2951 + local.get $l2952 + i32.add + local.set $l2953 + local.get $l2952 + local.get $l2953 + i32.add + local.set $l2954 + local.get $l2953 + local.get $l2954 + i32.add + local.set $l2955 + local.get $l2954 + local.get $l2955 + i32.add + local.set $l2956 + local.get $l2955 + local.get $l2956 + i32.add + local.set $l2957 + local.get $l2956 + local.get $l2957 + i32.add + local.set $l2958 + local.get $l2957 + local.get $l2958 + i32.add + local.set $l2959 + local.get $l2958 + local.get $l2959 + i32.add + local.set $l2960 + local.get $l2959 + local.get $l2960 + i32.add + local.set $l2961 + local.get $l2960 + local.get $l2961 + i32.add + local.set $l2962 + local.get $l2961 + local.get $l2962 + i32.add + local.set $l2963 + local.get $l2962 + local.get $l2963 + i32.add + local.set $l2964 + local.get $l2963 + local.get $l2964 + i32.add + local.set $l2965 + local.get $l2964 + local.get $l2965 + i32.add + local.set $l2966 + local.get $l2965 + local.get $l2966 + i32.add + local.set $l2967 + local.get $l2966 + local.get $l2967 + i32.add + local.set $l2968 + local.get $l2967 + local.get $l2968 + i32.add + local.set $l2969 + local.get $l2968 + local.get $l2969 + i32.add + local.set $l2970 + local.get $l2969 + local.get $l2970 + i32.add + local.set $l2971 + local.get $l2970 + local.get $l2971 + i32.add + local.set $l2972 + local.get $l2971 + local.get $l2972 + i32.add + local.set $l2973 + local.get $l2972 + local.get $l2973 + i32.add + local.set $l2974 + local.get $l2973 + local.get $l2974 + i32.add + local.set $l2975 + local.get $l2974 + local.get $l2975 + i32.add + local.set $l2976 + local.get $l2975 + local.get $l2976 + i32.add + local.set $l2977 + local.get $l2976 + local.get $l2977 + i32.add + local.set $l2978 + local.get $l2977 + local.get $l2978 + i32.add + local.set $l2979 + local.get $l2978 + local.get $l2979 + i32.add + local.set $l2980 + local.get $l2979 + local.get $l2980 + i32.add + local.set $l2981 + local.get $l2980 + local.get $l2981 + i32.add + local.set $l2982 + local.get $l2981 + local.get $l2982 + i32.add + local.set $l2983 + local.get $l2982 + local.get $l2983 + i32.add + local.set $l2984 + local.get $l2983 + local.get $l2984 + i32.add + local.set $l2985 + local.get $l2984 + local.get $l2985 + i32.add + local.set $l2986 + local.get $l2985 + local.get $l2986 + i32.add + local.set $l2987 + local.get $l2986 + local.get $l2987 + i32.add + local.set $l2988 + local.get $l2987 + local.get $l2988 + i32.add + local.set $l2989 + local.get $l2988 + local.get $l2989 + i32.add + local.set $l2990 + local.get $l2989 + local.get $l2990 + i32.add + local.set $l2991 + local.get $l2990 + local.get $l2991 + i32.add + local.set $l2992 + local.get $l2991 + local.get $l2992 + i32.add + local.set $l2993 + local.get $l2992 + local.get $l2993 + i32.add + local.set $l2994 + local.get $l2993 + local.get $l2994 + i32.add + local.set $l2995 + local.get $l2994 + local.get $l2995 + i32.add + local.set $l2996 + local.get $l2995 + local.get $l2996 + i32.add + local.set $l2997 + local.get $l2996 + local.get $l2997 + i32.add + local.set $l2998 + local.get $l2997 + local.get $l2998 + i32.add + local.set $l2999 + local.get $l2998 + local.get $l2999 + i32.add + local.set $l3000 + local.get $l2999 + local.get $l3000 + i32.add + local.set $l3001 + local.get $l3000 + local.get $l3001 + i32.add + local.set $l3002 + local.get $l3001 + local.get $l3002 + i32.add + local.set $l3003 + local.get $l3002 + local.get $l3003 + i32.add + local.set $l3004 + local.get $l3003 + local.get $l3004 + i32.add + local.set $l3005 + local.get $l3004 + local.get $l3005 + i32.add + local.set $l3006 + local.get $l3005 + local.get $l3006 + i32.add + local.set $l3007 + local.get $l3006 + local.get $l3007 + i32.add + local.set $l3008 + local.get $l3007 + local.get $l3008 + i32.add + local.set $l3009 + local.get $l3008 + local.get $l3009 + i32.add + local.set $l3010 + local.get $l3009 + local.get $l3010 + i32.add + local.set $l3011 + local.get $l3010 + local.get $l3011 + i32.add + local.set $l3012 + local.get $l3011 + local.get $l3012 + i32.add + local.set $l3013 + local.get $l3012 + local.get $l3013 + i32.add + local.set $l3014 + local.get $l3013 + local.get $l3014 + i32.add + local.set $l3015 + local.get $l3014 + local.get $l3015 + i32.add + local.set $l3016 + local.get $l3015 + local.get $l3016 + i32.add + local.set $l3017 + local.get $l3016 + local.get $l3017 + i32.add + local.set $l3018 + local.get $l3017 + local.get $l3018 + i32.add + local.set $l3019 + local.get $l3018 + local.get $l3019 + i32.add + local.set $l3020 + local.get $l3019 + local.get $l3020 + i32.add + local.set $l3021 + local.get $l3020 + local.get $l3021 + i32.add + local.set $l3022 + local.get $l3021 + local.get $l3022 + i32.add + local.set $l3023 + local.get $l3022 + local.get $l3023 + i32.add + local.set $l3024 + local.get $l3023 + local.get $l3024 + i32.add + local.set $l3025 + local.get $l3024 + local.get $l3025 + i32.add + local.set $l3026 + local.get $l3025 + local.get $l3026 + i32.add + local.set $l3027 + local.get $l3026 + local.get $l3027 + i32.add + local.set $l3028 + local.get $l3027 + local.get $l3028 + i32.add + local.set $l3029 + local.get $l3028 + local.get $l3029 + i32.add + local.set $l3030 + local.get $l3029 + local.get $l3030 + i32.add + local.set $l3031 + local.get $l3030 + local.get $l3031 + i32.add + local.set $l3032 + local.get $l3031 + local.get $l3032 + i32.add + local.set $l3033 + local.get $l3032 + local.get $l3033 + i32.add + local.set $l3034 + local.get $l3033 + local.get $l3034 + i32.add + local.set $l3035 + local.get $l3034 + local.get $l3035 + i32.add + local.set $l3036 + local.get $l3035 + local.get $l3036 + i32.add + local.set $l3037 + local.get $l3036 + local.get $l3037 + i32.add + local.set $l3038 + local.get $l3037 + local.get $l3038 + i32.add + local.set $l3039 + local.get $l3038 + local.get $l3039 + i32.add + local.set $l3040 + local.get $l3039 + local.get $l3040 + i32.add + local.set $l3041 + local.get $l3040 + local.get $l3041 + i32.add + local.set $l3042 + local.get $l3041 + local.get $l3042 + i32.add + local.set $l3043 + local.get $l3042 + local.get $l3043 + i32.add + local.set $l3044 + local.get $l3043 + local.get $l3044 + i32.add + local.set $l3045 + local.get $l3044 + local.get $l3045 + i32.add + local.set $l3046 + local.get $l3045 + local.get $l3046 + i32.add + local.set $l3047 + local.get $l3046 + local.get $l3047 + i32.add + local.set $l3048 + local.get $l3047 + local.get $l3048 + i32.add + local.set $l3049 + local.get $l3048 + local.get $l3049 + i32.add + local.set $l3050 + local.get $l3049 + local.get $l3050 + i32.add + local.set $l3051 + local.get $l3050 + local.get $l3051 + i32.add + local.set $l3052 + local.get $l3051 + local.get $l3052 + i32.add + local.set $l3053 + local.get $l3052 + local.get $l3053 + i32.add + local.set $l3054 + local.get $l3053 + local.get $l3054 + i32.add + local.set $l3055 + local.get $l3054 + local.get $l3055 + i32.add + local.set $l3056 + local.get $l3055 + local.get $l3056 + i32.add + local.set $l3057 + local.get $l3056 + local.get $l3057 + i32.add + local.set $l3058 + local.get $l3057 + local.get $l3058 + i32.add + local.set $l3059 + local.get $l3058 + local.get $l3059 + i32.add + local.set $l3060 + local.get $l3059 + local.get $l3060 + i32.add + local.set $l3061 + local.get $l3060 + local.get $l3061 + i32.add + local.set $l3062 + local.get $l3061 + local.get $l3062 + i32.add + local.set $l3063 + local.get $l3062 + local.get $l3063 + i32.add + local.set $l3064 + local.get $l3063 + local.get $l3064 + i32.add + local.set $l3065 + local.get $l3064 + local.get $l3065 + i32.add + local.set $l3066 + local.get $l3065 + local.get $l3066 + i32.add + local.set $l3067 + local.get $l3066 + local.get $l3067 + i32.add + local.set $l3068 + local.get $l3067 + local.get $l3068 + i32.add + local.set $l3069 + local.get $l3068 + local.get $l3069 + i32.add + local.set $l3070 + local.get $l3069 + local.get $l3070 + i32.add + local.set $l3071 + local.get $l3070 + local.get $l3071 + i32.add + local.set $l3072 + local.get $l3071 + local.get $l3072 + i32.add + local.set $l3073 + local.get $l3072 + local.get $l3073 + i32.add + local.set $l3074 + local.get $l3073 + local.get $l3074 + i32.add + local.set $l3075 + local.get $l3074 + local.get $l3075 + i32.add + local.set $l3076 + local.get $l3075 + local.get $l3076 + i32.add + local.set $l3077 + local.get $l3076 + local.get $l3077 + i32.add + local.set $l3078 + local.get $l3077 + local.get $l3078 + i32.add + local.set $l3079 + local.get $l3078 + local.get $l3079 + i32.add + local.set $l3080 + local.get $l3079 + local.get $l3080 + i32.add + local.set $l3081 + local.get $l3080 + local.get $l3081 + i32.add + local.set $l3082 + local.get $l3081 + local.get $l3082 + i32.add + local.set $l3083 + local.get $l3082 + local.get $l3083 + i32.add + local.set $l3084 + local.get $l3083 + local.get $l3084 + i32.add + local.set $l3085 + local.get $l3084 + local.get $l3085 + i32.add + local.set $l3086 + local.get $l3085 + local.get $l3086 + i32.add + local.set $l3087 + local.get $l3086 + local.get $l3087 + i32.add + local.set $l3088 + local.get $l3087 + local.get $l3088 + i32.add + local.set $l3089 + local.get $l3088 + local.get $l3089 + i32.add + local.set $l3090 + local.get $l3089 + local.get $l3090 + i32.add + local.set $l3091 + local.get $l3090 + local.get $l3091 + i32.add + local.set $l3092 + local.get $l3091 + local.get $l3092 + i32.add + local.set $l3093 + local.get $l3092 + local.get $l3093 + i32.add + local.set $l3094 + local.get $l3093 + local.get $l3094 + i32.add + local.set $l3095 + local.get $l3094 + local.get $l3095 + i32.add + local.set $l3096 + local.get $l3095 + local.get $l3096 + i32.add + local.set $l3097 + local.get $l3096 + local.get $l3097 + i32.add + local.set $l3098 + local.get $l3097 + local.get $l3098 + i32.add + local.set $l3099 + local.get $l3098 + local.get $l3099 + i32.add + local.set $l3100 + local.get $l3099 + local.get $l3100 + i32.add + local.set $l3101 + local.get $l3100 + local.get $l3101 + i32.add + local.set $l3102 + local.get $l3101 + local.get $l3102 + i32.add + local.set $l3103 + local.get $l3102 + local.get $l3103 + i32.add + local.set $l3104 + local.get $l3103 + local.get $l3104 + i32.add + local.set $l3105 + local.get $l3104 + local.get $l3105 + i32.add + local.set $l3106 + local.get $l3105 + local.get $l3106 + i32.add + local.set $l3107 + local.get $l3106 + local.get $l3107 + i32.add + local.set $l3108 + local.get $l3107 + local.get $l3108 + i32.add + local.set $l3109 + local.get $l3108 + local.get $l3109 + i32.add + local.set $l3110 + local.get $l3109 + local.get $l3110 + i32.add + local.set $l3111 + local.get $l3110 + local.get $l3111 + i32.add + local.set $l3112 + local.get $l3111 + local.get $l3112 + i32.add + local.set $l3113 + local.get $l3112 + local.get $l3113 + i32.add + local.set $l3114 + local.get $l3113 + local.get $l3114 + i32.add + local.set $l3115 + local.get $l3114 + local.get $l3115 + i32.add + local.set $l3116 + local.get $l3115 + local.get $l3116 + i32.add + local.set $l3117 + local.get $l3116 + local.get $l3117 + i32.add + local.set $l3118 + local.get $l3117 + local.get $l3118 + i32.add + local.set $l3119 + local.get $l3118 + local.get $l3119 + i32.add + local.set $l3120 + local.get $l3119 + local.get $l3120 + i32.add + local.set $l3121 + local.get $l3120 + local.get $l3121 + i32.add + local.set $l3122 + local.get $l3121 + local.get $l3122 + i32.add + local.set $l3123 + local.get $l3122 + local.get $l3123 + i32.add + local.set $l3124 + local.get $l3123 + local.get $l3124 + i32.add + local.set $l3125 + local.get $l3124 + local.get $l3125 + i32.add + local.set $l3126 + local.get $l3125 + local.get $l3126 + i32.add + local.set $l3127 + local.get $l3126 + local.get $l3127 + i32.add + local.set $l3128 + local.get $l3127 + local.get $l3128 + i32.add + local.set $l3129 + local.get $l3128 + local.get $l3129 + i32.add + local.set $l3130 + local.get $l3129 + local.get $l3130 + i32.add + local.set $l3131 + local.get $l3130 + local.get $l3131 + i32.add + local.set $l3132 + local.get $l3131 + local.get $l3132 + i32.add + local.set $l3133 + local.get $l3132 + local.get $l3133 + i32.add + local.set $l3134 + local.get $l3133 + local.get $l3134 + i32.add + local.set $l3135 + local.get $l3134 + local.get $l3135 + i32.add + local.set $l3136 + local.get $l3135 + local.get $l3136 + i32.add + local.set $l3137 + local.get $l3136 + local.get $l3137 + i32.add + local.set $l3138 + local.get $l3137 + local.get $l3138 + i32.add + local.set $l3139 + local.get $l3138 + local.get $l3139 + i32.add + local.set $l3140 + local.get $l3139 + local.get $l3140 + i32.add + local.set $l3141 + local.get $l3140 + local.get $l3141 + i32.add + local.set $l3142 + local.get $l3141 + local.get $l3142 + i32.add + local.set $l3143 + local.get $l3142 + local.get $l3143 + i32.add + local.set $l3144 + local.get $l3143 + local.get $l3144 + i32.add + local.set $l3145 + local.get $l3144 + local.get $l3145 + i32.add + local.set $l3146 + local.get $l3145 + local.get $l3146 + i32.add + local.set $l3147 + local.get $l3146 + local.get $l3147 + i32.add + local.set $l3148 + local.get $l3147 + local.get $l3148 + i32.add + local.set $l3149 + local.get $l3148 + local.get $l3149 + i32.add + local.set $l3150 + local.get $l3149 + local.get $l3150 + i32.add + local.set $l3151 + local.get $l3150 + local.get $l3151 + i32.add + local.set $l3152 + local.get $l3151 + local.get $l3152 + i32.add + local.set $l3153 + local.get $l3152 + local.get $l3153 + i32.add + local.set $l3154 + local.get $l3153 + local.get $l3154 + i32.add + local.set $l3155 + local.get $l3154 + local.get $l3155 + i32.add + local.set $l3156 + local.get $l3155 + local.get $l3156 + i32.add + local.set $l3157 + local.get $l3156 + local.get $l3157 + i32.add + local.set $l3158 + local.get $l3157 + local.get $l3158 + i32.add + local.set $l3159 + local.get $l3158 + local.get $l3159 + i32.add + local.set $l3160 + local.get $l3159 + local.get $l3160 + i32.add + local.set $l3161 + local.get $l3160 + local.get $l3161 + i32.add + local.set $l3162 + local.get $l3161 + local.get $l3162 + i32.add + local.set $l3163 + local.get $l3162 + local.get $l3163 + i32.add + local.set $l3164 + local.get $l3163 + local.get $l3164 + i32.add + local.set $l3165 + local.get $l3164 + local.get $l3165 + i32.add + local.set $l3166 + local.get $l3165 + local.get $l3166 + i32.add + local.set $l3167 + local.get $l3166 + local.get $l3167 + i32.add + local.set $l3168 + local.get $l3167 + local.get $l3168 + i32.add + local.set $l3169 + local.get $l3168 + local.get $l3169 + i32.add + local.set $l3170 + local.get $l3169 + local.get $l3170 + i32.add + local.set $l3171 + local.get $l3170 + local.get $l3171 + i32.add + local.set $l3172 + local.get $l3171 + local.get $l3172 + i32.add + local.set $l3173 + local.get $l3172 + local.get $l3173 + i32.add + local.set $l3174 + local.get $l3173 + local.get $l3174 + i32.add + local.set $l3175 + local.get $l3174 + local.get $l3175 + i32.add + local.set $l3176 + local.get $l3175 + local.get $l3176 + i32.add + local.set $l3177 + local.get $l3176 + local.get $l3177 + i32.add + local.set $l3178 + local.get $l3177 + local.get $l3178 + i32.add + local.set $l3179 + local.get $l3178 + local.get $l3179 + i32.add + local.set $l3180 + local.get $l3179 + local.get $l3180 + i32.add + local.set $l3181 + local.get $l3180 + local.get $l3181 + i32.add + local.set $l3182 + local.get $l3181 + local.get $l3182 + i32.add + local.set $l3183 + local.get $l3182 + local.get $l3183 + i32.add + local.set $l3184 + local.get $l3183 + local.get $l3184 + i32.add + local.set $l3185 + local.get $l3184 + local.get $l3185 + i32.add + local.set $l3186 + local.get $l3185 + local.get $l3186 + i32.add + local.set $l3187 + local.get $l3186 + local.get $l3187 + i32.add + local.set $l3188 + local.get $l3187 + local.get $l3188 + i32.add + local.set $l3189 + local.get $l3188 + local.get $l3189 + i32.add + local.set $l3190 + local.get $l3189 + local.get $l3190 + i32.add + local.set $l3191 + local.get $l3190 + local.get $l3191 + i32.add + local.set $l3192 + local.get $l3191 + local.get $l3192 + i32.add + local.set $l3193 + local.get $l3192 + local.get $l3193 + i32.add + local.set $l3194 + local.get $l3193 + local.get $l3194 + i32.add + local.set $l3195 + local.get $l3194 + local.get $l3195 + i32.add + local.set $l3196 + local.get $l3195 + local.get $l3196 + i32.add + local.set $l3197 + local.get $l3196 + local.get $l3197 + i32.add + local.set $l3198 + local.get $l3197 + local.get $l3198 + i32.add + local.set $l3199 + local.get $l3198 + local.get $l3199 + i32.add + local.set $l3200 + local.get $l3199 + local.get $l3200 + i32.add + local.set $l3201 + local.get $l3200 + local.get $l3201 + i32.add + local.set $l3202 + local.get $l3201 + local.get $l3202 + i32.add + local.set $l3203 + local.get $l3202 + local.get $l3203 + i32.add + local.set $l3204 + local.get $l3203 + local.get $l3204 + i32.add + local.set $l3205 + local.get $l3204 + local.get $l3205 + i32.add + local.set $l3206 + local.get $l3205 + local.get $l3206 + i32.add + local.set $l3207 + local.get $l3206 + local.get $l3207 + i32.add + local.set $l3208 + local.get $l3207 + local.get $l3208 + i32.add + local.set $l3209 + local.get $l3208 + local.get $l3209 + i32.add + local.set $l3210 + local.get $l3209 + local.get $l3210 + i32.add + local.set $l3211 + local.get $l3210 + local.get $l3211 + i32.add + local.set $l3212 + local.get $l3211 + local.get $l3212 + i32.add + local.set $l3213 + local.get $l3212 + local.get $l3213 + i32.add + local.set $l3214 + local.get $l3213 + local.get $l3214 + i32.add + local.set $l3215 + local.get $l3214 + local.get $l3215 + i32.add + local.set $l3216 + local.get $l3215 + local.get $l3216 + i32.add + local.set $l3217 + local.get $l3216 + local.get $l3217 + i32.add + local.set $l3218 + local.get $l3217 + local.get $l3218 + i32.add + local.set $l3219 + local.get $l3218 + local.get $l3219 + i32.add + local.set $l3220 + local.get $l3219 + local.get $l3220 + i32.add + local.set $l3221 + local.get $l3220 + local.get $l3221 + i32.add + local.set $l3222 + local.get $l3221 + local.get $l3222 + i32.add + local.set $l3223 + local.get $l3222 + local.get $l3223 + i32.add + local.set $l3224 + local.get $l3223 + local.get $l3224 + i32.add + local.set $l3225 + local.get $l3224 + local.get $l3225 + i32.add + local.set $l3226 + local.get $l3225 + local.get $l3226 + i32.add + local.set $l3227 + local.get $l3226 + local.get $l3227 + i32.add + local.set $l3228 + local.get $l3227 + local.get $l3228 + i32.add + local.set $l3229 + local.get $l3228 + local.get $l3229 + i32.add + local.set $l3230 + local.get $l3229 + local.get $l3230 + i32.add + local.set $l3231 + local.get $l3230 + local.get $l3231 + i32.add + local.set $l3232 + local.get $l3231 + local.get $l3232 + i32.add + local.set $l3233 + local.get $l3232 + local.get $l3233 + i32.add + local.set $l3234 + local.get $l3233 + local.get $l3234 + i32.add + local.set $l3235 + local.get $l3234 + local.get $l3235 + i32.add + local.set $l3236 + local.get $l3235 + local.get $l3236 + i32.add + local.set $l3237 + local.get $l3236 + local.get $l3237 + i32.add + local.set $l3238 + local.get $l3237 + local.get $l3238 + i32.add + local.set $l3239 + local.get $l3238 + local.get $l3239 + i32.add + local.set $l3240 + local.get $l3239 + local.get $l3240 + i32.add + local.set $l3241 + local.get $l3240 + local.get $l3241 + i32.add + local.set $l3242 + local.get $l3241 + local.get $l3242 + i32.add + local.set $l3243 + local.get $l3242 + local.get $l3243 + i32.add + local.set $l3244 + local.get $l3243 + local.get $l3244 + i32.add + local.set $l3245 + local.get $l3244 + local.get $l3245 + i32.add + local.set $l3246 + local.get $l3245 + local.get $l3246 + i32.add + local.set $l3247 + local.get $l3246 + local.get $l3247 + i32.add + local.set $l3248 + local.get $l3247 + local.get $l3248 + i32.add + local.set $l3249 + local.get $l3248 + local.get $l3249 + i32.add + local.set $l3250 + local.get $l3249 + local.get $l3250 + i32.add + local.set $l3251 + local.get $l3250 + local.get $l3251 + i32.add + local.set $l3252 + local.get $l3251 + local.get $l3252 + i32.add + local.set $l3253 + local.get $l3252 + local.get $l3253 + i32.add + local.set $l3254 + local.get $l3253 + local.get $l3254 + i32.add + local.set $l3255 + local.get $l3254 + local.get $l3255 + i32.add + local.set $l3256 + local.get $l3255 + local.get $l3256 + i32.add + local.set $l3257 + local.get $l3256 + local.get $l3257 + i32.add + local.set $l3258 + local.get $l3257 + local.get $l3258 + i32.add + local.set $l3259 + local.get $l3258 + local.get $l3259 + i32.add + local.set $l3260 + local.get $l3259 + local.get $l3260 + i32.add + local.set $l3261 + local.get $l3260 + local.get $l3261 + i32.add + local.set $l3262 + local.get $l3261 + local.get $l3262 + i32.add + local.set $l3263 + local.get $l3262 + local.get $l3263 + i32.add + local.set $l3264 + local.get $l3263 + local.get $l3264 + i32.add + local.set $l3265 + local.get $l3264 + local.get $l3265 + i32.add + local.set $l3266 + local.get $l3265 + local.get $l3266 + i32.add + local.set $l3267 + local.get $l3266 + local.get $l3267 + i32.add + local.set $l3268 + local.get $l3267 + local.get $l3268 + i32.add + local.set $l3269 + local.get $l3268 + local.get $l3269 + i32.add + local.set $l3270 + local.get $l3269 + local.get $l3270 + i32.add + local.set $l3271 + local.get $l3270 + local.get $l3271 + i32.add + local.set $l3272 + local.get $l3271 + local.get $l3272 + i32.add + local.set $l3273 + local.get $l3272 + local.get $l3273 + i32.add + local.set $l3274 + local.get $l3273 + local.get $l3274 + i32.add + local.set $l3275 + local.get $l3274 + local.get $l3275 + i32.add + local.set $l3276 + local.get $l3275 + local.get $l3276 + i32.add + local.set $l3277 + local.get $l3276 + local.get $l3277 + i32.add + local.set $l3278 + local.get $l3277 + local.get $l3278 + i32.add + local.set $l3279 + local.get $l3278 + local.get $l3279 + i32.add + local.set $l3280 + local.get $l3279 + local.get $l3280 + i32.add + local.set $l3281 + local.get $l3280 + local.get $l3281 + i32.add + local.set $l3282 + local.get $l3281 + local.get $l3282 + i32.add + local.set $l3283 + local.get $l3282 + local.get $l3283 + i32.add + local.set $l3284 + local.get $l3283 + local.get $l3284 + i32.add + local.set $l3285 + local.get $l3284 + local.get $l3285 + i32.add + local.set $l3286 + local.get $l3285 + local.get $l3286 + i32.add + local.set $l3287 + local.get $l3286 + local.get $l3287 + i32.add + local.set $l3288 + local.get $l3287 + local.get $l3288 + i32.add + local.set $l3289 + local.get $l3288 + local.get $l3289 + i32.add + local.set $l3290 + local.get $l3289 + local.get $l3290 + i32.add + local.set $l3291 + local.get $l3290 + local.get $l3291 + i32.add + local.set $l3292 + local.get $l3291 + local.get $l3292 + i32.add + local.set $l3293 + local.get $l3292 + local.get $l3293 + i32.add + local.set $l3294 + local.get $l3293 + local.get $l3294 + i32.add + local.set $l3295 + local.get $l3294 + local.get $l3295 + i32.add + local.set $l3296 + local.get $l3295 + local.get $l3296 + i32.add + local.set $l3297 + local.get $l3296 + local.get $l3297 + i32.add + local.set $l3298 + local.get $l3297 + local.get $l3298 + i32.add + local.set $l3299 + local.get $l3298 + local.get $l3299 + i32.add + local.set $l3300 + local.get $l3299 + local.get $l3300 + i32.add + local.set $l3301 + local.get $l3300 + local.get $l3301 + i32.add + local.set $l3302 + local.get $l3301 + local.get $l3302 + i32.add + local.set $l3303 + local.get $l3302 + local.get $l3303 + i32.add + local.set $l3304 + local.get $l3303 + local.get $l3304 + i32.add + local.set $l3305 + local.get $l3304 + local.get $l3305 + i32.add + local.set $l3306 + local.get $l3305 + local.get $l3306 + i32.add + local.set $l3307 + local.get $l3306 + local.get $l3307 + i32.add + local.set $l3308 + local.get $l3307 + local.get $l3308 + i32.add + local.set $l3309 + local.get $l3308 + local.get $l3309 + i32.add + local.set $l3310 + local.get $l3309 + local.get $l3310 + i32.add + local.set $l3311 + local.get $l3310 + local.get $l3311 + i32.add + local.set $l3312 + local.get $l3311 + local.get $l3312 + i32.add + local.set $l3313 + local.get $l3312 + local.get $l3313 + i32.add + local.set $l3314 + local.get $l3313 + local.get $l3314 + i32.add + local.set $l3315 + local.get $l3314 + local.get $l3315 + i32.add + local.set $l3316 + local.get $l3315 + local.get $l3316 + i32.add + local.set $l3317 + local.get $l3316 + local.get $l3317 + i32.add + local.set $l3318 + local.get $l3317 + local.get $l3318 + i32.add + local.set $l3319 + local.get $l3318 + local.get $l3319 + i32.add + local.set $l3320 + local.get $l3319 + local.get $l3320 + i32.add + local.set $l3321 + local.get $l3320 + local.get $l3321 + i32.add + local.set $l3322 + local.get $l3321 + local.get $l3322 + i32.add + local.set $l3323 + local.get $l3322 + local.get $l3323 + i32.add + local.set $l3324 + local.get $l3323 + local.get $l3324 + i32.add + local.set $l3325 + local.get $l3324 + local.get $l3325 + i32.add + local.set $l3326 + local.get $l3325 + local.get $l3326 + i32.add + local.set $l3327 + local.get $l3326 + local.get $l3327 + i32.add + local.set $l3328 + local.get $l3327 + local.get $l3328 + i32.add + local.set $l3329 + local.get $l3328 + local.get $l3329 + i32.add + local.set $l3330 + local.get $l3329 + local.get $l3330 + i32.add + local.set $l3331 + local.get $l3330 + local.get $l3331 + i32.add + local.set $l3332 + local.get $l3331 + local.get $l3332 + i32.add + local.set $l3333 + local.get $l3332 + local.get $l3333 + i32.add + local.set $l3334 + local.get $l3333 + local.get $l3334 + i32.add + local.set $l3335 + local.get $l3334 + local.get $l3335 + i32.add + local.set $l3336 + local.get $l3335 + local.get $l3336 + i32.add + local.set $l3337 + local.get $l3336 + local.get $l3337 + i32.add + local.set $l3338 + local.get $l3337 + local.get $l3338 + i32.add + local.set $l3339 + local.get $l3338 + local.get $l3339 + i32.add + local.set $l3340 + local.get $l3339 + local.get $l3340 + i32.add + local.set $l3341 + local.get $l3340 + local.get $l3341 + i32.add + local.set $l3342 + local.get $l3341 + local.get $l3342 + i32.add + local.set $l3343 + local.get $l3342 + local.get $l3343 + i32.add + local.set $l3344 + local.get $l3343 + local.get $l3344 + i32.add + local.set $l3345 + local.get $l3344 + local.get $l3345 + i32.add + local.set $l3346 + local.get $l3345 + local.get $l3346 + i32.add + local.set $l3347 + local.get $l3346 + local.get $l3347 + i32.add + local.set $l3348 + local.get $l3347 + local.get $l3348 + i32.add + local.set $l3349 + local.get $l3348 + local.get $l3349 + i32.add + local.set $l3350 + local.get $l3349 + local.get $l3350 + i32.add + local.set $l3351 + local.get $l3350 + local.get $l3351 + i32.add + local.set $l3352 + local.get $l3351 + local.get $l3352 + i32.add + local.set $l3353 + local.get $l3352 + local.get $l3353 + i32.add + local.set $l3354 + local.get $l3353 + local.get $l3354 + i32.add + local.set $l3355 + local.get $l3354 + local.get $l3355 + i32.add + local.set $l3356 + local.get $l3355 + local.get $l3356 + i32.add + local.set $l3357 + local.get $l3356 + local.get $l3357 + i32.add + local.set $l3358 + local.get $l3357 + local.get $l3358 + i32.add + local.set $l3359 + local.get $l3358 + local.get $l3359 + i32.add + local.set $l3360 + local.get $l3359 + local.get $l3360 + i32.add + local.set $l3361 + local.get $l3360 + local.get $l3361 + i32.add + local.set $l3362 + local.get $l3361 + local.get $l3362 + i32.add + local.set $l3363 + local.get $l3362 + local.get $l3363 + i32.add + local.set $l3364 + local.get $l3363 + local.get $l3364 + i32.add + local.set $l3365 + local.get $l3364 + local.get $l3365 + i32.add + local.set $l3366 + local.get $l3365 + local.get $l3366 + i32.add + local.set $l3367 + local.get $l3366 + local.get $l3367 + i32.add + local.set $l3368 + local.get $l3367 + local.get $l3368 + i32.add + local.set $l3369 + local.get $l3368 + local.get $l3369 + i32.add + local.set $l3370 + local.get $l3369 + local.get $l3370 + i32.add + local.set $l3371 + local.get $l3370 + local.get $l3371 + i32.add + local.set $l3372 + local.get $l3371 + local.get $l3372 + i32.add + local.set $l3373 + local.get $l3372 + local.get $l3373 + i32.add + local.set $l3374 + local.get $l3373 + local.get $l3374 + i32.add + local.set $l3375 + local.get $l3374 + local.get $l3375 + i32.add + local.set $l3376 + local.get $l3375 + local.get $l3376 + i32.add + local.set $l3377 + local.get $l3376 + local.get $l3377 + i32.add + local.set $l3378 + local.get $l3377 + local.get $l3378 + i32.add + local.set $l3379 + local.get $l3378 + local.get $l3379 + i32.add + local.set $l3380 + local.get $l3379 + local.get $l3380 + i32.add + local.set $l3381 + local.get $l3380 + local.get $l3381 + i32.add + local.set $l3382 + local.get $l3381 + local.get $l3382 + i32.add + local.set $l3383 + local.get $l3382 + local.get $l3383 + i32.add + local.set $l3384 + local.get $l3383 + local.get $l3384 + i32.add + local.set $l3385 + local.get $l3384 + local.get $l3385 + i32.add + local.set $l3386 + local.get $l3385 + local.get $l3386 + i32.add + local.set $l3387 + local.get $l3386 + local.get $l3387 + i32.add + local.set $l3388 + local.get $l3387 + local.get $l3388 + i32.add + local.set $l3389 + local.get $l3388 + local.get $l3389 + i32.add + local.set $l3390 + local.get $l3389 + local.get $l3390 + i32.add + local.set $l3391 + local.get $l3390 + local.get $l3391 + i32.add + local.set $l3392 + local.get $l3391 + local.get $l3392 + i32.add + local.set $l3393 + local.get $l3392 + local.get $l3393 + i32.add + local.set $l3394 + local.get $l3393 + local.get $l3394 + i32.add + local.set $l3395 + local.get $l3394 + local.get $l3395 + i32.add + local.set $l3396 + local.get $l3395 + local.get $l3396 + i32.add + local.set $l3397 + local.get $l3396 + local.get $l3397 + i32.add + local.set $l3398 + local.get $l3397 + local.get $l3398 + i32.add + local.set $l3399 + local.get $l3398 + local.get $l3399 + i32.add + local.set $l3400 + local.get $l3399 + local.get $l3400 + i32.add + local.set $l3401 + local.get $l3400 + local.get $l3401 + i32.add + local.set $l3402 + local.get $l3401 + local.get $l3402 + i32.add + local.set $l3403 + local.get $l3402 + local.get $l3403 + i32.add + local.set $l3404 + local.get $l3403 + local.get $l3404 + i32.add + local.set $l3405 + local.get $l3404 + local.get $l3405 + i32.add + local.set $l3406 + local.get $l3405 + local.get $l3406 + i32.add + local.set $l3407 + local.get $l3406 + local.get $l3407 + i32.add + local.set $l3408 + local.get $l3407 + local.get $l3408 + i32.add + local.set $l3409 + local.get $l3408 + local.get $l3409 + i32.add + local.set $l3410 + local.get $l3409 + local.get $l3410 + i32.add + local.set $l3411 + local.get $l3410 + local.get $l3411 + i32.add + local.set $l3412 + local.get $l3411 + local.get $l3412 + i32.add + local.set $l3413 + local.get $l3412 + local.get $l3413 + i32.add + local.set $l3414 + local.get $l3413 + local.get $l3414 + i32.add + local.set $l3415 + local.get $l3414 + local.get $l3415 + i32.add + local.set $l3416 + local.get $l3415 + local.get $l3416 + i32.add + local.set $l3417 + local.get $l3416 + local.get $l3417 + i32.add + local.set $l3418 + local.get $l3417 + local.get $l3418 + i32.add + local.set $l3419 + local.get $l3418 + local.get $l3419 + i32.add + local.set $l3420 + local.get $l3419 + local.get $l3420 + i32.add + local.set $l3421 + local.get $l3420 + local.get $l3421 + i32.add + local.set $l3422 + local.get $l3421 + local.get $l3422 + i32.add + local.set $l3423 + local.get $l3422 + local.get $l3423 + i32.add + local.set $l3424 + local.get $l3423 + local.get $l3424 + i32.add + local.set $l3425 + local.get $l3424 + local.get $l3425 + i32.add + local.set $l3426 + local.get $l3425 + local.get $l3426 + i32.add + local.set $l3427 + local.get $l3426 + local.get $l3427 + i32.add + local.set $l3428 + local.get $l3427 + local.get $l3428 + i32.add + local.set $l3429 + local.get $l3428 + local.get $l3429 + i32.add + local.set $l3430 + local.get $l3429 + local.get $l3430 + i32.add + local.set $l3431 + local.get $l3430 + local.get $l3431 + i32.add + local.set $l3432 + local.get $l3431 + local.get $l3432 + i32.add + local.set $l3433 + local.get $l3432 + local.get $l3433 + i32.add + local.set $l3434 + local.get $l3433 + local.get $l3434 + i32.add + local.set $l3435 + local.get $l3434 + local.get $l3435 + i32.add + local.set $l3436 + local.get $l3435 + local.get $l3436 + i32.add + local.set $l3437 + local.get $l3436 + local.get $l3437 + i32.add + local.set $l3438 + local.get $l3437 + local.get $l3438 + i32.add + local.set $l3439 + local.get $l3438 + local.get $l3439 + i32.add + local.set $l3440 + local.get $l3439 + local.get $l3440 + i32.add + local.set $l3441 + local.get $l3440 + local.get $l3441 + i32.add + local.set $l3442 + local.get $l3441 + local.get $l3442 + i32.add + local.set $l3443 + local.get $l3442 + local.get $l3443 + i32.add + local.set $l3444 + local.get $l3443 + local.get $l3444 + i32.add + local.set $l3445 + local.get $l3444 + local.get $l3445 + i32.add + local.set $l3446 + local.get $l3445 + local.get $l3446 + i32.add + local.set $l3447 + local.get $l3446 + local.get $l3447 + i32.add + local.set $l3448 + local.get $l3447 + local.get $l3448 + i32.add + local.set $l3449 + local.get $l3448 + local.get $l3449 + i32.add + local.set $l3450 + local.get $l3449 + local.get $l3450 + i32.add + local.set $l3451 + local.get $l3450 + local.get $l3451 + i32.add + local.set $l3452 + local.get $l3451 + local.get $l3452 + i32.add + local.set $l3453 + local.get $l3452 + local.get $l3453 + i32.add + local.set $l3454 + local.get $l3453 + local.get $l3454 + i32.add + local.set $l3455 + local.get $l3454 + local.get $l3455 + i32.add + local.set $l3456 + local.get $l3455 + local.get $l3456 + i32.add + local.set $l3457 + local.get $l3456 + local.get $l3457 + i32.add + local.set $l3458 + local.get $l3457 + local.get $l3458 + i32.add + local.set $l3459 + local.get $l3458 + local.get $l3459 + i32.add + local.set $l3460 + local.get $l3459 + local.get $l3460 + i32.add + local.set $l3461 + local.get $l3460 + local.get $l3461 + i32.add + local.set $l3462 + local.get $l3461 + local.get $l3462 + i32.add + local.set $l3463 + local.get $l3462 + local.get $l3463 + i32.add + local.set $l3464 + local.get $l3463 + local.get $l3464 + i32.add + local.set $l3465 + local.get $l3464 + local.get $l3465 + i32.add + local.set $l3466 + local.get $l3465 + local.get $l3466 + i32.add + local.set $l3467 + local.get $l3466 + local.get $l3467 + i32.add + local.set $l3468 + local.get $l3467 + local.get $l3468 + i32.add + local.set $l3469 + local.get $l3468 + local.get $l3469 + i32.add + local.set $l3470 + local.get $l3469 + local.get $l3470 + i32.add + local.set $l3471 + local.get $l3470 + local.get $l3471 + i32.add + local.set $l3472 + local.get $l3471 + local.get $l3472 + i32.add + local.set $l3473 + local.get $l3472 + local.get $l3473 + i32.add + local.set $l3474 + local.get $l3473 + local.get $l3474 + i32.add + local.set $l3475 + local.get $l3474 + local.get $l3475 + i32.add + local.set $l3476 + local.get $l3475 + local.get $l3476 + i32.add + local.set $l3477 + local.get $l3476 + local.get $l3477 + i32.add + local.set $l3478 + local.get $l3477 + local.get $l3478 + i32.add + local.set $l3479 + local.get $l3478 + local.get $l3479 + i32.add + local.set $l3480 + local.get $l3479 + local.get $l3480 + i32.add + local.set $l3481 + local.get $l3480 + local.get $l3481 + i32.add + local.set $l3482 + local.get $l3481 + local.get $l3482 + i32.add + local.set $l3483 + local.get $l3482 + local.get $l3483 + i32.add + local.set $l3484 + local.get $l3483 + local.get $l3484 + i32.add + local.set $l3485 + local.get $l3484 + local.get $l3485 + i32.add + local.set $l3486 + local.get $l3485 + local.get $l3486 + i32.add + local.set $l3487 + local.get $l3486 + local.get $l3487 + i32.add + local.set $l3488 + local.get $l3487 + local.get $l3488 + i32.add + local.set $l3489 + local.get $l3488 + local.get $l3489 + i32.add + local.set $l3490 + local.get $l3489 + local.get $l3490 + i32.add + local.set $l3491 + local.get $l3490 + local.get $l3491 + i32.add + local.set $l3492 + local.get $l3491 + local.get $l3492 + i32.add + local.set $l3493 + local.get $l3492 + local.get $l3493 + i32.add + local.set $l3494 + local.get $l3493 + local.get $l3494 + i32.add + local.set $l3495 + local.get $l3494 + local.get $l3495 + i32.add + local.set $l3496 + local.get $l3495 + local.get $l3496 + i32.add + local.set $l3497 + local.get $l3496 + local.get $l3497 + i32.add + local.set $l3498 + local.get $l3497 + local.get $l3498 + i32.add + local.set $l3499 + local.get $l3498 + local.get $l3499 + i32.add + local.set $l3500 + local.get $l3499 + local.get $l3500 + i32.add + local.set $l3501 + local.get $l3500 + local.get $l3501 + i32.add + local.set $l3502 + local.get $l3501 + local.get $l3502 + i32.add + local.set $l3503 + local.get $l3502 + local.get $l3503 + i32.add + local.set $l3504 + local.get $l3503 + local.get $l3504 + i32.add + local.set $l3505 + local.get $l3504 + local.get $l3505 + i32.add + local.set $l3506 + local.get $l3505 + local.get $l3506 + i32.add + local.set $l3507 + local.get $l3506 + local.get $l3507 + i32.add + local.set $l3508 + local.get $l3507 + local.get $l3508 + i32.add + local.set $l3509 + local.get $l3508 + local.get $l3509 + i32.add + local.set $l3510 + local.get $l3509 + local.get $l3510 + i32.add + local.set $l3511 + local.get $l3510 + local.get $l3511 + i32.add + local.set $l3512 + local.get $l3511 + local.get $l3512 + i32.add + local.set $l3513 + local.get $l3512 + local.get $l3513 + i32.add + local.set $l3514 + local.get $l3513 + local.get $l3514 + i32.add + local.set $l3515 + local.get $l3514 + local.get $l3515 + i32.add + local.set $l3516 + local.get $l3515 + local.get $l3516 + i32.add + local.set $l3517 + local.get $l3516 + local.get $l3517 + i32.add + local.set $l3518 + local.get $l3517 + local.get $l3518 + i32.add + local.set $l3519 + local.get $l3518 + local.get $l3519 + i32.add + local.set $l3520 + local.get $l3519 + local.get $l3520 + i32.add + local.set $l3521 + local.get $l3520 + local.get $l3521 + i32.add + local.set $l3522 + local.get $l3521 + local.get $l3522 + i32.add + local.set $l3523 + local.get $l3522 + local.get $l3523 + i32.add + local.set $l3524 + local.get $l3523 + local.get $l3524 + i32.add + local.set $l3525 + local.get $l3524 + local.get $l3525 + i32.add + local.set $l3526 + local.get $l3525 + local.get $l3526 + i32.add + local.set $l3527 + local.get $l3526 + local.get $l3527 + i32.add + local.set $l3528 + local.get $l3527 + local.get $l3528 + i32.add + local.set $l3529 + local.get $l3528 + local.get $l3529 + i32.add + local.set $l3530 + local.get $l3529 + local.get $l3530 + i32.add + local.set $l3531 + local.get $l3530 + local.get $l3531 + i32.add + local.set $l3532 + local.get $l3531 + local.get $l3532 + i32.add + local.set $l3533 + local.get $l3532 + local.get $l3533 + i32.add + local.set $l3534 + local.get $l3533 + local.get $l3534 + i32.add + local.set $l3535 + local.get $l3534 + local.get $l3535 + i32.add + local.set $l3536 + local.get $l3535 + local.get $l3536 + i32.add + local.set $l3537 + local.get $l3536 + local.get $l3537 + i32.add + local.set $l3538 + local.get $l3537 + local.get $l3538 + i32.add + local.set $l3539 + local.get $l3538 + local.get $l3539 + i32.add + local.set $l3540 + local.get $l3539 + local.get $l3540 + i32.add + local.set $l3541 + local.get $l3540 + local.get $l3541 + i32.add + local.set $l3542 + local.get $l3541 + local.get $l3542 + i32.add + local.set $l3543 + local.get $l3542 + local.get $l3543 + i32.add + local.set $l3544 + local.get $l3543 + local.get $l3544 + i32.add + local.set $l3545 + local.get $l3544 + local.get $l3545 + i32.add + local.set $l3546 + local.get $l3545 + local.get $l3546 + i32.add + local.set $l3547 + local.get $l3546 + local.get $l3547 + i32.add + local.set $l3548 + local.get $l3547 + local.get $l3548 + i32.add + local.set $l3549 + local.get $l3548 + local.get $l3549 + i32.add + local.set $l3550 + local.get $l3549 + local.get $l3550 + i32.add + local.set $l3551 + local.get $l3550 + local.get $l3551 + i32.add + local.set $l3552 + local.get $l3551 + local.get $l3552 + i32.add + local.set $l3553 + local.get $l3552 + local.get $l3553 + i32.add + local.set $l3554 + local.get $l3553 + local.get $l3554 + i32.add + local.set $l3555 + local.get $l3554 + local.get $l3555 + i32.add + local.set $l3556 + local.get $l3555 + local.get $l3556 + i32.add + local.set $l3557 + local.get $l3556 + local.get $l3557 + i32.add + local.set $l3558 + local.get $l3557 + local.get $l3558 + i32.add + local.set $l3559 + local.get $l3558 + local.get $l3559 + i32.add + local.set $l3560 + local.get $l3559 + local.get $l3560 + i32.add + local.set $l3561 + local.get $l3560 + local.get $l3561 + i32.add + local.set $l3562 + local.get $l3561 + local.get $l3562 + i32.add + local.set $l3563 + local.get $l3562 + local.get $l3563 + i32.add + local.set $l3564 + local.get $l3563 + local.get $l3564 + i32.add + local.set $l3565 + local.get $l3564 + local.get $l3565 + i32.add + local.set $l3566 + local.get $l3565 + local.get $l3566 + i32.add + local.set $l3567 + local.get $l3566 + local.get $l3567 + i32.add + local.set $l3568 + local.get $l3567 + local.get $l3568 + i32.add + local.set $l3569 + local.get $l3568 + local.get $l3569 + i32.add + local.set $l3570 + local.get $l3569 + local.get $l3570 + i32.add + local.set $l3571 + local.get $l3570 + local.get $l3571 + i32.add + local.set $l3572 + local.get $l3571 + local.get $l3572 + i32.add + local.set $l3573 + local.get $l3572 + local.get $l3573 + i32.add + local.set $l3574 + local.get $l3573 + local.get $l3574 + i32.add + local.set $l3575 + local.get $l3574 + local.get $l3575 + i32.add + local.set $l3576 + local.get $l3575 + local.get $l3576 + i32.add + local.set $l3577 + local.get $l3576 + local.get $l3577 + i32.add + local.set $l3578 + local.get $l3577 + local.get $l3578 + i32.add + local.set $l3579 + local.get $l3578 + local.get $l3579 + i32.add + local.set $l3580 + local.get $l3579 + local.get $l3580 + i32.add + local.set $l3581 + local.get $l3580 + local.get $l3581 + i32.add + local.set $l3582 + local.get $l3581 + local.get $l3582 + i32.add + local.set $l3583 + local.get $l3582 + local.get $l3583 + i32.add + local.set $l3584 + local.get $l3583 + local.get $l3584 + i32.add + local.set $l3585 + local.get $l3584 + local.get $l3585 + i32.add + local.set $l3586 + local.get $l3585 + local.get $l3586 + i32.add + local.set $l3587 + local.get $l3586 + local.get $l3587 + i32.add + local.set $l3588 + local.get $l3587 + local.get $l3588 + i32.add + local.set $l3589 + local.get $l3588 + local.get $l3589 + i32.add + local.set $l3590 + local.get $l3589 + local.get $l3590 + i32.add + local.set $l3591 + local.get $l3590 + local.get $l3591 + i32.add + local.set $l3592 + local.get $l3591 + local.get $l3592 + i32.add + local.set $l3593 + local.get $l3592 + local.get $l3593 + i32.add + local.set $l3594 + local.get $l3593 + local.get $l3594 + i32.add + local.set $l3595 + local.get $l3594 + local.get $l3595 + i32.add + local.set $l3596 + local.get $l3595 + local.get $l3596 + i32.add + local.set $l3597 + local.get $l3596 + local.get $l3597 + i32.add + local.set $l3598 + local.get $l3597 + local.get $l3598 + i32.add + local.set $l3599 + local.get $l3598 + local.get $l3599 + i32.add + local.set $l3600 + local.get $l3599 + local.get $l3600 + i32.add + local.set $l3601 + local.get $l3600 + local.get $l3601 + i32.add + local.set $l3602 + local.get $l3601 + local.get $l3602 + i32.add + local.set $l3603 + local.get $l3602 + local.get $l3603 + i32.add + local.set $l3604 + local.get $l3603 + local.get $l3604 + i32.add + local.set $l3605 + local.get $l3604 + local.get $l3605 + i32.add + local.set $l3606 + local.get $l3605 + local.get $l3606 + i32.add + local.set $l3607 + local.get $l3606 + local.get $l3607 + i32.add + local.set $l3608 + local.get $l3607 + local.get $l3608 + i32.add + local.set $l3609 + local.get $l3608 + local.get $l3609 + i32.add + local.set $l3610 + local.get $l3609 + local.get $l3610 + i32.add + local.set $l3611 + local.get $l3610 + local.get $l3611 + i32.add + local.set $l3612 + local.get $l3611 + local.get $l3612 + i32.add + local.set $l3613 + local.get $l3612 + local.get $l3613 + i32.add + local.set $l3614 + local.get $l3613 + local.get $l3614 + i32.add + local.set $l3615 + local.get $l3614 + local.get $l3615 + i32.add + local.set $l3616 + local.get $l3615 + local.get $l3616 + i32.add + local.set $l3617 + local.get $l3616 + local.get $l3617 + i32.add + local.set $l3618 + local.get $l3617 + local.get $l3618 + i32.add + local.set $l3619 + local.get $l3618 + local.get $l3619 + i32.add + local.set $l3620 + local.get $l3619 + local.get $l3620 + i32.add + local.set $l3621 + local.get $l3620 + local.get $l3621 + i32.add + local.set $l3622 + local.get $l3621 + local.get $l3622 + i32.add + local.set $l3623 + local.get $l3622 + local.get $l3623 + i32.add + local.set $l3624 + local.get $l3623 + local.get $l3624 + i32.add + local.set $l3625 + local.get $l3624 + local.get $l3625 + i32.add + local.set $l3626 + local.get $l3625 + local.get $l3626 + i32.add + local.set $l3627 + local.get $l3626 + local.get $l3627 + i32.add + local.set $l3628 + local.get $l3627 + local.get $l3628 + i32.add + local.set $l3629 + local.get $l3628 + local.get $l3629 + i32.add + local.set $l3630 + local.get $l3629 + local.get $l3630 + i32.add + local.set $l3631 + local.get $l3630 + local.get $l3631 + i32.add + local.set $l3632 + local.get $l3631 + local.get $l3632 + i32.add + local.set $l3633 + local.get $l3632 + local.get $l3633 + i32.add + local.set $l3634 + local.get $l3633 + local.get $l3634 + i32.add + local.set $l3635 + local.get $l3634 + local.get $l3635 + i32.add + local.set $l3636 + local.get $l3635 + local.get $l3636 + i32.add + local.set $l3637 + local.get $l3636 + local.get $l3637 + i32.add + local.set $l3638 + local.get $l3637 + local.get $l3638 + i32.add + local.set $l3639 + local.get $l3638 + local.get $l3639 + i32.add + local.set $l3640 + local.get $l3639 + local.get $l3640 + i32.add + local.set $l3641 + local.get $l3640 + local.get $l3641 + i32.add + local.set $l3642 + local.get $l3641 + local.get $l3642 + i32.add + local.set $l3643 + local.get $l3642 + local.get $l3643 + i32.add + local.set $l3644 + local.get $l3643 + local.get $l3644 + i32.add + local.set $l3645 + local.get $l3644 + local.get $l3645 + i32.add + local.set $l3646 + local.get $l3645 + local.get $l3646 + i32.add + local.set $l3647 + local.get $l3646 + local.get $l3647 + i32.add + local.set $l3648 + local.get $l3647 + local.get $l3648 + i32.add + local.set $l3649 + local.get $l3648 + local.get $l3649 + i32.add + local.set $l3650 + local.get $l3649 + local.get $l3650 + i32.add + local.set $l3651 + local.get $l3650 + local.get $l3651 + i32.add + local.set $l3652 + local.get $l3651 + local.get $l3652 + i32.add + local.set $l3653 + local.get $l3652 + local.get $l3653 + i32.add + local.set $l3654 + local.get $l3653 + local.get $l3654 + i32.add + local.set $l3655 + local.get $l3654 + local.get $l3655 + i32.add + local.set $l3656 + local.get $l3655 + local.get $l3656 + i32.add + local.set $l3657 + local.get $l3656 + local.get $l3657 + i32.add + local.set $l3658 + local.get $l3657 + local.get $l3658 + i32.add + local.set $l3659 + local.get $l3658 + local.get $l3659 + i32.add + local.set $l3660 + local.get $l3659 + local.get $l3660 + i32.add + local.set $l3661 + local.get $l3660 + local.get $l3661 + i32.add + local.set $l3662 + local.get $l3661 + local.get $l3662 + i32.add + local.set $l3663 + local.get $l3662 + local.get $l3663 + i32.add + local.set $l3664 + local.get $l3663 + local.get $l3664 + i32.add + local.set $l3665 + local.get $l3664 + local.get $l3665 + i32.add + local.set $l3666 + local.get $l3665 + local.get $l3666 + i32.add + local.set $l3667 + local.get $l3666 + local.get $l3667 + i32.add + local.set $l3668 + local.get $l3667 + local.get $l3668 + i32.add + local.set $l3669 + local.get $l3668 + local.get $l3669 + i32.add + local.set $l3670 + local.get $l3669 + local.get $l3670 + i32.add + local.set $l3671 + local.get $l3670 + local.get $l3671 + i32.add + local.set $l3672 + local.get $l3671 + local.get $l3672 + i32.add + local.set $l3673 + local.get $l3672 + local.get $l3673 + i32.add + local.set $l3674 + local.get $l3673 + local.get $l3674 + i32.add + local.set $l3675 + local.get $l3674 + local.get $l3675 + i32.add + local.set $l3676 + local.get $l3675 + local.get $l3676 + i32.add + local.set $l3677 + local.get $l3676 + local.get $l3677 + i32.add + local.set $l3678 + local.get $l3677 + local.get $l3678 + i32.add + local.set $l3679 + local.get $l3678 + local.get $l3679 + i32.add + local.set $l3680 + local.get $l3679 + local.get $l3680 + i32.add + local.set $l3681 + local.get $l3680 + local.get $l3681 + i32.add + local.set $l3682 + local.get $l3681 + local.get $l3682 + i32.add + local.set $l3683 + local.get $l3682 + local.get $l3683 + i32.add + local.set $l3684 + local.get $l3683 + local.get $l3684 + i32.add + local.set $l3685 + local.get $l3684 + local.get $l3685 + i32.add + local.set $l3686 + local.get $l3685 + local.get $l3686 + i32.add + local.set $l3687 + local.get $l3686 + local.get $l3687 + i32.add + local.set $l3688 + local.get $l3687 + local.get $l3688 + i32.add + local.set $l3689 + local.get $l3688 + local.get $l3689 + i32.add + local.set $l3690 + local.get $l3689 + local.get $l3690 + i32.add + local.set $l3691 + local.get $l3690 + local.get $l3691 + i32.add + local.set $l3692 + local.get $l3691 + local.get $l3692 + i32.add + local.set $l3693 + local.get $l3692 + local.get $l3693 + i32.add + local.set $l3694 + local.get $l3693 + local.get $l3694 + i32.add + local.set $l3695 + local.get $l3694 + local.get $l3695 + i32.add + local.set $l3696 + local.get $l3695 + local.get $l3696 + i32.add + local.set $l3697 + local.get $l3696 + local.get $l3697 + i32.add + local.set $l3698 + local.get $l3697 + local.get $l3698 + i32.add + local.set $l3699 + local.get $l3698 + local.get $l3699 + i32.add + local.set $l3700 + local.get $l3699 + local.get $l3700 + i32.add + local.set $l3701 + local.get $l3700 + local.get $l3701 + i32.add + local.set $l3702 + local.get $l3701 + local.get $l3702 + i32.add + local.set $l3703 + local.get $l3702 + local.get $l3703 + i32.add + local.set $l3704 + local.get $l3703 + local.get $l3704 + i32.add + local.set $l3705 + local.get $l3704 + local.get $l3705 + i32.add + local.set $l3706 + local.get $l3705 + local.get $l3706 + i32.add + local.set $l3707 + local.get $l3706 + local.get $l3707 + i32.add + local.set $l3708 + local.get $l3707 + local.get $l3708 + i32.add + local.set $l3709 + local.get $l3708 + local.get $l3709 + i32.add + local.set $l3710 + local.get $l3709 + local.get $l3710 + i32.add + local.set $l3711 + local.get $l3710 + local.get $l3711 + i32.add + local.set $l3712 + local.get $l3711 + local.get $l3712 + i32.add + local.set $l3713 + local.get $l3712 + local.get $l3713 + i32.add + local.set $l3714 + local.get $l3713 + local.get $l3714 + i32.add + local.set $l3715 + local.get $l3714 + local.get $l3715 + i32.add + local.set $l3716 + local.get $l3715 + local.get $l3716 + i32.add + local.set $l3717 + local.get $l3716 + local.get $l3717 + i32.add + local.set $l3718 + local.get $l3717 + local.get $l3718 + i32.add + local.set $l3719 + local.get $l3718 + local.get $l3719 + i32.add + local.set $l3720 + local.get $l3719 + local.get $l3720 + i32.add + local.set $l3721 + local.get $l3720 + local.get $l3721 + i32.add + local.set $l3722 + local.get $l3721 + local.get $l3722 + i32.add + local.set $l3723 + local.get $l3722 + local.get $l3723 + i32.add + local.set $l3724 + local.get $l3723 + local.get $l3724 + i32.add + local.set $l3725 + local.get $l3724 + local.get $l3725 + i32.add + local.set $l3726 + local.get $l3725 + local.get $l3726 + i32.add + local.set $l3727 + local.get $l3726 + local.get $l3727 + i32.add + local.set $l3728 + local.get $l3727 + local.get $l3728 + i32.add + local.set $l3729 + local.get $l3728 + local.get $l3729 + i32.add + local.set $l3730 + local.get $l3729 + local.get $l3730 + i32.add + local.set $l3731 + local.get $l3730 + local.get $l3731 + i32.add + local.set $l3732 + local.get $l3731 + local.get $l3732 + i32.add + local.set $l3733 + local.get $l3732 + local.get $l3733 + i32.add + local.set $l3734 + local.get $l3733 + local.get $l3734 + i32.add + local.set $l3735 + local.get $l3734 + local.get $l3735 + i32.add + local.set $l3736 + local.get $l3735 + local.get $l3736 + i32.add + local.set $l3737 + local.get $l3736 + local.get $l3737 + i32.add + local.set $l3738 + local.get $l3737 + local.get $l3738 + i32.add + local.set $l3739 + local.get $l3738 + local.get $l3739 + i32.add + local.set $l3740 + local.get $l3739 + local.get $l3740 + i32.add + local.set $l3741 + local.get $l3740 + local.get $l3741 + i32.add + local.set $l3742 + local.get $l3741 + local.get $l3742 + i32.add + local.set $l3743 + local.get $l3742 + local.get $l3743 + i32.add + local.set $l3744 + local.get $l3743 + local.get $l3744 + i32.add + local.set $l3745 + local.get $l3744 + local.get $l3745 + i32.add + local.set $l3746 + local.get $l3745 + local.get $l3746 + i32.add + local.set $l3747 + local.get $l3746 + local.get $l3747 + i32.add + local.set $l3748 + local.get $l3747 + local.get $l3748 + i32.add + local.set $l3749 + local.get $l3748 + local.get $l3749 + i32.add + local.set $l3750 + local.get $l3749 + local.get $l3750 + i32.add + local.set $l3751 + local.get $l3750 + local.get $l3751 + i32.add + local.set $l3752 + local.get $l3751 + local.get $l3752 + i32.add + local.set $l3753 + local.get $l3752 + local.get $l3753 + i32.add + local.set $l3754 + local.get $l3753 + local.get $l3754 + i32.add + local.set $l3755 + local.get $l3754 + local.get $l3755 + i32.add + local.set $l3756 + local.get $l3755 + local.get $l3756 + i32.add + local.set $l3757 + local.get $l3756 + local.get $l3757 + i32.add + local.set $l3758 + local.get $l3757 + local.get $l3758 + i32.add + local.set $l3759 + local.get $l3758 + local.get $l3759 + i32.add + local.set $l3760 + local.get $l3759 + local.get $l3760 + i32.add + local.set $l3761 + local.get $l3760 + local.get $l3761 + i32.add + local.set $l3762 + local.get $l3761 + local.get $l3762 + i32.add + local.set $l3763 + local.get $l3762 + local.get $l3763 + i32.add + local.set $l3764 + local.get $l3763 + local.get $l3764 + i32.add + local.set $l3765 + local.get $l3764 + local.get $l3765 + i32.add + local.set $l3766 + local.get $l3765 + local.get $l3766 + i32.add + local.set $l3767 + local.get $l3766 + local.get $l3767 + i32.add + local.set $l3768 + local.get $l3767 + local.get $l3768 + i32.add + local.set $l3769 + local.get $l3768 + local.get $l3769 + i32.add + local.set $l3770 + local.get $l3769 + local.get $l3770 + i32.add + local.set $l3771 + local.get $l3770 + local.get $l3771 + i32.add + local.set $l3772 + local.get $l3771 + local.get $l3772 + i32.add + local.set $l3773 + local.get $l3772 + local.get $l3773 + i32.add + local.set $l3774 + local.get $l3773 + local.get $l3774 + i32.add + local.set $l3775 + local.get $l3774 + local.get $l3775 + i32.add + local.set $l3776 + local.get $l3775 + local.get $l3776 + i32.add + local.set $l3777 + local.get $l3776 + local.get $l3777 + i32.add + local.set $l3778 + local.get $l3777 + local.get $l3778 + i32.add + local.set $l3779 + local.get $l3778 + local.get $l3779 + i32.add + local.set $l3780 + local.get $l3779 + local.get $l3780 + i32.add + local.set $l3781 + local.get $l3780 + local.get $l3781 + i32.add + local.set $l3782 + local.get $l3781 + local.get $l3782 + i32.add + local.set $l3783 + local.get $l3782 + local.get $l3783 + i32.add + local.set $l3784 + local.get $l3783 + local.get $l3784 + i32.add + local.set $l3785 + local.get $l3784 + local.get $l3785 + i32.add + local.set $l3786 + local.get $l3785 + local.get $l3786 + i32.add + local.set $l3787 + local.get $l3786 + local.get $l3787 + i32.add + local.set $l3788 + local.get $l3787 + local.get $l3788 + i32.add + local.set $l3789 + local.get $l3788 + local.get $l3789 + i32.add + local.set $l3790 + local.get $l3789 + local.get $l3790 + i32.add + local.set $l3791 + local.get $l3790 + local.get $l3791 + i32.add + local.set $l3792 + local.get $l3791 + local.get $l3792 + i32.add + local.set $l3793 + local.get $l3792 + local.get $l3793 + i32.add + local.set $l3794 + local.get $l3793 + local.get $l3794 + i32.add + local.set $l3795 + local.get $l3794 + local.get $l3795 + i32.add + local.set $l3796 + local.get $l3795 + local.get $l3796 + i32.add + local.set $l3797 + local.get $l3796 + local.get $l3797 + i32.add + local.set $l3798 + local.get $l3797 + local.get $l3798 + i32.add + local.set $l3799 + local.get $l3798 + local.get $l3799 + i32.add + local.set $l3800 + local.get $l3799 + local.get $l3800 + i32.add + local.set $l3801 + local.get $l3800 + local.get $l3801 + i32.add + local.set $l3802 + local.get $l3801 + local.get $l3802 + i32.add + local.set $l3803 + local.get $l3802 + local.get $l3803 + i32.add + local.set $l3804 + local.get $l3803 + local.get $l3804 + i32.add + local.set $l3805 + local.get $l3804 + local.get $l3805 + i32.add + local.set $l3806 + local.get $l3805 + local.get $l3806 + i32.add + local.set $l3807 + local.get $l3806 + local.get $l3807 + i32.add + local.set $l3808 + local.get $l3807 + local.get $l3808 + i32.add + local.set $l3809 + local.get $l3808 + local.get $l3809 + i32.add + local.set $l3810 + local.get $l3809 + local.get $l3810 + i32.add + local.set $l3811 + local.get $l3810 + local.get $l3811 + i32.add + local.set $l3812 + local.get $l3811 + local.get $l3812 + i32.add + local.set $l3813 + local.get $l3812 + local.get $l3813 + i32.add + local.set $l3814 + local.get $l3813 + local.get $l3814 + i32.add + local.set $l3815 + local.get $l3814 + local.get $l3815 + i32.add + local.set $l3816 + local.get $l3815 + local.get $l3816 + i32.add + local.set $l3817 + local.get $l3816 + local.get $l3817 + i32.add + local.set $l3818 + local.get $l3817 + local.get $l3818 + i32.add + local.set $l3819 + local.get $l3818 + local.get $l3819 + i32.add + local.set $l3820 + local.get $l3819 + local.get $l3820 + i32.add + local.set $l3821 + local.get $l3820 + local.get $l3821 + i32.add + local.set $l3822 + local.get $l3821 + local.get $l3822 + i32.add + local.set $l3823 + local.get $l3822 + local.get $l3823 + i32.add + local.set $l3824 + local.get $l3823 + local.get $l3824 + i32.add + local.set $l3825 + local.get $l3824 + local.get $l3825 + i32.add + local.set $l3826 + local.get $l3825 + local.get $l3826 + i32.add + local.set $l3827 + local.get $l3826 + local.get $l3827 + i32.add + local.set $l3828 + local.get $l3827 + local.get $l3828 + i32.add + local.set $l3829 + local.get $l3828 + local.get $l3829 + i32.add + local.set $l3830 + local.get $l3829 + local.get $l3830 + i32.add + local.set $l3831 + local.get $l3830 + local.get $l3831 + i32.add + local.set $l3832 + local.get $l3831 + local.get $l3832 + i32.add + local.set $l3833 + local.get $l3832 + local.get $l3833 + i32.add + local.set $l3834 + local.get $l3833 + local.get $l3834 + i32.add + local.set $l3835 + local.get $l3834 + local.get $l3835 + i32.add + local.set $l3836 + local.get $l3835 + local.get $l3836 + i32.add + local.set $l3837 + local.get $l3836 + local.get $l3837 + i32.add + local.set $l3838 + local.get $l3837 + local.get $l3838 + i32.add + local.set $l3839 + local.get $l3838 + local.get $l3839 + i32.add + local.set $l3840 + local.get $l3839 + local.get $l3840 + i32.add + local.set $l3841 + local.get $l3840 + local.get $l3841 + i32.add + local.set $l3842 + local.get $l3841 + local.get $l3842 + i32.add + local.set $l3843 + local.get $l3842 + local.get $l3843 + i32.add + local.set $l3844 + local.get $l3843 + local.get $l3844 + i32.add + local.set $l3845 + local.get $l3844 + local.get $l3845 + i32.add + local.set $l3846 + local.get $l3845 + local.get $l3846 + i32.add + local.set $l3847 + local.get $l3846 + local.get $l3847 + i32.add + local.set $l3848 + local.get $l3847 + local.get $l3848 + i32.add + local.set $l3849 + local.get $l3848 + local.get $l3849 + i32.add + local.set $l3850 + local.get $l3849 + local.get $l3850 + i32.add + local.set $l3851 + local.get $l3850 + local.get $l3851 + i32.add + local.set $l3852 + local.get $l3851 + local.get $l3852 + i32.add + local.set $l3853 + local.get $l3852 + local.get $l3853 + i32.add + local.set $l3854 + local.get $l3853 + local.get $l3854 + i32.add + local.set $l3855 + local.get $l3854 + local.get $l3855 + i32.add + local.set $l3856 + local.get $l3855 + local.get $l3856 + i32.add + local.set $l3857 + local.get $l3856 + local.get $l3857 + i32.add + local.set $l3858 + local.get $l3857 + local.get $l3858 + i32.add + local.set $l3859 + local.get $l3858 + local.get $l3859 + i32.add + local.set $l3860 + local.get $l3859 + local.get $l3860 + i32.add + local.set $l3861 + local.get $l3860 + local.get $l3861 + i32.add + local.set $l3862 + local.get $l3861 + local.get $l3862 + i32.add + local.set $l3863 + local.get $l3862 + local.get $l3863 + i32.add + local.set $l3864 + local.get $l3863 + local.get $l3864 + i32.add + local.set $l3865 + local.get $l3864 + local.get $l3865 + i32.add + local.set $l3866 + local.get $l3865 + local.get $l3866 + i32.add + local.set $l3867 + local.get $l3866 + local.get $l3867 + i32.add + local.set $l3868 + local.get $l3867 + local.get $l3868 + i32.add + local.set $l3869 + local.get $l3868 + local.get $l3869 + i32.add + local.set $l3870 + local.get $l3869 + local.get $l3870 + i32.add + local.set $l3871 + local.get $l3870 + local.get $l3871 + i32.add + local.set $l3872 + local.get $l3871 + local.get $l3872 + i32.add + local.set $l3873 + local.get $l3872 + local.get $l3873 + i32.add + local.set $l3874 + local.get $l3873 + local.get $l3874 + i32.add + local.set $l3875 + local.get $l3874 + local.get $l3875 + i32.add + local.set $l3876 + local.get $l3875 + local.get $l3876 + i32.add + local.set $l3877 + local.get $l3876 + local.get $l3877 + i32.add + local.set $l3878 + local.get $l3877 + local.get $l3878 + i32.add + local.set $l3879 + local.get $l3878 + local.get $l3879 + i32.add + local.set $l3880 + local.get $l3879 + local.get $l3880 + i32.add + local.set $l3881 + local.get $l3880 + local.get $l3881 + i32.add + local.set $l3882 + local.get $l3881 + local.get $l3882 + i32.add + local.set $l3883 + local.get $l3882 + local.get $l3883 + i32.add + local.set $l3884 + local.get $l3883 + local.get $l3884 + i32.add + local.set $l3885 + local.get $l3884 + local.get $l3885 + i32.add + local.set $l3886 + local.get $l3885 + local.get $l3886 + i32.add + local.set $l3887 + local.get $l3886 + local.get $l3887 + i32.add + local.set $l3888 + local.get $l3887 + local.get $l3888 + i32.add + local.set $l3889 + local.get $l3888 + local.get $l3889 + i32.add + local.set $l3890 + local.get $l3889 + local.get $l3890 + i32.add + local.set $l3891 + local.get $l3890 + local.get $l3891 + i32.add + local.set $l3892 + local.get $l3891 + local.get $l3892 + i32.add + local.set $l3893 + local.get $l3892 + local.get $l3893 + i32.add + local.set $l3894 + local.get $l3893 + local.get $l3894 + i32.add + local.set $l3895 + local.get $l3894 + local.get $l3895 + i32.add + local.set $l3896 + local.get $l3895 + local.get $l3896 + i32.add + local.set $l3897 + local.get $l3896 + local.get $l3897 + i32.add + local.set $l3898 + local.get $l3897 + local.get $l3898 + i32.add + local.set $l3899 + local.get $l3898 + local.get $l3899 + i32.add + local.set $l3900 + local.get $l3899 + local.get $l3900 + i32.add + local.set $l3901 + local.get $l3900 + local.get $l3901 + i32.add + local.set $l3902 + local.get $l3901 + local.get $l3902 + i32.add + local.set $l3903 + local.get $l3902 + local.get $l3903 + i32.add + local.set $l3904 + local.get $l3903 + local.get $l3904 + i32.add + local.set $l3905 + local.get $l3904 + local.get $l3905 + i32.add + local.set $l3906 + local.get $l3905 + local.get $l3906 + i32.add + local.set $l3907 + local.get $l3906 + local.get $l3907 + i32.add + local.set $l3908 + local.get $l3907 + local.get $l3908 + i32.add + local.set $l3909 + local.get $l3908 + local.get $l3909 + i32.add + local.set $l3910 + local.get $l3909 + local.get $l3910 + i32.add + local.set $l3911 + local.get $l3910 + local.get $l3911 + i32.add + local.set $l3912 + local.get $l3911 + local.get $l3912 + i32.add + local.set $l3913 + local.get $l3912 + local.get $l3913 + i32.add + local.set $l3914 + local.get $l3913 + local.get $l3914 + i32.add + local.set $l3915 + local.get $l3914 + local.get $l3915 + i32.add + local.set $l3916 + local.get $l3915 + local.get $l3916 + i32.add + local.set $l3917 + local.get $l3916 + local.get $l3917 + i32.add + local.set $l3918 + local.get $l3917 + local.get $l3918 + i32.add + local.set $l3919 + local.get $l3918 + local.get $l3919 + i32.add + local.set $l3920 + local.get $l3919 + local.get $l3920 + i32.add + local.set $l3921 + local.get $l3920 + local.get $l3921 + i32.add + local.set $l3922 + local.get $l3921 + local.get $l3922 + i32.add + local.set $l3923 + local.get $l3922 + local.get $l3923 + i32.add + local.set $l3924 + local.get $l3923 + local.get $l3924 + i32.add + local.set $l3925 + local.get $l3924 + local.get $l3925 + i32.add + local.set $l3926 + local.get $l3925 + local.get $l3926 + i32.add + local.set $l3927 + local.get $l3926 + local.get $l3927 + i32.add + local.set $l3928 + local.get $l3927 + local.get $l3928 + i32.add + local.set $l3929 + local.get $l3928 + local.get $l3929 + i32.add + local.set $l3930 + local.get $l3929 + local.get $l3930 + i32.add + local.set $l3931 + local.get $l3930 + local.get $l3931 + i32.add + local.set $l3932 + local.get $l3931 + local.get $l3932 + i32.add + local.set $l3933 + local.get $l3932 + local.get $l3933 + i32.add + local.set $l3934 + local.get $l3933 + local.get $l3934 + i32.add + local.set $l3935 + local.get $l3934 + local.get $l3935 + i32.add + local.set $l3936 + local.get $l3935 + local.get $l3936 + i32.add + local.set $l3937 + local.get $l3936 + local.get $l3937 + i32.add + local.set $l3938 + local.get $l3937 + local.get $l3938 + i32.add + local.set $l3939 + local.get $l3938 + local.get $l3939 + i32.add + local.set $l3940 + local.get $l3939 + local.get $l3940 + i32.add + local.set $l3941 + local.get $l3940 + local.get $l3941 + i32.add + local.set $l3942 + local.get $l3941 + local.get $l3942 + i32.add + local.set $l3943 + local.get $l3942 + local.get $l3943 + i32.add + local.set $l3944 + local.get $l3943 + local.get $l3944 + i32.add + local.set $l3945 + local.get $l3944 + local.get $l3945 + i32.add + local.set $l3946 + local.get $l3945 + local.get $l3946 + i32.add + local.set $l3947 + local.get $l3946 + local.get $l3947 + i32.add + local.set $l3948 + local.get $l3947 + local.get $l3948 + i32.add + local.set $l3949 + local.get $l3948 + local.get $l3949 + i32.add + local.set $l3950 + local.get $l3949 + local.get $l3950 + i32.add + local.set $l3951 + local.get $l3950 + local.get $l3951 + i32.add + local.set $l3952 + local.get $l3951 + local.get $l3952 + i32.add + local.set $l3953 + local.get $l3952 + local.get $l3953 + i32.add + local.set $l3954 + local.get $l3953 + local.get $l3954 + i32.add + local.set $l3955 + local.get $l3954 + local.get $l3955 + i32.add + local.set $l3956 + local.get $l3955 + local.get $l3956 + i32.add + local.set $l3957 + local.get $l3956 + local.get $l3957 + i32.add + local.set $l3958 + local.get $l3957 + local.get $l3958 + i32.add + local.set $l3959 + local.get $l3958 + local.get $l3959 + i32.add + local.set $l3960 + local.get $l3959 + local.get $l3960 + i32.add + local.set $l3961 + local.get $l3960 + local.get $l3961 + i32.add + local.set $l3962 + local.get $l3961 + local.get $l3962 + i32.add + local.set $l3963 + local.get $l3962 + local.get $l3963 + i32.add + local.set $l3964 + local.get $l3963 + local.get $l3964 + i32.add + local.set $l3965 + local.get $l3964 + local.get $l3965 + i32.add + local.set $l3966 + local.get $l3965 + local.get $l3966 + i32.add + local.set $l3967 + local.get $l3966 + local.get $l3967 + i32.add + local.set $l3968 + local.get $l3967 + local.get $l3968 + i32.add + local.set $l3969 + local.get $l3968 + local.get $l3969 + i32.add + local.set $l3970 + local.get $l3969 + local.get $l3970 + i32.add + local.set $l3971 + local.get $l3970 + local.get $l3971 + i32.add + local.set $l3972 + local.get $l3971 + local.get $l3972 + i32.add + local.set $l3973 + local.get $l3972 + local.get $l3973 + i32.add + local.set $l3974 + local.get $l3973 + local.get $l3974 + i32.add + local.set $l3975 + local.get $l3974 + local.get $l3975 + i32.add + local.set $l3976 + local.get $l3975 + local.get $l3976 + i32.add + local.set $l3977 + local.get $l3976 + local.get $l3977 + i32.add + local.set $l3978 + local.get $l3977 + local.get $l3978 + i32.add + local.set $l3979 + local.get $l3978 + local.get $l3979 + i32.add + local.set $l3980 + local.get $l3979 + local.get $l3980 + i32.add + local.set $l3981 + local.get $l3980 + local.get $l3981 + i32.add + local.set $l3982 + local.get $l3981 + local.get $l3982 + i32.add + local.set $l3983 + local.get $l3982 + local.get $l3983 + i32.add + local.set $l3984 + local.get $l3983 + local.get $l3984 + i32.add + local.set $l3985 + local.get $l3984 + local.get $l3985 + i32.add + local.set $l3986 + local.get $l3985 + local.get $l3986 + i32.add + local.set $l3987 + local.get $l3986 + local.get $l3987 + i32.add + local.set $l3988 + local.get $l3987 + local.get $l3988 + i32.add + local.set $l3989 + local.get $l3988 + local.get $l3989 + i32.add + local.set $l3990 + local.get $l3989 + local.get $l3990 + i32.add + local.set $l3991 + local.get $l3990 + local.get $l3991 + i32.add + local.set $l3992 + local.get $l3991 + local.get $l3992 + i32.add + local.set $l3993 + local.get $l3992 + local.get $l3993 + i32.add + local.set $l3994 + local.get $l3993 + local.get $l3994 + i32.add + local.set $l3995 + local.get $l3994 + local.get $l3995 + i32.add + local.set $l3996 + local.get $l3995 + local.get $l3996 + i32.add + local.set $l3997 + local.get $l3996 + local.get $l3997 + i32.add + local.set $l3998 + local.get $l3997 + local.get $l3998 + i32.add + local.set $l3999 + local.get $l3998 + local.get $l3999 + i32.add + local.set $l4000 + local.get $l3999 + local.get $l4000 + i32.add + local.set $l4001 + local.get $l4000 + local.get $l4001 + i32.add + local.set $l4002 + local.get $l4001 + local.get $l4002 + i32.add + local.set $l4003 + local.get $l4002 + local.get $l4003 + i32.add + local.set $l4004 + local.get $l4003 + local.get $l4004 + i32.add + local.set $l4005 + local.get $l4004 + local.get $l4005 + i32.add + local.set $l4006 + local.get $l4005 + local.get $l4006 + i32.add + local.set $l4007 + local.get $l4006 + local.get $l4007 + i32.add + local.set $l4008 + local.get $l4007 + local.get $l4008 + i32.add + local.set $l4009 + local.get $l4008 + local.get $l4009 + i32.add + local.set $l4010 + local.get $l4009 + local.get $l4010 + i32.add + local.set $l4011 + local.get $l4010 + local.get $l4011 + i32.add + local.set $l4012 + local.get $l4011 + local.get $l4012 + i32.add + local.set $l4013 + local.get $l4012 + local.get $l4013 + i32.add + local.set $l4014 + local.get $l4013 + local.get $l4014 + i32.add + local.set $l4015 + local.get $l4014 + local.get $l4015 + i32.add + local.set $l4016 + local.get $l4015 + local.get $l4016 + i32.add + local.set $l4017 + local.get $l4016 + local.get $l4017 + i32.add + local.set $l4018 + local.get $l4017 + local.get $l4018 + i32.add + local.set $l4019 + local.get $l4018 + local.get $l4019 + i32.add + local.set $l4020 + local.get $l4019 + local.get $l4020 + i32.add + local.set $l4021 + local.get $l4020 + local.get $l4021 + i32.add + local.set $l4022 + local.get $l4021 + local.get $l4022 + i32.add + local.set $l4023 + local.get $l4022 + local.get $l4023 + i32.add + local.set $l4024 + local.get $l4023 + local.get $l4024 + i32.add + local.set $l4025 + local.get $l4024 + local.get $l4025 + i32.add + local.set $l4026 + local.get $l4025 + local.get $l4026 + i32.add + local.set $l4027 + local.get $l4026 + local.get $l4027 + i32.add + local.set $l4028 + local.get $l4027 + local.get $l4028 + i32.add + local.set $l4029 + local.get $l4028 + local.get $l4029 + i32.add + local.set $l4030 + local.get $l4029 + local.get $l4030 + i32.add + local.set $l4031 + local.get $l4030 + local.get $l4031 + i32.add + local.set $l4032 + local.get $l4031 + local.get $l4032 + i32.add + local.set $l4033 + local.get $l4032 + local.get $l4033 + i32.add + local.set $l4034 + local.get $l4033 + local.get $l4034 + i32.add + local.set $l4035 + local.get $l4034 + local.get $l4035 + i32.add + local.set $l4036 + local.get $l4035 + local.get $l4036 + i32.add + local.set $l4037 + local.get $l4036 + local.get $l4037 + i32.add + local.set $l4038 + local.get $l4037 + local.get $l4038 + i32.add + local.set $l4039 + local.get $l4038 + local.get $l4039 + i32.add + local.set $l4040 + local.get $l4039 + local.get $l4040 + i32.add + local.set $l4041 + local.get $l4040 + local.get $l4041 + i32.add + local.set $l4042 + local.get $l4041 + local.get $l4042 + i32.add + local.set $l4043 + local.get $l4042 + local.get $l4043 + i32.add + local.set $l4044 + local.get $l4043 + local.get $l4044 + i32.add + local.set $l4045 + local.get $l4044 + local.get $l4045 + i32.add + local.set $l4046 + local.get $l4045 + local.get $l4046 + i32.add + local.set $l4047 + local.get $l4046 + local.get $l4047 + i32.add + local.set $l4048 + local.get $l4047 + local.get $l4048 + i32.add + local.set $l4049 + local.get $l4048 + local.get $l4049 + i32.add + local.set $l4050 + local.get $l4049 + local.get $l4050 + i32.add + local.set $l4051 + local.get $l4050 + local.get $l4051 + i32.add + local.set $l4052 + local.get $l4051 + local.get $l4052 + i32.add + local.set $l4053 + local.get $l4052 + local.get $l4053 + i32.add + local.set $l4054 + local.get $l4053 + local.get $l4054 + i32.add + local.set $l4055 + local.get $l4054 + local.get $l4055 + i32.add + local.set $l4056 + local.get $l4055 + local.get $l4056 + i32.add + local.set $l4057 + local.get $l4056 + local.get $l4057 + i32.add + local.set $l4058 + local.get $l4057 + local.get $l4058 + i32.add + local.set $l4059 + local.get $l4058 + local.get $l4059 + i32.add + local.set $l4060 + local.get $l4059 + local.get $l4060 + i32.add + local.set $l4061 + local.get $l4060 + local.get $l4061 + i32.add + local.set $l4062 + local.get $l4061 + local.get $l4062 + i32.add + local.set $l4063 + local.get $l4062 + local.get $l4063 + i32.add + local.set $l4064 + local.get $l4063 + local.get $l4064 + i32.add + local.set $l4065 + local.get $l4064 + local.get $l4065 + i32.add + local.set $l4066 + local.get $l4065 + local.get $l4066 + i32.add + local.set $l4067 + local.get $l4066 + local.get $l4067 + i32.add + local.set $l4068 + local.get $l4067 + local.get $l4068 + i32.add + local.set $l4069 + local.get $l4068 + local.get $l4069 + i32.add + local.set $l4070 + local.get $l4069 + local.get $l4070 + i32.add + local.set $l4071 + local.get $l4070 + local.get $l4071 + i32.add + local.set $l4072 + local.get $l4071 + local.get $l4072 + i32.add + local.set $l4073 + local.get $l4072 + local.get $l4073 + i32.add + local.set $l4074 + local.get $l4073 + local.get $l4074 + i32.add + local.set $l4075 + local.get $l4074 + local.get $l4075 + i32.add + local.set $l4076 + local.get $l4075 + local.get $l4076 + i32.add + local.set $l4077 + local.get $l4076 + local.get $l4077 + i32.add + local.set $l4078 + local.get $l4077 + local.get $l4078 + i32.add + local.set $l4079 + local.get $l4078 + local.get $l4079 + i32.add + local.set $l4080 + local.get $l4079 + local.get $l4080 + i32.add + local.set $l4081 + local.get $l4080 + local.get $l4081 + i32.add + local.set $l4082 + local.get $l4081 + local.get $l4082 + i32.add + local.set $l4083 + local.get $l4082 + local.get $l4083 + i32.add + local.set $l4084 + local.get $l4083 + local.get $l4084 + i32.add + local.set $l4085 + local.get $l4084 + local.get $l4085 + i32.add + local.set $l4086 + local.get $l4085 + local.get $l4086 + i32.add + local.set $l4087 + local.get $l4086 + local.get $l4087 + i32.add + local.set $l4088 + local.get $l4087 + local.get $l4088 + i32.add + local.set $l4089 + local.get $l4088 + local.get $l4089 + i32.add + local.set $l4090 + local.get $l4089 + local.get $l4090 + i32.add + local.set $l4091 + local.get $l4090 + local.get $l4091 + i32.add + local.set $l4092 + local.get $l4091 + local.get $l4092 + i32.add + local.set $l4093 + local.get $l4092 + local.get $l4093 + i32.add + local.set $l4094 + local.get $l4093 + local.get $l4094 + i32.add + local.set $l4095 + local.get $l4094 + local.get $l4095 + i32.add + local.set $l4096 + local.get $l4095 + local.get $l4096 + i32.add + local.set $l4097 + local.get $l4096 + local.get $l4097 + i32.add + local.set $l4098 + local.get $l4097 + local.get $l4098 + i32.add + local.set $l4099 + local.get $l4098 + local.get $l4099 + i32.add + local.set $l4100 + local.get $l4099 + local.get $l4100 + i32.add + local.set $l4101 + local.get $l4100 + local.get $l4101 + i32.add + local.set $l4102 + local.get $l4101 + local.get $l4102 + i32.add + local.set $l4103 + local.get $l4102 + local.get $l4103 + i32.add + local.set $l4104 + local.get $l4103 + local.get $l4104 + i32.add + local.set $l4105 + local.get $l4104 + local.get $l4105 + i32.add + local.set $l4106 + local.get $l4105 + local.get $l4106 + i32.add + local.set $l4107 + local.get $l4106 + local.get $l4107 + i32.add + local.set $l4108 + local.get $l4107 + local.get $l4108 + i32.add + local.set $l4109 + local.get $l4108 + local.get $l4109 + i32.add + local.set $l4110 + local.get $l4109 + local.get $l4110 + i32.add + local.set $l4111 + local.get $l4110 + local.get $l4111 + i32.add + local.set $l4112 + local.get $l4111 + local.get $l4112 + i32.add + local.set $l4113 + local.get $l4112 + local.get $l4113 + i32.add + local.set $l4114 + local.get $l4113 + local.get $l4114 + i32.add + local.set $l4115 + local.get $l4114 + local.get $l4115 + i32.add + local.set $l4116 + local.get $l4115 + local.get $l4116 + i32.add + local.set $l4117 + local.get $l4116 + local.get $l4117 + i32.add + local.set $l4118 + local.get $l4117 + local.get $l4118 + i32.add + local.set $l4119 + local.get $l4118 + local.get $l4119 + i32.add + local.set $l4120 + local.get $l4119 + local.get $l4120 + i32.add + local.set $l4121 + local.get $l4120 + local.get $l4121 + i32.add + local.set $l4122 + local.get $l4121 + local.get $l4122 + i32.add + local.set $l4123 + local.get $l4122 + local.get $l4123 + i32.add + local.set $l4124 + local.get $l4123 + local.get $l4124 + i32.add + local.set $l4125 + local.get $l4124 + local.get $l4125 + i32.add + local.set $l4126 + local.get $l4125 + local.get $l4126 + i32.add + local.set $l4127 + local.get $l4126 + local.get $l4127 + i32.add + local.set $l4128 + local.get $l4127 + local.get $l4128 + i32.add + local.set $l4129 + local.get $l4128 + local.get $l4129 + i32.add + local.set $l4130 + local.get $l4129 + local.get $l4130 + i32.add + local.set $l4131 + local.get $l4130 + local.get $l4131 + i32.add + local.set $l4132 + local.get $l4131 + local.get $l4132 + i32.add + local.set $l4133 + local.get $l4132 + local.get $l4133 + i32.add + local.set $l4134 + local.get $l4133 + local.get $l4134 + i32.add + local.set $l4135 + local.get $l4134 + local.get $l4135 + i32.add + local.set $l4136 + local.get $l4135 + local.get $l4136 + i32.add + local.set $l4137 + local.get $l4136 + local.get $l4137 + i32.add + local.set $l4138 + local.get $l4137 + local.get $l4138 + i32.add + local.set $l4139 + local.get $l4138 + local.get $l4139 + i32.add + local.set $l4140 + local.get $l4139 + local.get $l4140 + i32.add + local.set $l4141 + local.get $l4140 + local.get $l4141 + i32.add + local.set $l4142 + local.get $l4141 + local.get $l4142 + i32.add + local.set $l4143 + local.get $l4142 + local.get $l4143 + i32.add + local.set $l4144 + local.get $l4143 + local.get $l4144 + i32.add + local.set $l4145 + local.get $l4144 + local.get $l4145 + i32.add + local.set $l4146 + local.get $l4145 + local.get $l4146 + i32.add + local.set $l4147 + local.get $l4146 + local.get $l4147 + i32.add + local.set $l4148 + local.get $l4147 + local.get $l4148 + i32.add + local.set $l4149 + local.get $l4148 + local.get $l4149 + i32.add + local.set $l4150 + local.get $l4149 + local.get $l4150 + i32.add + local.set $l4151 + local.get $l4150 + local.get $l4151 + i32.add + local.set $l4152 + local.get $l4151 + local.get $l4152 + i32.add + local.set $l4153 + local.get $l4152 + local.get $l4153 + i32.add + local.set $l4154 + local.get $l4153 + local.get $l4154 + i32.add + local.set $l4155 + local.get $l4154 + local.get $l4155 + i32.add + local.set $l4156 + local.get $l4155 + local.get $l4156 + i32.add + local.set $l4157 + local.get $l4156 + local.get $l4157 + i32.add + local.set $l4158 + local.get $l4157 + local.get $l4158 + i32.add + local.set $l4159 + local.get $l4158 + local.get $l4159 + i32.add + local.set $l4160 + local.get $l4159 + local.get $l4160 + i32.add + local.set $l4161 + local.get $l4160 + local.get $l4161 + i32.add + local.set $l4162 + local.get $l4161 + local.get $l4162 + i32.add + local.set $l4163 + local.get $l4162 + local.get $l4163 + i32.add + local.set $l4164 + local.get $l4163 + local.get $l4164 + i32.add + local.set $l4165 + local.get $l4164 + local.get $l4165 + i32.add + local.set $l4166 + local.get $l4165 + local.get $l4166 + i32.add + local.set $l4167 + local.get $l4166 + local.get $l4167 + i32.add + local.set $l4168 + local.get $l4167 + local.get $l4168 + i32.add + local.set $l4169 + local.get $l4168 + local.get $l4169 + i32.add + local.set $l4170 + local.get $l4169 + local.get $l4170 + i32.add + local.set $l4171 + local.get $l4170 + local.get $l4171 + i32.add + local.set $l4172 + local.get $l4171 + local.get $l4172 + i32.add + local.set $l4173 + local.get $l4172 + local.get $l4173 + i32.add + local.set $l4174 + local.get $l4173 + local.get $l4174 + i32.add + local.set $l4175 + local.get $l4174 + local.get $l4175 + i32.add + local.set $l4176 + local.get $l4175 + local.get $l4176 + i32.add + local.set $l4177 + local.get $l4176 + local.get $l4177 + i32.add + local.set $l4178 + local.get $l4177 + local.get $l4178 + i32.add + local.set $l4179 + local.get $l4178 + local.get $l4179 + i32.add + local.set $l4180 + local.get $l4179 + local.get $l4180 + i32.add + local.set $l4181 + local.get $l4180 + local.get $l4181 + i32.add + local.set $l4182 + local.get $l4181 + local.get $l4182 + i32.add + local.set $l4183 + local.get $l4182 + local.get $l4183 + i32.add + local.set $l4184 + local.get $l4183 + local.get $l4184 + i32.add + local.set $l4185 + local.get $l4184 + local.get $l4185 + i32.add + local.set $l4186 + local.get $l4185 + local.get $l4186 + i32.add + local.set $l4187 + local.get $l4186 + local.get $l4187 + i32.add + local.set $l4188 + local.get $l4187 + local.get $l4188 + i32.add + local.set $l4189 + local.get $l4188 + local.get $l4189 + i32.add + local.set $l4190 + local.get $l4189 + local.get $l4190 + i32.add + local.set $l4191 + local.get $l4190 + local.get $l4191 + i32.add + local.set $l4192 + local.get $l4191 + local.get $l4192 + i32.add + local.set $l4193 + local.get $l4192 + local.get $l4193 + i32.add + local.set $l4194 + local.get $l4193 + local.get $l4194 + i32.add + local.set $l4195 + local.get $l4194 + local.get $l4195 + i32.add + local.set $l4196 + local.get $l4195 + local.get $l4196 + i32.add + local.set $l4197 + local.get $l4196 + local.get $l4197 + i32.add + local.set $l4198 + local.get $l4197 + local.get $l4198 + i32.add + local.set $l4199 + local.get $l4198 + local.get $l4199 + i32.add + local.set $l4200 + local.get $l4199 + local.get $l4200 + i32.add + local.set $l4201 + local.get $l4200 + local.get $l4201 + i32.add + local.set $l4202 + local.get $l4201 + local.get $l4202 + i32.add + local.set $l4203 + local.get $l4202 + local.get $l4203 + i32.add + local.set $l4204 + local.get $l4203 + local.get $l4204 + i32.add + local.set $l4205 + local.get $l4204 + local.get $l4205 + i32.add + local.set $l4206 + local.get $l4205 + local.get $l4206 + i32.add + local.set $l4207 + local.get $l4206 + local.get $l4207 + i32.add + local.set $l4208 + local.get $l4207 + local.get $l4208 + i32.add + local.set $l4209 + local.get $l4208 + local.get $l4209 + i32.add + local.set $l4210 + local.get $l4209 + local.get $l4210 + i32.add + local.set $l4211 + local.get $l4210 + local.get $l4211 + i32.add + local.set $l4212 + local.get $l4211 + local.get $l4212 + i32.add + local.set $l4213 + local.get $l4212 + local.get $l4213 + i32.add + local.set $l4214 + local.get $l4213 + local.get $l4214 + i32.add + local.set $l4215 + local.get $l4214 + local.get $l4215 + i32.add + local.set $l4216 + local.get $l4215 + local.get $l4216 + i32.add + local.set $l4217 + local.get $l4216 + local.get $l4217 + i32.add + local.set $l4218 + local.get $l4217 + local.get $l4218 + i32.add + local.set $l4219 + local.get $l4218 + local.get $l4219 + i32.add + local.set $l4220 + local.get $l4219 + local.get $l4220 + i32.add + local.set $l4221 + local.get $l4220 + local.get $l4221 + i32.add + local.set $l4222 + local.get $l4221 + local.get $l4222 + i32.add + local.set $l4223 + local.get $l4222 + local.get $l4223 + i32.add + local.set $l4224 + local.get $l4223 + local.get $l4224 + i32.add + local.set $l4225 + local.get $l4224 + local.get $l4225 + i32.add + local.set $l4226 + local.get $l4225 + local.get $l4226 + i32.add + local.set $l4227 + local.get $l4226 + local.get $l4227 + i32.add + local.set $l4228 + local.get $l4227 + local.get $l4228 + i32.add + local.set $l4229 + local.get $l4228 + local.get $l4229 + i32.add + local.set $l4230 + local.get $l4229 + local.get $l4230 + i32.add + local.set $l4231 + local.get $l4230 + local.get $l4231 + i32.add + local.set $l4232 + local.get $l4231 + local.get $l4232 + i32.add + local.set $l4233 + local.get $l4232 + local.get $l4233 + i32.add + local.set $l4234 + local.get $l4233 + local.get $l4234 + i32.add + local.set $l4235 + local.get $l4234 + local.get $l4235 + i32.add + local.set $l4236 + local.get $l4235 + local.get $l4236 + i32.add + local.set $l4237 + local.get $l4236 + local.get $l4237 + i32.add + local.set $l4238 + local.get $l4237 + local.get $l4238 + i32.add + local.set $l4239 + local.get $l4238 + local.get $l4239 + i32.add + local.set $l4240 + local.get $l4239 + local.get $l4240 + i32.add + local.set $l4241 + local.get $l4240 + local.get $l4241 + i32.add + local.set $l4242 + local.get $l4241 + local.get $l4242 + i32.add + local.set $l4243 + local.get $l4242 + local.get $l4243 + i32.add + local.set $l4244 + local.get $l4243 + local.get $l4244 + i32.add + local.set $l4245 + local.get $l4244 + local.get $l4245 + i32.add + local.set $l4246 + local.get $l4245 + local.get $l4246 + i32.add + local.set $l4247 + local.get $l4246 + local.get $l4247 + i32.add + local.set $l4248 + local.get $l4247 + local.get $l4248 + i32.add + local.set $l4249 + local.get $l4248 + local.get $l4249 + i32.add + local.set $l4250 + local.get $l4249 + local.get $l4250 + i32.add + local.set $l4251 + local.get $l4250 + local.get $l4251 + i32.add + local.set $l4252 + local.get $l4251 + local.get $l4252 + i32.add + local.set $l4253 + local.get $l4252 + local.get $l4253 + i32.add + local.set $l4254 + local.get $l4253 + local.get $l4254 + i32.add + local.set $l4255 + local.get $l4254 + local.get $l4255 + i32.add + local.set $l4256 + local.get $l4255 + local.get $l4256 + i32.add + local.set $l4257 + local.get $l4256 + local.get $l4257 + i32.add + local.set $l4258 + local.get $l4257 + local.get $l4258 + i32.add + local.set $l4259 + local.get $l4258 + local.get $l4259 + i32.add + local.set $l4260 + local.get $l4259 + local.get $l4260 + i32.add + local.set $l4261 + local.get $l4260 + local.get $l4261 + i32.add + local.set $l4262 + local.get $l4261 + local.get $l4262 + i32.add + local.set $l4263 + local.get $l4262 + local.get $l4263 + i32.add + local.set $l4264 + local.get $l4263 + local.get $l4264 + i32.add + local.set $l4265 + local.get $l4264 + local.get $l4265 + i32.add + local.set $l4266 + local.get $l4265 + local.get $l4266 + i32.add + local.set $l4267 + local.get $l4266 + local.get $l4267 + i32.add + local.set $l4268 + local.get $l4267 + local.get $l4268 + i32.add + local.set $l4269 + local.get $l4268 + local.get $l4269 + i32.add + local.set $l4270 + local.get $l4269 + local.get $l4270 + i32.add + local.set $l4271 + local.get $l4270 + local.get $l4271 + i32.add + local.set $l4272 + local.get $l4271 + local.get $l4272 + i32.add + local.set $l4273 + local.get $l4272 + local.get $l4273 + i32.add + local.set $l4274 + local.get $l4273 + local.get $l4274 + i32.add + local.set $l4275 + local.get $l4274 + local.get $l4275 + i32.add + local.set $l4276 + local.get $l4275 + local.get $l4276 + i32.add + local.set $l4277 + local.get $l4276 + local.get $l4277 + i32.add + local.set $l4278 + local.get $l4277 + local.get $l4278 + i32.add + local.set $l4279 + local.get $l4278 + local.get $l4279 + i32.add + local.set $l4280 + local.get $l4279 + local.get $l4280 + i32.add + local.set $l4281 + local.get $l4280 + local.get $l4281 + i32.add + local.set $l4282 + local.get $l4281 + local.get $l4282 + i32.add + local.set $l4283 + local.get $l4282 + local.get $l4283 + i32.add + local.set $l4284 + local.get $l4283 + local.get $l4284 + i32.add + local.set $l4285 + local.get $l4284 + local.get $l4285 + i32.add + local.set $l4286 + local.get $l4285 + local.get $l4286 + i32.add + local.set $l4287 + local.get $l4286 + local.get $l4287 + i32.add + local.set $l4288 + local.get $l4287 + local.get $l4288 + i32.add + local.set $l4289 + local.get $l4288 + local.get $l4289 + i32.add + local.set $l4290 + local.get $l4289 + local.get $l4290 + i32.add + local.set $l4291 + local.get $l4290 + local.get $l4291 + i32.add + local.set $l4292 + local.get $l4291 + local.get $l4292 + i32.add + local.set $l4293 + local.get $l4292 + local.get $l4293 + i32.add + local.set $l4294 + local.get $l4293 + local.get $l4294 + i32.add + local.set $l4295 + local.get $l4294 + local.get $l4295 + i32.add + local.set $l4296 + local.get $l4295 + local.get $l4296 + i32.add + local.set $l4297 + local.get $l4296 + local.get $l4297 + i32.add + local.set $l4298 + local.get $l4297 + local.get $l4298 + i32.add + local.set $l4299 + local.get $l4298 + local.get $l4299 + i32.add + local.set $l4300 + local.get $l4299 + local.get $l4300 + i32.add + local.set $l4301 + local.get $l4300 + local.get $l4301 + i32.add + local.set $l4302 + local.get $l4301 + local.get $l4302 + i32.add + local.set $l4303 + local.get $l4302 + local.get $l4303 + i32.add + local.set $l4304 + local.get $l4303 + local.get $l4304 + i32.add + local.set $l4305 + local.get $l4304 + local.get $l4305 + i32.add + local.set $l4306 + local.get $l4305 + local.get $l4306 + i32.add + local.set $l4307 + local.get $l4306 + local.get $l4307 + i32.add + local.set $l4308 + local.get $l4307 + local.get $l4308 + i32.add + local.set $l4309 + local.get $l4308 + local.get $l4309 + i32.add + local.set $l4310 + local.get $l4309 + local.get $l4310 + i32.add + local.set $l4311 + local.get $l4310 + local.get $l4311 + i32.add + local.set $l4312 + local.get $l4311 + local.get $l4312 + i32.add + local.set $l4313 + local.get $l4312 + local.get $l4313 + i32.add + local.set $l4314 + local.get $l4313 + local.get $l4314 + i32.add + local.set $l4315 + local.get $l4314 + local.get $l4315 + i32.add + local.set $l4316 + local.get $l4315 + local.get $l4316 + i32.add + local.set $l4317 + local.get $l4316 + local.get $l4317 + i32.add + local.set $l4318 + local.get $l4317 + local.get $l4318 + i32.add + local.set $l4319 + local.get $l4318 + local.get $l4319 + i32.add + local.set $l4320 + local.get $l4319 + local.get $l4320 + i32.add + local.set $l4321 + local.get $l4320 + local.get $l4321 + i32.add + local.set $l4322 + local.get $l4321 + local.get $l4322 + i32.add + local.set $l4323 + local.get $l4322 + local.get $l4323 + i32.add + local.set $l4324 + local.get $l4323 + local.get $l4324 + i32.add + local.set $l4325 + local.get $l4324 + local.get $l4325 + i32.add + local.set $l4326 + local.get $l4325 + local.get $l4326 + i32.add + local.set $l4327 + local.get $l4326 + local.get $l4327 + i32.add + local.set $l4328 + local.get $l4327 + local.get $l4328 + i32.add + local.set $l4329 + local.get $l4328 + local.get $l4329 + i32.add + local.set $l4330 + local.get $l4329 + local.get $l4330 + i32.add + local.set $l4331 + local.get $l4330 + local.get $l4331 + i32.add + local.set $l4332 + local.get $l4331 + local.get $l4332 + i32.add + local.set $l4333 + local.get $l4332 + local.get $l4333 + i32.add + local.set $l4334 + local.get $l4333 + local.get $l4334 + i32.add + local.set $l4335 + local.get $l4334 + local.get $l4335 + i32.add + local.set $l4336 + local.get $l4335 + local.get $l4336 + i32.add + local.set $l4337 + local.get $l4336 + local.get $l4337 + i32.add + local.set $l4338 + local.get $l4337 + local.get $l4338 + i32.add + local.set $l4339 + local.get $l4338 + local.get $l4339 + i32.add + local.set $l4340 + local.get $l4339 + local.get $l4340 + i32.add + local.set $l4341 + local.get $l4340 + local.get $l4341 + i32.add + local.set $l4342 + local.get $l4341 + local.get $l4342 + i32.add + local.set $l4343 + local.get $l4342 + local.get $l4343 + i32.add + local.set $l4344 + local.get $l4343 + local.get $l4344 + i32.add + local.set $l4345 + local.get $l4344 + local.get $l4345 + i32.add + local.set $l4346 + local.get $l4345 + local.get $l4346 + i32.add + local.set $l4347 + local.get $l4346 + local.get $l4347 + i32.add + local.set $l4348 + local.get $l4347 + local.get $l4348 + i32.add + local.set $l4349 + local.get $l4348 + local.get $l4349 + i32.add + local.set $l4350 + local.get $l4349 + local.get $l4350 + i32.add + local.set $l4351 + local.get $l4350 + local.get $l4351 + i32.add + local.set $l4352 + local.get $l4351 + local.get $l4352 + i32.add + local.set $l4353 + local.get $l4352 + local.get $l4353 + i32.add + local.set $l4354 + local.get $l4353 + local.get $l4354 + i32.add + local.set $l4355 + local.get $l4354 + local.get $l4355 + i32.add + local.set $l4356 + local.get $l4355 + local.get $l4356 + i32.add + local.set $l4357 + local.get $l4356 + local.get $l4357 + i32.add + local.set $l4358 + local.get $l4357 + local.get $l4358 + i32.add + local.set $l4359 + local.get $l4358 + local.get $l4359 + i32.add + local.set $l4360 + local.get $l4359 + local.get $l4360 + i32.add + local.set $l4361 + local.get $l4360 + local.get $l4361 + i32.add + local.set $l4362 + local.get $l4361 + local.get $l4362 + i32.add + local.set $l4363 + local.get $l4362 + local.get $l4363 + i32.add + local.set $l4364 + local.get $l4363 + local.get $l4364 + i32.add + local.set $l4365 + local.get $l4364 + local.get $l4365 + i32.add + local.set $l4366 + local.get $l4365 + local.get $l4366 + i32.add + local.set $l4367 + local.get $l4366 + local.get $l4367 + i32.add + local.set $l4368 + local.get $l4367 + local.get $l4368 + i32.add + local.set $l4369 + local.get $l4368 + local.get $l4369 + i32.add + local.set $l4370 + local.get $l4369 + local.get $l4370 + i32.add + local.set $l4371 + local.get $l4370 + local.get $l4371 + i32.add + local.set $l4372 + local.get $l4371 + local.get $l4372 + i32.add + local.set $l4373 + local.get $l4372 + local.get $l4373 + i32.add + local.set $l4374 + local.get $l4373 + local.get $l4374 + i32.add + local.set $l4375 + local.get $l4374 + local.get $l4375 + i32.add + local.set $l4376 + local.get $l4375 + local.get $l4376 + i32.add + local.set $l4377 + local.get $l4376 + local.get $l4377 + i32.add + local.set $l4378 + local.get $l4377 + local.get $l4378 + i32.add + local.set $l4379 + local.get $l4378 + local.get $l4379 + i32.add + local.set $l4380 + local.get $l4379 + local.get $l4380 + i32.add + local.set $l4381 + local.get $l4380 + local.get $l4381 + i32.add + local.set $l4382 + local.get $l4381 + local.get $l4382 + i32.add + local.set $l4383 + local.get $l4382 + local.get $l4383 + i32.add + local.set $l4384 + local.get $l4383 + local.get $l4384 + i32.add + local.set $l4385 + local.get $l4384 + local.get $l4385 + i32.add + local.set $l4386 + local.get $l4385 + local.get $l4386 + i32.add + local.set $l4387 + local.get $l4386 + local.get $l4387 + i32.add + local.set $l4388 + local.get $l4387 + local.get $l4388 + i32.add + local.set $l4389 + local.get $l4388 + local.get $l4389 + i32.add + local.set $l4390 + local.get $l4389 + local.get $l4390 + i32.add + local.set $l4391 + local.get $l4390 + local.get $l4391 + i32.add + local.set $l4392 + local.get $l4391 + local.get $l4392 + i32.add + local.set $l4393 + local.get $l4392 + local.get $l4393 + i32.add + local.set $l4394 + local.get $l4393 + local.get $l4394 + i32.add + local.set $l4395 + local.get $l4394 + local.get $l4395 + i32.add + local.set $l4396 + local.get $l4395 + local.get $l4396 + i32.add + local.set $l4397 + local.get $l4396 + local.get $l4397 + i32.add + local.set $l4398 + local.get $l4397 + local.get $l4398 + i32.add + local.set $l4399 + local.get $l4398 + local.get $l4399 + i32.add + local.set $l4400 + local.get $l4399 + local.get $l4400 + i32.add + local.set $l4401 + local.get $l4400 + local.get $l4401 + i32.add + local.set $l4402 + local.get $l4401 + local.get $l4402 + i32.add + local.set $l4403 + local.get $l4402 + local.get $l4403 + i32.add + local.set $l4404 + local.get $l4403 + local.get $l4404 + i32.add + local.set $l4405 + local.get $l4404 + local.get $l4405 + i32.add + local.set $l4406 + local.get $l4405 + local.get $l4406 + i32.add + local.set $l4407 + local.get $l4406 + local.get $l4407 + i32.add + local.set $l4408 + local.get $l4407 + local.get $l4408 + i32.add + local.set $l4409 + local.get $l4408 + local.get $l4409 + i32.add + local.set $l4410 + local.get $l4409 + local.get $l4410 + i32.add + local.set $l4411 + local.get $l4410 + local.get $l4411 + i32.add + local.set $l4412 + local.get $l4411 + local.get $l4412 + i32.add + local.set $l4413 + local.get $l4412 + local.get $l4413 + i32.add + local.set $l4414 + local.get $l4413 + local.get $l4414 + i32.add + local.set $l4415 + local.get $l4414 + local.get $l4415 + i32.add + local.set $l4416 + local.get $l4415 + local.get $l4416 + i32.add + local.set $l4417 + local.get $l4416 + local.get $l4417 + i32.add + local.set $l4418 + local.get $l4417 + local.get $l4418 + i32.add + local.set $l4419 + local.get $l4418 + local.get $l4419 + i32.add + local.set $l4420 + local.get $l4419 + local.get $l4420 + i32.add + local.set $l4421 + local.get $l4420 + local.get $l4421 + i32.add + local.set $l4422 + local.get $l4421 + local.get $l4422 + i32.add + local.set $l4423 + local.get $l4422 + local.get $l4423 + i32.add + local.set $l4424 + local.get $l4423 + local.get $l4424 + i32.add + local.set $l4425 + local.get $l4424 + local.get $l4425 + i32.add + local.set $l4426 + local.get $l4425 + local.get $l4426 + i32.add + local.set $l4427 + local.get $l4426 + local.get $l4427 + i32.add + local.set $l4428 + local.get $l4427 + local.get $l4428 + i32.add + local.set $l4429 + local.get $l4428 + local.get $l4429 + i32.add + local.set $l4430 + local.get $l4429 + local.get $l4430 + i32.add + local.set $l4431 + local.get $l4430 + local.get $l4431 + i32.add + local.set $l4432 + local.get $l4431 + local.get $l4432 + i32.add + local.set $l4433 + local.get $l4432 + local.get $l4433 + i32.add + local.set $l4434 + local.get $l4433 + local.get $l4434 + i32.add + local.set $l4435 + local.get $l4434 + local.get $l4435 + i32.add + local.set $l4436 + local.get $l4435 + local.get $l4436 + i32.add + local.set $l4437 + local.get $l4436 + local.get $l4437 + i32.add + local.set $l4438 + local.get $l4437 + local.get $l4438 + i32.add + local.set $l4439 + local.get $l4438 + local.get $l4439 + i32.add + local.set $l4440 + local.get $l4439 + local.get $l4440 + i32.add + local.set $l4441 + local.get $l4440 + local.get $l4441 + i32.add + local.set $l4442 + local.get $l4441 + local.get $l4442 + i32.add + local.set $l4443 + local.get $l4442 + local.get $l4443 + i32.add + local.set $l4444 + local.get $l4443 + local.get $l4444 + i32.add + local.set $l4445 + local.get $l4444 + local.get $l4445 + i32.add + local.set $l4446 + local.get $l4445 + local.get $l4446 + i32.add + local.set $l4447 + local.get $l4446 + local.get $l4447 + i32.add + local.set $l4448 + local.get $l4447 + local.get $l4448 + i32.add + local.set $l4449 + local.get $l4448 + local.get $l4449 + i32.add + local.set $l4450 + local.get $l4449 + local.get $l4450 + i32.add + local.set $l4451 + local.get $l4450 + local.get $l4451 + i32.add + local.set $l4452 + local.get $l4451 + local.get $l4452 + i32.add + local.set $l4453 + local.get $l4452 + local.get $l4453 + i32.add + local.set $l4454 + local.get $l4453 + local.get $l4454 + i32.add + local.set $l4455 + local.get $l4454 + local.get $l4455 + i32.add + local.set $l4456 + local.get $l4455 + local.get $l4456 + i32.add + local.set $l4457 + local.get $l4456 + local.get $l4457 + i32.add + local.set $l4458 + local.get $l4457 + local.get $l4458 + i32.add + local.set $l4459 + local.get $l4458 + local.get $l4459 + i32.add + local.set $l4460 + local.get $l4459 + local.get $l4460 + i32.add + local.set $l4461 + local.get $l4460 + local.get $l4461 + i32.add + local.set $l4462 + local.get $l4461 + local.get $l4462 + i32.add + local.set $l4463 + local.get $l4462 + local.get $l4463 + i32.add + local.set $l4464 + local.get $l4463 + local.get $l4464 + i32.add + local.set $l4465 + local.get $l4464 + local.get $l4465 + i32.add + local.set $l4466 + local.get $l4465 + local.get $l4466 + i32.add + local.set $l4467 + local.get $l4466 + local.get $l4467 + i32.add + local.set $l4468 + local.get $l4467 + local.get $l4468 + i32.add + local.set $l4469 + local.get $l4468 + local.get $l4469 + i32.add + local.set $l4470 + local.get $l4469 + local.get $l4470 + i32.add + local.set $l4471 + local.get $l4470 + local.get $l4471 + i32.add + local.set $l4472 + local.get $l4471 + local.get $l4472 + i32.add + local.set $l4473 + local.get $l4472 + local.get $l4473 + i32.add + local.set $l4474 + local.get $l4473 + local.get $l4474 + i32.add + local.set $l4475 + local.get $l4474 + local.get $l4475 + i32.add + local.set $l4476 + local.get $l4475 + local.get $l4476 + i32.add + local.set $l4477 + local.get $l4476 + local.get $l4477 + i32.add + local.set $l4478 + local.get $l4477 + local.get $l4478 + i32.add + local.set $l4479 + local.get $l4478 + local.get $l4479 + i32.add + local.set $l4480 + local.get $l4479 + local.get $l4480 + i32.add + local.set $l4481 + local.get $l4480 + local.get $l4481 + i32.add + local.set $l4482 + local.get $l4481 + local.get $l4482 + i32.add + local.set $l4483 + local.get $l4482 + local.get $l4483 + i32.add + local.set $l4484 + local.get $l4483 + local.get $l4484 + i32.add + local.set $l4485 + local.get $l4484 + local.get $l4485 + i32.add + local.set $l4486 + local.get $l4485 + local.get $l4486 + i32.add + local.set $l4487 + local.get $l4486 + local.get $l4487 + i32.add + local.set $l4488 + local.get $l4487 + local.get $l4488 + i32.add + local.set $l4489 + local.get $l4488 + local.get $l4489 + i32.add + local.set $l4490 + local.get $l4489 + local.get $l4490 + i32.add + local.set $l4491 + local.get $l4490 + local.get $l4491 + i32.add + local.set $l4492 + local.get $l4491 + local.get $l4492 + i32.add + local.set $l4493 + local.get $l4492 + local.get $l4493 + i32.add + local.set $l4494 + local.get $l4493 + local.get $l4494 + i32.add + local.set $l4495 + local.get $l4494 + local.get $l4495 + i32.add + local.set $l4496 + local.get $l4495 + local.get $l4496 + i32.add + local.set $l4497 + local.get $l4496 + local.get $l4497 + i32.add + local.set $l4498 + local.get $l4497 + local.get $l4498 + i32.add + local.set $l4499 + local.get $l4498 + local.get $l4499 + i32.add + local.set $l4500 + local.get $l4499 + local.get $l4500 + i32.add + local.set $l4501 + local.get $l4500 + local.get $l4501 + i32.add + local.set $l4502 + local.get $l4501 + local.get $l4502 + i32.add + local.set $l4503 + local.get $l4502 + local.get $l4503 + i32.add + local.set $l4504 + local.get $l4503 + local.get $l4504 + i32.add + local.set $l4505 + local.get $l4504 + local.get $l4505 + i32.add + local.set $l4506 + local.get $l4505 + local.get $l4506 + i32.add + local.set $l4507 + local.get $l4506 + local.get $l4507 + i32.add + local.set $l4508 + local.get $l4507 + local.get $l4508 + i32.add + local.set $l4509 + local.get $l4508 + local.get $l4509 + i32.add + local.set $l4510 + local.get $l4509 + local.get $l4510 + i32.add + local.set $l4511 + local.get $l4510 + local.get $l4511 + i32.add + local.set $l4512 + local.get $l4511 + local.get $l4512 + i32.add + local.set $l4513 + local.get $l4512 + local.get $l4513 + i32.add + local.set $l4514 + local.get $l4513 + local.get $l4514 + i32.add + local.set $l4515 + local.get $l4514 + local.get $l4515 + i32.add + local.set $l4516 + local.get $l4515 + local.get $l4516 + i32.add + local.set $l4517 + local.get $l4516 + local.get $l4517 + i32.add + local.set $l4518 + local.get $l4517 + local.get $l4518 + i32.add + local.set $l4519 + local.get $l4518 + local.get $l4519 + i32.add + local.set $l4520 + local.get $l4519 + local.get $l4520 + i32.add + local.set $l4521 + local.get $l4520 + local.get $l4521 + i32.add + local.set $l4522 + local.get $l4521 + local.get $l4522 + i32.add + local.set $l4523 + local.get $l4522 + local.get $l4523 + i32.add + local.set $l4524 + local.get $l4523 + local.get $l4524 + i32.add + local.set $l4525 + local.get $l4524 + local.get $l4525 + i32.add + local.set $l4526 + local.get $l4525 + local.get $l4526 + i32.add + local.set $l4527 + local.get $l4526 + local.get $l4527 + i32.add + local.set $l4528 + local.get $l4527 + local.get $l4528 + i32.add + local.set $l4529 + local.get $l4528 + local.get $l4529 + i32.add + local.set $l4530 + local.get $l4529 + local.get $l4530 + i32.add + local.set $l4531 + local.get $l4530 + local.get $l4531 + i32.add + local.set $l4532 + local.get $l4531 + local.get $l4532 + i32.add + local.set $l4533 + local.get $l4532 + local.get $l4533 + i32.add + local.set $l4534 + local.get $l4533 + local.get $l4534 + i32.add + local.set $l4535 + local.get $l4534 + local.get $l4535 + i32.add + local.set $l4536 + local.get $l4535 + local.get $l4536 + i32.add + local.set $l4537 + local.get $l4536 + local.get $l4537 + i32.add + local.set $l4538 + local.get $l4537 + local.get $l4538 + i32.add + local.set $l4539 + local.get $l4538 + local.get $l4539 + i32.add + local.set $l4540 + local.get $l4539 + local.get $l4540 + i32.add + local.set $l4541 + local.get $l4540 + local.get $l4541 + i32.add + local.set $l4542 + local.get $l4541 + local.get $l4542 + i32.add + local.set $l4543 + local.get $l4542 + local.get $l4543 + i32.add + local.set $l4544 + local.get $l4543 + local.get $l4544 + i32.add + local.set $l4545 + local.get $l4544 + local.get $l4545 + i32.add + local.set $l4546 + local.get $l4545 + local.get $l4546 + i32.add + local.set $l4547 + local.get $l4546 + local.get $l4547 + i32.add + local.set $l4548 + local.get $l4547 + local.get $l4548 + i32.add + local.set $l4549 + local.get $l4548 + local.get $l4549 + i32.add + local.set $l4550 + local.get $l4549 + local.get $l4550 + i32.add + local.set $l4551 + local.get $l4550 + local.get $l4551 + i32.add + local.set $l4552 + local.get $l4551 + local.get $l4552 + i32.add + local.set $l4553 + local.get $l4552 + local.get $l4553 + i32.add + local.set $l4554 + local.get $l4553 + local.get $l4554 + i32.add + local.set $l4555 + local.get $l4554 + local.get $l4555 + i32.add + local.set $l4556 + local.get $l4555 + local.get $l4556 + i32.add + local.set $l4557 + local.get $l4556 + local.get $l4557 + i32.add + local.set $l4558 + local.get $l4557 + local.get $l4558 + i32.add + local.set $l4559 + local.get $l4558 + local.get $l4559 + i32.add + local.set $l4560 + local.get $l4559 + local.get $l4560 + i32.add + local.set $l4561 + local.get $l4560 + local.get $l4561 + i32.add + local.set $l4562 + local.get $l4561 + local.get $l4562 + i32.add + local.set $l4563 + local.get $l4562 + local.get $l4563 + i32.add + local.set $l4564 + local.get $l4563 + local.get $l4564 + i32.add + local.set $l4565 + local.get $l4564 + local.get $l4565 + i32.add + local.set $l4566 + local.get $l4565 + local.get $l4566 + i32.add + local.set $l4567 + local.get $l4566 + local.get $l4567 + i32.add + local.set $l4568 + local.get $l4567 + local.get $l4568 + i32.add + local.set $l4569 + local.get $l4568 + local.get $l4569 + i32.add + local.set $l4570 + local.get $l4569 + local.get $l4570 + i32.add + local.set $l4571 + local.get $l4570 + local.get $l4571 + i32.add + local.set $l4572 + local.get $l4571 + local.get $l4572 + i32.add + local.set $l4573 + local.get $l4572 + local.get $l4573 + i32.add + local.set $l4574 + local.get $l4573 + local.get $l4574 + i32.add + local.set $l4575 + local.get $l4574 + local.get $l4575 + i32.add + local.set $l4576 + local.get $l4575 + local.get $l4576 + i32.add + local.set $l4577 + local.get $l4576 + local.get $l4577 + i32.add + local.set $l4578 + local.get $l4577 + local.get $l4578 + i32.add + local.set $l4579 + local.get $l4578 + local.get $l4579 + i32.add + local.set $l4580 + local.get $l4579 + local.get $l4580 + i32.add + local.set $l4581 + local.get $l4580 + local.get $l4581 + i32.add + local.set $l4582 + local.get $l4581 + local.get $l4582 + i32.add + local.set $l4583 + local.get $l4582 + local.get $l4583 + i32.add + local.set $l4584 + local.get $l4583 + local.get $l4584 + i32.add + local.set $l4585 + local.get $l4584 + local.get $l4585 + i32.add + local.set $l4586 + local.get $l4585 + local.get $l4586 + i32.add + local.set $l4587 + local.get $l4586 + local.get $l4587 + i32.add + local.set $l4588 + local.get $l4587 + local.get $l4588 + i32.add + local.set $l4589 + local.get $l4588 + local.get $l4589 + i32.add + local.set $l4590 + local.get $l4589 + local.get $l4590 + i32.add + local.set $l4591 + local.get $l4590 + local.get $l4591 + i32.add + local.set $l4592 + local.get $l4591 + local.get $l4592 + i32.add + local.set $l4593 + local.get $l4592 + local.get $l4593 + i32.add + local.set $l4594 + local.get $l4593 + local.get $l4594 + i32.add + local.set $l4595 + local.get $l4594 + local.get $l4595 + i32.add + local.set $l4596 + local.get $l4595 + local.get $l4596 + i32.add + local.set $l4597 + local.get $l4596 + local.get $l4597 + i32.add + local.set $l4598 + local.get $l4597 + local.get $l4598 + i32.add + local.set $l4599 + local.get $l4598 + local.get $l4599 + i32.add + local.set $l4600 + local.get $l4599 + local.get $l4600 + i32.add + local.set $l4601 + local.get $l4600 + local.get $l4601 + i32.add + local.set $l4602 + local.get $l4601 + local.get $l4602 + i32.add + local.set $l4603 + local.get $l4602 + local.get $l4603 + i32.add + local.set $l4604 + local.get $l4603 + local.get $l4604 + i32.add + local.set $l4605 + local.get $l4604 + local.get $l4605 + i32.add + local.set $l4606 + local.get $l4605 + local.get $l4606 + i32.add + local.set $l4607 + local.get $l4606 + local.get $l4607 + i32.add + local.set $l4608 + local.get $l4607 + local.get $l4608 + i32.add + local.set $l4609 + local.get $l4608 + local.get $l4609 + i32.add + local.set $l4610 + local.get $l4609 + local.get $l4610 + i32.add + local.set $l4611 + local.get $l4610 + local.get $l4611 + i32.add + local.set $l4612 + local.get $l4611 + local.get $l4612 + i32.add + local.set $l4613 + local.get $l4612 + local.get $l4613 + i32.add + local.set $l4614 + local.get $l4613 + local.get $l4614 + i32.add + local.set $l4615 + local.get $l4614 + local.get $l4615 + i32.add + local.set $l4616 + local.get $l4615 + local.get $l4616 + i32.add + local.set $l4617 + local.get $l4616 + local.get $l4617 + i32.add + local.set $l4618 + local.get $l4617 + local.get $l4618 + i32.add + local.set $l4619 + local.get $l4618 + local.get $l4619 + i32.add + local.set $l4620 + local.get $l4619 + local.get $l4620 + i32.add + local.set $l4621 + local.get $l4620 + local.get $l4621 + i32.add + local.set $l4622 + local.get $l4621 + local.get $l4622 + i32.add + local.set $l4623 + local.get $l4622 + local.get $l4623 + i32.add + local.set $l4624 + local.get $l4623 + local.get $l4624 + i32.add + local.set $l4625 + local.get $l4624 + local.get $l4625 + i32.add + local.set $l4626 + local.get $l4625 + local.get $l4626 + i32.add + local.set $l4627 + local.get $l4626 + local.get $l4627 + i32.add + local.set $l4628 + local.get $l4627 + local.get $l4628 + i32.add + local.set $l4629 + local.get $l4628 + local.get $l4629 + i32.add + local.set $l4630 + local.get $l4629 + local.get $l4630 + i32.add + local.set $l4631 + local.get $l4630 + local.get $l4631 + i32.add + local.set $l4632 + local.get $l4631 + local.get $l4632 + i32.add + local.set $l4633 + local.get $l4632 + local.get $l4633 + i32.add + local.set $l4634 + local.get $l4633 + local.get $l4634 + i32.add + local.set $l4635 + local.get $l4634 + local.get $l4635 + i32.add + local.set $l4636 + local.get $l4635 + local.get $l4636 + i32.add + local.set $l4637 + local.get $l4636 + local.get $l4637 + i32.add + local.set $l4638 + local.get $l4637 + local.get $l4638 + i32.add + local.set $l4639 + local.get $l4638 + local.get $l4639 + i32.add + local.set $l4640 + local.get $l4639 + local.get $l4640 + i32.add + local.set $l4641 + local.get $l4640 + local.get $l4641 + i32.add + local.set $l4642 + local.get $l4641 + local.get $l4642 + i32.add + local.set $l4643 + local.get $l4642 + local.get $l4643 + i32.add + local.set $l4644 + local.get $l4643 + local.get $l4644 + i32.add + local.set $l4645 + local.get $l4644 + local.get $l4645 + i32.add + local.set $l4646 + local.get $l4645 + local.get $l4646 + i32.add + local.set $l4647 + local.get $l4646 + local.get $l4647 + i32.add + local.set $l4648 + local.get $l4647 + local.get $l4648 + i32.add + local.set $l4649 + local.get $l4648 + local.get $l4649 + i32.add + local.set $l4650 + local.get $l4649 + local.get $l4650 + i32.add + local.set $l4651 + local.get $l4650 + local.get $l4651 + i32.add + local.set $l4652 + local.get $l4651 + local.get $l4652 + i32.add + local.set $l4653 + local.get $l4652 + local.get $l4653 + i32.add + local.set $l4654 + local.get $l4653 + local.get $l4654 + i32.add + local.set $l4655 + local.get $l4654 + local.get $l4655 + i32.add + local.set $l4656 + local.get $l4655 + local.get $l4656 + i32.add + local.set $l4657 + local.get $l4656 + local.get $l4657 + i32.add + local.set $l4658 + local.get $l4657 + local.get $l4658 + i32.add + local.set $l4659 + local.get $l4658 + local.get $l4659 + i32.add + local.set $l4660 + local.get $l4659 + local.get $l4660 + i32.add + local.set $l4661 + local.get $l4660 + local.get $l4661 + i32.add + local.set $l4662 + local.get $l4661 + local.get $l4662 + i32.add + local.set $l4663 + local.get $l4662 + local.get $l4663 + i32.add + local.set $l4664 + local.get $l4663 + local.get $l4664 + i32.add + local.set $l4665 + local.get $l4664 + local.get $l4665 + i32.add + local.set $l4666 + local.get $l4665 + local.get $l4666 + i32.add + local.set $l4667 + local.get $l4666 + local.get $l4667 + i32.add + local.set $l4668 + local.get $l4667 + local.get $l4668 + i32.add + local.set $l4669 + local.get $l4668 + local.get $l4669 + i32.add + local.set $l4670 + local.get $l4669 + local.get $l4670 + i32.add + local.set $l4671 + local.get $l4670 + local.get $l4671 + i32.add + local.set $l4672 + local.get $l4671 + local.get $l4672 + i32.add + local.set $l4673 + local.get $l4672 + local.get $l4673 + i32.add + local.set $l4674 + local.get $l4673 + local.get $l4674 + i32.add + local.set $l4675 + local.get $l4674 + local.get $l4675 + i32.add + local.set $l4676 + local.get $l4675 + local.get $l4676 + i32.add + local.set $l4677 + local.get $l4676 + local.get $l4677 + i32.add + local.set $l4678 + local.get $l4677 + local.get $l4678 + i32.add + local.set $l4679 + local.get $l4678 + local.get $l4679 + i32.add + local.set $l4680 + local.get $l4679 + local.get $l4680 + i32.add + local.set $l4681 + local.get $l4680 + local.get $l4681 + i32.add + local.set $l4682 + local.get $l4681 + local.get $l4682 + i32.add + local.set $l4683 + local.get $l4682 + local.get $l4683 + i32.add + local.set $l4684 + local.get $l4683 + local.get $l4684 + i32.add + local.set $l4685 + local.get $l4684 + local.get $l4685 + i32.add + local.set $l4686 + local.get $l4685 + local.get $l4686 + i32.add + local.set $l4687 + local.get $l4686 + local.get $l4687 + i32.add + local.set $l4688 + local.get $l4687 + local.get $l4688 + i32.add + local.set $l4689 + local.get $l4688 + local.get $l4689 + i32.add + local.set $l4690 + local.get $l4689 + local.get $l4690 + i32.add + local.set $l4691 + local.get $l4690 + local.get $l4691 + i32.add + local.set $l4692 + local.get $l4691 + local.get $l4692 + i32.add + local.set $l4693 + local.get $l4692 + local.get $l4693 + i32.add + local.set $l4694 + local.get $l4693 + local.get $l4694 + i32.add + local.set $l4695 + local.get $l4694 + local.get $l4695 + i32.add + local.set $l4696 + local.get $l4695 + local.get $l4696 + i32.add + local.set $l4697 + local.get $l4696 + local.get $l4697 + i32.add + local.set $l4698 + local.get $l4697 + local.get $l4698 + i32.add + local.set $l4699 + local.get $l4698 + local.get $l4699 + i32.add + local.set $l4700 + local.get $l4699 + local.get $l4700 + i32.add + local.set $l4701 + local.get $l4700 + local.get $l4701 + i32.add + local.set $l4702 + local.get $l4701 + local.get $l4702 + i32.add + local.set $l4703 + local.get $l4702 + local.get $l4703 + i32.add + local.set $l4704 + local.get $l4703 + local.get $l4704 + i32.add + local.set $l4705 + local.get $l4704 + local.get $l4705 + i32.add + local.set $l4706 + local.get $l4705 + local.get $l4706 + i32.add + local.set $l4707 + local.get $l4706 + local.get $l4707 + i32.add + local.set $l4708 + local.get $l4707 + local.get $l4708 + i32.add + local.set $l4709 + local.get $l4708 + local.get $l4709 + i32.add + local.set $l4710 + local.get $l4709 + local.get $l4710 + i32.add + local.set $l4711 + local.get $l4710 + local.get $l4711 + i32.add + local.set $l4712 + local.get $l4711 + local.get $l4712 + i32.add + local.set $l4713 + local.get $l4712 + local.get $l4713 + i32.add + local.set $l4714 + local.get $l4713 + local.get $l4714 + i32.add + local.set $l4715 + local.get $l4714 + local.get $l4715 + i32.add + local.set $l4716 + local.get $l4715 + local.get $l4716 + i32.add + local.set $l4717 + local.get $l4716 + local.get $l4717 + i32.add + local.set $l4718 + local.get $l4717 + local.get $l4718 + i32.add + local.set $l4719 + local.get $l4718 + local.get $l4719 + i32.add + local.set $l4720 + local.get $l4719 + local.get $l4720 + i32.add + local.set $l4721 + local.get $l4720 + local.get $l4721 + i32.add + local.set $l4722 + local.get $l4721 + local.get $l4722 + i32.add + local.set $l4723 + local.get $l4722 + local.get $l4723 + i32.add + local.set $l4724 + local.get $l4723 + local.get $l4724 + i32.add + local.set $l4725 + local.get $l4724 + local.get $l4725 + i32.add + local.set $l4726 + local.get $l4725 + local.get $l4726 + i32.add + local.set $l4727 + local.get $l4726 + local.get $l4727 + i32.add + local.set $l4728 + local.get $l4727 + local.get $l4728 + i32.add + local.set $l4729 + local.get $l4728 + local.get $l4729 + i32.add + local.set $l4730 + local.get $l4729 + local.get $l4730 + i32.add + local.set $l4731 + local.get $l4730 + local.get $l4731 + i32.add + local.set $l4732 + local.get $l4731 + local.get $l4732 + i32.add + local.set $l4733 + local.get $l4732 + local.get $l4733 + i32.add + local.set $l4734 + local.get $l4733 + local.get $l4734 + i32.add + local.set $l4735 + local.get $l4734 + local.get $l4735 + i32.add + local.set $l4736 + local.get $l4735 + local.get $l4736 + i32.add + local.set $l4737 + local.get $l4736 + local.get $l4737 + i32.add + local.set $l4738 + local.get $l4737 + local.get $l4738 + i32.add + local.set $l4739 + local.get $l4738 + local.get $l4739 + i32.add + local.set $l4740 + local.get $l4739 + local.get $l4740 + i32.add + local.set $l4741 + local.get $l4740 + local.get $l4741 + i32.add + local.set $l4742 + local.get $l4741 + local.get $l4742 + i32.add + local.set $l4743 + local.get $l4742 + local.get $l4743 + i32.add + local.set $l4744 + local.get $l4743 + local.get $l4744 + i32.add + local.set $l4745 + local.get $l4744 + local.get $l4745 + i32.add + local.set $l4746 + local.get $l4745 + local.get $l4746 + i32.add + local.set $l4747 + local.get $l4746 + local.get $l4747 + i32.add + local.set $l4748 + local.get $l4747 + local.get $l4748 + i32.add + local.set $l4749 + local.get $l4748 + local.get $l4749 + i32.add + local.set $l4750 + local.get $l4749 + local.get $l4750 + i32.add + local.set $l4751 + local.get $l4750 + local.get $l4751 + i32.add + local.set $l4752 + local.get $l4751 + local.get $l4752 + i32.add + local.set $l4753 + local.get $l4752 + local.get $l4753 + i32.add + local.set $l4754 + local.get $l4753 + local.get $l4754 + i32.add + local.set $l4755 + local.get $l4754 + local.get $l4755 + i32.add + local.set $l4756 + local.get $l4755 + local.get $l4756 + i32.add + local.set $l4757 + local.get $l4756 + local.get $l4757 + i32.add + local.set $l4758 + local.get $l4757 + local.get $l4758 + i32.add + local.set $l4759 + local.get $l4758 + local.get $l4759 + i32.add + local.set $l4760 + local.get $l4759 + local.get $l4760 + i32.add + local.set $l4761 + local.get $l4760 + local.get $l4761 + i32.add + local.set $l4762 + local.get $l4761 + local.get $l4762 + i32.add + local.set $l4763 + local.get $l4762 + local.get $l4763 + i32.add + local.set $l4764 + local.get $l4763 + local.get $l4764 + i32.add + local.set $l4765 + local.get $l4764 + local.get $l4765 + i32.add + local.set $l4766 + local.get $l4765 + local.get $l4766 + i32.add + local.set $l4767 + local.get $l4766 + local.get $l4767 + i32.add + local.set $l4768 + local.get $l4767 + local.get $l4768 + i32.add + local.set $l4769 + local.get $l4768 + local.get $l4769 + i32.add + local.set $l4770 + local.get $l4769 + local.get $l4770 + i32.add + local.set $l4771 + local.get $l4770 + local.get $l4771 + i32.add + local.set $l4772 + local.get $l4771 + local.get $l4772 + i32.add + local.set $l4773 + local.get $l4772 + local.get $l4773 + i32.add + local.set $l4774 + local.get $l4773 + local.get $l4774 + i32.add + local.set $l4775 + local.get $l4774 + local.get $l4775 + i32.add + local.set $l4776 + local.get $l4775 + local.get $l4776 + i32.add + local.set $l4777 + local.get $l4776 + local.get $l4777 + i32.add + local.set $l4778 + local.get $l4777 + local.get $l4778 + i32.add + local.set $l4779 + local.get $l4778 + local.get $l4779 + i32.add + local.set $l4780 + local.get $l4779 + local.get $l4780 + i32.add + local.set $l4781 + local.get $l4780 + local.get $l4781 + i32.add + local.set $l4782 + local.get $l4781 + local.get $l4782 + i32.add + local.set $l4783 + local.get $l4782 + local.get $l4783 + i32.add + local.set $l4784 + local.get $l4783 + local.get $l4784 + i32.add + local.set $l4785 + local.get $l4784 + local.get $l4785 + i32.add + local.set $l4786 + local.get $l4785 + local.get $l4786 + i32.add + local.set $l4787 + local.get $l4786 + local.get $l4787 + i32.add + local.set $l4788 + local.get $l4787 + local.get $l4788 + i32.add + local.set $l4789 + local.get $l4788 + local.get $l4789 + i32.add + local.set $l4790 + local.get $l4789 + local.get $l4790 + i32.add + local.set $l4791 + local.get $l4790 + local.get $l4791 + i32.add + local.set $l4792 + local.get $l4791 + local.get $l4792 + i32.add + local.set $l4793 + local.get $l4792 + local.get $l4793 + i32.add + local.set $l4794 + local.get $l4793 + local.get $l4794 + i32.add + local.set $l4795 + local.get $l4794 + local.get $l4795 + i32.add + local.set $l4796 + local.get $l4795 + local.get $l4796 + i32.add + local.set $l4797 + local.get $l4796 + local.get $l4797 + i32.add + local.set $l4798 + local.get $l4797 + local.get $l4798 + i32.add + local.set $l4799 + local.get $l4798 + local.get $l4799 + i32.add + local.set $l4800 + local.get $l4799 + local.get $l4800 + i32.add + local.set $l4801 + local.get $l4800 + local.get $l4801 + i32.add + local.set $l4802 + local.get $l4801 + local.get $l4802 + i32.add + local.set $l4803 + local.get $l4802 + local.get $l4803 + i32.add + local.set $l4804 + local.get $l4803 + local.get $l4804 + i32.add + local.set $l4805 + local.get $l4804 + local.get $l4805 + i32.add + local.set $l4806 + local.get $l4805 + local.get $l4806 + i32.add + local.set $l4807 + local.get $l4806 + local.get $l4807 + i32.add + local.set $l4808 + local.get $l4807 + local.get $l4808 + i32.add + local.set $l4809 + local.get $l4808 + local.get $l4809 + i32.add + local.set $l4810 + local.get $l4809 + local.get $l4810 + i32.add + local.set $l4811 + local.get $l4810 + local.get $l4811 + i32.add + local.set $l4812 + local.get $l4811 + local.get $l4812 + i32.add + local.set $l4813 + local.get $l4812 + local.get $l4813 + i32.add + local.set $l4814 + local.get $l4813 + local.get $l4814 + i32.add + local.set $l4815 + local.get $l4814 + local.get $l4815 + i32.add + local.set $l4816 + local.get $l4815 + local.get $l4816 + i32.add + local.set $l4817 + local.get $l4816 + local.get $l4817 + i32.add + local.set $l4818 + local.get $l4817 + local.get $l4818 + i32.add + local.set $l4819 + local.get $l4818 + local.get $l4819 + i32.add + local.set $l4820 + local.get $l4819 + local.get $l4820 + i32.add + local.set $l4821 + local.get $l4820 + local.get $l4821 + i32.add + local.set $l4822 + local.get $l4821 + local.get $l4822 + i32.add + local.set $l4823 + local.get $l4822 + local.get $l4823 + i32.add + local.set $l4824 + local.get $l4823 + local.get $l4824 + i32.add + local.set $l4825 + local.get $l4824 + local.get $l4825 + i32.add + local.set $l4826 + local.get $l4825 + local.get $l4826 + i32.add + local.set $l4827 + local.get $l4826 + local.get $l4827 + i32.add + local.set $l4828 + local.get $l4827 + local.get $l4828 + i32.add + local.set $l4829 + local.get $l4828 + local.get $l4829 + i32.add + local.set $l4830 + local.get $l4829 + local.get $l4830 + i32.add + local.set $l4831 + local.get $l4830 + local.get $l4831 + i32.add + local.set $l4832 + local.get $l4831 + local.get $l4832 + i32.add + local.set $l4833 + local.get $l4832 + local.get $l4833 + i32.add + local.set $l4834 + local.get $l4833 + local.get $l4834 + i32.add + local.set $l4835 + local.get $l4834 + local.get $l4835 + i32.add + local.set $l4836 + local.get $l4835 + local.get $l4836 + i32.add + local.set $l4837 + local.get $l4836 + local.get $l4837 + i32.add + local.set $l4838 + local.get $l4837 + local.get $l4838 + i32.add + local.set $l4839 + local.get $l4838 + local.get $l4839 + i32.add + local.set $l4840 + local.get $l4839 + local.get $l4840 + i32.add + local.set $l4841 + local.get $l4840 + local.get $l4841 + i32.add + local.set $l4842 + local.get $l4841 + local.get $l4842 + i32.add + local.set $l4843 + local.get $l4842 + local.get $l4843 + i32.add + local.set $l4844 + local.get $l4843 + local.get $l4844 + i32.add + local.set $l4845 + local.get $l4844 + local.get $l4845 + i32.add + local.set $l4846 + local.get $l4845 + local.get $l4846 + i32.add + local.set $l4847 + local.get $l4846 + local.get $l4847 + i32.add + local.set $l4848 + local.get $l4847 + local.get $l4848 + i32.add + local.set $l4849 + local.get $l4848 + local.get $l4849 + i32.add + local.set $l4850 + local.get $l4849 + local.get $l4850 + i32.add + local.set $l4851 + local.get $l4850 + local.get $l4851 + i32.add + local.set $l4852 + local.get $l4851 + local.get $l4852 + i32.add + local.set $l4853 + local.get $l4852 + local.get $l4853 + i32.add + local.set $l4854 + local.get $l4853 + local.get $l4854 + i32.add + local.set $l4855 + local.get $l4854 + local.get $l4855 + i32.add + local.set $l4856 + local.get $l4855 + local.get $l4856 + i32.add + local.set $l4857 + local.get $l4856 + local.get $l4857 + i32.add + local.set $l4858 + local.get $l4857 + local.get $l4858 + i32.add + local.set $l4859 + local.get $l4858 + local.get $l4859 + i32.add + local.set $l4860 + local.get $l4859 + local.get $l4860 + i32.add + local.set $l4861 + local.get $l4860 + local.get $l4861 + i32.add + local.set $l4862 + local.get $l4861 + local.get $l4862 + i32.add + local.set $l4863 + local.get $l4862 + local.get $l4863 + i32.add + local.set $l4864 + local.get $l4863 + local.get $l4864 + i32.add + local.set $l4865 + local.get $l4864 + local.get $l4865 + i32.add + local.set $l4866 + local.get $l4865 + local.get $l4866 + i32.add + local.set $l4867 + local.get $l4866 + local.get $l4867 + i32.add + local.set $l4868 + local.get $l4867 + local.get $l4868 + i32.add + local.set $l4869 + local.get $l4868 + local.get $l4869 + i32.add + local.set $l4870 + local.get $l4869 + local.get $l4870 + i32.add + local.set $l4871 + local.get $l4870 + local.get $l4871 + i32.add + local.set $l4872 + local.get $l4871 + local.get $l4872 + i32.add + local.set $l4873 + local.get $l4872 + local.get $l4873 + i32.add + local.set $l4874 + local.get $l4873 + local.get $l4874 + i32.add + local.set $l4875 + local.get $l4874 + local.get $l4875 + i32.add + local.set $l4876 + local.get $l4875 + local.get $l4876 + i32.add + local.set $l4877 + local.get $l4876 + local.get $l4877 + i32.add + local.set $l4878 + local.get $l4877 + local.get $l4878 + i32.add + local.set $l4879 + local.get $l4878 + local.get $l4879 + i32.add + local.set $l4880 + local.get $l4879 + local.get $l4880 + i32.add + local.set $l4881 + local.get $l4880 + local.get $l4881 + i32.add + local.set $l4882 + local.get $l4881 + local.get $l4882 + i32.add + local.set $l4883 + local.get $l4882 + local.get $l4883 + i32.add + local.set $l4884 + local.get $l4883 + local.get $l4884 + i32.add + local.set $l4885 + local.get $l4884 + local.get $l4885 + i32.add + local.set $l4886 + local.get $l4885 + local.get $l4886 + i32.add + local.set $l4887 + local.get $l4886 + local.get $l4887 + i32.add + local.set $l4888 + local.get $l4887 + local.get $l4888 + i32.add + local.set $l4889 + local.get $l4888 + local.get $l4889 + i32.add + local.set $l4890 + local.get $l4889 + local.get $l4890 + i32.add + local.set $l4891 + local.get $l4890 + local.get $l4891 + i32.add + local.set $l4892 + local.get $l4891 + local.get $l4892 + i32.add + local.set $l4893 + local.get $l4892 + local.get $l4893 + i32.add + local.set $l4894 + local.get $l4893 + local.get $l4894 + i32.add + local.set $l4895 + local.get $l4894 + local.get $l4895 + i32.add + local.set $l4896 + local.get $l4895 + local.get $l4896 + i32.add + local.set $l4897 + local.get $l4896 + local.get $l4897 + i32.add + local.set $l4898 + local.get $l4897 + local.get $l4898 + i32.add + local.set $l4899 + local.get $l4898 + local.get $l4899 + i32.add + local.set $l4900 + local.get $l4899 + local.get $l4900 + i32.add + local.set $l4901 + local.get $l4900 + local.get $l4901 + i32.add + local.set $l4902 + local.get $l4901 + local.get $l4902 + i32.add + local.set $l4903 + local.get $l4902 + local.get $l4903 + i32.add + local.set $l4904 + local.get $l4903 + local.get $l4904 + i32.add + local.set $l4905 + local.get $l4904 + local.get $l4905 + i32.add + local.set $l4906 + local.get $l4905 + local.get $l4906 + i32.add + local.set $l4907 + local.get $l4906 + local.get $l4907 + i32.add + local.set $l4908 + local.get $l4907 + local.get $l4908 + i32.add + local.set $l4909 + local.get $l4908 + local.get $l4909 + i32.add + local.set $l4910 + local.get $l4909 + local.get $l4910 + i32.add + local.set $l4911 + local.get $l4910 + local.get $l4911 + i32.add + local.set $l4912 + local.get $l4911 + local.get $l4912 + i32.add + local.set $l4913 + local.get $l4912 + local.get $l4913 + i32.add + local.set $l4914 + local.get $l4913 + local.get $l4914 + i32.add + local.set $l4915 + local.get $l4914 + local.get $l4915 + i32.add + local.set $l4916 + local.get $l4915 + local.get $l4916 + i32.add + local.set $l4917 + local.get $l4916 + local.get $l4917 + i32.add + local.set $l4918 + local.get $l4917 + local.get $l4918 + i32.add + local.set $l4919 + local.get $l4918 + local.get $l4919 + i32.add + local.set $l4920 + local.get $l4919 + local.get $l4920 + i32.add + local.set $l4921 + local.get $l4920 + local.get $l4921 + i32.add + local.set $l4922 + local.get $l4921 + local.get $l4922 + i32.add + local.set $l4923 + local.get $l4922 + local.get $l4923 + i32.add + local.set $l4924 + local.get $l4923 + local.get $l4924 + i32.add + local.set $l4925 + local.get $l4924 + local.get $l4925 + i32.add + local.set $l4926 + local.get $l4925 + local.get $l4926 + i32.add + local.set $l4927 + local.get $l4926 + local.get $l4927 + i32.add + local.set $l4928 + local.get $l4927 + local.get $l4928 + i32.add + local.set $l4929 + local.get $l4928 + local.get $l4929 + i32.add + local.set $l4930 + local.get $l4929 + local.get $l4930 + i32.add + local.set $l4931 + local.get $l4930 + local.get $l4931 + i32.add + local.set $l4932 + local.get $l4931 + local.get $l4932 + i32.add + local.set $l4933 + local.get $l4932 + local.get $l4933 + i32.add + local.set $l4934 + local.get $l4933 + local.get $l4934 + i32.add + local.set $l4935 + local.get $l4934 + local.get $l4935 + i32.add + local.set $l4936 + local.get $l4935 + local.get $l4936 + i32.add + local.set $l4937 + local.get $l4936 + local.get $l4937 + i32.add + local.set $l4938 + local.get $l4937 + local.get $l4938 + i32.add + local.set $l4939 + local.get $l4938 + local.get $l4939 + i32.add + local.set $l4940 + local.get $l4939 + local.get $l4940 + i32.add + local.set $l4941 + local.get $l4940 + local.get $l4941 + i32.add + local.set $l4942 + local.get $l4941 + local.get $l4942 + i32.add + local.set $l4943 + local.get $l4942 + local.get $l4943 + i32.add + local.set $l4944 + local.get $l4943 + local.get $l4944 + i32.add + local.set $l4945 + local.get $l4944 + local.get $l4945 + i32.add + local.set $l4946 + local.get $l4945 + local.get $l4946 + i32.add + local.set $l4947 + local.get $l4946 + local.get $l4947 + i32.add + local.set $l4948 + local.get $l4947 + local.get $l4948 + i32.add + local.set $l4949 + local.get $l4948 + local.get $l4949 + i32.add + local.set $l4950 + local.get $l4949 + local.get $l4950 + i32.add + local.set $l4951 + local.get $l4950 + local.get $l4951 + i32.add + local.set $l4952 + local.get $l4951 + local.get $l4952 + i32.add + local.set $l4953 + local.get $l4952 + local.get $l4953 + i32.add + local.set $l4954 + local.get $l4953 + local.get $l4954 + i32.add + local.set $l4955 + local.get $l4954 + local.get $l4955 + i32.add + local.set $l4956 + local.get $l4955 + local.get $l4956 + i32.add + local.set $l4957 + local.get $l4956 + local.get $l4957 + i32.add + local.set $l4958 + local.get $l4957 + local.get $l4958 + i32.add + local.set $l4959 + local.get $l4958 + local.get $l4959 + i32.add + local.set $l4960 + local.get $l4959 + local.get $l4960 + i32.add + local.set $l4961 + local.get $l4960 + local.get $l4961 + i32.add + local.set $l4962 + local.get $l4961 + local.get $l4962 + i32.add + local.set $l4963 + local.get $l4962 + local.get $l4963 + i32.add + local.set $l4964 + local.get $l4963 + local.get $l4964 + i32.add + local.set $l4965 + local.get $l4964 + local.get $l4965 + i32.add + local.set $l4966 + local.get $l4965 + local.get $l4966 + i32.add + local.set $l4967 + local.get $l4966 + local.get $l4967 + i32.add + local.set $l4968 + local.get $l4967 + local.get $l4968 + i32.add + local.set $l4969 + local.get $l4968 + local.get $l4969 + i32.add + local.set $l4970 + local.get $l4969 + local.get $l4970 + i32.add + local.set $l4971 + local.get $l4970 + local.get $l4971 + i32.add + local.set $l4972 + local.get $l4971 + local.get $l4972 + i32.add + local.set $l4973 + local.get $l4972 + local.get $l4973 + i32.add + local.set $l4974 + local.get $l4973 + local.get $l4974 + i32.add + local.set $l4975 + local.get $l4974 + local.get $l4975 + i32.add + local.set $l4976 + local.get $l4975 + local.get $l4976 + i32.add + local.set $l4977 + local.get $l4976 + local.get $l4977 + i32.add + local.set $l4978 + local.get $l4977 + local.get $l4978 + i32.add + local.set $l4979 + local.get $l4978 + local.get $l4979 + i32.add + local.set $l4980 + local.get $l4979 + local.get $l4980 + i32.add + local.set $l4981 + local.get $l4980 + local.get $l4981 + i32.add + local.set $l4982 + local.get $l4981 + local.get $l4982 + i32.add + local.set $l4983 + local.get $l4982 + local.get $l4983 + i32.add + local.set $l4984 + local.get $l4983 + local.get $l4984 + i32.add + local.set $l4985 + local.get $l4984 + local.get $l4985 + i32.add + local.set $l4986 + local.get $l4985 + local.get $l4986 + i32.add + local.set $l4987 + local.get $l4986 + local.get $l4987 + i32.add + local.set $l4988 + local.get $l4987 + local.get $l4988 + i32.add + local.set $l4989 + local.get $l4988 + local.get $l4989 + i32.add + local.set $l4990 + local.get $l4989 + local.get $l4990 + i32.add + local.set $l4991 + local.get $l4990 + local.get $l4991 + i32.add + local.set $l4992 + local.get $l4991 + local.get $l4992 + i32.add + local.set $l4993 + local.get $l4992 + local.get $l4993 + i32.add + local.set $l4994 + local.get $l4993 + local.get $l4994 + i32.add + local.set $l4995 + local.get $l4994 + local.get $l4995 + i32.add + local.set $l4996 + local.get $l4995 + local.get $l4996 + i32.add + local.set $l4997 + local.get $l4996 + local.get $l4997 + i32.add + local.set $l4998 + local.get $l4997 + local.get $l4998 + i32.add + local.set $l4999 + local.get $l4998 + local.get $l4999 + i32.add + local.set $l5000 + local.get $l4999 + local.get $l5000 + i32.add + local.set $l5001 + local.get $l5000 + local.get $l5001 + i32.add + local.set $l5002 + local.get $l5001 + local.get $l5002 + i32.add + local.set $l5003 + local.get $l5002 + local.get $l5003 + i32.add + local.set $l5004 + local.get $l5003 + local.get $l5004 + i32.add + local.set $l5005 + local.get $l5004 + local.get $l5005 + i32.add + local.set $l5006 + local.get $l5005 + local.get $l5006 + i32.add + local.set $l5007 + local.get $l5006 + local.get $l5007 + i32.add + local.set $l5008 + local.get $l5007 + local.get $l5008 + i32.add + local.set $l5009 + local.get $l5008 + local.get $l5009 + i32.add + local.set $l5010 + local.get $l5009 + local.get $l5010 + i32.add + local.set $l5011 + local.get $l5010 + local.get $l5011 + i32.add + local.set $l5012 + local.get $l5011 + local.get $l5012 + i32.add + local.set $l5013 + local.get $l5012 + local.get $l5013 + i32.add + local.set $l5014 + local.get $l5013 + local.get $l5014 + i32.add + local.set $l5015 + local.get $l5014 + local.get $l5015 + i32.add + local.set $l5016 + local.get $l5015 + local.get $l5016 + i32.add + local.set $l5017 + local.get $l5016 + local.get $l5017 + i32.add + local.set $l5018 + local.get $l5017 + local.get $l5018 + i32.add + local.set $l5019 + local.get $l5018 + local.get $l5019 + i32.add + local.set $l5020 + local.get $l5019 + local.get $l5020 + i32.add + local.set $l5021 + local.get $l5020 + local.get $l5021 + i32.add + local.set $l5022 + local.get $l5021 + local.get $l5022 + i32.add + local.set $l5023 + local.get $l5022 + local.get $l5023 + i32.add + local.set $l5024 + local.get $l5023 + local.get $l5024 + i32.add + local.set $l5025 + local.get $l5024 + local.get $l5025 + i32.add + local.set $l5026 + local.get $l5025 + local.get $l5026 + i32.add + local.set $l5027 + local.get $l5026 + local.get $l5027 + i32.add + local.set $l5028 + local.get $l5027 + local.get $l5028 + i32.add + local.set $l5029 + local.get $l5028 + local.get $l5029 + i32.add + local.set $l5030 + local.get $l5029 + local.get $l5030 + i32.add + local.set $l5031 + local.get $l5030 + local.get $l5031 + i32.add + local.set $l5032 + local.get $l5031 + local.get $l5032 + i32.add + local.set $l5033 + local.get $l5032 + local.get $l5033 + i32.add + local.set $l5034 + local.get $l5033 + local.get $l5034 + i32.add + local.set $l5035 + local.get $l5034 + local.get $l5035 + i32.add + local.set $l5036 + local.get $l5035 + local.get $l5036 + i32.add + local.set $l5037 + local.get $l5036 + local.get $l5037 + i32.add + local.set $l5038 + local.get $l5037 + local.get $l5038 + i32.add + local.set $l5039 + local.get $l5038 + local.get $l5039 + i32.add + local.set $l5040 + local.get $l5039 + local.get $l5040 + i32.add + local.set $l5041 + local.get $l5040 + local.get $l5041 + i32.add + local.set $l5042 + local.get $l5041 + local.get $l5042 + i32.add + local.set $l5043 + local.get $l5042 + local.get $l5043 + i32.add + local.set $l5044 + local.get $l5043 + local.get $l5044 + i32.add + local.set $l5045 + local.get $l5044 + local.get $l5045 + i32.add + local.set $l5046 + local.get $l5045 + local.get $l5046 + i32.add + local.set $l5047 + local.get $l5046 + local.get $l5047 + i32.add + local.set $l5048 + local.get $l5047 + local.get $l5048 + i32.add + local.set $l5049 + local.get $l5048 + local.get $l5049 + i32.add + local.set $l5050 + local.get $l5049 + local.get $l5050 + i32.add + local.set $l5051 + local.get $l5050 + local.get $l5051 + i32.add + local.set $l5052 + local.get $l5051 + local.get $l5052 + i32.add + local.set $l5053 + local.get $l5052 + local.get $l5053 + i32.add + local.set $l5054 + local.get $l5053 + local.get $l5054 + i32.add + local.set $l5055 + local.get $l5054 + local.get $l5055 + i32.add + local.set $l5056 + local.get $l5055 + local.get $l5056 + i32.add + local.set $l5057 + local.get $l5056 + local.get $l5057 + i32.add + local.set $l5058 + local.get $l5057 + local.get $l5058 + i32.add + local.set $l5059 + local.get $l5058 + local.get $l5059 + i32.add + local.set $l5060 + local.get $l5059 + local.get $l5060 + i32.add + local.set $l5061 + local.get $l5060 + local.get $l5061 + i32.add + local.set $l5062 + local.get $l5061 + local.get $l5062 + i32.add + local.set $l5063 + local.get $l5062 + local.get $l5063 + i32.add + local.set $l5064 + local.get $l5063 + local.get $l5064 + i32.add + local.set $l5065 + local.get $l5064 + local.get $l5065 + i32.add + local.set $l5066 + local.get $l5065 + local.get $l5066 + i32.add + local.set $l5067 + local.get $l5066 + local.get $l5067 + i32.add + local.set $l5068 + local.get $l5067 + local.get $l5068 + i32.add + local.set $l5069 + local.get $l5068 + local.get $l5069 + i32.add + local.set $l5070 + local.get $l5069 + local.get $l5070 + i32.add + local.set $l5071 + local.get $l5070 + local.get $l5071 + i32.add + local.set $l5072 + local.get $l5071 + local.get $l5072 + i32.add + local.set $l5073 + local.get $l5072 + local.get $l5073 + i32.add + local.set $l5074 + local.get $l5073 + local.get $l5074 + i32.add + local.set $l5075 + local.get $l5074 + local.get $l5075 + i32.add + local.set $l5076 + local.get $l5075 + local.get $l5076 + i32.add + local.set $l5077 + local.get $l5076 + local.get $l5077 + i32.add + local.set $l5078 + local.get $l5077 + local.get $l5078 + i32.add + local.set $l5079 + local.get $l5078 + local.get $l5079 + i32.add + local.set $l5080 + local.get $l5079 + local.get $l5080 + i32.add + local.set $l5081 + local.get $l5080 + local.get $l5081 + i32.add + local.set $l5082 + local.get $l5081 + local.get $l5082 + i32.add + local.set $l5083 + local.get $l5082 + local.get $l5083 + i32.add + local.set $l5084 + local.get $l5083 + local.get $l5084 + i32.add + local.set $l5085 + local.get $l5084 + local.get $l5085 + i32.add + local.set $l5086 + local.get $l5085 + local.get $l5086 + i32.add + local.set $l5087 + local.get $l5086 + local.get $l5087 + i32.add + local.set $l5088 + local.get $l5087 + local.get $l5088 + i32.add + local.set $l5089 + local.get $l5088 + local.get $l5089 + i32.add + local.set $l5090 + local.get $l5089 + local.get $l5090 + i32.add + local.set $l5091 + local.get $l5090 + local.get $l5091 + i32.add + local.set $l5092 + local.get $l5091 + local.get $l5092 + i32.add + local.set $l5093 + local.get $l5092 + local.get $l5093 + i32.add + local.set $l5094 + local.get $l5093 + local.get $l5094 + i32.add + local.set $l5095 + local.get $l5094 + local.get $l5095 + i32.add + local.set $l5096 + local.get $l5095 + local.get $l5096 + i32.add + local.set $l5097 + local.get $l5096 + local.get $l5097 + i32.add + local.set $l5098 + local.get $l5097 + local.get $l5098 + i32.add + local.set $l5099 + local.get $l5098 + local.get $l5099 + i32.add + local.set $l5100 + local.get $l5099 + local.get $l5100 + i32.add + local.set $l5101 + local.get $l5100 + local.get $l5101 + i32.add + local.set $l5102 + local.get $l5101 + local.get $l5102 + i32.add + local.set $l5103 + local.get $l5102 + local.get $l5103 + i32.add + local.set $l5104 + local.get $l5103 + local.get $l5104 + i32.add + local.set $l5105 + local.get $l5104 + local.get $l5105 + i32.add + local.set $l5106 + local.get $l5105 + local.get $l5106 + i32.add + local.set $l5107 + local.get $l5106 + local.get $l5107 + i32.add + local.set $l5108 + local.get $l5107 + local.get $l5108 + i32.add + local.set $l5109 + local.get $l5108 + local.get $l5109 + i32.add + local.set $l5110 + local.get $l5109 + local.get $l5110 + i32.add + local.set $l5111 + local.get $l5110 + local.get $l5111 + i32.add + local.set $l5112 + local.get $l5111 + local.get $l5112 + i32.add + local.set $l5113 + local.get $l5112 + local.get $l5113 + i32.add + local.set $l5114 + local.get $l5113 + local.get $l5114 + i32.add + local.set $l5115 + local.get $l5114 + local.get $l5115 + i32.add + local.set $l5116 + local.get $l5115 + local.get $l5116 + i32.add + local.set $l5117 + local.get $l5116 + local.get $l5117 + i32.add + local.set $l5118 + local.get $l5117 + local.get $l5118 + i32.add + local.set $l5119 + local.get $l5118 + local.get $l5119 + i32.add + local.set $l5120 + local.get $l5119 + local.get $l5120 + i32.add + local.set $l5121 + local.get $l5120 + local.get $l5121 + i32.add + local.set $l5122 + local.get $l5121 + local.get $l5122 + i32.add + local.set $l5123 + local.get $l5122 + local.get $l5123 + i32.add + local.set $l5124 + local.get $l5123 + local.get $l5124 + i32.add + local.set $l5125 + local.get $l5124 + local.get $l5125 + i32.add + local.set $l5126 + local.get $l5125 + local.get $l5126 + i32.add + local.set $l5127 + local.get $l5126 + local.get $l5127 + i32.add + local.set $l5128 + local.get $l5127 + local.get $l5128 + i32.add + local.set $l5129 + local.get $l5128 + local.get $l5129 + i32.add + local.set $l5130 + local.get $l5129 + local.get $l5130 + i32.add + local.set $l5131 + local.get $l5130 + local.get $l5131 + i32.add + local.set $l5132 + local.get $l5131 + local.get $l5132 + i32.add + local.set $l5133 + local.get $l5132 + local.get $l5133 + i32.add + local.set $l5134 + local.get $l5133 + local.get $l5134 + i32.add + local.set $l5135 + local.get $l5134 + local.get $l5135 + i32.add + local.set $l5136 + local.get $l5135 + local.get $l5136 + i32.add + local.set $l5137 + local.get $l5136 + local.get $l5137 + i32.add + local.set $l5138 + local.get $l5137 + local.get $l5138 + i32.add + local.set $l5139 + local.get $l5138 + local.get $l5139 + i32.add + local.set $l5140 + local.get $l5139 + local.get $l5140 + i32.add + local.set $l5141 + local.get $l5140 + local.get $l5141 + i32.add + local.set $l5142 + local.get $l5141 + local.get $l5142 + i32.add + local.set $l5143 + local.get $l5142 + local.get $l5143 + i32.add + local.set $l5144 + local.get $l5143 + local.get $l5144 + i32.add + local.set $l5145 + local.get $l5144 + local.get $l5145 + i32.add + local.set $l5146 + local.get $l5145 + local.get $l5146 + i32.add + local.set $l5147 + local.get $l5146 + local.get $l5147 + i32.add + local.set $l5148 + local.get $l5147 + local.get $l5148 + i32.add + local.set $l5149 + local.get $l5148 + local.get $l5149 + i32.add + local.set $l5150 + local.get $l5149 + local.get $l5150 + i32.add + local.set $l5151 + local.get $l5150 + local.get $l5151 + i32.add + local.set $l5152 + local.get $l5151 + local.get $l5152 + i32.add + local.set $l5153 + local.get $l5152 + local.get $l5153 + i32.add + local.set $l5154 + local.get $l5153 + local.get $l5154 + i32.add + local.set $l5155 + local.get $l5154 + local.get $l5155 + i32.add + local.set $l5156 + local.get $l5155 + local.get $l5156 + i32.add + local.set $l5157 + local.get $l5156 + local.get $l5157 + i32.add + local.set $l5158 + local.get $l5157 + local.get $l5158 + i32.add + local.set $l5159 + local.get $l5158 + local.get $l5159 + i32.add + local.set $l5160 + local.get $l5159 + local.get $l5160 + i32.add + local.set $l5161 + local.get $l5160 + local.get $l5161 + i32.add + local.set $l5162 + local.get $l5161 + local.get $l5162 + i32.add + local.set $l5163 + local.get $l5162 + local.get $l5163 + i32.add + local.set $l5164 + local.get $l5163 + local.get $l5164 + i32.add + local.set $l5165 + local.get $l5164 + local.get $l5165 + i32.add + local.set $l5166 + local.get $l5165 + local.get $l5166 + i32.add + local.set $l5167 + local.get $l5166 + local.get $l5167 + i32.add + local.set $l5168 + local.get $l5167 + local.get $l5168 + i32.add + local.set $l5169 + local.get $l5168 + local.get $l5169 + i32.add + local.set $l5170 + local.get $l5169 + local.get $l5170 + i32.add + local.set $l5171 + local.get $l5170 + local.get $l5171 + i32.add + local.set $l5172 + local.get $l5171 + local.get $l5172 + i32.add + local.set $l5173 + local.get $l5172 + local.get $l5173 + i32.add + local.set $l5174 + local.get $l5173 + local.get $l5174 + i32.add + local.set $l5175 + local.get $l5174 + local.get $l5175 + i32.add + local.set $l5176 + local.get $l5175 + local.get $l5176 + i32.add + local.set $l5177 + local.get $l5176 + local.get $l5177 + i32.add + local.set $l5178 + local.get $l5177 + local.get $l5178 + i32.add + local.set $l5179 + local.get $l5178 + local.get $l5179 + i32.add + local.set $l5180 + local.get $l5179 + local.get $l5180 + i32.add + local.set $l5181 + local.get $l5180 + local.get $l5181 + i32.add + local.set $l5182 + local.get $l5181 + local.get $l5182 + i32.add + local.set $l5183 + local.get $l5182 + local.get $l5183 + i32.add + local.set $l5184 + local.get $l5183 + local.get $l5184 + i32.add + local.set $l5185 + local.get $l5184 + local.get $l5185 + i32.add + local.set $l5186 + local.get $l5185 + local.get $l5186 + i32.add + local.set $l5187 + local.get $l5186 + local.get $l5187 + i32.add + local.set $l5188 + local.get $l5187 + local.get $l5188 + i32.add + local.set $l5189 + local.get $l5188 + local.get $l5189 + i32.add + local.set $l5190 + local.get $l5189 + local.get $l5190 + i32.add + local.set $l5191 + local.get $l5190 + local.get $l5191 + i32.add + local.set $l5192 + local.get $l5191 + local.get $l5192 + i32.add + local.set $l5193 + local.get $l5192 + local.get $l5193 + i32.add + local.set $l5194 + local.get $l5193 + local.get $l5194 + i32.add + local.set $l5195 + local.get $l5194 + local.get $l5195 + i32.add + local.set $l5196 + local.get $l5195 + local.get $l5196 + i32.add + local.set $l5197 + local.get $l5196 + local.get $l5197 + i32.add + local.set $l5198 + local.get $l5197 + local.get $l5198 + i32.add + local.set $l5199 + local.get $l5198 + local.get $l5199 + i32.add + local.set $l5200 + local.get $l5199 + local.get $l5200 + i32.add + local.set $l5201 + local.get $l5200 + local.get $l5201 + i32.add + local.set $l5202 + local.get $l5201 + local.get $l5202 + i32.add + local.set $l5203 + local.get $l5202 + local.get $l5203 + i32.add + local.set $l5204 + local.get $l5203 + local.get $l5204 + i32.add + local.set $l5205 + local.get $l5204 + local.get $l5205 + i32.add + local.set $l5206 + local.get $l5205 + local.get $l5206 + i32.add + local.set $l5207 + local.get $l5206 + local.get $l5207 + i32.add + local.set $l5208 + local.get $l5207 + local.get $l5208 + i32.add + local.set $l5209 + local.get $l5208 + local.get $l5209 + i32.add + local.set $l5210 + local.get $l5209 + local.get $l5210 + i32.add + local.set $l5211 + local.get $l5210 + local.get $l5211 + i32.add + local.set $l5212 + local.get $l5211 + local.get $l5212 + i32.add + local.set $l5213 + local.get $l5212 + local.get $l5213 + i32.add + local.set $l5214 + local.get $l5213 + local.get $l5214 + i32.add + local.set $l5215 + local.get $l5214 + local.get $l5215 + i32.add + local.set $l5216 + local.get $l5215 + local.get $l5216 + i32.add + local.set $l5217 + local.get $l5216 + local.get $l5217 + i32.add + local.set $l5218 + local.get $l5217 + local.get $l5218 + i32.add + local.set $l5219 + local.get $l5218 + local.get $l5219 + i32.add + local.set $l5220 + local.get $l5219 + local.get $l5220 + i32.add + local.set $l5221 + local.get $l5220 + local.get $l5221 + i32.add + local.set $l5222 + local.get $l5221 + local.get $l5222 + i32.add + local.set $l5223 + local.get $l5222 + local.get $l5223 + i32.add + local.set $l5224 + local.get $l5223 + local.get $l5224 + i32.add + local.set $l5225 + local.get $l5224 + local.get $l5225 + i32.add + local.set $l5226 + local.get $l5225 + local.get $l5226 + i32.add + local.set $l5227 + local.get $l5226 + local.get $l5227 + i32.add + local.set $l5228 + local.get $l5227 + local.get $l5228 + i32.add + local.set $l5229 + local.get $l5228 + local.get $l5229 + i32.add + local.set $l5230 + local.get $l5229 + local.get $l5230 + i32.add + local.set $l5231 + local.get $l5230 + local.get $l5231 + i32.add + local.set $l5232 + local.get $l5231 + local.get $l5232 + i32.add + local.set $l5233 + local.get $l5232 + local.get $l5233 + i32.add + local.set $l5234 + local.get $l5233 + local.get $l5234 + i32.add + local.set $l5235 + local.get $l5234 + local.get $l5235 + i32.add + local.set $l5236 + local.get $l5235 + local.get $l5236 + i32.add + local.set $l5237 + local.get $l5236 + local.get $l5237 + i32.add + local.set $l5238 + local.get $l5237 + local.get $l5238 + i32.add + local.set $l5239 + local.get $l5238 + local.get $l5239 + i32.add + local.set $l5240 + local.get $l5239 + local.get $l5240 + i32.add + local.set $l5241 + local.get $l5240 + local.get $l5241 + i32.add + local.set $l5242 + local.get $l5241 + local.get $l5242 + i32.add + local.set $l5243 + local.get $l5242 + local.get $l5243 + i32.add + local.set $l5244 + local.get $l5243 + local.get $l5244 + i32.add + local.set $l5245 + local.get $l5244 + local.get $l5245 + i32.add + local.set $l5246 + local.get $l5245 + local.get $l5246 + i32.add + local.set $l5247 + local.get $l5246 + local.get $l5247 + i32.add + local.set $l5248 + local.get $l5247 + local.get $l5248 + i32.add + local.set $l5249 + local.get $l5248 + local.get $l5249 + i32.add + local.set $l5250 + local.get $l5249 + local.get $l5250 + i32.add + local.set $l5251 + local.get $l5250 + local.get $l5251 + i32.add + local.set $l5252 + local.get $l5251 + local.get $l5252 + i32.add + local.set $l5253 + local.get $l5252 + local.get $l5253 + i32.add + local.set $l5254 + local.get $l5253 + local.get $l5254 + i32.add + local.set $l5255 + local.get $l5254 + local.get $l5255 + i32.add + local.set $l5256 + local.get $l5255 + local.get $l5256 + i32.add + local.set $l5257 + local.get $l5256 + local.get $l5257 + i32.add + local.set $l5258 + local.get $l5257 + local.get $l5258 + i32.add + local.set $l5259 + local.get $l5258 + local.get $l5259 + i32.add + local.set $l5260 + local.get $l5259 + local.get $l5260 + i32.add + local.set $l5261 + local.get $l5260 + local.get $l5261 + i32.add + local.set $l5262 + local.get $l5261 + local.get $l5262 + i32.add + local.set $l5263 + local.get $l5262 + local.get $l5263 + i32.add + local.set $l5264 + local.get $l5263 + local.get $l5264 + i32.add + local.set $l5265 + local.get $l5264 + local.get $l5265 + i32.add + local.set $l5266 + local.get $l5265 + local.get $l5266 + i32.add + local.set $l5267 + local.get $l5266 + local.get $l5267 + i32.add + local.set $l5268 + local.get $l5267 + local.get $l5268 + i32.add + local.set $l5269 + local.get $l5268 + local.get $l5269 + i32.add + local.set $l5270 + local.get $l5269 + local.get $l5270 + i32.add + local.set $l5271 + local.get $l5270 + local.get $l5271 + i32.add + local.set $l5272 + local.get $l5271 + local.get $l5272 + i32.add + local.set $l5273 + local.get $l5272 + local.get $l5273 + i32.add + local.set $l5274 + local.get $l5273 + local.get $l5274 + i32.add + local.set $l5275 + local.get $l5274 + local.get $l5275 + i32.add + local.set $l5276 + local.get $l5275 + local.get $l5276 + i32.add + local.set $l5277 + local.get $l5276 + local.get $l5277 + i32.add + local.set $l5278 + local.get $l5277 + local.get $l5278 + i32.add + local.set $l5279 + local.get $l5278 + local.get $l5279 + i32.add + local.set $l5280 + local.get $l5279 + local.get $l5280 + i32.add + local.set $l5281 + local.get $l5280 + local.get $l5281 + i32.add + local.set $l5282 + local.get $l5281 + local.get $l5282 + i32.add + local.set $l5283 + local.get $l5282 + local.get $l5283 + i32.add + local.set $l5284 + local.get $l5283 + local.get $l5284 + i32.add + local.set $l5285 + local.get $l5284 + local.get $l5285 + i32.add + local.set $l5286 + local.get $l5285 + local.get $l5286 + i32.add + local.set $l5287 + local.get $l5286 + local.get $l5287 + i32.add + local.set $l5288 + local.get $l5287 + local.get $l5288 + i32.add + local.set $l5289 + local.get $l5288 + local.get $l5289 + i32.add + local.set $l5290 + local.get $l5289 + local.get $l5290 + i32.add + local.set $l5291 + local.get $l5290 + local.get $l5291 + i32.add + local.set $l5292 + local.get $l5291 + local.get $l5292 + i32.add + local.set $l5293 + local.get $l5292 + local.get $l5293 + i32.add + local.set $l5294 + local.get $l5293 + local.get $l5294 + i32.add + local.set $l5295 + local.get $l5294 + local.get $l5295 + i32.add + local.set $l5296 + local.get $l5295 + local.get $l5296 + i32.add + local.set $l5297 + local.get $l5296 + local.get $l5297 + i32.add + local.set $l5298 + local.get $l5297 + local.get $l5298 + i32.add + local.set $l5299 + local.get $l5298 + local.get $l5299 + i32.add + local.set $l5300 + local.get $l5299 + local.get $l5300 + i32.add + local.set $l5301 + local.get $l5300 + local.get $l5301 + i32.add + local.set $l5302 + local.get $l5301 + local.get $l5302 + i32.add + local.set $l5303 + local.get $l5302 + local.get $l5303 + i32.add + local.set $l5304 + local.get $l5303 + local.get $l5304 + i32.add + local.set $l5305 + local.get $l5304 + local.get $l5305 + i32.add + local.set $l5306 + local.get $l5305 + local.get $l5306 + i32.add + local.set $l5307 + local.get $l5306 + local.get $l5307 + i32.add + local.set $l5308 + local.get $l5307 + local.get $l5308 + i32.add + local.set $l5309 + local.get $l5308 + local.get $l5309 + i32.add + local.set $l5310 + local.get $l5309 + local.get $l5310 + i32.add + local.set $l5311 + local.get $l5310 + local.get $l5311 + i32.add + local.set $l5312 + local.get $l5311 + local.get $l5312 + i32.add + local.set $l5313 + local.get $l5312 + local.get $l5313 + i32.add + local.set $l5314 + local.get $l5313 + local.get $l5314 + i32.add + local.set $l5315 + local.get $l5314 + local.get $l5315 + i32.add + local.set $l5316 + local.get $l5315 + local.get $l5316 + i32.add + local.set $l5317 + local.get $l5316 + local.get $l5317 + i32.add + local.set $l5318 + local.get $l5317 + local.get $l5318 + i32.add + local.set $l5319 + local.get $l5318 + local.get $l5319 + i32.add + local.set $l5320 + local.get $l5319 + local.get $l5320 + i32.add + local.set $l5321 + local.get $l5320 + local.get $l5321 + i32.add + local.set $l5322 + local.get $l5321 + local.get $l5322 + i32.add + local.set $l5323 + local.get $l5322 + local.get $l5323 + i32.add + local.set $l5324 + local.get $l5323 + local.get $l5324 + i32.add + local.set $l5325 + local.get $l5324 + local.get $l5325 + i32.add + local.set $l5326 + local.get $l5325 + local.get $l5326 + i32.add + local.set $l5327 + local.get $l5326 + local.get $l5327 + i32.add + local.set $l5328 + local.get $l5327 + local.get $l5328 + i32.add + local.set $l5329 + local.get $l5328 + local.get $l5329 + i32.add + local.set $l5330 + local.get $l5329 + local.get $l5330 + i32.add + local.set $l5331 + local.get $l5330 + local.get $l5331 + i32.add + local.set $l5332 + local.get $l5331 + local.get $l5332 + i32.add + local.set $l5333 + local.get $l5332 + local.get $l5333 + i32.add + local.set $l5334 + local.get $l5333 + local.get $l5334 + i32.add + local.set $l5335 + local.get $l5334 + local.get $l5335 + i32.add + local.set $l5336 + local.get $l5335 + local.get $l5336 + i32.add + local.set $l5337 + local.get $l5336 + local.get $l5337 + i32.add + local.set $l5338 + local.get $l5337 + local.get $l5338 + i32.add + local.set $l5339 + local.get $l5338 + local.get $l5339 + i32.add + local.set $l5340 + local.get $l5339 + local.get $l5340 + i32.add + local.set $l5341 + local.get $l5340 + local.get $l5341 + i32.add + local.set $l5342 + local.get $l5341 + local.get $l5342 + i32.add + local.set $l5343 + local.get $l5342 + local.get $l5343 + i32.add + local.set $l5344 + local.get $l5343 + local.get $l5344 + i32.add + local.set $l5345 + local.get $l5344 + local.get $l5345 + i32.add + local.set $l5346 + local.get $l5345 + local.get $l5346 + i32.add + local.set $l5347 + local.get $l5346 + local.get $l5347 + i32.add + local.set $l5348 + local.get $l5347 + local.get $l5348 + i32.add + local.set $l5349 + local.get $l5348 + local.get $l5349 + i32.add + local.set $l5350 + local.get $l5349 + local.get $l5350 + i32.add + local.set $l5351 + local.get $l5350 + local.get $l5351 + i32.add + local.set $l5352 + local.get $l5351 + local.get $l5352 + i32.add + local.set $l5353 + local.get $l5352 + local.get $l5353 + i32.add + local.set $l5354 + local.get $l5353 + local.get $l5354 + i32.add + local.set $l5355 + local.get $l5354 + local.get $l5355 + i32.add + local.set $l5356 + local.get $l5355 + local.get $l5356 + i32.add + local.set $l5357 + local.get $l5356 + local.get $l5357 + i32.add + local.set $l5358 + local.get $l5357 + local.get $l5358 + i32.add + local.set $l5359 + local.get $l5358 + local.get $l5359 + i32.add + local.set $l5360 + local.get $l5359 + local.get $l5360 + i32.add + local.set $l5361 + local.get $l5360 + local.get $l5361 + i32.add + local.set $l5362 + local.get $l5361 + local.get $l5362 + i32.add + local.set $l5363 + local.get $l5362 + local.get $l5363 + i32.add + local.set $l5364 + local.get $l5363 + local.get $l5364 + i32.add + local.set $l5365 + local.get $l5364 + local.get $l5365 + i32.add + local.set $l5366 + local.get $l5365 + local.get $l5366 + i32.add + local.set $l5367 + local.get $l5366 + local.get $l5367 + i32.add + local.set $l5368 + local.get $l5367 + local.get $l5368 + i32.add + local.set $l5369 + local.get $l5368 + local.get $l5369 + i32.add + local.set $l5370 + local.get $l5369 + local.get $l5370 + i32.add + local.set $l5371 + local.get $l5370 + local.get $l5371 + i32.add + local.set $l5372 + local.get $l5371 + local.get $l5372 + i32.add + local.set $l5373 + local.get $l5372 + local.get $l5373 + i32.add + local.set $l5374 + local.get $l5373 + local.get $l5374 + i32.add + local.set $l5375 + local.get $l5374 + local.get $l5375 + i32.add + local.set $l5376 + local.get $l5375 + local.get $l5376 + i32.add + local.set $l5377 + local.get $l5376 + local.get $l5377 + i32.add + local.set $l5378 + local.get $l5377 + local.get $l5378 + i32.add + local.set $l5379 + local.get $l5378 + local.get $l5379 + i32.add + local.set $l5380 + local.get $l5379 + local.get $l5380 + i32.add + local.set $l5381 + local.get $l5380 + local.get $l5381 + i32.add + local.set $l5382 + local.get $l5381 + local.get $l5382 + i32.add + local.set $l5383 + local.get $l5382 + local.get $l5383 + i32.add + local.set $l5384 + local.get $l5383 + local.get $l5384 + i32.add + local.set $l5385 + local.get $l5384 + local.get $l5385 + i32.add + local.set $l5386 + local.get $l5385 + local.get $l5386 + i32.add + local.set $l5387 + local.get $l5386 + local.get $l5387 + i32.add + local.set $l5388 + local.get $l5387 + local.get $l5388 + i32.add + local.set $l5389 + local.get $l5388 + local.get $l5389 + i32.add + local.set $l5390 + local.get $l5389 + local.get $l5390 + i32.add + local.set $l5391 + local.get $l5390 + local.get $l5391 + i32.add + local.set $l5392 + local.get $l5391 + local.get $l5392 + i32.add + local.set $l5393 + local.get $l5392 + local.get $l5393 + i32.add + local.set $l5394 + local.get $l5393 + local.get $l5394 + i32.add + local.set $l5395 + local.get $l5394 + local.get $l5395 + i32.add + local.set $l5396 + local.get $l5395 + local.get $l5396 + i32.add + local.set $l5397 + local.get $l5396 + local.get $l5397 + i32.add + local.set $l5398 + local.get $l5397 + local.get $l5398 + i32.add + local.set $l5399 + local.get $l5398 + local.get $l5399 + i32.add + local.set $l5400 + local.get $l5399 + local.get $l5400 + i32.add + local.set $l5401 + local.get $l5400 + local.get $l5401 + i32.add + local.set $l5402 + local.get $l5401 + local.get $l5402 + i32.add + local.set $l5403 + local.get $l5402 + local.get $l5403 + i32.add + local.set $l5404 + local.get $l5403 + local.get $l5404 + i32.add + local.set $l5405 + local.get $l5404 + local.get $l5405 + i32.add + local.set $l5406 + local.get $l5405 + local.get $l5406 + i32.add + local.set $l5407 + local.get $l5406 + local.get $l5407 + i32.add + local.set $l5408 + local.get $l5407 + local.get $l5408 + i32.add + local.set $l5409 + local.get $l5408 + local.get $l5409 + i32.add + local.set $l5410 + local.get $l5409 + local.get $l5410 + i32.add + local.set $l5411 + local.get $l5410 + local.get $l5411 + i32.add + local.set $l5412 + local.get $l5411 + local.get $l5412 + i32.add + local.set $l5413 + local.get $l5412 + local.get $l5413 + i32.add + local.set $l5414 + local.get $l5413 + local.get $l5414 + i32.add + local.set $l5415 + local.get $l5414 + local.get $l5415 + i32.add + local.set $l5416 + local.get $l5415 + local.get $l5416 + i32.add + local.set $l5417 + local.get $l5416 + local.get $l5417 + i32.add + local.set $l5418 + local.get $l5417 + local.get $l5418 + i32.add + local.set $l5419 + local.get $l5418 + local.get $l5419 + i32.add + local.set $l5420 + local.get $l5419 + local.get $l5420 + i32.add + local.set $l5421 + local.get $l5420 + local.get $l5421 + i32.add + local.set $l5422 + local.get $l5421 + local.get $l5422 + i32.add + local.set $l5423 + local.get $l5422 + local.get $l5423 + i32.add + local.set $l5424 + local.get $l5423 + local.get $l5424 + i32.add + local.set $l5425 + local.get $l5424 + local.get $l5425 + i32.add + local.set $l5426 + local.get $l5425 + local.get $l5426 + i32.add + local.set $l5427 + local.get $l5426 + local.get $l5427 + i32.add + local.set $l5428 + local.get $l5427 + local.get $l5428 + i32.add + local.set $l5429 + local.get $l5428 + local.get $l5429 + i32.add + local.set $l5430 + local.get $l5429 + local.get $l5430 + i32.add + local.set $l5431 + local.get $l5430 + local.get $l5431 + i32.add + local.set $l5432 + local.get $l5431 + local.get $l5432 + i32.add + local.set $l5433 + local.get $l5432 + local.get $l5433 + i32.add + local.set $l5434 + local.get $l5433 + local.get $l5434 + i32.add + local.set $l5435 + local.get $l5434 + local.get $l5435 + i32.add + local.set $l5436 + local.get $l5435 + local.get $l5436 + i32.add + local.set $l5437 + local.get $l5436 + local.get $l5437 + i32.add + local.set $l5438 + local.get $l5437 + local.get $l5438 + i32.add + local.set $l5439 + local.get $l5438 + local.get $l5439 + i32.add + local.set $l5440 + local.get $l5439 + local.get $l5440 + i32.add + local.set $l5441 + local.get $l5440 + local.get $l5441 + i32.add + local.set $l5442 + local.get $l5441 + local.get $l5442 + i32.add + local.set $l5443 + local.get $l5442 + local.get $l5443 + i32.add + local.set $l5444 + local.get $l5443 + local.get $l5444 + i32.add + local.set $l5445 + local.get $l5444 + local.get $l5445 + i32.add + local.set $l5446 + local.get $l5445 + local.get $l5446 + i32.add + local.set $l5447 + local.get $l5446 + local.get $l5447 + i32.add + local.set $l5448 + local.get $l5447 + local.get $l5448 + i32.add + local.set $l5449 + local.get $l5448 + local.get $l5449 + i32.add + local.set $l5450 + local.get $l5449 + local.get $l5450 + i32.add + local.set $l5451 + local.get $l5450 + local.get $l5451 + i32.add + local.set $l5452 + local.get $l5451 + local.get $l5452 + i32.add + local.set $l5453 + local.get $l5452 + local.get $l5453 + i32.add + local.set $l5454 + local.get $l5453 + local.get $l5454 + i32.add + local.set $l5455 + local.get $l5454 + local.get $l5455 + i32.add + local.set $l5456 + local.get $l5455 + local.get $l5456 + i32.add + local.set $l5457 + local.get $l5456 + local.get $l5457 + i32.add + local.set $l5458 + local.get $l5457 + local.get $l5458 + i32.add + local.set $l5459 + local.get $l5458 + local.get $l5459 + i32.add + local.set $l5460 + local.get $l5459 + local.get $l5460 + i32.add + local.set $l5461 + local.get $l5460 + local.get $l5461 + i32.add + local.set $l5462 + local.get $l5461 + local.get $l5462 + i32.add + local.set $l5463 + local.get $l5462 + local.get $l5463 + i32.add + local.set $l5464 + local.get $l5463 + local.get $l5464 + i32.add + local.set $l5465 + local.get $l5464 + local.get $l5465 + i32.add + local.set $l5466 + local.get $l5465 + local.get $l5466 + i32.add + local.set $l5467 + local.get $l5466 + local.get $l5467 + i32.add + local.set $l5468 + local.get $l5467 + local.get $l5468 + i32.add + local.set $l5469 + local.get $l5468 + local.get $l5469 + i32.add + local.set $l5470 + local.get $l5469 + local.get $l5470 + i32.add + local.set $l5471 + local.get $l5470 + local.get $l5471 + i32.add + local.set $l5472 + local.get $l5471 + local.get $l5472 + i32.add + local.set $l5473 + local.get $l5472 + local.get $l5473 + i32.add + local.set $l5474 + local.get $l5473 + local.get $l5474 + i32.add + local.set $l5475 + local.get $l5474 + local.get $l5475 + i32.add + local.set $l5476 + local.get $l5475 + local.get $l5476 + i32.add + local.set $l5477 + local.get $l5476 + local.get $l5477 + i32.add + local.set $l5478 + local.get $l5477 + local.get $l5478 + i32.add + local.set $l5479 + local.get $l5478 + local.get $l5479 + i32.add + local.set $l5480 + local.get $l5479 + local.get $l5480 + i32.add + local.set $l5481 + local.get $l5480 + local.get $l5481 + i32.add + local.set $l5482 + local.get $l5481 + local.get $l5482 + i32.add + local.set $l5483 + local.get $l5482 + local.get $l5483 + i32.add + local.set $l5484 + local.get $l5483 + local.get $l5484 + i32.add + local.set $l5485 + local.get $l5484 + local.get $l5485 + i32.add + local.set $l5486 + local.get $l5485 + local.get $l5486 + i32.add + local.set $l5487 + local.get $l5486 + local.get $l5487 + i32.add + local.set $l5488 + local.get $l5487 + local.get $l5488 + i32.add + local.set $l5489 + local.get $l5488 + local.get $l5489 + i32.add + local.set $l5490 + local.get $l5489 + local.get $l5490 + i32.add + local.set $l5491 + local.get $l5490 + local.get $l5491 + i32.add + local.set $l5492 + local.get $l5491 + local.get $l5492 + i32.add + local.set $l5493 + local.get $l5492 + local.get $l5493 + i32.add + local.set $l5494 + local.get $l5493 + local.get $l5494 + i32.add + local.set $l5495 + local.get $l5494 + local.get $l5495 + i32.add + local.set $l5496 + local.get $l5495 + local.get $l5496 + i32.add + local.set $l5497 + local.get $l5496 + local.get $l5497 + i32.add + local.set $l5498 + local.get $l5497 + local.get $l5498 + i32.add + local.set $l5499 + local.get $l5498 + local.get $l5499 + i32.add + local.set $l5500 + local.get $l5499 + local.get $l5500 + i32.add + local.set $l5501 + local.get $l5500 + local.get $l5501 + i32.add + local.set $l5502 + local.get $l5501 + local.get $l5502 + i32.add + local.set $l5503 + local.get $l5502 + local.get $l5503 + i32.add + local.set $l5504 + local.get $l5503 + local.get $l5504 + i32.add + local.set $l5505 + local.get $l5504 + local.get $l5505 + i32.add + local.set $l5506 + local.get $l5505 + local.get $l5506 + i32.add + local.set $l5507 + local.get $l5506 + local.get $l5507 + i32.add + local.set $l5508 + local.get $l5507 + local.get $l5508 + i32.add + local.set $l5509 + local.get $l5508 + local.get $l5509 + i32.add + local.set $l5510 + local.get $l5509 + local.get $l5510 + i32.add + local.set $l5511 + local.get $l5510 + local.get $l5511 + i32.add + local.set $l5512 + local.get $l5511 + local.get $l5512 + i32.add + local.set $l5513 + local.get $l5512 + local.get $l5513 + i32.add + local.set $l5514 + local.get $l5513 + local.get $l5514 + i32.add + local.set $l5515 + local.get $l5514 + local.get $l5515 + i32.add + local.set $l5516 + local.get $l5515 + local.get $l5516 + i32.add + local.set $l5517 + local.get $l5516 + local.get $l5517 + i32.add + local.set $l5518 + local.get $l5517 + local.get $l5518 + i32.add + local.set $l5519 + local.get $l5518 + local.get $l5519 + i32.add + local.set $l5520 + local.get $l5519 + local.get $l5520 + i32.add + local.set $l5521 + local.get $l5520 + local.get $l5521 + i32.add + local.set $l5522 + local.get $l5521 + local.get $l5522 + i32.add + local.set $l5523 + local.get $l5522 + local.get $l5523 + i32.add + local.set $l5524 + local.get $l5523 + local.get $l5524 + i32.add + local.set $l5525 + local.get $l5524 + local.get $l5525 + i32.add + local.set $l5526 + local.get $l5525 + local.get $l5526 + i32.add + local.set $l5527 + local.get $l5526 + local.get $l5527 + i32.add + local.set $l5528 + local.get $l5527 + local.get $l5528 + i32.add + local.set $l5529 + local.get $l5528 + local.get $l5529 + i32.add + local.set $l5530 + local.get $l5529 + local.get $l5530 + i32.add + local.set $l5531 + local.get $l5530 + local.get $l5531 + i32.add + local.set $l5532 + local.get $l5531 + local.get $l5532 + i32.add + local.set $l5533 + local.get $l5532 + local.get $l5533 + i32.add + local.set $l5534 + local.get $l5533 + local.get $l5534 + i32.add + local.set $l5535 + local.get $l5534 + local.get $l5535 + i32.add + local.set $l5536 + local.get $l5535 + local.get $l5536 + i32.add + local.set $l5537 + local.get $l5536 + local.get $l5537 + i32.add + local.set $l5538 + local.get $l5537 + local.get $l5538 + i32.add + local.set $l5539 + local.get $l5538 + local.get $l5539 + i32.add + local.set $l5540 + local.get $l5539 + local.get $l5540 + i32.add + local.set $l5541 + local.get $l5540 + local.get $l5541 + i32.add + local.set $l5542 + local.get $l5541 + local.get $l5542 + i32.add + local.set $l5543 + local.get $l5542 + local.get $l5543 + i32.add + local.set $l5544 + local.get $l5543 + local.get $l5544 + i32.add + local.set $l5545 + local.get $l5544 + local.get $l5545 + i32.add + local.set $l5546 + local.get $l5545 + local.get $l5546 + i32.add + local.set $l5547 + local.get $l5546 + local.get $l5547 + i32.add + local.set $l5548 + local.get $l5547 + local.get $l5548 + i32.add + local.set $l5549 + local.get $l5548 + local.get $l5549 + i32.add + local.set $l5550 + local.get $l5549 + local.get $l5550 + i32.add + local.set $l5551 + local.get $l5550 + local.get $l5551 + i32.add + local.set $l5552 + local.get $l5551 + local.get $l5552 + i32.add + local.set $l5553 + local.get $l5552 + local.get $l5553 + i32.add + local.set $l5554 + local.get $l5553 + local.get $l5554 + i32.add + local.set $l5555 + local.get $l5554 + local.get $l5555 + i32.add + local.set $l5556 + local.get $l5555 + local.get $l5556 + i32.add + local.set $l5557 + local.get $l5556 + local.get $l5557 + i32.add + local.set $l5558 + local.get $l5557 + local.get $l5558 + i32.add + local.set $l5559 + local.get $l5558 + local.get $l5559 + i32.add + local.set $l5560 + local.get $l5559 + local.get $l5560 + i32.add + local.set $l5561 + local.get $l5560 + local.get $l5561 + i32.add + local.set $l5562 + local.get $l5561 + local.get $l5562 + i32.add + local.set $l5563 + local.get $l5562 + local.get $l5563 + i32.add + local.set $l5564 + local.get $l5563 + local.get $l5564 + i32.add + local.set $l5565 + local.get $l5564 + local.get $l5565 + i32.add + local.set $l5566 + local.get $l5565 + local.get $l5566 + i32.add + local.set $l5567 + local.get $l5566 + local.get $l5567 + i32.add + local.set $l5568 + local.get $l5567 + local.get $l5568 + i32.add + local.set $l5569 + local.get $l5568 + local.get $l5569 + i32.add + local.set $l5570 + local.get $l5569 + local.get $l5570 + i32.add + local.set $l5571 + local.get $l5570 + local.get $l5571 + i32.add + local.set $l5572 + local.get $l5571 + local.get $l5572 + i32.add + local.set $l5573 + local.get $l5572 + local.get $l5573 + i32.add + local.set $l5574 + local.get $l5573 + local.get $l5574 + i32.add + local.set $l5575 + local.get $l5574 + local.get $l5575 + i32.add + local.set $l5576 + local.get $l5575 + local.get $l5576 + i32.add + local.set $l5577 + local.get $l5576 + local.get $l5577 + i32.add + local.set $l5578 + local.get $l5577 + local.get $l5578 + i32.add + local.set $l5579 + local.get $l5578 + local.get $l5579 + i32.add + local.set $l5580 + local.get $l5579 + local.get $l5580 + i32.add + local.set $l5581 + local.get $l5580 + local.get $l5581 + i32.add + local.set $l5582 + local.get $l5581 + local.get $l5582 + i32.add + local.set $l5583 + local.get $l5582 + local.get $l5583 + i32.add + local.set $l5584 + local.get $l5583 + local.get $l5584 + i32.add + local.set $l5585 + local.get $l5584 + local.get $l5585 + i32.add + local.set $l5586 + local.get $l5585 + local.get $l5586 + i32.add + local.set $l5587 + local.get $l5586 + local.get $l5587 + i32.add + local.set $l5588 + local.get $l5587 + local.get $l5588 + i32.add + local.set $l5589 + local.get $l5588 + local.get $l5589 + i32.add + local.set $l5590 + local.get $l5589 + local.get $l5590 + i32.add + local.set $l5591 + local.get $l5590 + local.get $l5591 + i32.add + local.set $l5592 + local.get $l5591 + local.get $l5592 + i32.add + local.set $l5593 + local.get $l5592 + local.get $l5593 + i32.add + local.set $l5594 + local.get $l5593 + local.get $l5594 + i32.add + local.set $l5595 + local.get $l5594 + local.get $l5595 + i32.add + local.set $l5596 + local.get $l5595 + local.get $l5596 + i32.add + local.set $l5597 + local.get $l5596 + local.get $l5597 + i32.add + local.set $l5598 + local.get $l5597 + local.get $l5598 + i32.add + local.set $l5599 + local.get $l5598 + local.get $l5599 + i32.add + local.set $l5600 + local.get $l5599 + local.get $l5600 + i32.add + local.set $l5601 + local.get $l5600 + local.get $l5601 + i32.add + local.set $l5602 + local.get $l5601 + local.get $l5602 + i32.add + local.set $l5603 + local.get $l5602 + local.get $l5603 + i32.add + local.set $l5604 + local.get $l5603 + local.get $l5604 + i32.add + local.set $l5605 + local.get $l5604 + local.get $l5605 + i32.add + local.set $l5606 + local.get $l5605 + local.get $l5606 + i32.add + local.set $l5607 + local.get $l5606 + local.get $l5607 + i32.add + local.set $l5608 + local.get $l5607 + local.get $l5608 + i32.add + local.set $l5609 + local.get $l5608 + local.get $l5609 + i32.add + local.set $l5610 + local.get $l5609 + local.get $l5610 + i32.add + local.set $l5611 + local.get $l5610 + local.get $l5611 + i32.add + local.set $l5612 + local.get $l5611 + local.get $l5612 + i32.add + local.set $l5613 + local.get $l5612 + local.get $l5613 + i32.add + local.set $l5614 + local.get $l5613 + local.get $l5614 + i32.add + local.set $l5615 + local.get $l5614 + local.get $l5615 + i32.add + local.set $l5616 + local.get $l5615 + local.get $l5616 + i32.add + local.set $l5617 + local.get $l5616 + local.get $l5617 + i32.add + local.set $l5618 + local.get $l5617 + local.get $l5618 + i32.add + local.set $l5619 + local.get $l5618 + local.get $l5619 + i32.add + local.set $l5620 + local.get $l5619 + local.get $l5620 + i32.add + local.set $l5621 + local.get $l5620 + local.get $l5621 + i32.add + local.set $l5622 + local.get $l5621 + local.get $l5622 + i32.add + local.set $l5623 + local.get $l5622 + local.get $l5623 + i32.add + local.set $l5624 + local.get $l5623 + local.get $l5624 + i32.add + local.set $l5625 + local.get $l5624 + local.get $l5625 + i32.add + local.set $l5626 + local.get $l5625 + local.get $l5626 + i32.add + local.set $l5627 + local.get $l5626 + local.get $l5627 + i32.add + local.set $l5628 + local.get $l5627 + local.get $l5628 + i32.add + local.set $l5629 + local.get $l5628 + local.get $l5629 + i32.add + local.set $l5630 + local.get $l5629 + local.get $l5630 + i32.add + local.set $l5631 + local.get $l5630 + local.get $l5631 + i32.add + local.set $l5632 + local.get $l5631 + local.get $l5632 + i32.add + local.set $l5633 + local.get $l5632 + local.get $l5633 + i32.add + local.set $l5634 + local.get $l5633 + local.get $l5634 + i32.add + local.set $l5635 + local.get $l5634 + local.get $l5635 + i32.add + local.set $l5636 + local.get $l5635 + local.get $l5636 + i32.add + local.set $l5637 + local.get $l5636 + local.get $l5637 + i32.add + local.set $l5638 + local.get $l5637 + local.get $l5638 + i32.add + local.set $l5639 + local.get $l5638 + local.get $l5639 + i32.add + local.set $l5640 + local.get $l5639 + local.get $l5640 + i32.add + local.set $l5641 + local.get $l5640 + local.get $l5641 + i32.add + local.set $l5642 + local.get $l5641 + local.get $l5642 + i32.add + local.set $l5643 + local.get $l5642 + local.get $l5643 + i32.add + local.set $l5644 + local.get $l5643 + local.get $l5644 + i32.add + local.set $l5645 + local.get $l5644 + local.get $l5645 + i32.add + local.set $l5646 + local.get $l5645 + local.get $l5646 + i32.add + local.set $l5647 + local.get $l5646 + local.get $l5647 + i32.add + local.set $l5648 + local.get $l5647 + local.get $l5648 + i32.add + local.set $l5649 + local.get $l5648 + local.get $l5649 + i32.add + local.set $l5650 + local.get $l5649 + local.get $l5650 + i32.add + local.set $l5651 + local.get $l5650 + local.get $l5651 + i32.add + local.set $l5652 + local.get $l5651 + local.get $l5652 + i32.add + local.set $l5653 + local.get $l5652 + local.get $l5653 + i32.add + local.set $l5654 + local.get $l5653 + local.get $l5654 + i32.add + local.set $l5655 + local.get $l5654 + local.get $l5655 + i32.add + local.set $l5656 + local.get $l5655 + local.get $l5656 + i32.add + local.set $l5657 + local.get $l5656 + local.get $l5657 + i32.add + local.set $l5658 + local.get $l5657 + local.get $l5658 + i32.add + local.set $l5659 + local.get $l5658 + local.get $l5659 + i32.add + local.set $l5660 + local.get $l5659 + local.get $l5660 + i32.add + local.set $l5661 + local.get $l5660 + local.get $l5661 + i32.add + local.set $l5662 + local.get $l5661 + local.get $l5662 + i32.add + local.set $l5663 + local.get $l5662 + local.get $l5663 + i32.add + local.set $l5664 + local.get $l5663 + local.get $l5664 + i32.add + local.set $l5665 + local.get $l5664 + local.get $l5665 + i32.add + local.set $l5666 + local.get $l5665 + local.get $l5666 + i32.add + local.set $l5667 + local.get $l5666 + local.get $l5667 + i32.add + local.set $l5668 + local.get $l5667 + local.get $l5668 + i32.add + local.set $l5669 + local.get $l5668 + local.get $l5669 + i32.add + local.set $l5670 + local.get $l5669 + local.get $l5670 + i32.add + local.set $l5671 + local.get $l5670 + local.get $l5671 + i32.add + local.set $l5672 + local.get $l5671 + local.get $l5672 + i32.add + local.set $l5673 + local.get $l5672 + local.get $l5673 + i32.add + local.set $l5674 + local.get $l5673 + local.get $l5674 + i32.add + local.set $l5675 + local.get $l5674 + local.get $l5675 + i32.add + local.set $l5676 + local.get $l5675 + local.get $l5676 + i32.add + local.set $l5677 + local.get $l5676 + local.get $l5677 + i32.add + local.set $l5678 + local.get $l5677 + local.get $l5678 + i32.add + local.set $l5679 + local.get $l5678 + local.get $l5679 + i32.add + local.set $l5680 + local.get $l5679 + local.get $l5680 + i32.add + local.set $l5681 + local.get $l5680 + local.get $l5681 + i32.add + local.set $l5682 + local.get $l5681 + local.get $l5682 + i32.add + local.set $l5683 + local.get $l5682 + local.get $l5683 + i32.add + local.set $l5684 + local.get $l5683 + local.get $l5684 + i32.add + local.set $l5685 + local.get $l5684 + local.get $l5685 + i32.add + local.set $l5686 + local.get $l5685 + local.get $l5686 + i32.add + local.set $l5687 + local.get $l5686 + local.get $l5687 + i32.add + local.set $l5688 + local.get $l5687 + local.get $l5688 + i32.add + local.set $l5689 + local.get $l5688 + local.get $l5689 + i32.add + local.set $l5690 + local.get $l5689 + local.get $l5690 + i32.add + local.set $l5691 + local.get $l5690 + local.get $l5691 + i32.add + local.set $l5692 + local.get $l5691 + local.get $l5692 + i32.add + local.set $l5693 + local.get $l5692 + local.get $l5693 + i32.add + local.set $l5694 + local.get $l5693 + local.get $l5694 + i32.add + local.set $l5695 + local.get $l5694 + local.get $l5695 + i32.add + local.set $l5696 + local.get $l5695 + local.get $l5696 + i32.add + local.set $l5697 + local.get $l5696 + local.get $l5697 + i32.add + local.set $l5698 + local.get $l5697 + local.get $l5698 + i32.add + local.set $l5699 + local.get $l5698 + local.get $l5699 + i32.add + local.set $l5700 + local.get $l5699 + local.get $l5700 + i32.add + local.set $l5701 + local.get $l5700 + local.get $l5701 + i32.add + local.set $l5702 + local.get $l5701 + local.get $l5702 + i32.add + local.set $l5703 + local.get $l5702 + local.get $l5703 + i32.add + local.set $l5704 + local.get $l5703 + local.get $l5704 + i32.add + local.set $l5705 + local.get $l5704 + local.get $l5705 + i32.add + local.set $l5706 + local.get $l5705 + local.get $l5706 + i32.add + local.set $l5707 + local.get $l5706 + local.get $l5707 + i32.add + local.set $l5708 + local.get $l5707 + local.get $l5708 + i32.add + local.set $l5709 + local.get $l5708 + local.get $l5709 + i32.add + local.set $l5710 + local.get $l5709 + local.get $l5710 + i32.add + local.set $l5711 + local.get $l5710 + local.get $l5711 + i32.add + local.set $l5712 + local.get $l5711 + local.get $l5712 + i32.add + local.set $l5713 + local.get $l5712 + local.get $l5713 + i32.add + local.set $l5714 + local.get $l5713 + local.get $l5714 + i32.add + local.set $l5715 + local.get $l5714 + local.get $l5715 + i32.add + local.set $l5716 + local.get $l5715 + local.get $l5716 + i32.add + local.set $l5717 + local.get $l5716 + local.get $l5717 + i32.add + local.set $l5718 + local.get $l5717 + local.get $l5718 + i32.add + local.set $l5719 + local.get $l5718 + local.get $l5719 + i32.add + local.set $l5720 + local.get $l5719 + local.get $l5720 + i32.add + local.set $l5721 + local.get $l5720 + local.get $l5721 + i32.add + local.set $l5722 + local.get $l5721 + local.get $l5722 + i32.add + local.set $l5723 + local.get $l5722 + local.get $l5723 + i32.add + local.set $l5724 + local.get $l5723 + local.get $l5724 + i32.add + local.set $l5725 + local.get $l5724 + local.get $l5725 + i32.add + local.set $l5726 + local.get $l5725 + local.get $l5726 + i32.add + local.set $l5727 + local.get $l5726 + local.get $l5727 + i32.add + local.set $l5728 + local.get $l5727 + local.get $l5728 + i32.add + local.set $l5729 + local.get $l5728 + local.get $l5729 + i32.add + local.set $l5730 + local.get $l5729 + local.get $l5730 + i32.add + local.set $l5731 + local.get $l5730 + local.get $l5731 + i32.add + local.set $l5732 + local.get $l5731 + local.get $l5732 + i32.add + local.set $l5733 + local.get $l5732 + local.get $l5733 + i32.add + local.set $l5734 + local.get $l5733 + local.get $l5734 + i32.add + local.set $l5735 + local.get $l5734 + local.get $l5735 + i32.add + local.set $l5736 + local.get $l5735 + local.get $l5736 + i32.add + local.set $l5737 + local.get $l5736 + local.get $l5737 + i32.add + local.set $l5738 + local.get $l5737 + local.get $l5738 + i32.add + local.set $l5739 + local.get $l5738 + local.get $l5739 + i32.add + local.set $l5740 + local.get $l5739 + local.get $l5740 + i32.add + local.set $l5741 + local.get $l5740 + local.get $l5741 + i32.add + local.set $l5742 + local.get $l5741 + local.get $l5742 + i32.add + local.set $l5743 + local.get $l5742 + local.get $l5743 + i32.add + local.set $l5744 + local.get $l5743 + local.get $l5744 + i32.add + local.set $l5745 + local.get $l5744 + local.get $l5745 + i32.add + local.set $l5746 + local.get $l5745 + local.get $l5746 + i32.add + local.set $l5747 + local.get $l5746 + local.get $l5747 + i32.add + local.set $l5748 + local.get $l5747 + local.get $l5748 + i32.add + local.set $l5749 + local.get $l5748 + local.get $l5749 + i32.add + local.set $l5750 + local.get $l5749 + local.get $l5750 + i32.add + local.set $l5751 + local.get $l5750 + local.get $l5751 + i32.add + local.set $l5752 + local.get $l5751 + local.get $l5752 + i32.add + local.set $l5753 + local.get $l5752 + local.get $l5753 + i32.add + local.set $l5754 + local.get $l5753 + local.get $l5754 + i32.add + local.set $l5755 + local.get $l5754 + local.get $l5755 + i32.add + local.set $l5756 + local.get $l5755 + local.get $l5756 + i32.add + local.set $l5757 + local.get $l5756 + local.get $l5757 + i32.add + local.set $l5758 + local.get $l5757 + local.get $l5758 + i32.add + local.set $l5759 + local.get $l5758 + local.get $l5759 + i32.add + local.set $l5760 + local.get $l5759 + local.get $l5760 + i32.add + local.set $l5761 + local.get $l5760 + local.get $l5761 + i32.add + local.set $l5762 + local.get $l5761 + local.get $l5762 + i32.add + local.set $l5763 + local.get $l5762 + local.get $l5763 + i32.add + local.set $l5764 + local.get $l5763 + local.get $l5764 + i32.add + local.set $l5765 + local.get $l5764 + local.get $l5765 + i32.add + local.set $l5766 + local.get $l5765 + local.get $l5766 + i32.add + local.set $l5767 + local.get $l5766 + local.get $l5767 + i32.add + local.set $l5768 + local.get $l5767 + local.get $l5768 + i32.add + local.set $l5769 + local.get $l5768 + local.get $l5769 + i32.add + local.set $l5770 + local.get $l5769 + local.get $l5770 + i32.add + local.set $l5771 + local.get $l5770 + local.get $l5771 + i32.add + local.set $l5772 + local.get $l5771 + local.get $l5772 + i32.add + local.set $l5773 + local.get $l5772 + local.get $l5773 + i32.add + local.set $l5774 + local.get $l5773 + local.get $l5774 + i32.add + local.set $l5775 + local.get $l5774 + local.get $l5775 + i32.add + local.set $l5776 + local.get $l5775 + local.get $l5776 + i32.add + local.set $l5777 + local.get $l5776 + local.get $l5777 + i32.add + local.set $l5778 + local.get $l5777 + local.get $l5778 + i32.add + local.set $l5779 + local.get $l5778 + local.get $l5779 + i32.add + local.set $l5780 + local.get $l5779 + local.get $l5780 + i32.add + local.set $l5781 + local.get $l5780 + local.get $l5781 + i32.add + local.set $l5782 + local.get $l5781 + local.get $l5782 + i32.add + local.set $l5783 + local.get $l5782 + local.get $l5783 + i32.add + local.set $l5784 + local.get $l5783 + local.get $l5784 + i32.add + local.set $l5785 + local.get $l5784 + local.get $l5785 + i32.add + local.set $l5786 + local.get $l5785 + local.get $l5786 + i32.add + local.set $l5787 + local.get $l5786 + local.get $l5787 + i32.add + local.set $l5788 + local.get $l5787 + local.get $l5788 + i32.add + local.set $l5789 + local.get $l5788 + local.get $l5789 + i32.add + local.set $l5790 + local.get $l5789 + local.get $l5790 + i32.add + local.set $l5791 + local.get $l5790 + local.get $l5791 + i32.add + local.set $l5792 + local.get $l5791 + local.get $l5792 + i32.add + local.set $l5793 + local.get $l5792 + local.get $l5793 + i32.add + local.set $l5794 + local.get $l5793 + local.get $l5794 + i32.add + local.set $l5795 + local.get $l5794 + local.get $l5795 + i32.add + local.set $l5796 + local.get $l5795 + local.get $l5796 + i32.add + local.set $l5797 + local.get $l5796 + local.get $l5797 + i32.add + local.set $l5798 + local.get $l5797 + local.get $l5798 + i32.add + local.set $l5799 + local.get $l5798 + local.get $l5799 + i32.add + local.set $l5800 + local.get $l5799 + local.get $l5800 + i32.add + local.set $l5801 + local.get $l5800 + local.get $l5801 + i32.add + local.set $l5802 + local.get $l5801 + local.get $l5802 + i32.add + local.set $l5803 + local.get $l5802 + local.get $l5803 + i32.add + local.set $l5804 + local.get $l5803 + local.get $l5804 + i32.add + local.set $l5805 + local.get $l5804 + local.get $l5805 + i32.add + local.set $l5806 + local.get $l5805 + local.get $l5806 + i32.add + local.set $l5807 + local.get $l5806 + local.get $l5807 + i32.add + local.set $l5808 + local.get $l5807 + local.get $l5808 + i32.add + local.set $l5809 + local.get $l5808 + local.get $l5809 + i32.add + local.set $l5810 + local.get $l5809 + local.get $l5810 + i32.add + local.set $l5811 + local.get $l5810 + local.get $l5811 + i32.add + local.set $l5812 + local.get $l5811 + local.get $l5812 + i32.add + local.set $l5813 + local.get $l5812 + local.get $l5813 + i32.add + local.set $l5814 + local.get $l5813 + local.get $l5814 + i32.add + local.set $l5815 + local.get $l5814 + local.get $l5815 + i32.add + local.set $l5816 + local.get $l5815 + local.get $l5816 + i32.add + local.set $l5817 + local.get $l5816 + local.get $l5817 + i32.add + local.set $l5818 + local.get $l5817 + local.get $l5818 + i32.add + local.set $l5819 + local.get $l5818 + local.get $l5819 + i32.add + local.set $l5820 + local.get $l5819 + local.get $l5820 + i32.add + local.set $l5821 + local.get $l5820 + local.get $l5821 + i32.add + local.set $l5822 + local.get $l5821 + local.get $l5822 + i32.add + local.set $l5823 + local.get $l5822 + local.get $l5823 + i32.add + local.set $l5824 + local.get $l5823 + local.get $l5824 + i32.add + local.set $l5825 + local.get $l5824 + local.get $l5825 + i32.add + local.set $l5826 + local.get $l5825 + local.get $l5826 + i32.add + local.set $l5827 + local.get $l5826 + local.get $l5827 + i32.add + local.set $l5828 + local.get $l5827 + local.get $l5828 + i32.add + local.set $l5829 + local.get $l5828 + local.get $l5829 + i32.add + local.set $l5830 + local.get $l5829 + local.get $l5830 + i32.add + local.set $l5831 + local.get $l5830 + local.get $l5831 + i32.add + local.set $l5832 + local.get $l5831 + local.get $l5832 + i32.add + local.set $l5833 + local.get $l5832 + local.get $l5833 + i32.add + local.set $l5834 + local.get $l5833 + local.get $l5834 + i32.add + local.set $l5835 + local.get $l5834 + local.get $l5835 + i32.add + local.set $l5836 + local.get $l5835 + local.get $l5836 + i32.add + local.set $l5837 + local.get $l5836 + local.get $l5837 + i32.add + local.set $l5838 + local.get $l5837 + local.get $l5838 + i32.add + local.set $l5839 + local.get $l5838 + local.get $l5839 + i32.add + local.set $l5840 + local.get $l5839 + local.get $l5840 + i32.add + local.set $l5841 + local.get $l5840 + local.get $l5841 + i32.add + local.set $l5842 + local.get $l5841 + local.get $l5842 + i32.add + local.set $l5843 + local.get $l5842 + local.get $l5843 + i32.add + local.set $l5844 + local.get $l5843 + local.get $l5844 + i32.add + local.set $l5845 + local.get $l5844 + local.get $l5845 + i32.add + local.set $l5846 + local.get $l5845 + local.get $l5846 + i32.add + local.set $l5847 + local.get $l5846 + local.get $l5847 + i32.add + local.set $l5848 + local.get $l5847 + local.get $l5848 + i32.add + local.set $l5849 + local.get $l5848 + local.get $l5849 + i32.add + local.set $l5850 + local.get $l5849 + local.get $l5850 + i32.add + local.set $l5851 + local.get $l5850 + local.get $l5851 + i32.add + local.set $l5852 + local.get $l5851 + local.get $l5852 + i32.add + local.set $l5853 + local.get $l5852 + local.get $l5853 + i32.add + local.set $l5854 + local.get $l5853 + local.get $l5854 + i32.add + local.set $l5855 + local.get $l5854 + local.get $l5855 + i32.add + local.set $l5856 + local.get $l5855 + local.get $l5856 + i32.add + local.set $l5857 + local.get $l5856 + local.get $l5857 + i32.add + local.set $l5858 + local.get $l5857 + local.get $l5858 + i32.add + local.set $l5859 + local.get $l5858 + local.get $l5859 + i32.add + local.set $l5860 + local.get $l5859 + local.get $l5860 + i32.add + local.set $l5861 + local.get $l5860 + local.get $l5861 + i32.add + local.set $l5862 + local.get $l5861 + local.get $l5862 + i32.add + local.set $l5863 + local.get $l5862 + local.get $l5863 + i32.add + local.set $l5864 + local.get $l5863 + local.get $l5864 + i32.add + local.set $l5865 + local.get $l5864 + local.get $l5865 + i32.add + local.set $l5866 + local.get $l5865 + local.get $l5866 + i32.add + local.set $l5867 + local.get $l5866 + local.get $l5867 + i32.add + local.set $l5868 + local.get $l5867 + local.get $l5868 + i32.add + local.set $l5869 + local.get $l5868 + local.get $l5869 + i32.add + local.set $l5870 + local.get $l5869 + local.get $l5870 + i32.add + local.set $l5871 + local.get $l5870 + local.get $l5871 + i32.add + local.set $l5872 + local.get $l5871 + local.get $l5872 + i32.add + local.set $l5873 + local.get $l5872 + local.get $l5873 + i32.add + local.set $l5874 + local.get $l5873 + local.get $l5874 + i32.add + local.set $l5875 + local.get $l5874 + local.get $l5875 + i32.add + local.set $l5876 + local.get $l5875 + local.get $l5876 + i32.add + local.set $l5877 + local.get $l5876 + local.get $l5877 + i32.add + local.set $l5878 + local.get $l5877 + local.get $l5878 + i32.add + local.set $l5879 + local.get $l5878 + local.get $l5879 + i32.add + local.set $l5880 + local.get $l5879 + local.get $l5880 + i32.add + local.set $l5881 + local.get $l5880 + local.get $l5881 + i32.add + local.set $l5882 + local.get $l5881 + local.get $l5882 + i32.add + local.set $l5883 + local.get $l5882 + local.get $l5883 + i32.add + local.set $l5884 + local.get $l5883 + local.get $l5884 + i32.add + local.set $l5885 + local.get $l5884 + local.get $l5885 + i32.add + local.set $l5886 + local.get $l5885 + local.get $l5886 + i32.add + local.set $l5887 + local.get $l5886 + local.get $l5887 + i32.add + local.set $l5888 + local.get $l5887 + local.get $l5888 + i32.add + local.set $l5889 + local.get $l5888 + local.get $l5889 + i32.add + local.set $l5890 + local.get $l5889 + local.get $l5890 + i32.add + local.set $l5891 + local.get $l5890 + local.get $l5891 + i32.add + local.set $l5892 + local.get $l5891 + local.get $l5892 + i32.add + local.set $l5893 + local.get $l5892 + local.get $l5893 + i32.add + local.set $l5894 + local.get $l5893 + local.get $l5894 + i32.add + local.set $l5895 + local.get $l5894 + local.get $l5895 + i32.add + local.set $l5896 + local.get $l5895 + local.get $l5896 + i32.add + local.set $l5897 + local.get $l5896 + local.get $l5897 + i32.add + local.set $l5898 + local.get $l5897 + local.get $l5898 + i32.add + local.set $l5899 + local.get $l5898 + local.get $l5899 + i32.add + local.set $l5900 + local.get $l5899 + local.get $l5900 + i32.add + local.set $l5901 + local.get $l5900 + local.get $l5901 + i32.add + local.set $l5902 + local.get $l5901 + local.get $l5902 + i32.add + local.set $l5903 + local.get $l5902 + local.get $l5903 + i32.add + local.set $l5904 + local.get $l5903 + local.get $l5904 + i32.add + local.set $l5905 + local.get $l5904 + local.get $l5905 + i32.add + local.set $l5906 + local.get $l5905 + local.get $l5906 + i32.add + local.set $l5907 + local.get $l5906 + local.get $l5907 + i32.add + local.set $l5908 + local.get $l5907 + local.get $l5908 + i32.add + local.set $l5909 + local.get $l5908 + local.get $l5909 + i32.add + local.set $l5910 + local.get $l5909 + local.get $l5910 + i32.add + local.set $l5911 + local.get $l5910 + local.get $l5911 + i32.add + local.set $l5912 + local.get $l5911 + local.get $l5912 + i32.add + local.set $l5913 + local.get $l5912 + local.get $l5913 + i32.add + local.set $l5914 + local.get $l5913 + local.get $l5914 + i32.add + local.set $l5915 + local.get $l5914 + local.get $l5915 + i32.add + local.set $l5916 + local.get $l5915 + local.get $l5916 + i32.add + local.set $l5917 + local.get $l5916 + local.get $l5917 + i32.add + local.set $l5918 + local.get $l5917 + local.get $l5918 + i32.add + local.set $l5919 + local.get $l5918 + local.get $l5919 + i32.add + local.set $l5920 + local.get $l5919 + local.get $l5920 + i32.add + local.set $l5921 + local.get $l5920 + local.get $l5921 + i32.add + local.set $l5922 + local.get $l5921 + local.get $l5922 + i32.add + local.set $l5923 + local.get $l5922 + local.get $l5923 + i32.add + local.set $l5924 + local.get $l5923 + local.get $l5924 + i32.add + local.set $l5925 + local.get $l5924 + local.get $l5925 + i32.add + local.set $l5926 + local.get $l5925 + local.get $l5926 + i32.add + local.set $l5927 + local.get $l5926 + local.get $l5927 + i32.add + local.set $l5928 + local.get $l5927 + local.get $l5928 + i32.add + local.set $l5929 + local.get $l5928 + local.get $l5929 + i32.add + local.set $l5930 + local.get $l5929 + local.get $l5930 + i32.add + local.set $l5931 + local.get $l5930 + local.get $l5931 + i32.add + local.set $l5932 + local.get $l5931 + local.get $l5932 + i32.add + local.set $l5933 + local.get $l5932 + local.get $l5933 + i32.add + local.set $l5934 + local.get $l5933 + local.get $l5934 + i32.add + local.set $l5935 + local.get $l5934 + local.get $l5935 + i32.add + local.set $l5936 + local.get $l5935 + local.get $l5936 + i32.add + local.set $l5937 + local.get $l5936 + local.get $l5937 + i32.add + local.set $l5938 + local.get $l5937 + local.get $l5938 + i32.add + local.set $l5939 + local.get $l5938 + local.get $l5939 + i32.add + local.set $l5940 + local.get $l5939 + local.get $l5940 + i32.add + local.set $l5941 + local.get $l5940 + local.get $l5941 + i32.add + local.set $l5942 + local.get $l5941 + local.get $l5942 + i32.add + local.set $l5943 + local.get $l5942 + local.get $l5943 + i32.add + local.set $l5944 + local.get $l5943 + local.get $l5944 + i32.add + local.set $l5945 + local.get $l5944 + local.get $l5945 + i32.add + local.set $l5946 + local.get $l5945 + local.get $l5946 + i32.add + local.set $l5947 + local.get $l5946 + local.get $l5947 + i32.add + local.set $l5948 + local.get $l5947 + local.get $l5948 + i32.add + local.set $l5949 + local.get $l5948 + local.get $l5949 + i32.add + local.set $l5950 + local.get $l5949 + local.get $l5950 + i32.add + local.set $l5951 + local.get $l5950 + local.get $l5951 + i32.add + local.set $l5952 + local.get $l5951 + local.get $l5952 + i32.add + local.set $l5953 + local.get $l5952 + local.get $l5953 + i32.add + local.set $l5954 + local.get $l5953 + local.get $l5954 + i32.add + local.set $l5955 + local.get $l5954 + local.get $l5955 + i32.add + local.set $l5956 + local.get $l5955 + local.get $l5956 + i32.add + local.set $l5957 + local.get $l5956 + local.get $l5957 + i32.add + local.set $l5958 + local.get $l5957 + local.get $l5958 + i32.add + local.set $l5959 + local.get $l5958 + local.get $l5959 + i32.add + local.set $l5960 + local.get $l5959 + local.get $l5960 + i32.add + local.set $l5961 + local.get $l5960 + local.get $l5961 + i32.add + local.set $l5962 + local.get $l5961 + local.get $l5962 + i32.add + local.set $l5963 + local.get $l5962 + local.get $l5963 + i32.add + local.set $l5964 + local.get $l5963 + local.get $l5964 + i32.add + local.set $l5965 + local.get $l5964 + local.get $l5965 + i32.add + local.set $l5966 + local.get $l5965 + local.get $l5966 + i32.add + local.set $l5967 + local.get $l5966 + local.get $l5967 + i32.add + local.set $l5968 + local.get $l5967 + local.get $l5968 + i32.add + local.set $l5969 + local.get $l5968 + local.get $l5969 + i32.add + local.set $l5970 + local.get $l5969 + local.get $l5970 + i32.add + local.set $l5971 + local.get $l5970 + local.get $l5971 + i32.add + local.set $l5972 + local.get $l5971 + local.get $l5972 + i32.add + local.set $l5973 + local.get $l5972 + local.get $l5973 + i32.add + local.set $l5974 + local.get $l5973 + local.get $l5974 + i32.add + local.set $l5975 + local.get $l5974 + local.get $l5975 + i32.add + local.set $l5976 + local.get $l5975 + local.get $l5976 + i32.add + local.set $l5977 + local.get $l5976 + local.get $l5977 + i32.add + local.set $l5978 + local.get $l5977 + local.get $l5978 + i32.add + local.set $l5979 + local.get $l5978 + local.get $l5979 + i32.add + local.set $l5980 + local.get $l5979 + local.get $l5980 + i32.add + local.set $l5981 + local.get $l5980 + local.get $l5981 + i32.add + local.set $l5982 + local.get $l5981 + local.get $l5982 + i32.add + local.set $l5983 + local.get $l5982 + local.get $l5983 + i32.add + local.set $l5984 + local.get $l5983 + local.get $l5984 + i32.add + local.set $l5985 + local.get $l5984 + local.get $l5985 + i32.add + local.set $l5986 + local.get $l5985 + local.get $l5986 + i32.add + local.set $l5987 + local.get $l5986 + local.get $l5987 + i32.add + local.set $l5988 + local.get $l5987 + local.get $l5988 + i32.add + local.set $l5989 + local.get $l5988 + local.get $l5989 + i32.add + local.set $l5990 + local.get $l5989 + local.get $l5990 + i32.add + local.set $l5991 + local.get $l5990 + local.get $l5991 + i32.add + local.set $l5992 + local.get $l5991 + local.get $l5992 + i32.add + local.set $l5993 + local.get $l5992 + local.get $l5993 + i32.add + local.set $l5994 + local.get $l5993 + local.get $l5994 + i32.add + local.set $l5995 + local.get $l5994 + local.get $l5995 + i32.add + local.set $l5996 + local.get $l5995 + local.get $l5996 + i32.add + local.set $l5997 + local.get $l5996 + local.get $l5997 + i32.add + local.set $l5998 + local.get $l5997 + local.get $l5998 + i32.add + local.set $l5999 + local.get $l5998 + local.get $l5999 + i32.add + local.set $l6000 + local.get $l5999 + local.get $l6000 + i32.add + local.set $l6001 + local.get $l6000 + local.get $l6001 + i32.add + local.set $l6002 + local.get $l6001 + local.get $l6002 + i32.add + local.set $l6003 + local.get $l6002 + local.get $l6003 + i32.add + local.set $l6004 + local.get $l6003 + local.get $l6004 + i32.add + local.set $l6005 + local.get $l6004 + local.get $l6005 + i32.add + local.set $l6006 + local.get $l6005 + local.get $l6006 + i32.add + local.set $l6007 + local.get $l6006 + local.get $l6007 + i32.add + local.set $l6008 + local.get $l6007 + local.get $l6008 + i32.add + local.set $l6009 + local.get $l6008 + local.get $l6009 + i32.add + local.set $l6010 + local.get $l6009 + local.get $l6010 + i32.add + local.set $l6011 + local.get $l6010 + local.get $l6011 + i32.add + local.set $l6012 + local.get $l6011 + local.get $l6012 + i32.add + local.set $l6013 + local.get $l6012 + local.get $l6013 + i32.add + local.set $l6014 + local.get $l6013 + local.get $l6014 + i32.add + local.set $l6015 + local.get $l6014 + local.get $l6015 + i32.add + local.set $l6016 + local.get $l6015 + local.get $l6016 + i32.add + local.set $l6017 + local.get $l6016 + local.get $l6017 + i32.add + local.set $l6018 + local.get $l6017 + local.get $l6018 + i32.add + local.set $l6019 + local.get $l6018 + local.get $l6019 + i32.add + local.set $l6020 + local.get $l6019 + local.get $l6020 + i32.add + local.set $l6021 + local.get $l6020 + local.get $l6021 + i32.add + local.set $l6022 + local.get $l6021 + local.get $l6022 + i32.add + local.set $l6023 + local.get $l6022 + local.get $l6023 + i32.add + local.set $l6024 + local.get $l6023 + local.get $l6024 + i32.add + local.set $l6025 + local.get $l6024 + local.get $l6025 + i32.add + local.set $l6026 + local.get $l6025 + local.get $l6026 + i32.add + local.set $l6027 + local.get $l6026 + local.get $l6027 + i32.add + local.set $l6028 + local.get $l6027 + local.get $l6028 + i32.add + local.set $l6029 + local.get $l6028 + local.get $l6029 + i32.add + local.set $l6030 + local.get $l6029 + local.get $l6030 + i32.add + local.set $l6031 + local.get $l6030 + local.get $l6031 + i32.add + local.set $l6032 + local.get $l6031 + local.get $l6032 + i32.add + local.set $l6033 + local.get $l6032 + local.get $l6033 + i32.add + local.set $l6034 + local.get $l6033 + local.get $l6034 + i32.add + local.set $l6035 + local.get $l6034 + local.get $l6035 + i32.add + local.set $l6036 + local.get $l6035 + local.get $l6036 + i32.add + local.set $l6037 + local.get $l6036 + local.get $l6037 + i32.add + local.set $l6038 + local.get $l6037 + local.get $l6038 + i32.add + local.set $l6039 + local.get $l6038 + local.get $l6039 + i32.add + local.set $l6040 + local.get $l6039 + local.get $l6040 + i32.add + local.set $l6041 + local.get $l6040 + local.get $l6041 + i32.add + local.set $l6042 + local.get $l6041 + local.get $l6042 + i32.add + local.set $l6043 + local.get $l6042 + local.get $l6043 + i32.add + local.set $l6044 + local.get $l6043 + local.get $l6044 + i32.add + local.set $l6045 + local.get $l6044 + local.get $l6045 + i32.add + local.set $l6046 + local.get $l6045 + local.get $l6046 + i32.add + local.set $l6047 + local.get $l6046 + local.get $l6047 + i32.add + local.set $l6048 + local.get $l6047 + local.get $l6048 + i32.add + local.set $l6049 + local.get $l6048 + local.get $l6049 + i32.add + local.set $l6050 + local.get $l6049 + local.get $l6050 + i32.add + local.set $l6051 + local.get $l6050 + local.get $l6051 + i32.add + local.set $l6052 + local.get $l6051 + local.get $l6052 + i32.add + local.set $l6053 + local.get $l6052 + local.get $l6053 + i32.add + local.set $l6054 + local.get $l6053 + local.get $l6054 + i32.add + local.set $l6055 + local.get $l6054 + local.get $l6055 + i32.add + local.set $l6056 + local.get $l6055 + local.get $l6056 + i32.add + local.set $l6057 + local.get $l6056 + local.get $l6057 + i32.add + local.set $l6058 + local.get $l6057 + local.get $l6058 + i32.add + local.set $l6059 + local.get $l6058 + local.get $l6059 + i32.add + local.set $l6060 + local.get $l6059 + local.get $l6060 + i32.add + local.set $l6061 + local.get $l6060 + local.get $l6061 + i32.add + local.set $l6062 + local.get $l6061 + local.get $l6062 + i32.add + local.set $l6063 + local.get $l6062 + local.get $l6063 + i32.add + local.set $l6064 + local.get $l6063 + local.get $l6064 + i32.add + local.set $l6065 + local.get $l6064 + local.get $l6065 + i32.add + local.set $l6066 + local.get $l6065 + local.get $l6066 + i32.add + local.set $l6067 + local.get $l6066 + local.get $l6067 + i32.add + local.set $l6068 + local.get $l6067 + local.get $l6068 + i32.add + local.set $l6069 + local.get $l6068 + local.get $l6069 + i32.add + local.set $l6070 + local.get $l6069 + local.get $l6070 + i32.add + local.set $l6071 + local.get $l6070 + local.get $l6071 + i32.add + local.set $l6072 + local.get $l6071 + local.get $l6072 + i32.add + local.set $l6073 + local.get $l6072 + local.get $l6073 + i32.add + local.set $l6074 + local.get $l6073 + local.get $l6074 + i32.add + local.set $l6075 + local.get $l6074 + local.get $l6075 + i32.add + local.set $l6076 + local.get $l6075 + local.get $l6076 + i32.add + local.set $l6077 + local.get $l6076 + local.get $l6077 + i32.add + local.set $l6078 + local.get $l6077 + local.get $l6078 + i32.add + local.set $l6079 + local.get $l6078 + local.get $l6079 + i32.add + local.set $l6080 + local.get $l6079 + local.get $l6080 + i32.add + local.set $l6081 + local.get $l6080 + local.get $l6081 + i32.add + local.set $l6082 + local.get $l6081 + local.get $l6082 + i32.add + local.set $l6083 + local.get $l6082 + local.get $l6083 + i32.add + local.set $l6084 + local.get $l6083 + local.get $l6084 + i32.add + local.set $l6085 + local.get $l6084 + local.get $l6085 + i32.add + local.set $l6086 + local.get $l6085 + local.get $l6086 + i32.add + local.set $l6087 + local.get $l6086 + local.get $l6087 + i32.add + local.set $l6088 + local.get $l6087 + local.get $l6088 + i32.add + local.set $l6089 + local.get $l6088 + local.get $l6089 + i32.add + local.set $l6090 + local.get $l6089 + local.get $l6090 + i32.add + local.set $l6091 + local.get $l6090 + local.get $l6091 + i32.add + local.set $l6092 + local.get $l6091 + local.get $l6092 + i32.add + local.set $l6093 + local.get $l6092 + local.get $l6093 + i32.add + local.set $l6094 + local.get $l6093 + local.get $l6094 + i32.add + local.set $l6095 + local.get $l6094 + local.get $l6095 + i32.add + local.set $l6096 + local.get $l6095 + local.get $l6096 + i32.add + local.set $l6097 + local.get $l6096 + local.get $l6097 + i32.add + local.set $l6098 + local.get $l6097 + local.get $l6098 + i32.add + local.set $l6099 + local.get $l6098 + local.get $l6099 + i32.add + local.set $l6100 + local.get $l6099 + local.get $l6100 + i32.add + local.set $l6101 + local.get $l6100 + local.get $l6101 + i32.add + local.set $l6102 + local.get $l6101 + local.get $l6102 + i32.add + local.set $l6103 + local.get $l6102 + local.get $l6103 + i32.add + local.set $l6104 + local.get $l6103 + local.get $l6104 + i32.add + local.set $l6105 + local.get $l6104 + local.get $l6105 + i32.add + local.set $l6106 + local.get $l6105 + local.get $l6106 + i32.add + local.set $l6107 + local.get $l6106 + local.get $l6107 + i32.add + local.set $l6108 + local.get $l6107 + local.get $l6108 + i32.add + local.set $l6109 + local.get $l6108 + local.get $l6109 + i32.add + local.set $l6110 + local.get $l6109 + local.get $l6110 + i32.add + local.set $l6111 + local.get $l6110 + local.get $l6111 + i32.add + local.set $l6112 + local.get $l6111 + local.get $l6112 + i32.add + local.set $l6113 + local.get $l6112 + local.get $l6113 + i32.add + local.set $l6114 + local.get $l6113 + local.get $l6114 + i32.add + local.set $l6115 + local.get $l6114 + local.get $l6115 + i32.add + local.set $l6116 + local.get $l6115 + local.get $l6116 + i32.add + local.set $l6117 + local.get $l6116 + local.get $l6117 + i32.add + local.set $l6118 + local.get $l6117 + local.get $l6118 + i32.add + local.set $l6119 + local.get $l6118 + local.get $l6119 + i32.add + local.set $l6120 + local.get $l6119 + local.get $l6120 + i32.add + local.set $l6121 + local.get $l6120 + local.get $l6121 + i32.add + local.set $l6122 + local.get $l6121 + local.get $l6122 + i32.add + local.set $l6123 + local.get $l6122 + local.get $l6123 + i32.add + local.set $l6124 + local.get $l6123 + local.get $l6124 + i32.add + local.set $l6125 + local.get $l6124 + local.get $l6125 + i32.add + local.set $l6126 + local.get $l6125 + local.get $l6126 + i32.add + local.set $l6127 + local.get $l6126 + local.get $l6127 + i32.add + local.set $l6128 + local.get $l6127 + local.get $l6128 + i32.add + local.set $l6129 + local.get $l6128 + local.get $l6129 + i32.add + local.set $l6130 + local.get $l6129 + local.get $l6130 + i32.add + local.set $l6131 + local.get $l6130 + local.get $l6131 + i32.add + local.set $l6132 + local.get $l6131 + local.get $l6132 + i32.add + local.set $l6133 + local.get $l6132 + local.get $l6133 + i32.add + local.set $l6134 + local.get $l6133 + local.get $l6134 + i32.add + local.set $l6135 + local.get $l6134 + local.get $l6135 + i32.add + local.set $l6136 + local.get $l6135 + local.get $l6136 + i32.add + local.set $l6137 + local.get $l6136 + local.get $l6137 + i32.add + local.set $l6138 + local.get $l6137 + local.get $l6138 + i32.add + local.set $l6139 + local.get $l6138 + local.get $l6139 + i32.add + local.set $l6140 + local.get $l6139 + local.get $l6140 + i32.add + local.set $l6141 + local.get $l6140 + local.get $l6141 + i32.add + local.set $l6142 + local.get $l6141 + local.get $l6142 + i32.add + local.set $l6143 + local.get $l6142 + local.get $l6143 + i32.add + local.set $l6144 + local.get $l6143 + local.get $l6144 + i32.add + local.set $l6145 + local.get $l6144 + local.get $l6145 + i32.add + local.set $l6146 + local.get $l6145 + local.get $l6146 + i32.add + local.set $l6147 + local.get $l6146 + local.get $l6147 + i32.add + local.set $l6148 + local.get $l6147 + local.get $l6148 + i32.add + local.set $l6149 + local.get $l6148 + local.get $l6149 + i32.add + local.set $l6150 + local.get $l6149 + local.get $l6150 + i32.add + local.set $l6151 + local.get $l6150 + local.get $l6151 + i32.add + local.set $l6152 + local.get $l6151 + local.get $l6152 + i32.add + local.set $l6153 + local.get $l6152 + local.get $l6153 + i32.add + local.set $l6154 + local.get $l6153 + local.get $l6154 + i32.add + local.set $l6155 + local.get $l6154 + local.get $l6155 + i32.add + local.set $l6156 + local.get $l6155 + local.get $l6156 + i32.add + local.set $l6157 + local.get $l6156 + local.get $l6157 + i32.add + local.set $l6158 + local.get $l6157 + local.get $l6158 + i32.add + local.set $l6159 + local.get $l6158 + local.get $l6159 + i32.add + local.set $l6160 + local.get $l6159 + local.get $l6160 + i32.add + local.set $l6161 + local.get $l6160 + local.get $l6161 + i32.add + local.set $l6162 + local.get $l6161 + local.get $l6162 + i32.add + local.set $l6163 + local.get $l6162 + local.get $l6163 + i32.add + local.set $l6164 + local.get $l6163 + local.get $l6164 + i32.add + local.set $l6165 + local.get $l6164 + local.get $l6165 + i32.add + local.set $l6166 + local.get $l6165 + local.get $l6166 + i32.add + local.set $l6167 + local.get $l6166 + local.get $l6167 + i32.add + local.set $l6168 + local.get $l6167 + local.get $l6168 + i32.add + local.set $l6169 + local.get $l6168 + local.get $l6169 + i32.add + local.set $l6170 + local.get $l6169 + local.get $l6170 + i32.add + local.set $l6171 + local.get $l6170 + local.get $l6171 + i32.add + local.set $l6172 + local.get $l6171 + local.get $l6172 + i32.add + local.set $l6173 + local.get $l6172 + local.get $l6173 + i32.add + local.set $l6174 + local.get $l6173 + local.get $l6174 + i32.add + local.set $l6175 + local.get $l6174 + local.get $l6175 + i32.add + local.set $l6176 + local.get $l6175 + local.get $l6176 + i32.add + local.set $l6177 + local.get $l6176 + local.get $l6177 + i32.add + local.set $l6178 + local.get $l6177 + local.get $l6178 + i32.add + local.set $l6179 + local.get $l6178 + local.get $l6179 + i32.add + local.set $l6180 + local.get $l6179 + local.get $l6180 + i32.add + local.set $l6181 + local.get $l6180 + local.get $l6181 + i32.add + local.set $l6182 + local.get $l6181 + local.get $l6182 + i32.add + local.set $l6183 + local.get $l6182 + local.get $l6183 + i32.add + local.set $l6184 + local.get $l6183 + local.get $l6184 + i32.add + local.set $l6185 + local.get $l6184 + local.get $l6185 + i32.add + local.set $l6186 + local.get $l6185 + local.get $l6186 + i32.add + local.set $l6187 + local.get $l6186 + local.get $l6187 + i32.add + local.set $l6188 + local.get $l6187 + local.get $l6188 + i32.add + local.set $l6189 + local.get $l6188 + local.get $l6189 + i32.add + local.set $l6190 + local.get $l6189 + local.get $l6190 + i32.add + local.set $l6191 + local.get $l6190 + local.get $l6191 + i32.add + local.set $l6192 + local.get $l6191 + local.get $l6192 + i32.add + local.set $l6193 + local.get $l6192 + local.get $l6193 + i32.add + local.set $l6194 + local.get $l6193 + local.get $l6194 + i32.add + local.set $l6195 + local.get $l6194 + local.get $l6195 + i32.add + local.set $l6196 + local.get $l6195 + local.get $l6196 + i32.add + local.set $l6197 + local.get $l6196 + local.get $l6197 + i32.add + local.set $l6198 + local.get $l6197 + local.get $l6198 + i32.add + local.set $l6199 + local.get $l6198 + local.get $l6199 + i32.add + local.set $l6200 + local.get $l6199 + local.get $l6200 + i32.add + local.set $l6201 + local.get $l6200 + local.get $l6201 + i32.add + local.set $l6202 + local.get $l6201 + local.get $l6202 + i32.add + local.set $l6203 + local.get $l6202 + local.get $l6203 + i32.add + local.set $l6204 + local.get $l6203 + local.get $l6204 + i32.add + local.set $l6205 + local.get $l6204 + local.get $l6205 + i32.add + local.set $l6206 + local.get $l6205 + local.get $l6206 + i32.add + local.set $l6207 + local.get $l6206 + local.get $l6207 + i32.add + local.set $l6208 + local.get $l6207 + local.get $l6208 + i32.add + local.set $l6209 + local.get $l6208 + local.get $l6209 + i32.add + local.set $l6210 + local.get $l6209 + local.get $l6210 + i32.add + local.set $l6211 + local.get $l6210 + local.get $l6211 + i32.add + local.set $l6212 + local.get $l6211 + local.get $l6212 + i32.add + local.set $l6213 + local.get $l6212 + local.get $l6213 + i32.add + local.set $l6214 + local.get $l6213 + local.get $l6214 + i32.add + local.set $l6215 + local.get $l6214 + local.get $l6215 + i32.add + local.set $l6216 + local.get $l6215 + local.get $l6216 + i32.add + local.set $l6217 + local.get $l6216 + local.get $l6217 + i32.add + local.set $l6218 + local.get $l6217 + local.get $l6218 + i32.add + local.set $l6219 + local.get $l6218 + local.get $l6219 + i32.add + local.set $l6220 + local.get $l6219 + local.get $l6220 + i32.add + local.set $l6221 + local.get $l6220 + local.get $l6221 + i32.add + local.set $l6222 + local.get $l6221 + local.get $l6222 + i32.add + local.set $l6223 + local.get $l6222 + local.get $l6223 + i32.add + local.set $l6224 + local.get $l6223 + local.get $l6224 + i32.add + local.set $l6225 + local.get $l6224 + local.get $l6225 + i32.add + local.set $l6226 + local.get $l6225 + local.get $l6226 + i32.add + local.set $l6227 + local.get $l6226 + local.get $l6227 + i32.add + local.set $l6228 + local.get $l6227 + local.get $l6228 + i32.add + local.set $l6229 + local.get $l6228 + local.get $l6229 + i32.add + local.set $l6230 + local.get $l6229 + local.get $l6230 + i32.add + local.set $l6231 + local.get $l6230 + local.get $l6231 + i32.add + local.set $l6232 + local.get $l6231 + local.get $l6232 + i32.add + local.set $l6233 + local.get $l6232 + local.get $l6233 + i32.add + local.set $l6234 + local.get $l6233 + local.get $l6234 + i32.add + local.set $l6235 + local.get $l6234 + local.get $l6235 + i32.add + local.set $l6236 + local.get $l6235 + local.get $l6236 + i32.add + local.set $l6237 + local.get $l6236 + local.get $l6237 + i32.add + local.set $l6238 + local.get $l6237 + local.get $l6238 + i32.add + local.set $l6239 + local.get $l6238 + local.get $l6239 + i32.add + local.set $l6240 + local.get $l6239 + local.get $l6240 + i32.add + local.set $l6241 + local.get $l6240 + local.get $l6241 + i32.add + local.set $l6242 + local.get $l6241 + local.get $l6242 + i32.add + local.set $l6243 + local.get $l6242 + local.get $l6243 + i32.add + local.set $l6244 + local.get $l6243 + local.get $l6244 + i32.add + local.set $l6245 + local.get $l6244 + local.get $l6245 + i32.add + local.set $l6246 + local.get $l6245 + local.get $l6246 + i32.add + local.set $l6247 + local.get $l6246 + local.get $l6247 + i32.add + local.set $l6248 + local.get $l6247 + local.get $l6248 + i32.add + local.set $l6249 + local.get $l6248 + local.get $l6249 + i32.add + local.set $l6250 + local.get $l6249 + local.get $l6250 + i32.add + local.set $l6251 + local.get $l6250 + local.get $l6251 + i32.add + local.set $l6252 + local.get $l6251 + local.get $l6252 + i32.add + local.set $l6253 + local.get $l6252 + local.get $l6253 + i32.add + local.set $l6254 + local.get $l6253 + local.get $l6254 + i32.add + local.set $l6255 + local.get $l6254 + local.get $l6255 + i32.add + local.set $l6256 + local.get $l6255 + local.get $l6256 + i32.add + local.set $l6257 + local.get $l6256 + local.get $l6257 + i32.add + local.set $l6258 + local.get $l6257 + local.get $l6258 + i32.add + local.set $l6259 + local.get $l6258 + local.get $l6259 + i32.add + local.set $l6260 + local.get $l6259 + local.get $l6260 + i32.add + local.set $l6261 + local.get $l6260 + local.get $l6261 + i32.add + local.set $l6262 + local.get $l6261 + local.get $l6262 + i32.add + local.set $l6263 + local.get $l6262 + local.get $l6263 + i32.add + local.set $l6264 + local.get $l6263 + local.get $l6264 + i32.add + local.set $l6265 + local.get $l6264 + local.get $l6265 + i32.add + local.set $l6266 + local.get $l6265 + local.get $l6266 + i32.add + local.set $l6267 + local.get $l6266 + local.get $l6267 + i32.add + local.set $l6268 + local.get $l6267 + local.get $l6268 + i32.add + local.set $l6269 + local.get $l6268 + local.get $l6269 + i32.add + local.set $l6270 + local.get $l6269 + local.get $l6270 + i32.add + local.set $l6271 + local.get $l6270 + local.get $l6271 + i32.add + local.set $l6272 + local.get $l6271 + local.get $l6272 + i32.add + local.set $l6273 + local.get $l6272 + local.get $l6273 + i32.add + local.set $l6274 + local.get $l6273 + local.get $l6274 + i32.add + local.set $l6275 + local.get $l6274 + local.get $l6275 + i32.add + local.set $l6276 + local.get $l6275 + local.get $l6276 + i32.add + local.set $l6277 + local.get $l6276 + local.get $l6277 + i32.add + local.set $l6278 + local.get $l6277 + local.get $l6278 + i32.add + local.set $l6279 + local.get $l6278 + local.get $l6279 + i32.add + local.set $l6280 + local.get $l6279 + local.get $l6280 + i32.add + local.set $l6281 + local.get $l6280 + local.get $l6281 + i32.add + local.set $l6282 + local.get $l6281 + local.get $l6282 + i32.add + local.set $l6283 + local.get $l6282 + local.get $l6283 + i32.add + local.set $l6284 + local.get $l6283 + local.get $l6284 + i32.add + local.set $l6285 + local.get $l6284 + local.get $l6285 + i32.add + local.set $l6286 + local.get $l6285 + local.get $l6286 + i32.add + local.set $l6287 + local.get $l6286 + local.get $l6287 + i32.add + local.set $l6288 + local.get $l6287 + local.get $l6288 + i32.add + local.set $l6289 + local.get $l6288 + local.get $l6289 + i32.add + local.set $l6290 + local.get $l6289 + local.get $l6290 + i32.add + local.set $l6291 + local.get $l6290 + local.get $l6291 + i32.add + local.set $l6292 + local.get $l6291 + local.get $l6292 + i32.add + local.set $l6293 + local.get $l6292 + local.get $l6293 + i32.add + local.set $l6294 + local.get $l6293 + local.get $l6294 + i32.add + local.set $l6295 + local.get $l6294 + local.get $l6295 + i32.add + local.set $l6296 + local.get $l6295 + local.get $l6296 + i32.add + local.set $l6297 + local.get $l6296 + local.get $l6297 + i32.add + local.set $l6298 + local.get $l6297 + local.get $l6298 + i32.add + local.set $l6299 + local.get $l6298 + local.get $l6299 + i32.add + local.set $l6300 + local.get $l6299 + local.get $l6300 + i32.add + local.set $l6301 + local.get $l6300 + local.get $l6301 + i32.add + local.set $l6302 + local.get $l6301 + local.get $l6302 + i32.add + local.set $l6303 + local.get $l6302 + local.get $l6303 + i32.add + local.set $l6304 + local.get $l6303 + local.get $l6304 + i32.add + local.set $l6305 + local.get $l6304 + local.get $l6305 + i32.add + local.set $l6306 + local.get $l6305 + local.get $l6306 + i32.add + local.set $l6307 + local.get $l6306 + local.get $l6307 + i32.add + local.set $l6308 + local.get $l6307 + local.get $l6308 + i32.add + local.set $l6309 + local.get $l6308 + local.get $l6309 + i32.add + local.set $l6310 + local.get $l6309 + local.get $l6310 + i32.add + local.set $l6311 + local.get $l6310 + local.get $l6311 + i32.add + local.set $l6312 + local.get $l6311 + local.get $l6312 + i32.add + local.set $l6313 + local.get $l6312 + local.get $l6313 + i32.add + local.set $l6314 + local.get $l6313 + local.get $l6314 + i32.add + local.set $l6315 + local.get $l6314 + local.get $l6315 + i32.add + local.set $l6316 + local.get $l6315 + local.get $l6316 + i32.add + local.set $l6317 + local.get $l6316 + local.get $l6317 + i32.add + local.set $l6318 + local.get $l6317 + local.get $l6318 + i32.add + local.set $l6319 + local.get $l6318 + local.get $l6319 + i32.add + local.set $l6320 + local.get $l6319 + local.get $l6320 + i32.add + local.set $l6321 + local.get $l6320 + local.get $l6321 + i32.add + local.set $l6322 + local.get $l6321 + local.get $l6322 + i32.add + local.set $l6323 + local.get $l6322 + local.get $l6323 + i32.add + local.set $l6324 + local.get $l6323 + local.get $l6324 + i32.add + local.set $l6325 + local.get $l6324 + local.get $l6325 + i32.add + local.set $l6326 + local.get $l6325 + local.get $l6326 + i32.add + local.set $l6327 + local.get $l6326 + local.get $l6327 + i32.add + local.set $l6328 + local.get $l6327 + local.get $l6328 + i32.add + local.set $l6329 + local.get $l6328 + local.get $l6329 + i32.add + local.set $l6330 + local.get $l6329 + local.get $l6330 + i32.add + local.set $l6331 + local.get $l6330 + local.get $l6331 + i32.add + local.set $l6332 + local.get $l6331 + local.get $l6332 + i32.add + local.set $l6333 + local.get $l6332 + local.get $l6333 + i32.add + local.set $l6334 + local.get $l6333 + local.get $l6334 + i32.add + local.set $l6335 + local.get $l6334 + local.get $l6335 + i32.add + local.set $l6336 + local.get $l6335 + local.get $l6336 + i32.add + local.set $l6337 + local.get $l6336 + local.get $l6337 + i32.add + local.set $l6338 + local.get $l6337 + local.get $l6338 + i32.add + local.set $l6339 + local.get $l6338 + local.get $l6339 + i32.add + local.set $l6340 + local.get $l6339 + local.get $l6340 + i32.add + local.set $l6341 + local.get $l6340 + local.get $l6341 + i32.add + local.set $l6342 + local.get $l6341 + local.get $l6342 + i32.add + local.set $l6343 + local.get $l6342 + local.get $l6343 + i32.add + local.set $l6344 + local.get $l6343 + local.get $l6344 + i32.add + local.set $l6345 + local.get $l6344 + local.get $l6345 + i32.add + local.set $l6346 + local.get $l6345 + local.get $l6346 + i32.add + local.set $l6347 + local.get $l6346 + local.get $l6347 + i32.add + local.set $l6348 + local.get $l6347 + local.get $l6348 + i32.add + local.set $l6349 + local.get $l6348 + local.get $l6349 + i32.add + local.set $l6350 + local.get $l6349 + local.get $l6350 + i32.add + local.set $l6351 + local.get $l6350 + local.get $l6351 + i32.add + local.set $l6352 + local.get $l6351 + local.get $l6352 + i32.add + local.set $l6353 + local.get $l6352 + local.get $l6353 + i32.add + local.set $l6354 + local.get $l6353 + local.get $l6354 + i32.add + local.set $l6355 + local.get $l6354 + local.get $l6355 + i32.add + local.set $l6356 + local.get $l6355 + local.get $l6356 + i32.add + local.set $l6357 + local.get $l6356 + local.get $l6357 + i32.add + local.set $l6358 + local.get $l6357 + local.get $l6358 + i32.add + local.set $l6359 + local.get $l6358 + local.get $l6359 + i32.add + local.set $l6360 + local.get $l6359 + local.get $l6360 + i32.add + local.set $l6361 + local.get $l6360 + local.get $l6361 + i32.add + local.set $l6362 + local.get $l6361 + local.get $l6362 + i32.add + local.set $l6363 + local.get $l6362 + local.get $l6363 + i32.add + local.set $l6364 + local.get $l6363 + local.get $l6364 + i32.add + local.set $l6365 + local.get $l6364 + local.get $l6365 + i32.add + local.set $l6366 + local.get $l6365 + local.get $l6366 + i32.add + local.set $l6367 + local.get $l6366 + local.get $l6367 + i32.add + local.set $l6368 + local.get $l6367 + local.get $l6368 + i32.add + local.set $l6369 + local.get $l6368 + local.get $l6369 + i32.add + local.set $l6370 + local.get $l6369 + local.get $l6370 + i32.add + local.set $l6371 + local.get $l6370 + local.get $l6371 + i32.add + local.set $l6372 + local.get $l6371 + local.get $l6372 + i32.add + local.set $l6373 + local.get $l6372 + local.get $l6373 + i32.add + local.set $l6374 + local.get $l6373 + local.get $l6374 + i32.add + local.set $l6375 + local.get $l6374 + local.get $l6375 + i32.add + local.set $l6376 + local.get $l6375 + local.get $l6376 + i32.add + local.set $l6377 + local.get $l6376 + local.get $l6377 + i32.add + local.set $l6378 + local.get $l6377 + local.get $l6378 + i32.add + local.set $l6379 + local.get $l6378 + local.get $l6379 + i32.add + local.set $l6380 + local.get $l6379 + local.get $l6380 + i32.add + local.set $l6381 + local.get $l6380 + local.get $l6381 + i32.add + local.set $l6382 + local.get $l6381 + local.get $l6382 + i32.add + local.set $l6383 + local.get $l6382 + local.get $l6383 + i32.add + local.set $l6384 + local.get $l6383 + local.get $l6384 + i32.add + local.set $l6385 + local.get $l6384 + local.get $l6385 + i32.add + local.set $l6386 + local.get $l6385 + local.get $l6386 + i32.add + local.set $l6387 + local.get $l6386 + local.get $l6387 + i32.add + local.set $l6388 + local.get $l6387 + local.get $l6388 + i32.add + local.set $l6389 + local.get $l6388 + local.get $l6389 + i32.add + local.set $l6390 + local.get $l6389 + local.get $l6390 + i32.add + local.set $l6391 + local.get $l6390 + local.get $l6391 + i32.add + local.set $l6392 + local.get $l6391 + local.get $l6392 + i32.add + local.set $l6393 + local.get $l6392 + local.get $l6393 + i32.add + local.set $l6394 + local.get $l6393 + local.get $l6394 + i32.add + local.set $l6395 + local.get $l6394 + local.get $l6395 + i32.add + local.set $l6396 + local.get $l6395 + local.get $l6396 + i32.add + local.set $l6397 + local.get $l6396 + local.get $l6397 + i32.add + local.set $l6398 + local.get $l6397 + local.get $l6398 + i32.add + local.set $l6399 + local.get $l6398 + local.get $l6399 + i32.add + local.set $l6400 + local.get $l6399 + local.get $l6400 + i32.add + local.set $l6401 + local.get $l6400 + local.get $l6401 + i32.add + local.set $l6402 + local.get $l6401 + local.get $l6402 + i32.add + local.set $l6403 + local.get $l6402 + local.get $l6403 + i32.add + local.set $l6404 + local.get $l6403 + local.get $l6404 + i32.add + local.set $l6405 + local.get $l6404 + local.get $l6405 + i32.add + local.set $l6406 + local.get $l6405 + local.get $l6406 + i32.add + local.set $l6407 + local.get $l6406 + local.get $l6407 + i32.add + local.set $l6408 + local.get $l6407 + local.get $l6408 + i32.add + local.set $l6409 + local.get $l6408 + local.get $l6409 + i32.add + local.set $l6410 + local.get $l6409 + local.get $l6410 + i32.add + local.set $l6411 + local.get $l6410 + local.get $l6411 + i32.add + local.set $l6412 + local.get $l6411 + local.get $l6412 + i32.add + local.set $l6413 + local.get $l6412 + local.get $l6413 + i32.add + local.set $l6414 + local.get $l6413 + local.get $l6414 + i32.add + local.set $l6415 + local.get $l6414 + local.get $l6415 + i32.add + local.set $l6416 + local.get $l6415 + local.get $l6416 + i32.add + local.set $l6417 + local.get $l6416 + local.get $l6417 + i32.add + local.set $l6418 + local.get $l6417 + local.get $l6418 + i32.add + local.set $l6419 + local.get $l6418 + local.get $l6419 + i32.add + local.set $l6420 + local.get $l6419 + local.get $l6420 + i32.add + local.set $l6421 + local.get $l6420 + local.get $l6421 + i32.add + local.set $l6422 + local.get $l6421 + local.get $l6422 + i32.add + local.set $l6423 + local.get $l6422 + local.get $l6423 + i32.add + local.set $l6424 + local.get $l6423 + local.get $l6424 + i32.add + local.set $l6425 + local.get $l6424 + local.get $l6425 + i32.add + local.set $l6426 + local.get $l6425 + local.get $l6426 + i32.add + local.set $l6427 + local.get $l6426 + local.get $l6427 + i32.add + local.set $l6428 + local.get $l6427 + local.get $l6428 + i32.add + local.set $l6429 + local.get $l6428 + local.get $l6429 + i32.add + local.set $l6430 + local.get $l6429 + local.get $l6430 + i32.add + local.set $l6431 + local.get $l6430 + local.get $l6431 + i32.add + local.set $l6432 + local.get $l6431 + local.get $l6432 + i32.add + local.set $l6433 + local.get $l6432 + local.get $l6433 + i32.add + local.set $l6434 + local.get $l6433 + local.get $l6434 + i32.add + local.set $l6435 + local.get $l6434 + local.get $l6435 + i32.add + local.set $l6436 + local.get $l6435 + local.get $l6436 + i32.add + local.set $l6437 + local.get $l6436 + local.get $l6437 + i32.add + local.set $l6438 + local.get $l6437 + local.get $l6438 + i32.add + local.set $l6439 + local.get $l6438 + local.get $l6439 + i32.add + local.set $l6440 + local.get $l6439 + local.get $l6440 + i32.add + local.set $l6441 + local.get $l6440 + local.get $l6441 + i32.add + local.set $l6442 + local.get $l6441 + local.get $l6442 + i32.add + local.set $l6443 + local.get $l6442 + local.get $l6443 + i32.add + local.set $l6444 + local.get $l6443 + local.get $l6444 + i32.add + local.set $l6445 + local.get $l6444 + local.get $l6445 + i32.add + local.set $l6446 + local.get $l6445 + local.get $l6446 + i32.add + local.set $l6447 + local.get $l6446 + local.get $l6447 + i32.add + local.set $l6448 + local.get $l6447 + local.get $l6448 + i32.add + local.set $l6449 + local.get $l6448 + local.get $l6449 + i32.add + local.set $l6450 + local.get $l6449 + local.get $l6450 + i32.add + local.set $l6451 + local.get $l6450 + local.get $l6451 + i32.add + local.set $l6452 + local.get $l6451 + local.get $l6452 + i32.add + local.set $l6453 + local.get $l6452 + local.get $l6453 + i32.add + local.set $l6454 + local.get $l6453 + local.get $l6454 + i32.add + local.set $l6455 + local.get $l6454 + local.get $l6455 + i32.add + local.set $l6456 + local.get $l6455 + local.get $l6456 + i32.add + local.set $l6457 + local.get $l6456 + local.get $l6457 + i32.add + local.set $l6458 + local.get $l6457 + local.get $l6458 + i32.add + local.set $l6459 + local.get $l6458 + local.get $l6459 + i32.add + local.set $l6460 + local.get $l6459 + local.get $l6460 + i32.add + local.set $l6461 + local.get $l6460 + local.get $l6461 + i32.add + local.set $l6462 + local.get $l6461 + local.get $l6462 + i32.add + local.set $l6463 + local.get $l6462 + local.get $l6463 + i32.add + local.set $l6464 + local.get $l6463 + local.get $l6464 + i32.add + local.set $l6465 + local.get $l6464 + local.get $l6465 + i32.add + local.set $l6466 + local.get $l6465 + local.get $l6466 + i32.add + local.set $l6467 + local.get $l6466 + local.get $l6467 + i32.add + local.set $l6468 + local.get $l6467 + local.get $l6468 + i32.add + local.set $l6469 + local.get $l6468 + local.get $l6469 + i32.add + local.set $l6470 + local.get $l6469 + local.get $l6470 + i32.add + local.set $l6471 + local.get $l6470 + local.get $l6471 + i32.add + local.set $l6472 + local.get $l6471 + local.get $l6472 + i32.add + local.set $l6473 + local.get $l6472 + local.get $l6473 + i32.add + local.set $l6474 + local.get $l6473 + local.get $l6474 + i32.add + local.set $l6475 + local.get $l6474 + local.get $l6475 + i32.add + local.set $l6476 + local.get $l6475 + local.get $l6476 + i32.add + local.set $l6477 + local.get $l6476 + local.get $l6477 + i32.add + local.set $l6478 + local.get $l6477 + local.get $l6478 + i32.add + local.set $l6479 + local.get $l6478 + local.get $l6479 + i32.add + local.set $l6480 + local.get $l6479 + local.get $l6480 + i32.add + local.set $l6481 + local.get $l6480 + local.get $l6481 + i32.add + local.set $l6482 + local.get $l6481 + local.get $l6482 + i32.add + local.set $l6483 + local.get $l6482 + local.get $l6483 + i32.add + local.set $l6484 + local.get $l6483 + local.get $l6484 + i32.add + local.set $l6485 + local.get $l6484 + local.get $l6485 + i32.add + local.set $l6486 + local.get $l6485 + local.get $l6486 + i32.add + local.set $l6487 + local.get $l6486 + local.get $l6487 + i32.add + local.set $l6488 + local.get $l6487 + local.get $l6488 + i32.add + local.set $l6489 + local.get $l6488 + local.get $l6489 + i32.add + local.set $l6490 + local.get $l6489 + local.get $l6490 + i32.add + local.set $l6491 + local.get $l6490 + local.get $l6491 + i32.add + local.set $l6492 + local.get $l6491 + local.get $l6492 + i32.add + local.set $l6493 + local.get $l6492 + local.get $l6493 + i32.add + local.set $l6494 + local.get $l6493 + local.get $l6494 + i32.add + local.set $l6495 + local.get $l6494 + local.get $l6495 + i32.add + local.set $l6496 + local.get $l6495 + local.get $l6496 + i32.add + local.set $l6497 + local.get $l6496 + local.get $l6497 + i32.add + local.set $l6498 + local.get $l6497 + local.get $l6498 + i32.add + local.set $l6499 + local.get $l6498 + local.get $l6499 + i32.add + local.set $l6500 + local.get $l6499 + local.get $l6500 + i32.add + local.set $l6501 + local.get $l6500 + local.get $l6501 + i32.add + local.set $l6502 + local.get $l6501 + local.get $l6502 + i32.add + local.set $l6503 + local.get $l6502 + local.get $l6503 + i32.add + local.set $l6504 + local.get $l6503 + local.get $l6504 + i32.add + local.set $l6505 + local.get $l6504 + local.get $l6505 + i32.add + local.set $l6506 + local.get $l6505 + local.get $l6506 + i32.add + local.set $l6507 + local.get $l6506 + local.get $l6507 + i32.add + local.set $l6508 + local.get $l6507 + local.get $l6508 + i32.add + local.set $l6509 + local.get $l6508 + local.get $l6509 + i32.add + local.set $l6510 + local.get $l6509 + local.get $l6510 + i32.add + local.set $l6511 + local.get $l6510 + local.get $l6511 + i32.add + local.set $l6512 + local.get $l6511 + local.get $l6512 + i32.add + local.set $l6513 + local.get $l6512 + local.get $l6513 + i32.add + local.set $l6514 + local.get $l6513 + local.get $l6514 + i32.add + local.set $l6515 + local.get $l6514 + local.get $l6515 + i32.add + local.set $l6516 + local.get $l6515 + local.get $l6516 + i32.add + local.set $l6517 + local.get $l6516 + local.get $l6517 + i32.add + local.set $l6518 + local.get $l6517 + local.get $l6518 + i32.add + local.set $l6519 + local.get $l6518 + local.get $l6519 + i32.add + local.set $l6520 + local.get $l6519 + local.get $l6520 + i32.add + local.set $l6521 + local.get $l6520 + local.get $l6521 + i32.add + local.set $l6522 + local.get $l6521 + local.get $l6522 + i32.add + local.set $l6523 + local.get $l6522 + local.get $l6523 + i32.add + local.set $l6524 + local.get $l6523 + local.get $l6524 + i32.add + local.set $l6525 + local.get $l6524 + local.get $l6525 + i32.add + local.set $l6526 + local.get $l6525 + local.get $l6526 + i32.add + local.set $l6527 + local.get $l6526 + local.get $l6527 + i32.add + local.set $l6528 + local.get $l6527 + local.get $l6528 + i32.add + local.set $l6529 + local.get $l6528 + local.get $l6529 + i32.add + local.set $l6530 + local.get $l6529 + local.get $l6530 + i32.add + local.set $l6531 + local.get $l6530 + local.get $l6531 + i32.add + local.set $l6532 + local.get $l6531 + local.get $l6532 + i32.add + local.set $l6533 + local.get $l6532 + local.get $l6533 + i32.add + local.set $l6534 + local.get $l6533 + local.get $l6534 + i32.add + local.set $l6535 + local.get $l6534 + local.get $l6535 + i32.add + local.set $l6536 + local.get $l6535 + local.get $l6536 + i32.add + local.set $l6537 + local.get $l6536 + local.get $l6537 + i32.add + local.set $l6538 + local.get $l6537 + local.get $l6538 + i32.add + local.set $l6539 + local.get $l6538 + local.get $l6539 + i32.add + local.set $l6540 + local.get $l6539 + local.get $l6540 + i32.add + local.set $l6541 + local.get $l6540 + local.get $l6541 + i32.add + local.set $l6542 + local.get $l6541 + local.get $l6542 + i32.add + local.set $l6543 + local.get $l6542 + local.get $l6543 + i32.add + local.set $l6544 + local.get $l6543 + local.get $l6544 + i32.add + local.set $l6545 + local.get $l6544 + local.get $l6545 + i32.add + local.set $l6546 + local.get $l6545 + local.get $l6546 + i32.add + local.set $l6547 + local.get $l6546 + local.get $l6547 + i32.add + local.set $l6548 + local.get $l6547 + local.get $l6548 + i32.add + local.set $l6549 + local.get $l6548 + local.get $l6549 + i32.add + local.set $l6550 + local.get $l6549 + local.get $l6550 + i32.add + local.set $l6551 + local.get $l6550 + local.get $l6551 + i32.add + local.set $l6552 + local.get $l6551 + local.get $l6552 + i32.add + local.set $l6553 + local.get $l6552 + local.get $l6553 + i32.add + local.set $l6554 + local.get $l6553 + local.get $l6554 + i32.add + local.set $l6555 + local.get $l6554 + local.get $l6555 + i32.add + local.set $l6556 + local.get $l6555 + local.get $l6556 + i32.add + local.set $l6557 + local.get $l6556 + local.get $l6557 + i32.add + local.set $l6558 + local.get $l6557 + local.get $l6558 + i32.add + local.set $l6559 + local.get $l6558 + local.get $l6559 + i32.add + local.set $l6560 + local.get $l6559 + local.get $l6560 + i32.add + local.set $l6561 + local.get $l6560 + local.get $l6561 + i32.add + local.set $l6562 + local.get $l6561 + local.get $l6562 + i32.add + local.set $l6563 + local.get $l6562 + local.get $l6563 + i32.add + local.set $l6564 + local.get $l6563 + local.get $l6564 + i32.add + local.set $l6565 + local.get $l6564 + local.get $l6565 + i32.add + local.set $l6566 + local.get $l6565 + local.get $l6566 + i32.add + local.set $l6567 + local.get $l6566 + local.get $l6567 + i32.add + local.set $l6568 + local.get $l6567 + local.get $l6568 + i32.add + local.set $l6569 + local.get $l6568 + local.get $l6569 + i32.add + local.set $l6570 + local.get $l6569 + local.get $l6570 + i32.add + local.set $l6571 + local.get $l6570 + local.get $l6571 + i32.add + local.set $l6572 + local.get $l6571 + local.get $l6572 + i32.add + local.set $l6573 + local.get $l6572 + local.get $l6573 + i32.add + local.set $l6574 + local.get $l6573 + local.get $l6574 + i32.add + local.set $l6575 + local.get $l6574 + local.get $l6575 + i32.add + local.set $l6576 + local.get $l6575 + local.get $l6576 + i32.add + local.set $l6577 + local.get $l6576 + local.get $l6577 + i32.add + local.set $l6578 + local.get $l6577 + local.get $l6578 + i32.add + local.set $l6579 + local.get $l6578 + local.get $l6579 + i32.add + local.set $l6580 + local.get $l6579 + local.get $l6580 + i32.add + local.set $l6581 + local.get $l6580 + local.get $l6581 + i32.add + local.set $l6582 + local.get $l6581 + local.get $l6582 + i32.add + local.set $l6583 + local.get $l6582 + local.get $l6583 + i32.add + local.set $l6584 + local.get $l6583 + local.get $l6584 + i32.add + local.set $l6585 + local.get $l6584 + local.get $l6585 + i32.add + local.set $l6586 + local.get $l6585 + local.get $l6586 + i32.add + local.set $l6587 + local.get $l6586 + local.get $l6587 + i32.add + local.set $l6588 + local.get $l6587 + local.get $l6588 + i32.add + local.set $l6589 + local.get $l6588 + local.get $l6589 + i32.add + local.set $l6590 + local.get $l6589 + local.get $l6590 + i32.add + local.set $l6591 + local.get $l6590 + local.get $l6591 + i32.add + local.set $l6592 + local.get $l6591 + local.get $l6592 + i32.add + local.set $l6593 + local.get $l6592 + local.get $l6593 + i32.add + local.set $l6594 + local.get $l6593 + local.get $l6594 + i32.add + local.set $l6595 + local.get $l6594 + local.get $l6595 + i32.add + local.set $l6596 + local.get $l6595 + local.get $l6596 + i32.add + local.set $l6597 + local.get $l6596 + local.get $l6597 + i32.add + local.set $l6598 + local.get $l6597 + local.get $l6598 + i32.add + local.set $l6599 + local.get $l6598 + local.get $l6599 + i32.add + local.set $l6600 + local.get $l6599 + local.get $l6600 + i32.add + local.set $l6601 + local.get $l6600 + local.get $l6601 + i32.add + local.set $l6602 + local.get $l6601 + local.get $l6602 + i32.add + local.set $l6603 + local.get $l6602 + local.get $l6603 + i32.add + local.set $l6604 + local.get $l6603 + local.get $l6604 + i32.add + local.set $l6605 + local.get $l6604 + local.get $l6605 + i32.add + local.set $l6606 + local.get $l6605 + local.get $l6606 + i32.add + local.set $l6607 + local.get $l6606 + local.get $l6607 + i32.add + local.set $l6608 + local.get $l6607 + local.get $l6608 + i32.add + local.set $l6609 + local.get $l6608 + local.get $l6609 + i32.add + local.set $l6610 + local.get $l6609 + local.get $l6610 + i32.add + local.set $l6611 + local.get $l6610 + local.get $l6611 + i32.add + local.set $l6612 + local.get $l6611 + local.get $l6612 + i32.add + local.set $l6613 + local.get $l6612 + local.get $l6613 + i32.add + local.set $l6614 + local.get $l6613 + local.get $l6614 + i32.add + local.set $l6615 + local.get $l6614 + local.get $l6615 + i32.add + local.set $l6616 + local.get $l6615 + local.get $l6616 + i32.add + local.set $l6617 + local.get $l6616 + local.get $l6617 + i32.add + local.set $l6618 + local.get $l6617 + local.get $l6618 + i32.add + local.set $l6619 + local.get $l6618 + local.get $l6619 + i32.add + local.set $l6620 + local.get $l6619 + local.get $l6620 + i32.add + local.set $l6621 + local.get $l6620 + local.get $l6621 + i32.add + local.set $l6622 + local.get $l6621 + local.get $l6622 + i32.add + local.set $l6623 + local.get $l6622 + local.get $l6623 + i32.add + local.set $l6624 + local.get $l6623 + local.get $l6624 + i32.add + local.set $l6625 + local.get $l6624 + local.get $l6625 + i32.add + local.set $l6626 + local.get $l6625 + local.get $l6626 + i32.add + local.set $l6627 + local.get $l6626 + local.get $l6627 + i32.add + local.set $l6628 + local.get $l6627 + local.get $l6628 + i32.add + local.set $l6629 + local.get $l6628 + local.get $l6629 + i32.add + local.set $l6630 + local.get $l6629 + local.get $l6630 + i32.add + local.set $l6631 + local.get $l6630 + local.get $l6631 + i32.add + local.set $l6632 + local.get $l6631 + local.get $l6632 + i32.add + local.set $l6633 + local.get $l6632 + local.get $l6633 + i32.add + local.set $l6634 + local.get $l6633 + local.get $l6634 + i32.add + local.set $l6635 + local.get $l6634 + local.get $l6635 + i32.add + local.set $l6636 + local.get $l6635 + local.get $l6636 + i32.add + local.set $l6637 + local.get $l6636 + local.get $l6637 + i32.add + local.set $l6638 + local.get $l6637 + local.get $l6638 + i32.add + local.set $l6639 + local.get $l6638 + local.get $l6639 + i32.add + local.set $l6640 + local.get $l6639 + local.get $l6640 + i32.add + local.set $l6641 + local.get $l6640 + local.get $l6641 + i32.add + local.set $l6642 + local.get $l6641 + local.get $l6642 + i32.add + local.set $l6643 + local.get $l6642 + local.get $l6643 + i32.add + local.set $l6644 + local.get $l6643 + local.get $l6644 + i32.add + local.set $l6645 + local.get $l6644 + local.get $l6645 + i32.add + local.set $l6646 + local.get $l6645 + local.get $l6646 + i32.add + local.set $l6647 + local.get $l6646 + local.get $l6647 + i32.add + local.set $l6648 + local.get $l6647 + local.get $l6648 + i32.add + local.set $l6649 + local.get $l6648 + local.get $l6649 + i32.add + local.set $l6650 + local.get $l6649 + local.get $l6650 + i32.add + local.set $l6651 + local.get $l6650 + local.get $l6651 + i32.add + local.set $l6652 + local.get $l6651 + local.get $l6652 + i32.add + local.set $l6653 + local.get $l6652 + local.get $l6653 + i32.add + local.set $l6654 + local.get $l6653 + local.get $l6654 + i32.add + local.set $l6655 + local.get $l6654 + local.get $l6655 + i32.add + local.set $l6656 + local.get $l6655 + local.get $l6656 + i32.add + local.set $l6657 + local.get $l6656 + local.get $l6657 + i32.add + local.set $l6658 + local.get $l6657 + local.get $l6658 + i32.add + local.set $l6659 + local.get $l6658 + local.get $l6659 + i32.add + local.set $l6660 + local.get $l6659 + local.get $l6660 + i32.add + local.set $l6661 + local.get $l6660 + local.get $l6661 + i32.add + local.set $l6662 + local.get $l6661 + local.get $l6662 + i32.add + local.set $l6663 + local.get $l6662 + local.get $l6663 + i32.add + local.set $l6664 + local.get $l6663 + local.get $l6664 + i32.add + local.set $l6665 + local.get $l6664 + local.get $l6665 + i32.add + local.set $l6666 + local.get $l6665 + local.get $l6666 + i32.add + local.set $l6667 + local.get $l6666 + local.get $l6667 + i32.add + local.set $l6668 + local.get $l6667 + local.get $l6668 + i32.add + local.set $l6669 + local.get $l6668 + local.get $l6669 + i32.add + local.set $l6670 + local.get $l6669 + local.get $l6670 + i32.add + local.set $l6671 + local.get $l6670 + local.get $l6671 + i32.add + local.set $l6672 + local.get $l6671 + local.get $l6672 + i32.add + local.set $l6673 + local.get $l6672 + local.get $l6673 + i32.add + local.set $l6674 + local.get $l6673 + local.get $l6674 + i32.add + local.set $l6675 + local.get $l6674 + local.get $l6675 + i32.add + local.set $l6676 + local.get $l6675 + local.get $l6676 + i32.add + local.set $l6677 + local.get $l6676 + local.get $l6677 + i32.add + local.set $l6678 + local.get $l6677 + local.get $l6678 + i32.add + local.set $l6679 + local.get $l6678 + local.get $l6679 + i32.add + local.set $l6680 + local.get $l6679 + local.get $l6680 + i32.add + local.set $l6681 + local.get $l6680 + local.get $l6681 + i32.add + local.set $l6682 + local.get $l6681 + local.get $l6682 + i32.add + local.set $l6683 + local.get $l6682 + local.get $l6683 + i32.add + local.set $l6684 + local.get $l6683 + local.get $l6684 + i32.add + local.set $l6685 + local.get $l6684 + local.get $l6685 + i32.add + local.set $l6686 + local.get $l6685 + local.get $l6686 + i32.add + local.set $l6687 + local.get $l6686 + local.get $l6687 + i32.add + local.set $l6688 + local.get $l6687 + local.get $l6688 + i32.add + local.set $l6689 + local.get $l6688 + local.get $l6689 + i32.add + local.set $l6690 + local.get $l6689 + local.get $l6690 + i32.add + local.set $l6691 + local.get $l6690 + local.get $l6691 + i32.add + local.set $l6692 + local.get $l6691 + local.get $l6692 + i32.add + local.set $l6693 + local.get $l6692 + local.get $l6693 + i32.add + local.set $l6694 + local.get $l6693 + local.get $l6694 + i32.add + local.set $l6695 + local.get $l6694 + local.get $l6695 + i32.add + local.set $l6696 + local.get $l6695 + local.get $l6696 + i32.add + local.set $l6697 + local.get $l6696 + local.get $l6697 + i32.add + local.set $l6698 + local.get $l6697 + local.get $l6698 + i32.add + local.set $l6699 + local.get $l6698 + local.get $l6699 + i32.add + local.set $l6700 + local.get $l6699 + local.get $l6700 + i32.add + local.set $l6701 + local.get $l6700 + local.get $l6701 + i32.add + local.set $l6702 + local.get $l6701 + local.get $l6702 + i32.add + local.set $l6703 + local.get $l6702 + local.get $l6703 + i32.add + local.set $l6704 + local.get $l6703 + local.get $l6704 + i32.add + local.set $l6705 + local.get $l6704 + local.get $l6705 + i32.add + local.set $l6706 + local.get $l6705 + local.get $l6706 + i32.add + local.set $l6707 + local.get $l6706 + local.get $l6707 + i32.add + local.set $l6708 + local.get $l6707 + local.get $l6708 + i32.add + local.set $l6709 + local.get $l6708 + local.get $l6709 + i32.add + local.set $l6710 + local.get $l6709 + local.get $l6710 + i32.add + local.set $l6711 + local.get $l6710 + local.get $l6711 + i32.add + local.set $l6712 + local.get $l6711 + local.get $l6712 + i32.add + local.set $l6713 + local.get $l6712 + local.get $l6713 + i32.add + local.set $l6714 + local.get $l6713 + local.get $l6714 + i32.add + local.set $l6715 + local.get $l6714 + local.get $l6715 + i32.add + local.set $l6716 + local.get $l6715 + local.get $l6716 + i32.add + local.set $l6717 + local.get $l6716 + local.get $l6717 + i32.add + local.set $l6718 + local.get $l6717 + local.get $l6718 + i32.add + local.set $l6719 + local.get $l6718 + local.get $l6719 + i32.add + local.set $l6720 + local.get $l6719 + local.get $l6720 + i32.add + local.set $l6721 + local.get $l6720 + local.get $l6721 + i32.add + local.set $l6722 + local.get $l6721 + local.get $l6722 + i32.add + local.set $l6723 + local.get $l6722 + local.get $l6723 + i32.add + local.set $l6724 + local.get $l6723 + local.get $l6724 + i32.add + local.set $l6725 + local.get $l6724 + local.get $l6725 + i32.add + local.set $l6726 + local.get $l6725 + local.get $l6726 + i32.add + local.set $l6727 + local.get $l6726 + local.get $l6727 + i32.add + local.set $l6728 + local.get $l6727 + local.get $l6728 + i32.add + local.set $l6729 + local.get $l6728 + local.get $l6729 + i32.add + local.set $l6730 + local.get $l6729 + local.get $l6730 + i32.add + local.set $l6731 + local.get $l6730 + local.get $l6731 + i32.add + local.set $l6732 + local.get $l6731 + local.get $l6732 + i32.add + local.set $l6733 + local.get $l6732 + local.get $l6733 + i32.add + local.set $l6734 + local.get $l6733 + local.get $l6734 + i32.add + local.set $l6735 + local.get $l6734 + local.get $l6735 + i32.add + local.set $l6736 + local.get $l6735 + local.get $l6736 + i32.add + local.set $l6737 + local.get $l6736 + local.get $l6737 + i32.add + local.set $l6738 + local.get $l6737 + local.get $l6738 + i32.add + local.set $l6739 + local.get $l6738 + local.get $l6739 + i32.add + local.set $l6740 + local.get $l6739 + local.get $l6740 + i32.add + local.set $l6741 + local.get $l6740 + local.get $l6741 + i32.add + local.set $l6742 + local.get $l6741 + local.get $l6742 + i32.add + local.set $l6743 + local.get $l6742 + local.get $l6743 + i32.add + local.set $l6744 + local.get $l6743 + local.get $l6744 + i32.add + local.set $l6745 + local.get $l6744 + local.get $l6745 + i32.add + local.set $l6746 + local.get $l6745 + local.get $l6746 + i32.add + local.set $l6747 + local.get $l6746 + local.get $l6747 + i32.add + local.set $l6748 + local.get $l6747 + local.get $l6748 + i32.add + local.set $l6749 + local.get $l6748 + local.get $l6749 + i32.add + local.set $l6750 + local.get $l6749 + local.get $l6750 + i32.add + local.set $l6751 + local.get $l6750 + local.get $l6751 + i32.add + local.set $l6752 + local.get $l6751 + local.get $l6752 + i32.add + local.set $l6753 + local.get $l6752 + local.get $l6753 + i32.add + local.set $l6754 + local.get $l6753 + local.get $l6754 + i32.add + local.set $l6755 + local.get $l6754 + local.get $l6755 + i32.add + local.set $l6756 + local.get $l6755 + local.get $l6756 + i32.add + local.set $l6757 + local.get $l6756 + local.get $l6757 + i32.add + local.set $l6758 + local.get $l6757 + local.get $l6758 + i32.add + local.set $l6759 + local.get $l6758 + local.get $l6759 + i32.add + local.set $l6760 + local.get $l6759 + local.get $l6760 + i32.add + local.set $l6761 + local.get $l6760 + local.get $l6761 + i32.add + local.set $l6762 + local.get $l6761 + local.get $l6762 + i32.add + local.set $l6763 + local.get $l6762 + local.get $l6763 + i32.add + local.set $l6764 + local.get $l6763 + local.get $l6764 + i32.add + local.set $l6765 + local.get $l6764 + local.get $l6765 + i32.add + local.set $l6766 + local.get $l6765 + local.get $l6766 + i32.add + local.set $l6767 + local.get $l6766 + local.get $l6767 + i32.add + local.set $l6768 + local.get $l6767 + local.get $l6768 + i32.add + local.set $l6769 + local.get $l6768 + local.get $l6769 + i32.add + local.set $l6770 + local.get $l6769 + local.get $l6770 + i32.add + local.set $l6771 + local.get $l6770 + local.get $l6771 + i32.add + local.set $l6772 + local.get $l6771 + local.get $l6772 + i32.add + local.set $l6773 + local.get $l6772 + local.get $l6773 + i32.add + local.set $l6774 + local.get $l6773 + local.get $l6774 + i32.add + local.set $l6775 + local.get $l6774 + local.get $l6775 + i32.add + local.set $l6776 + local.get $l6775 + local.get $l6776 + i32.add + local.set $l6777 + local.get $l6776 + local.get $l6777 + i32.add + local.set $l6778 + local.get $l6777 + local.get $l6778 + i32.add + local.set $l6779 + local.get $l6778 + local.get $l6779 + i32.add + local.set $l6780 + local.get $l6779 + local.get $l6780 + i32.add + local.set $l6781 + local.get $l6780 + local.get $l6781 + i32.add + local.set $l6782 + local.get $l6781 + local.get $l6782 + i32.add + local.set $l6783 + local.get $l6782 + local.get $l6783 + i32.add + local.set $l6784 + local.get $l6783 + local.get $l6784 + i32.add + local.set $l6785 + local.get $l6784 + local.get $l6785 + i32.add + local.set $l6786 + local.get $l6785 + local.get $l6786 + i32.add + local.set $l6787 + local.get $l6786 + local.get $l6787 + i32.add + local.set $l6788 + local.get $l6787 + local.get $l6788 + i32.add + local.set $l6789 + local.get $l6788 + local.get $l6789 + i32.add + local.set $l6790 + local.get $l6789 + local.get $l6790 + i32.add + local.set $l6791 + local.get $l6790 + local.get $l6791 + i32.add + local.set $l6792 + local.get $l6791 + local.get $l6792 + i32.add + local.set $l6793 + local.get $l6792 + local.get $l6793 + i32.add + local.set $l6794 + local.get $l6793 + local.get $l6794 + i32.add + local.set $l6795 + local.get $l6794 + local.get $l6795 + i32.add + local.set $l6796 + local.get $l6795 + local.get $l6796 + i32.add + local.set $l6797 + local.get $l6796 + local.get $l6797 + i32.add + local.set $l6798 + local.get $l6797 + local.get $l6798 + i32.add + local.set $l6799 + local.get $l6798 + local.get $l6799 + i32.add + local.set $l6800 + local.get $l6799 + local.get $l6800 + i32.add + local.set $l6801 + local.get $l6800 + local.get $l6801 + i32.add + local.set $l6802 + local.get $l6801 + local.get $l6802 + i32.add + local.set $l6803 + local.get $l6802 + local.get $l6803 + i32.add + local.set $l6804 + local.get $l6803 + local.get $l6804 + i32.add + local.set $l6805 + local.get $l6804 + local.get $l6805 + i32.add + local.set $l6806 + local.get $l6805 + local.get $l6806 + i32.add + local.set $l6807 + local.get $l6806 + local.get $l6807 + i32.add + local.set $l6808 + local.get $l6807 + local.get $l6808 + i32.add + local.set $l6809 + local.get $l6808 + local.get $l6809 + i32.add + local.set $l6810 + local.get $l6809 + local.get $l6810 + i32.add + local.set $l6811 + local.get $l6810 + local.get $l6811 + i32.add + local.set $l6812 + local.get $l6811 + local.get $l6812 + i32.add + local.set $l6813 + local.get $l6812 + local.get $l6813 + i32.add + local.set $l6814 + local.get $l6813 + local.get $l6814 + i32.add + local.set $l6815 + local.get $l6814 + local.get $l6815 + i32.add + local.set $l6816 + local.get $l6815 + local.get $l6816 + i32.add + local.set $l6817 + local.get $l6816 + local.get $l6817 + i32.add + local.set $l6818 + local.get $l6817 + local.get $l6818 + i32.add + local.set $l6819 + local.get $l6818 + local.get $l6819 + i32.add + local.set $l6820 + local.get $l6819 + local.get $l6820 + i32.add + local.set $l6821 + local.get $l6820 + local.get $l6821 + i32.add + local.set $l6822 + local.get $l6821 + local.get $l6822 + i32.add + local.set $l6823 + local.get $l6822 + local.get $l6823 + i32.add + local.set $l6824 + local.get $l6823 + local.get $l6824 + i32.add + local.set $l6825 + local.get $l6824 + local.get $l6825 + i32.add + local.set $l6826 + local.get $l6825 + local.get $l6826 + i32.add + local.set $l6827 + local.get $l6826 + local.get $l6827 + i32.add + local.set $l6828 + local.get $l6827 + local.get $l6828 + i32.add + local.set $l6829 + local.get $l6828 + local.get $l6829 + i32.add + local.set $l6830 + local.get $l6829 + local.get $l6830 + i32.add + local.set $l6831 + local.get $l6830 + local.get $l6831 + i32.add + local.set $l6832 + local.get $l6831 + local.get $l6832 + i32.add + local.set $l6833 + local.get $l6832 + local.get $l6833 + i32.add + local.set $l6834 + local.get $l6833 + local.get $l6834 + i32.add + local.set $l6835 + local.get $l6834 + local.get $l6835 + i32.add + local.set $l6836 + local.get $l6835 + local.get $l6836 + i32.add + local.set $l6837 + local.get $l6836 + local.get $l6837 + i32.add + local.set $l6838 + local.get $l6837 + local.get $l6838 + i32.add + local.set $l6839 + local.get $l6838 + local.get $l6839 + i32.add + local.set $l6840 + local.get $l6839 + local.get $l6840 + i32.add + local.set $l6841 + local.get $l6840 + local.get $l6841 + i32.add + local.set $l6842 + local.get $l6841 + local.get $l6842 + i32.add + local.set $l6843 + local.get $l6842 + local.get $l6843 + i32.add + local.set $l6844 + local.get $l6843 + local.get $l6844 + i32.add + local.set $l6845 + local.get $l6844 + local.get $l6845 + i32.add + local.set $l6846 + local.get $l6845 + local.get $l6846 + i32.add + local.set $l6847 + local.get $l6846 + local.get $l6847 + i32.add + local.set $l6848 + local.get $l6847 + local.get $l6848 + i32.add + local.set $l6849 + local.get $l6848 + local.get $l6849 + i32.add + local.set $l6850 + local.get $l6849 + local.get $l6850 + i32.add + local.set $l6851 + local.get $l6850 + local.get $l6851 + i32.add + local.set $l6852 + local.get $l6851 + local.get $l6852 + i32.add + local.set $l6853 + local.get $l6852 + local.get $l6853 + i32.add + local.set $l6854 + local.get $l6853 + local.get $l6854 + i32.add + local.set $l6855 + local.get $l6854 + local.get $l6855 + i32.add + local.set $l6856 + local.get $l6855 + local.get $l6856 + i32.add + local.set $l6857 + local.get $l6856 + local.get $l6857 + i32.add + local.set $l6858 + local.get $l6857 + local.get $l6858 + i32.add + local.set $l6859 + local.get $l6858 + local.get $l6859 + i32.add + local.set $l6860 + local.get $l6859 + local.get $l6860 + i32.add + local.set $l6861 + local.get $l6860 + local.get $l6861 + i32.add + local.set $l6862 + local.get $l6861 + local.get $l6862 + i32.add + local.set $l6863 + local.get $l6862 + local.get $l6863 + i32.add + local.set $l6864 + local.get $l6863 + local.get $l6864 + i32.add + local.set $l6865 + local.get $l6864 + local.get $l6865 + i32.add + local.set $l6866 + local.get $l6865 + local.get $l6866 + i32.add + local.set $l6867 + local.get $l6866 + local.get $l6867 + i32.add + local.set $l6868 + local.get $l6867 + local.get $l6868 + i32.add + local.set $l6869 + local.get $l6868 + local.get $l6869 + i32.add + local.set $l6870 + local.get $l6869 + local.get $l6870 + i32.add + local.set $l6871 + local.get $l6870 + local.get $l6871 + i32.add + local.set $l6872 + local.get $l6871 + local.get $l6872 + i32.add + local.set $l6873 + local.get $l6872 + local.get $l6873 + i32.add + local.set $l6874 + local.get $l6873 + local.get $l6874 + i32.add + local.set $l6875 + local.get $l6874 + local.get $l6875 + i32.add + local.set $l6876 + local.get $l6875 + local.get $l6876 + i32.add + local.set $l6877 + local.get $l6876 + local.get $l6877 + i32.add + local.set $l6878 + local.get $l6877 + local.get $l6878 + i32.add + local.set $l6879 + local.get $l6878 + local.get $l6879 + i32.add + local.set $l6880 + local.get $l6879 + local.get $l6880 + i32.add + local.set $l6881 + local.get $l6880 + local.get $l6881 + i32.add + local.set $l6882 + local.get $l6881 + local.get $l6882 + i32.add + local.set $l6883 + local.get $l6882 + local.get $l6883 + i32.add + local.set $l6884 + local.get $l6883 + local.get $l6884 + i32.add + local.set $l6885 + local.get $l6884 + local.get $l6885 + i32.add + local.set $l6886 + local.get $l6885 + local.get $l6886 + i32.add + local.set $l6887 + local.get $l6886 + local.get $l6887 + i32.add + local.set $l6888 + local.get $l6887 + local.get $l6888 + i32.add + local.set $l6889 + local.get $l6888 + local.get $l6889 + i32.add + local.set $l6890 + local.get $l6889 + local.get $l6890 + i32.add + local.set $l6891 + local.get $l6890 + local.get $l6891 + i32.add + local.set $l6892 + local.get $l6891 + local.get $l6892 + i32.add + local.set $l6893 + local.get $l6892 + local.get $l6893 + i32.add + local.set $l6894 + local.get $l6893 + local.get $l6894 + i32.add + local.set $l6895 + local.get $l6894 + local.get $l6895 + i32.add + local.set $l6896 + local.get $l6895 + local.get $l6896 + i32.add + local.set $l6897 + local.get $l6896 + local.get $l6897 + i32.add + local.set $l6898 + local.get $l6897 + local.get $l6898 + i32.add + local.set $l6899 + local.get $l6898 + local.get $l6899 + i32.add + local.set $l6900 + local.get $l6899 + local.get $l6900 + i32.add + local.set $l6901 + local.get $l6900 + local.get $l6901 + i32.add + local.set $l6902 + local.get $l6901 + local.get $l6902 + i32.add + local.set $l6903 + local.get $l6902 + local.get $l6903 + i32.add + local.set $l6904 + local.get $l6903 + local.get $l6904 + i32.add + local.set $l6905 + local.get $l6904 + local.get $l6905 + i32.add + local.set $l6906 + local.get $l6905 + local.get $l6906 + i32.add + local.set $l6907 + local.get $l6906 + local.get $l6907 + i32.add + local.set $l6908 + local.get $l6907 + local.get $l6908 + i32.add + local.set $l6909 + local.get $l6908 + local.get $l6909 + i32.add + local.set $l6910 + local.get $l6909 + local.get $l6910 + i32.add + local.set $l6911 + local.get $l6910 + local.get $l6911 + i32.add + local.set $l6912 + local.get $l6911 + local.get $l6912 + i32.add + local.set $l6913 + local.get $l6912 + local.get $l6913 + i32.add + local.set $l6914 + local.get $l6913 + local.get $l6914 + i32.add + local.set $l6915 + local.get $l6914 + local.get $l6915 + i32.add + local.set $l6916 + local.get $l6915 + local.get $l6916 + i32.add + local.set $l6917 + local.get $l6916 + local.get $l6917 + i32.add + local.set $l6918 + local.get $l6917 + local.get $l6918 + i32.add + local.set $l6919 + local.get $l6918 + local.get $l6919 + i32.add + local.set $l6920 + local.get $l6919 + local.get $l6920 + i32.add + local.set $l6921 + local.get $l6920 + local.get $l6921 + i32.add + local.set $l6922 + local.get $l6921 + local.get $l6922 + i32.add + local.set $l6923 + local.get $l6922 + local.get $l6923 + i32.add + local.set $l6924 + local.get $l6923 + local.get $l6924 + i32.add + local.set $l6925 + local.get $l6924 + local.get $l6925 + i32.add + local.set $l6926 + local.get $l6925 + local.get $l6926 + i32.add + local.set $l6927 + local.get $l6926 + local.get $l6927 + i32.add + local.set $l6928 + local.get $l6927 + local.get $l6928 + i32.add + local.set $l6929 + local.get $l6928 + local.get $l6929 + i32.add + local.set $l6930 + local.get $l6929 + local.get $l6930 + i32.add + local.set $l6931 + local.get $l6930 + local.get $l6931 + i32.add + local.set $l6932 + local.get $l6931 + local.get $l6932 + i32.add + local.set $l6933 + local.get $l6932 + local.get $l6933 + i32.add + local.set $l6934 + local.get $l6933 + local.get $l6934 + i32.add + local.set $l6935 + local.get $l6934 + local.get $l6935 + i32.add + local.set $l6936 + local.get $l6935 + local.get $l6936 + i32.add + local.set $l6937 + local.get $l6936 + local.get $l6937 + i32.add + local.set $l6938 + local.get $l6937 + local.get $l6938 + i32.add + local.set $l6939 + local.get $l6938 + local.get $l6939 + i32.add + local.set $l6940 + local.get $l6939 + local.get $l6940 + i32.add + local.set $l6941 + local.get $l6940 + local.get $l6941 + i32.add + local.set $l6942 + local.get $l6941 + local.get $l6942 + i32.add + local.set $l6943 + local.get $l6942 + local.get $l6943 + i32.add + local.set $l6944 + local.get $l6943 + local.get $l6944 + i32.add + local.set $l6945 + local.get $l6944 + local.get $l6945 + i32.add + local.set $l6946 + local.get $l6945 + local.get $l6946 + i32.add + local.set $l6947 + local.get $l6946 + local.get $l6947 + i32.add + local.set $l6948 + local.get $l6947 + local.get $l6948 + i32.add + local.set $l6949 + local.get $l6948 + local.get $l6949 + i32.add + local.set $l6950 + local.get $l6949 + local.get $l6950 + i32.add + local.set $l6951 + local.get $l6950 + local.get $l6951 + i32.add + local.set $l6952 + local.get $l6951 + local.get $l6952 + i32.add + local.set $l6953 + local.get $l6952 + local.get $l6953 + i32.add + local.set $l6954 + local.get $l6953 + local.get $l6954 + i32.add + local.set $l6955 + local.get $l6954 + local.get $l6955 + i32.add + local.set $l6956 + local.get $l6955 + local.get $l6956 + i32.add + local.set $l6957 + local.get $l6956 + local.get $l6957 + i32.add + local.set $l6958 + local.get $l6957 + local.get $l6958 + i32.add + local.set $l6959 + local.get $l6958 + local.get $l6959 + i32.add + local.set $l6960 + local.get $l6959 + local.get $l6960 + i32.add + local.set $l6961 + local.get $l6960 + local.get $l6961 + i32.add + local.set $l6962 + local.get $l6961 + local.get $l6962 + i32.add + local.set $l6963 + local.get $l6962 + local.get $l6963 + i32.add + local.set $l6964 + local.get $l6963 + local.get $l6964 + i32.add + local.set $l6965 + local.get $l6964 + local.get $l6965 + i32.add + local.set $l6966 + local.get $l6965 + local.get $l6966 + i32.add + local.set $l6967 + local.get $l6966 + local.get $l6967 + i32.add + local.set $l6968 + local.get $l6967 + local.get $l6968 + i32.add + local.set $l6969 + local.get $l6968 + local.get $l6969 + i32.add + local.set $l6970 + local.get $l6969 + local.get $l6970 + i32.add + local.set $l6971 + local.get $l6970 + local.get $l6971 + i32.add + local.set $l6972 + local.get $l6971 + local.get $l6972 + i32.add + local.set $l6973 + local.get $l6972 + local.get $l6973 + i32.add + local.set $l6974 + local.get $l6973 + local.get $l6974 + i32.add + local.set $l6975 + local.get $l6974 + local.get $l6975 + i32.add + local.set $l6976 + local.get $l6975 + local.get $l6976 + i32.add + local.set $l6977 + local.get $l6976 + local.get $l6977 + i32.add + local.set $l6978 + local.get $l6977 + local.get $l6978 + i32.add + local.set $l6979 + local.get $l6978 + local.get $l6979 + i32.add + local.set $l6980 + local.get $l6979 + local.get $l6980 + i32.add + local.set $l6981 + local.get $l6980 + local.get $l6981 + i32.add + local.set $l6982 + local.get $l6981 + local.get $l6982 + i32.add + local.set $l6983 + local.get $l6982 + local.get $l6983 + i32.add + local.set $l6984 + local.get $l6983 + local.get $l6984 + i32.add + local.set $l6985 + local.get $l6984 + local.get $l6985 + i32.add + local.set $l6986 + local.get $l6985 + local.get $l6986 + i32.add + local.set $l6987 + local.get $l6986 + local.get $l6987 + i32.add + local.set $l6988 + local.get $l6987 + local.get $l6988 + i32.add + local.set $l6989 + local.get $l6988 + local.get $l6989 + i32.add + local.set $l6990 + local.get $l6989 + local.get $l6990 + i32.add + local.set $l6991 + local.get $l6990 + local.get $l6991 + i32.add + local.set $l6992 + local.get $l6991 + local.get $l6992 + i32.add + local.set $l6993 + local.get $l6992 + local.get $l6993 + i32.add + local.set $l6994 + local.get $l6993 + local.get $l6994 + i32.add + local.set $l6995 + local.get $l6994 + local.get $l6995 + i32.add + local.set $l6996 + local.get $l6995 + local.get $l6996 + i32.add + local.set $l6997 + local.get $l6996 + local.get $l6997 + i32.add + local.set $l6998 + local.get $l6997 + local.get $l6998 + i32.add + local.set $l6999 + local.get $l6998 + local.get $l6999 + i32.add + local.set $l7000 + local.get $l6999 + local.get $l7000 + i32.add + local.set $l7001 + local.get $l7000 + local.get $l7001 + i32.add + local.set $l7002 + local.get $l7001 + local.get $l7002 + i32.add + local.set $l7003 + local.get $l7002 + local.get $l7003 + i32.add + local.set $l7004 + local.get $l7003 + local.get $l7004 + i32.add + local.set $l7005 + local.get $l7004 + local.get $l7005 + i32.add + local.set $l7006 + local.get $l7005 + local.get $l7006 + i32.add + local.set $l7007 + local.get $l7006 + local.get $l7007 + i32.add + local.set $l7008 + local.get $l7007 + local.get $l7008 + i32.add + local.set $l7009 + local.get $l7008 + local.get $l7009 + i32.add + local.set $l7010 + local.get $l7009 + local.get $l7010 + i32.add + local.set $l7011 + local.get $l7010 + local.get $l7011 + i32.add + local.set $l7012 + local.get $l7011 + local.get $l7012 + i32.add + local.set $l7013 + local.get $l7012 + local.get $l7013 + i32.add + local.set $l7014 + local.get $l7013 + local.get $l7014 + i32.add + local.set $l7015 + local.get $l7014 + local.get $l7015 + i32.add + local.set $l7016 + local.get $l7015 + local.get $l7016 + i32.add + local.set $l7017 + local.get $l7016 + local.get $l7017 + i32.add + local.set $l7018 + local.get $l7017 + local.get $l7018 + i32.add + local.set $l7019 + local.get $l7018 + local.get $l7019 + i32.add + local.set $l7020 + local.get $l7019 + local.get $l7020 + i32.add + local.set $l7021 + local.get $l7020 + local.get $l7021 + i32.add + local.set $l7022 + local.get $l7021 + local.get $l7022 + i32.add + local.set $l7023 + local.get $l7022 + local.get $l7023 + i32.add + local.set $l7024 + local.get $l7023 + local.get $l7024 + i32.add + local.set $l7025 + local.get $l7024 + local.get $l7025 + i32.add + local.set $l7026 + local.get $l7025 + local.get $l7026 + i32.add + local.set $l7027 + local.get $l7026 + local.get $l7027 + i32.add + local.set $l7028 + local.get $l7027 + local.get $l7028 + i32.add + local.set $l7029 + local.get $l7028 + local.get $l7029 + i32.add + local.set $l7030 + local.get $l7029 + local.get $l7030 + i32.add + local.set $l7031 + local.get $l7030 + local.get $l7031 + i32.add + local.set $l7032 + local.get $l7031 + local.get $l7032 + i32.add + local.set $l7033 + local.get $l7032 + local.get $l7033 + i32.add + local.set $l7034 + local.get $l7033 + local.get $l7034 + i32.add + local.set $l7035 + local.get $l7034 + local.get $l7035 + i32.add + local.set $l7036 + local.get $l7035 + local.get $l7036 + i32.add + local.set $l7037 + local.get $l7036 + local.get $l7037 + i32.add + local.set $l7038 + local.get $l7037 + local.get $l7038 + i32.add + local.set $l7039 + local.get $l7038 + local.get $l7039 + i32.add + local.set $l7040 + local.get $l7039 + local.get $l7040 + i32.add + local.set $l7041 + local.get $l7040 + local.get $l7041 + i32.add + local.set $l7042 + local.get $l7041 + local.get $l7042 + i32.add + local.set $l7043 + local.get $l7042 + local.get $l7043 + i32.add + local.set $l7044 + local.get $l7043 + local.get $l7044 + i32.add + local.set $l7045 + local.get $l7044 + local.get $l7045 + i32.add + local.set $l7046 + local.get $l7045 + local.get $l7046 + i32.add + local.set $l7047 + local.get $l7046 + local.get $l7047 + i32.add + local.set $l7048 + local.get $l7047 + local.get $l7048 + i32.add + local.set $l7049 + local.get $l7048 + local.get $l7049 + i32.add + local.set $l7050 + local.get $l7049 + local.get $l7050 + i32.add + local.set $l7051 + local.get $l7050 + local.get $l7051 + i32.add + local.set $l7052 + local.get $l7051 + local.get $l7052 + i32.add + local.set $l7053 + local.get $l7052 + local.get $l7053 + i32.add + local.set $l7054 + local.get $l7053 + local.get $l7054 + i32.add + local.set $l7055 + local.get $l7054 + local.get $l7055 + i32.add + local.set $l7056 + local.get $l7055 + local.get $l7056 + i32.add + local.set $l7057 + local.get $l7056 + local.get $l7057 + i32.add + local.set $l7058 + local.get $l7057 + local.get $l7058 + i32.add + local.set $l7059 + local.get $l7058 + local.get $l7059 + i32.add + local.set $l7060 + local.get $l7059 + local.get $l7060 + i32.add + local.set $l7061 + local.get $l7060 + local.get $l7061 + i32.add + local.set $l7062 + local.get $l7061 + local.get $l7062 + i32.add + local.set $l7063 + local.get $l7062 + local.get $l7063 + i32.add + local.set $l7064 + local.get $l7063 + local.get $l7064 + i32.add + local.set $l7065 + local.get $l7064 + local.get $l7065 + i32.add + local.set $l7066 + local.get $l7065 + local.get $l7066 + i32.add + local.set $l7067 + local.get $l7066 + local.get $l7067 + i32.add + local.set $l7068 + local.get $l7067 + local.get $l7068 + i32.add + local.set $l7069 + local.get $l7068 + local.get $l7069 + i32.add + local.set $l7070 + local.get $l7069 + local.get $l7070 + i32.add + local.set $l7071 + local.get $l7070 + local.get $l7071 + i32.add + local.set $l7072 + local.get $l7071 + local.get $l7072 + i32.add + local.set $l7073 + local.get $l7072 + local.get $l7073 + i32.add + local.set $l7074 + local.get $l7073 + local.get $l7074 + i32.add + local.set $l7075 + local.get $l7074 + local.get $l7075 + i32.add + local.set $l7076 + local.get $l7075 + local.get $l7076 + i32.add + local.set $l7077 + local.get $l7076 + local.get $l7077 + i32.add + local.set $l7078 + local.get $l7077 + local.get $l7078 + i32.add + local.set $l7079 + local.get $l7078 + local.get $l7079 + i32.add + local.set $l7080 + local.get $l7079 + local.get $l7080 + i32.add + local.set $l7081 + local.get $l7080 + local.get $l7081 + i32.add + local.set $l7082 + local.get $l7081 + local.get $l7082 + i32.add + local.set $l7083 + local.get $l7082 + local.get $l7083 + i32.add + local.set $l7084 + local.get $l7083 + local.get $l7084 + i32.add + local.set $l7085 + local.get $l7084 + local.get $l7085 + i32.add + local.set $l7086 + local.get $l7085 + local.get $l7086 + i32.add + local.set $l7087 + local.get $l7086 + local.get $l7087 + i32.add + local.set $l7088 + local.get $l7087 + local.get $l7088 + i32.add + local.set $l7089 + local.get $l7088 + local.get $l7089 + i32.add + local.set $l7090 + local.get $l7089 + local.get $l7090 + i32.add + local.set $l7091 + local.get $l7090 + local.get $l7091 + i32.add + local.set $l7092 + local.get $l7091 + local.get $l7092 + i32.add + local.set $l7093 + local.get $l7092 + local.get $l7093 + i32.add + local.set $l7094 + local.get $l7093 + local.get $l7094 + i32.add + local.set $l7095 + local.get $l7094 + local.get $l7095 + i32.add + local.set $l7096 + local.get $l7095 + local.get $l7096 + i32.add + local.set $l7097 + local.get $l7096 + local.get $l7097 + i32.add + local.set $l7098 + local.get $l7097 + local.get $l7098 + i32.add + local.set $l7099 + local.get $l7098 + local.get $l7099 + i32.add + local.set $l7100 + local.get $l7099 + local.get $l7100 + i32.add + local.set $l7101 + local.get $l7100 + local.get $l7101 + i32.add + local.set $l7102 + local.get $l7101 + local.get $l7102 + i32.add + local.set $l7103 + local.get $l7102 + local.get $l7103 + i32.add + local.set $l7104 + local.get $l7103 + local.get $l7104 + i32.add + local.set $l7105 + local.get $l7104 + local.get $l7105 + i32.add + local.set $l7106 + local.get $l7105 + local.get $l7106 + i32.add + local.set $l7107 + local.get $l7106 + local.get $l7107 + i32.add + local.set $l7108 + local.get $l7107 + local.get $l7108 + i32.add + local.set $l7109 + local.get $l7108 + local.get $l7109 + i32.add + local.set $l7110 + local.get $l7109 + local.get $l7110 + i32.add + local.set $l7111 + local.get $l7110 + local.get $l7111 + i32.add + local.set $l7112 + local.get $l7111 + local.get $l7112 + i32.add + local.set $l7113 + local.get $l7112 + local.get $l7113 + i32.add + local.set $l7114 + local.get $l7113 + local.get $l7114 + i32.add + local.set $l7115 + local.get $l7114 + local.get $l7115 + i32.add + local.set $l7116 + local.get $l7115 + local.get $l7116 + i32.add + local.set $l7117 + local.get $l7116 + local.get $l7117 + i32.add + local.set $l7118 + local.get $l7117 + local.get $l7118 + i32.add + local.set $l7119 + local.get $l7118 + local.get $l7119 + i32.add + local.set $l7120 + local.get $l7119 + local.get $l7120 + i32.add + local.set $l7121 + local.get $l7120 + local.get $l7121 + i32.add + local.set $l7122 + local.get $l7121 + local.get $l7122 + i32.add + local.set $l7123 + local.get $l7122 + local.get $l7123 + i32.add + local.set $l7124 + local.get $l7123 + local.get $l7124 + i32.add + local.set $l7125 + local.get $l7124 + local.get $l7125 + i32.add + local.set $l7126 + local.get $l7125 + local.get $l7126 + i32.add + local.set $l7127 + local.get $l7126 + local.get $l7127 + i32.add + local.set $l7128 + local.get $l7127 + local.get $l7128 + i32.add + local.set $l7129 + local.get $l7128 + local.get $l7129 + i32.add + local.set $l7130 + local.get $l7129 + local.get $l7130 + i32.add + local.set $l7131 + local.get $l7130 + local.get $l7131 + i32.add + local.set $l7132 + local.get $l7131 + local.get $l7132 + i32.add + local.set $l7133 + local.get $l7132 + local.get $l7133 + i32.add + local.set $l7134 + local.get $l7133 + local.get $l7134 + i32.add + local.set $l7135 + local.get $l7134 + local.get $l7135 + i32.add + local.set $l7136 + local.get $l7135 + local.get $l7136 + i32.add + local.set $l7137 + local.get $l7136 + local.get $l7137 + i32.add + local.set $l7138 + local.get $l7137 + local.get $l7138 + i32.add + local.set $l7139 + local.get $l7138 + local.get $l7139 + i32.add + local.set $l7140 + local.get $l7139 + local.get $l7140 + i32.add + local.set $l7141 + local.get $l7140 + local.get $l7141 + i32.add + local.set $l7142 + local.get $l7141 + local.get $l7142 + i32.add + local.set $l7143 + local.get $l7142 + local.get $l7143 + i32.add + local.set $l7144 + local.get $l7143 + local.get $l7144 + i32.add + local.set $l7145 + local.get $l7144 + local.get $l7145 + i32.add + local.set $l7146 + local.get $l7145 + local.get $l7146 + i32.add + local.set $l7147 + local.get $l7146 + local.get $l7147 + i32.add + local.set $l7148 + local.get $l7147 + local.get $l7148 + i32.add + local.set $l7149 + local.get $l7148 + local.get $l7149 + i32.add + local.set $l7150 + local.get $l7149 + local.get $l7150 + i32.add + local.set $l7151 + local.get $l7150 + local.get $l7151 + i32.add + local.set $l7152 + local.get $l7151 + local.get $l7152 + i32.add + local.set $l7153 + local.get $l7152 + local.get $l7153 + i32.add + local.set $l7154 + local.get $l7153 + local.get $l7154 + i32.add + local.set $l7155 + local.get $l7154 + local.get $l7155 + i32.add + local.set $l7156 + local.get $l7155 + local.get $l7156 + i32.add + local.set $l7157 + local.get $l7156 + local.get $l7157 + i32.add + local.set $l7158 + local.get $l7157 + local.get $l7158 + i32.add + local.set $l7159 + local.get $l7158 + local.get $l7159 + i32.add + local.set $l7160 + local.get $l7159 + local.get $l7160 + i32.add + local.set $l7161 + local.get $l7160 + local.get $l7161 + i32.add + local.set $l7162 + local.get $l7161 + local.get $l7162 + i32.add + local.set $l7163 + local.get $l7162 + local.get $l7163 + i32.add + local.set $l7164 + local.get $l7163 + local.get $l7164 + i32.add + local.set $l7165 + local.get $l7164 + local.get $l7165 + i32.add + local.set $l7166 + local.get $l7165 + local.get $l7166 + i32.add + local.set $l7167 + local.get $l7166 + local.get $l7167 + i32.add + local.set $l7168 + local.get $l7167 + local.get $l7168 + i32.add + local.set $l7169 + local.get $l7168 + local.get $l7169 + i32.add + local.set $l7170 + local.get $l7169 + local.get $l7170 + i32.add + local.set $l7171 + local.get $l7170 + local.get $l7171 + i32.add + local.set $l7172 + local.get $l7171 + local.get $l7172 + i32.add + local.set $l7173 + local.get $l7172 + local.get $l7173 + i32.add + local.set $l7174 + local.get $l7173 + local.get $l7174 + i32.add + local.set $l7175 + local.get $l7174 + local.get $l7175 + i32.add + local.set $l7176 + local.get $l7175 + local.get $l7176 + i32.add + local.set $l7177 + local.get $l7176 + local.get $l7177 + i32.add + local.set $l7178 + local.get $l7177 + local.get $l7178 + i32.add + local.set $l7179 + local.get $l7178 + local.get $l7179 + i32.add + local.set $l7180 + local.get $l7179 + local.get $l7180 + i32.add + local.set $l7181 + local.get $l7180 + local.get $l7181 + i32.add + local.set $l7182 + local.get $l7181 + local.get $l7182 + i32.add + local.set $l7183 + local.get $l7182 + local.get $l7183 + i32.add + local.set $l7184 + local.get $l7183 + local.get $l7184 + i32.add + local.set $l7185 + local.get $l7184 + local.get $l7185 + i32.add + local.set $l7186 + local.get $l7185 + local.get $l7186 + i32.add + local.set $l7187 + local.get $l7186 + local.get $l7187 + i32.add + local.set $l7188 + local.get $l7187 + local.get $l7188 + i32.add + local.set $l7189 + local.get $l7188 + local.get $l7189 + i32.add + local.set $l7190 + local.get $l7189 + local.get $l7190 + i32.add + local.set $l7191 + local.get $l7190 + local.get $l7191 + i32.add + local.set $l7192 + local.get $l7191 + local.get $l7192 + i32.add + local.set $l7193 + local.get $l7192 + local.get $l7193 + i32.add + local.set $l7194 + local.get $l7193 + local.get $l7194 + i32.add + local.set $l7195 + local.get $l7194 + local.get $l7195 + i32.add + local.set $l7196 + local.get $l7195 + local.get $l7196 + i32.add + local.set $l7197 + local.get $l7196 + local.get $l7197 + i32.add + local.set $l7198 + local.get $l7197 + local.get $l7198 + i32.add + local.set $l7199 + local.get $l7198 + local.get $l7199 + i32.add + local.set $l7200 + local.get $l7199 + local.get $l7200 + i32.add + local.set $l7201 + local.get $l7200 + local.get $l7201 + i32.add + local.set $l7202 + local.get $l7201 + local.get $l7202 + i32.add + local.set $l7203 + local.get $l7202 + local.get $l7203 + i32.add + local.set $l7204 + local.get $l7203 + local.get $l7204 + i32.add + local.set $l7205 + local.get $l7204 + local.get $l7205 + i32.add + local.set $l7206 + local.get $l7205 + local.get $l7206 + i32.add + local.set $l7207 + local.get $l7206 + local.get $l7207 + i32.add + local.set $l7208 + local.get $l7207 + local.get $l7208 + i32.add + local.set $l7209 + local.get $l7208 + local.get $l7209 + i32.add + local.set $l7210 + local.get $l7209 + local.get $l7210 + i32.add + local.set $l7211 + local.get $l7210 + local.get $l7211 + i32.add + local.set $l7212 + local.get $l7211 + local.get $l7212 + i32.add + local.set $l7213 + local.get $l7212 + local.get $l7213 + i32.add + local.set $l7214 + local.get $l7213 + local.get $l7214 + i32.add + local.set $l7215 + local.get $l7214 + local.get $l7215 + i32.add + local.set $l7216 + local.get $l7215 + local.get $l7216 + i32.add + local.set $l7217 + local.get $l7216 + local.get $l7217 + i32.add + local.set $l7218 + local.get $l7217 + local.get $l7218 + i32.add + local.set $l7219 + local.get $l7218 + local.get $l7219 + i32.add + local.set $l7220 + local.get $l7219 + local.get $l7220 + i32.add + local.set $l7221 + local.get $l7220 + local.get $l7221 + i32.add + local.set $l7222 + local.get $l7221 + local.get $l7222 + i32.add + local.set $l7223 + local.get $l7222 + local.get $l7223 + i32.add + local.set $l7224 + local.get $l7223 + local.get $l7224 + i32.add + local.set $l7225 + local.get $l7224 + local.get $l7225 + i32.add + local.set $l7226 + local.get $l7225 + local.get $l7226 + i32.add + local.set $l7227 + local.get $l7226 + local.get $l7227 + i32.add + local.set $l7228 + local.get $l7227 + local.get $l7228 + i32.add + local.set $l7229 + local.get $l7228 + local.get $l7229 + i32.add + local.set $l7230 + local.get $l7229 + local.get $l7230 + i32.add + local.set $l7231 + local.get $l7230 + local.get $l7231 + i32.add + local.set $l7232 + local.get $l7231 + local.get $l7232 + i32.add + local.set $l7233 + local.get $l7232 + local.get $l7233 + i32.add + local.set $l7234 + local.get $l7233 + local.get $l7234 + i32.add + local.set $l7235 + local.get $l7234 + local.get $l7235 + i32.add + local.set $l7236 + local.get $l7235 + local.get $l7236 + i32.add + local.set $l7237 + local.get $l7236 + local.get $l7237 + i32.add + local.set $l7238 + local.get $l7237 + local.get $l7238 + i32.add + local.set $l7239 + local.get $l7238 + local.get $l7239 + i32.add + local.set $l7240 + local.get $l7239 + local.get $l7240 + i32.add + local.set $l7241 + local.get $l7240 + local.get $l7241 + i32.add + local.set $l7242 + local.get $l7241 + local.get $l7242 + i32.add + local.set $l7243 + local.get $l7242 + local.get $l7243 + i32.add + local.set $l7244 + local.get $l7243 + local.get $l7244 + i32.add + local.set $l7245 + local.get $l7244 + local.get $l7245 + i32.add + local.set $l7246 + local.get $l7245 + local.get $l7246 + i32.add + local.set $l7247 + local.get $l7246 + local.get $l7247 + i32.add + local.set $l7248 + local.get $l7247 + local.get $l7248 + i32.add + local.set $l7249 + local.get $l7248 + local.get $l7249 + i32.add + local.set $l7250 + local.get $l7249 + local.get $l7250 + i32.add + local.set $l7251 + local.get $l7250 + local.get $l7251 + i32.add + local.set $l7252 + local.get $l7251 + local.get $l7252 + i32.add + local.set $l7253 + local.get $l7252 + local.get $l7253 + i32.add + local.set $l7254 + local.get $l7253 + local.get $l7254 + i32.add + local.set $l7255 + local.get $l7254 + local.get $l7255 + i32.add + local.set $l7256 + local.get $l7255 + local.get $l7256 + i32.add + local.set $l7257 + local.get $l7256 + local.get $l7257 + i32.add + local.set $l7258 + local.get $l7257 + local.get $l7258 + i32.add + local.set $l7259 + local.get $l7258 + local.get $l7259 + i32.add + local.set $l7260 + local.get $l7259 + local.get $l7260 + i32.add + local.set $l7261 + local.get $l7260 + local.get $l7261 + i32.add + local.set $l7262 + local.get $l7261 + local.get $l7262 + i32.add + local.set $l7263 + local.get $l7262 + local.get $l7263 + i32.add + local.set $l7264 + local.get $l7263 + local.get $l7264 + i32.add + local.set $l7265 + local.get $l7264 + local.get $l7265 + i32.add + local.set $l7266 + local.get $l7265 + local.get $l7266 + i32.add + local.set $l7267 + local.get $l7266 + local.get $l7267 + i32.add + local.set $l7268 + local.get $l7267 + local.get $l7268 + i32.add + local.set $l7269 + local.get $l7268 + local.get $l7269 + i32.add + local.set $l7270 + local.get $l7269 + local.get $l7270 + i32.add + local.set $l7271 + local.get $l7270 + local.get $l7271 + i32.add + local.set $l7272 + local.get $l7271 + local.get $l7272 + i32.add + local.set $l7273 + local.get $l7272 + local.get $l7273 + i32.add + local.set $l7274 + local.get $l7273 + local.get $l7274 + i32.add + local.set $l7275 + local.get $l7274 + local.get $l7275 + i32.add + local.set $l7276 + local.get $l7275 + local.get $l7276 + i32.add + local.set $l7277 + local.get $l7276 + local.get $l7277 + i32.add + local.set $l7278 + local.get $l7277 + local.get $l7278 + i32.add + local.set $l7279 + local.get $l7278 + local.get $l7279 + i32.add + local.set $l7280 + local.get $l7279 + local.get $l7280 + i32.add + local.set $l7281 + local.get $l7280 + local.get $l7281 + i32.add + local.set $l7282 + local.get $l7281 + local.get $l7282 + i32.add + local.set $l7283 + local.get $l7282 + local.get $l7283 + i32.add + local.set $l7284 + local.get $l7283 + local.get $l7284 + i32.add + local.set $l7285 + local.get $l7284 + local.get $l7285 + i32.add + local.set $l7286 + local.get $l7285 + local.get $l7286 + i32.add + local.set $l7287 + local.get $l7286 + local.get $l7287 + i32.add + local.set $l7288 + local.get $l7287 + local.get $l7288 + i32.add + local.set $l7289 + local.get $l7288 + local.get $l7289 + i32.add + local.set $l7290 + local.get $l7289 + local.get $l7290 + i32.add + local.set $l7291 + local.get $l7290 + local.get $l7291 + i32.add + local.set $l7292 + local.get $l7291 + local.get $l7292 + i32.add + local.set $l7293 + local.get $l7292 + local.get $l7293 + i32.add + local.set $l7294 + local.get $l7293 + local.get $l7294 + i32.add + local.set $l7295 + local.get $l7294 + local.get $l7295 + i32.add + local.set $l7296 + local.get $l7295 + local.get $l7296 + i32.add + local.set $l7297 + local.get $l7296 + local.get $l7297 + i32.add + local.set $l7298 + local.get $l7297 + local.get $l7298 + i32.add + local.set $l7299 + local.get $l7298 + local.get $l7299 + i32.add + local.set $l7300 + local.get $l7299 + local.get $l7300 + i32.add + local.set $l7301 + local.get $l7300 + local.get $l7301 + i32.add + local.set $l7302 + local.get $l7301 + local.get $l7302 + i32.add + local.set $l7303 + local.get $l7302 + local.get $l7303 + i32.add + local.set $l7304 + local.get $l7303 + local.get $l7304 + i32.add + local.set $l7305 + local.get $l7304 + local.get $l7305 + i32.add + local.set $l7306 + local.get $l7305 + local.get $l7306 + i32.add + local.set $l7307 + local.get $l7306 + local.get $l7307 + i32.add + local.set $l7308 + local.get $l7307 + local.get $l7308 + i32.add + local.set $l7309 + local.get $l7308 + local.get $l7309 + i32.add + local.set $l7310 + local.get $l7309 + local.get $l7310 + i32.add + local.set $l7311 + local.get $l7310 + local.get $l7311 + i32.add + local.set $l7312 + local.get $l7311 + local.get $l7312 + i32.add + local.set $l7313 + local.get $l7312 + local.get $l7313 + i32.add + local.set $l7314 + local.get $l7313 + local.get $l7314 + i32.add + local.set $l7315 + local.get $l7314 + local.get $l7315 + i32.add + local.set $l7316 + local.get $l7315 + local.get $l7316 + i32.add + local.set $l7317 + local.get $l7316 + local.get $l7317 + i32.add + local.set $l7318 + local.get $l7317 + local.get $l7318 + i32.add + local.set $l7319 + local.get $l7318 + local.get $l7319 + i32.add + local.set $l7320 + local.get $l7319 + local.get $l7320 + i32.add + local.set $l7321 + local.get $l7320 + local.get $l7321 + i32.add + local.set $l7322 + local.get $l7321 + local.get $l7322 + i32.add + local.set $l7323 + local.get $l7322 + local.get $l7323 + i32.add + local.set $l7324 + local.get $l7323 + local.get $l7324 + i32.add + local.set $l7325 + local.get $l7324 + local.get $l7325 + i32.add + local.set $l7326 + local.get $l7325 + local.get $l7326 + i32.add + local.set $l7327 + local.get $l7326 + local.get $l7327 + i32.add + local.set $l7328 + local.get $l7327 + local.get $l7328 + i32.add + local.set $l7329 + local.get $l7328 + local.get $l7329 + i32.add + local.set $l7330 + local.get $l7329 + local.get $l7330 + i32.add + local.set $l7331 + local.get $l7330 + local.get $l7331 + i32.add + local.set $l7332 + local.get $l7331 + local.get $l7332 + i32.add + local.set $l7333 + local.get $l7332 + local.get $l7333 + i32.add + local.set $l7334 + local.get $l7333 + local.get $l7334 + i32.add + local.set $l7335 + local.get $l7334 + local.get $l7335 + i32.add + local.set $l7336 + local.get $l7335 + local.get $l7336 + i32.add + local.set $l7337 + local.get $l7336 + local.get $l7337 + i32.add + local.set $l7338 + local.get $l7337 + local.get $l7338 + i32.add + local.set $l7339 + local.get $l7338 + local.get $l7339 + i32.add + local.set $l7340 + local.get $l7339 + local.get $l7340 + i32.add + local.set $l7341 + local.get $l7340 + local.get $l7341 + i32.add + local.set $l7342 + local.get $l7341 + local.get $l7342 + i32.add + local.set $l7343 + local.get $l7342 + local.get $l7343 + i32.add + local.set $l7344 + local.get $l7343 + local.get $l7344 + i32.add + local.set $l7345 + local.get $l7344 + local.get $l7345 + i32.add + local.set $l7346 + local.get $l7345 + local.get $l7346 + i32.add + local.set $l7347 + local.get $l7346 + local.get $l7347 + i32.add + local.set $l7348 + local.get $l7347 + local.get $l7348 + i32.add + local.set $l7349 + local.get $l7348 + local.get $l7349 + i32.add + local.set $l7350 + local.get $l7349 + local.get $l7350 + i32.add + local.set $l7351 + local.get $l7350 + local.get $l7351 + i32.add + local.set $l7352 + local.get $l7351 + local.get $l7352 + i32.add + local.set $l7353 + local.get $l7352 + local.get $l7353 + i32.add + local.set $l7354 + local.get $l7353 + local.get $l7354 + i32.add + local.set $l7355 + local.get $l7354 + local.get $l7355 + i32.add + local.set $l7356 + local.get $l7355 + local.get $l7356 + i32.add + local.set $l7357 + local.get $l7356 + local.get $l7357 + i32.add + local.set $l7358 + local.get $l7357 + local.get $l7358 + i32.add + local.set $l7359 + local.get $l7358 + local.get $l7359 + i32.add + local.set $l7360 + local.get $l7359 + local.get $l7360 + i32.add + local.set $l7361 + local.get $l7360 + local.get $l7361 + i32.add + local.set $l7362 + local.get $l7361 + local.get $l7362 + i32.add + local.set $l7363 + local.get $l7362 + local.get $l7363 + i32.add + local.set $l7364 + local.get $l7363 + local.get $l7364 + i32.add + local.set $l7365 + local.get $l7364 + local.get $l7365 + i32.add + local.set $l7366 + local.get $l7365 + local.get $l7366 + i32.add + local.set $l7367 + local.get $l7366 + local.get $l7367 + i32.add + local.set $l7368 + local.get $l7367 + local.get $l7368 + i32.add + local.set $l7369 + local.get $l7368 + local.get $l7369 + i32.add + local.set $l7370 + local.get $l7369 + local.get $l7370 + i32.add + local.set $l7371 + local.get $l7370 + local.get $l7371 + i32.add + local.set $l7372 + local.get $l7371 + local.get $l7372 + i32.add + local.set $l7373 + local.get $l7372 + local.get $l7373 + i32.add + local.set $l7374 + local.get $l7373 + local.get $l7374 + i32.add + local.set $l7375 + local.get $l7374 + local.get $l7375 + i32.add + local.set $l7376 + local.get $l7375 + local.get $l7376 + i32.add + local.set $l7377 + local.get $l7376 + local.get $l7377 + i32.add + local.set $l7378 + local.get $l7377 + local.get $l7378 + i32.add + local.set $l7379 + local.get $l7378 + local.get $l7379 + i32.add + local.set $l7380 + local.get $l7379 + local.get $l7380 + i32.add + local.set $l7381 + local.get $l7380 + local.get $l7381 + i32.add + local.set $l7382 + local.get $l7381 + local.get $l7382 + i32.add + local.set $l7383 + local.get $l7382 + local.get $l7383 + i32.add + local.set $l7384 + local.get $l7383 + local.get $l7384 + i32.add + local.set $l7385 + local.get $l7384 + local.get $l7385 + i32.add + local.set $l7386 + local.get $l7385 + local.get $l7386 + i32.add + local.set $l7387 + local.get $l7386 + local.get $l7387 + i32.add + local.set $l7388 + local.get $l7387 + local.get $l7388 + i32.add + local.set $l7389 + local.get $l7388 + local.get $l7389 + i32.add + local.set $l7390 + local.get $l7389 + local.get $l7390 + i32.add + local.set $l7391 + local.get $l7390 + local.get $l7391 + i32.add + local.set $l7392 + local.get $l7391 + local.get $l7392 + i32.add + local.set $l7393 + local.get $l7392 + local.get $l7393 + i32.add + local.set $l7394 + local.get $l7393 + local.get $l7394 + i32.add + local.set $l7395 + local.get $l7394 + local.get $l7395 + i32.add + local.set $l7396 + local.get $l7395 + local.get $l7396 + i32.add + local.set $l7397 + local.get $l7396 + local.get $l7397 + i32.add + local.set $l7398 + local.get $l7397 + local.get $l7398 + i32.add + local.set $l7399 + local.get $l7398 + local.get $l7399 + i32.add + local.set $l7400 + local.get $l7399 + local.get $l7400 + i32.add + local.set $l7401 + local.get $l7400 + local.get $l7401 + i32.add + local.set $l7402 + local.get $l7401 + local.get $l7402 + i32.add + local.set $l7403 + local.get $l7402 + local.get $l7403 + i32.add + local.set $l7404 + local.get $l7403 + local.get $l7404 + i32.add + local.set $l7405 + local.get $l7404 + local.get $l7405 + i32.add + local.set $l7406 + local.get $l7405 + local.get $l7406 + i32.add + local.set $l7407 + local.get $l7406 + local.get $l7407 + i32.add + local.set $l7408 + local.get $l7407 + local.get $l7408 + i32.add + local.set $l7409 + local.get $l7408 + local.get $l7409 + i32.add + local.set $l7410 + local.get $l7409 + local.get $l7410 + i32.add + local.set $l7411 + local.get $l7410 + local.get $l7411 + i32.add + local.set $l7412 + local.get $l7411 + local.get $l7412 + i32.add + local.set $l7413 + local.get $l7412 + local.get $l7413 + i32.add + local.set $l7414 + local.get $l7413 + local.get $l7414 + i32.add + local.set $l7415 + local.get $l7414 + local.get $l7415 + i32.add + local.set $l7416 + local.get $l7415 + local.get $l7416 + i32.add + local.set $l7417 + local.get $l7416 + local.get $l7417 + i32.add + local.set $l7418 + local.get $l7417 + local.get $l7418 + i32.add + local.set $l7419 + local.get $l7418 + local.get $l7419 + i32.add + local.set $l7420 + local.get $l7419 + local.get $l7420 + i32.add + local.set $l7421 + local.get $l7420 + local.get $l7421 + i32.add + local.set $l7422 + local.get $l7421 + local.get $l7422 + i32.add + local.set $l7423 + local.get $l7422 + local.get $l7423 + i32.add + local.set $l7424 + local.get $l7423 + local.get $l7424 + i32.add + local.set $l7425 + local.get $l7424 + local.get $l7425 + i32.add + local.set $l7426 + local.get $l7425 + local.get $l7426 + i32.add + local.set $l7427 + local.get $l7426 + local.get $l7427 + i32.add + local.set $l7428 + local.get $l7427 + local.get $l7428 + i32.add + local.set $l7429 + local.get $l7428 + local.get $l7429 + i32.add + local.set $l7430 + local.get $l7429 + local.get $l7430 + i32.add + local.set $l7431 + local.get $l7430 + local.get $l7431 + i32.add + local.set $l7432 + local.get $l7431 + local.get $l7432 + i32.add + local.set $l7433 + local.get $l7432 + local.get $l7433 + i32.add + local.set $l7434 + local.get $l7433 + local.get $l7434 + i32.add + local.set $l7435 + local.get $l7434 + local.get $l7435 + i32.add + local.set $l7436 + local.get $l7435 + local.get $l7436 + i32.add + local.set $l7437 + local.get $l7436 + local.get $l7437 + i32.add + local.set $l7438 + local.get $l7437 + local.get $l7438 + i32.add + local.set $l7439 + local.get $l7438 + local.get $l7439 + i32.add + local.set $l7440 + local.get $l7439 + local.get $l7440 + i32.add + local.set $l7441 + local.get $l7440 + local.get $l7441 + i32.add + local.set $l7442 + local.get $l7441 + local.get $l7442 + i32.add + local.set $l7443 + local.get $l7442 + local.get $l7443 + i32.add + local.set $l7444 + local.get $l7443 + local.get $l7444 + i32.add + local.set $l7445 + local.get $l7444 + local.get $l7445 + i32.add + local.set $l7446 + local.get $l7445 + local.get $l7446 + i32.add + local.set $l7447 + local.get $l7446 + local.get $l7447 + i32.add + local.set $l7448 + local.get $l7447 + local.get $l7448 + i32.add + local.set $l7449 + local.get $l7448 + local.get $l7449 + i32.add + local.set $l7450 + local.get $l7449 + local.get $l7450 + i32.add + local.set $l7451 + local.get $l7450 + local.get $l7451 + i32.add + local.set $l7452 + local.get $l7451 + local.get $l7452 + i32.add + local.set $l7453 + local.get $l7452 + local.get $l7453 + i32.add + local.set $l7454 + local.get $l7453 + local.get $l7454 + i32.add + local.set $l7455 + local.get $l7454 + local.get $l7455 + i32.add + local.set $l7456 + local.get $l7455 + local.get $l7456 + i32.add + local.set $l7457 + local.get $l7456 + local.get $l7457 + i32.add + local.set $l7458 + local.get $l7457 + local.get $l7458 + i32.add + local.set $l7459 + local.get $l7458 + local.get $l7459 + i32.add + local.set $l7460 + local.get $l7459 + local.get $l7460 + i32.add + local.set $l7461 + local.get $l7460 + local.get $l7461 + i32.add + local.set $l7462 + local.get $l7461 + local.get $l7462 + i32.add + local.set $l7463 + local.get $l7462 + local.get $l7463 + i32.add + local.set $l7464 + local.get $l7463 + local.get $l7464 + i32.add + local.set $l7465 + local.get $l7464 + local.get $l7465 + i32.add + local.set $l7466 + local.get $l7465 + local.get $l7466 + i32.add + local.set $l7467 + local.get $l7466 + local.get $l7467 + i32.add + local.set $l7468 + local.get $l7467 + local.get $l7468 + i32.add + local.set $l7469 + local.get $l7468 + local.get $l7469 + i32.add + local.set $l7470 + local.get $l7469 + local.get $l7470 + i32.add + local.set $l7471 + local.get $l7470 + local.get $l7471 + i32.add + local.set $l7472 + local.get $l7471 + local.get $l7472 + i32.add + local.set $l7473 + local.get $l7472 + local.get $l7473 + i32.add + local.set $l7474 + local.get $l7473 + local.get $l7474 + i32.add + local.set $l7475 + local.get $l7474 + local.get $l7475 + i32.add + local.set $l7476 + local.get $l7475 + local.get $l7476 + i32.add + local.set $l7477 + local.get $l7476 + local.get $l7477 + i32.add + local.set $l7478 + local.get $l7477 + local.get $l7478 + i32.add + local.set $l7479 + local.get $l7478 + local.get $l7479 + i32.add + local.set $l7480 + local.get $l7479 + local.get $l7480 + i32.add + local.set $l7481 + local.get $l7480 + local.get $l7481 + i32.add + local.set $l7482 + local.get $l7481 + local.get $l7482 + i32.add + local.set $l7483 + local.get $l7482 + local.get $l7483 + i32.add + local.set $l7484 + local.get $l7483 + local.get $l7484 + i32.add + local.set $l7485 + local.get $l7484 + local.get $l7485 + i32.add + local.set $l7486 + local.get $l7485 + local.get $l7486 + i32.add + local.set $l7487 + local.get $l7486 + local.get $l7487 + i32.add + local.set $l7488 + local.get $l7487 + local.get $l7488 + i32.add + local.set $l7489 + local.get $l7488 + local.get $l7489 + i32.add + local.set $l7490 + local.get $l7489 + local.get $l7490 + i32.add + local.set $l7491 + local.get $l7490 + local.get $l7491 + i32.add + local.set $l7492 + local.get $l7491 + local.get $l7492 + i32.add + local.set $l7493 + local.get $l7492 + local.get $l7493 + i32.add + local.set $l7494 + local.get $l7493 + local.get $l7494 + i32.add + local.set $l7495 + local.get $l7494 + local.get $l7495 + i32.add + local.set $l7496 + local.get $l7495 + local.get $l7496 + i32.add + local.set $l7497 + local.get $l7496 + local.get $l7497 + i32.add + local.set $l7498 + local.get $l7497 + local.get $l7498 + i32.add + local.set $l7499 + local.get $l7498 + local.get $l7499 + i32.add + local.set $l7500 + local.get $l7499 + local.get $l7500 + i32.add + local.set $l7501 + local.get $l7500 + local.get $l7501 + i32.add + local.set $l7502 + local.get $l7501 + local.get $l7502 + i32.add + local.set $l7503 + local.get $l7502 + local.get $l7503 + i32.add + local.set $l7504 + local.get $l7503 + local.get $l7504 + i32.add + local.set $l7505 + local.get $l7504 + local.get $l7505 + i32.add + local.set $l7506 + local.get $l7505 + local.get $l7506 + i32.add + local.set $l7507 + local.get $l7506 + local.get $l7507 + i32.add + local.set $l7508 + local.get $l7507 + local.get $l7508 + i32.add + local.set $l7509 + local.get $l7508 + local.get $l7509 + i32.add + local.set $l7510 + local.get $l7509 + local.get $l7510 + i32.add + local.set $l7511 + local.get $l7510 + local.get $l7511 + i32.add + local.set $l7512 + local.get $l7511 + local.get $l7512 + i32.add + local.set $l7513 + local.get $l7512 + local.get $l7513 + i32.add + local.set $l7514 + local.get $l7513 + local.get $l7514 + i32.add + local.set $l7515 + local.get $l7514 + local.get $l7515 + i32.add + local.set $l7516 + local.get $l7515 + local.get $l7516 + i32.add + local.set $l7517 + local.get $l7516 + local.get $l7517 + i32.add + local.set $l7518 + local.get $l7517 + local.get $l7518 + i32.add + local.set $l7519 + local.get $l7518 + local.get $l7519 + i32.add + local.set $l7520 + local.get $l7519 + local.get $l7520 + i32.add + local.set $l7521 + local.get $l7520 + local.get $l7521 + i32.add + local.set $l7522 + local.get $l7521 + local.get $l7522 + i32.add + local.set $l7523 + local.get $l7522 + local.get $l7523 + i32.add + local.set $l7524 + local.get $l7523 + local.get $l7524 + i32.add + local.set $l7525 + local.get $l7524 + local.get $l7525 + i32.add + local.set $l7526 + local.get $l7525 + local.get $l7526 + i32.add + local.set $l7527 + local.get $l7526 + local.get $l7527 + i32.add + local.set $l7528 + local.get $l7527 + local.get $l7528 + i32.add + local.set $l7529 + local.get $l7528 + local.get $l7529 + i32.add + local.set $l7530 + local.get $l7529 + local.get $l7530 + i32.add + local.set $l7531 + local.get $l7530 + local.get $l7531 + i32.add + local.set $l7532 + local.get $l7531 + local.get $l7532 + i32.add + local.set $l7533 + local.get $l7532 + local.get $l7533 + i32.add + local.set $l7534 + local.get $l7533 + local.get $l7534 + i32.add + local.set $l7535 + local.get $l7534 + local.get $l7535 + i32.add + local.set $l7536 + local.get $l7535 + local.get $l7536 + i32.add + local.set $l7537 + local.get $l7536 + local.get $l7537 + i32.add + local.set $l7538 + local.get $l7537 + local.get $l7538 + i32.add + local.set $l7539 + local.get $l7538 + local.get $l7539 + i32.add + local.set $l7540 + local.get $l7539 + local.get $l7540 + i32.add + local.set $l7541 + local.get $l7540 + local.get $l7541 + i32.add + local.set $l7542 + local.get $l7541 + local.get $l7542 + i32.add + local.set $l7543 + local.get $l7542 + local.get $l7543 + i32.add + local.set $l7544 + local.get $l7543 + local.get $l7544 + i32.add + local.set $l7545 + local.get $l7544 + local.get $l7545 + i32.add + local.set $l7546 + local.get $l7545 + local.get $l7546 + i32.add + local.set $l7547 + local.get $l7546 + local.get $l7547 + i32.add + local.set $l7548 + local.get $l7547 + local.get $l7548 + i32.add + local.set $l7549 + local.get $l7548 + local.get $l7549 + i32.add + local.set $l7550 + local.get $l7549 + local.get $l7550 + i32.add + local.set $l7551 + local.get $l7550 + local.get $l7551 + i32.add + local.set $l7552 + local.get $l7551 + local.get $l7552 + i32.add + local.set $l7553 + local.get $l7552 + local.get $l7553 + i32.add + local.set $l7554 + local.get $l7553 + local.get $l7554 + i32.add + local.set $l7555 + local.get $l7554 + local.get $l7555 + i32.add + local.set $l7556 + local.get $l7555 + local.get $l7556 + i32.add + local.set $l7557 + local.get $l7556 + local.get $l7557 + i32.add + local.set $l7558 + local.get $l7557 + local.get $l7558 + i32.add + local.set $l7559 + local.get $l7558 + local.get $l7559 + i32.add + local.set $l7560 + local.get $l7559 + local.get $l7560 + i32.add + local.set $l7561 + local.get $l7560 + local.get $l7561 + i32.add + local.set $l7562 + local.get $l7561 + local.get $l7562 + i32.add + local.set $l7563 + local.get $l7562 + local.get $l7563 + i32.add + local.set $l7564 + local.get $l7563 + local.get $l7564 + i32.add + local.set $l7565 + local.get $l7564 + local.get $l7565 + i32.add + local.set $l7566 + local.get $l7565 + local.get $l7566 + i32.add + local.set $l7567 + local.get $l7566 + local.get $l7567 + i32.add + local.set $l7568 + local.get $l7567 + local.get $l7568 + i32.add + local.set $l7569 + local.get $l7568 + local.get $l7569 + i32.add + local.set $l7570 + local.get $l7569 + local.get $l7570 + i32.add + local.set $l7571 + local.get $l7570 + local.get $l7571 + i32.add + local.set $l7572 + local.get $l7571 + local.get $l7572 + i32.add + local.set $l7573 + local.get $l7572 + local.get $l7573 + i32.add + local.set $l7574 + local.get $l7573 + local.get $l7574 + i32.add + local.set $l7575 + local.get $l7574 + local.get $l7575 + i32.add + local.set $l7576 + local.get $l7575 + local.get $l7576 + i32.add + local.set $l7577 + local.get $l7576 + local.get $l7577 + i32.add + local.set $l7578 + local.get $l7577 + local.get $l7578 + i32.add + local.set $l7579 + local.get $l7578 + local.get $l7579 + i32.add + local.set $l7580 + local.get $l7579 + local.get $l7580 + i32.add + local.set $l7581 + local.get $l7580 + local.get $l7581 + i32.add + local.set $l7582 + local.get $l7581 + local.get $l7582 + i32.add + local.set $l7583 + local.get $l7582 + local.get $l7583 + i32.add + local.set $l7584 + local.get $l7583 + local.get $l7584 + i32.add + local.set $l7585 + local.get $l7584 + local.get $l7585 + i32.add + local.set $l7586 + local.get $l7585 + local.get $l7586 + i32.add + local.set $l7587 + local.get $l7586 + local.get $l7587 + i32.add + local.set $l7588 + local.get $l7587 + local.get $l7588 + i32.add + local.set $l7589 + local.get $l7588 + local.get $l7589 + i32.add + local.set $l7590 + local.get $l7589 + local.get $l7590 + i32.add + local.set $l7591 + local.get $l7590 + local.get $l7591 + i32.add + local.set $l7592 + local.get $l7591 + local.get $l7592 + i32.add + local.set $l7593 + local.get $l7592 + local.get $l7593 + i32.add + local.set $l7594 + local.get $l7593 + local.get $l7594 + i32.add + local.set $l7595 + local.get $l7594 + local.get $l7595 + i32.add + local.set $l7596 + local.get $l7595 + local.get $l7596 + i32.add + local.set $l7597 + local.get $l7596 + local.get $l7597 + i32.add + local.set $l7598 + local.get $l7597 + local.get $l7598 + i32.add + local.set $l7599 + local.get $l7598 + local.get $l7599 + i32.add + local.set $l7600 + local.get $l7599 + local.get $l7600 + i32.add + local.set $l7601 + local.get $l7600 + local.get $l7601 + i32.add + local.set $l7602 + local.get $l7601 + local.get $l7602 + i32.add + local.set $l7603 + local.get $l7602 + local.get $l7603 + i32.add + local.set $l7604 + local.get $l7603 + local.get $l7604 + i32.add + local.set $l7605 + local.get $l7604 + local.get $l7605 + i32.add + local.set $l7606 + local.get $l7605 + local.get $l7606 + i32.add + local.set $l7607 + local.get $l7606 + local.get $l7607 + i32.add + local.set $l7608 + local.get $l7607 + local.get $l7608 + i32.add + local.set $l7609 + local.get $l7608 + local.get $l7609 + i32.add + local.set $l7610 + local.get $l7609 + local.get $l7610 + i32.add + local.set $l7611 + local.get $l7610 + local.get $l7611 + i32.add + local.set $l7612 + local.get $l7611 + local.get $l7612 + i32.add + local.set $l7613 + local.get $l7612 + local.get $l7613 + i32.add + local.set $l7614 + local.get $l7613 + local.get $l7614 + i32.add + local.set $l7615 + local.get $l7614 + local.get $l7615 + i32.add + local.set $l7616 + local.get $l7615 + local.get $l7616 + i32.add + local.set $l7617 + local.get $l7616 + local.get $l7617 + i32.add + local.set $l7618 + local.get $l7617 + local.get $l7618 + i32.add + local.set $l7619 + local.get $l7618 + local.get $l7619 + i32.add + local.set $l7620 + local.get $l7619 + local.get $l7620 + i32.add + local.set $l7621 + local.get $l7620 + local.get $l7621 + i32.add + local.set $l7622 + local.get $l7621 + local.get $l7622 + i32.add + local.set $l7623 + local.get $l7622 + local.get $l7623 + i32.add + local.set $l7624 + local.get $l7623 + local.get $l7624 + i32.add + local.set $l7625 + local.get $l7624 + local.get $l7625 + i32.add + local.set $l7626 + local.get $l7625 + local.get $l7626 + i32.add + local.set $l7627 + local.get $l7626 + local.get $l7627 + i32.add + local.set $l7628 + local.get $l7627 + local.get $l7628 + i32.add + local.set $l7629 + local.get $l7628 + local.get $l7629 + i32.add + local.set $l7630 + local.get $l7629 + local.get $l7630 + i32.add + local.set $l7631 + local.get $l7630 + local.get $l7631 + i32.add + local.set $l7632 + local.get $l7631 + local.get $l7632 + i32.add + local.set $l7633 + local.get $l7632 + local.get $l7633 + i32.add + local.set $l7634 + local.get $l7633 + local.get $l7634 + i32.add + local.set $l7635 + local.get $l7634 + local.get $l7635 + i32.add + local.set $l7636 + local.get $l7635 + local.get $l7636 + i32.add + local.set $l7637 + local.get $l7636 + local.get $l7637 + i32.add + local.set $l7638 + local.get $l7637 + local.get $l7638 + i32.add + local.set $l7639 + local.get $l7638 + local.get $l7639 + i32.add + local.set $l7640 + local.get $l7639 + local.get $l7640 + i32.add + local.set $l7641 + local.get $l7640 + local.get $l7641 + i32.add + local.set $l7642 + local.get $l7641 + local.get $l7642 + i32.add + local.set $l7643 + local.get $l7642 + local.get $l7643 + i32.add + local.set $l7644 + local.get $l7643 + local.get $l7644 + i32.add + local.set $l7645 + local.get $l7644 + local.get $l7645 + i32.add + local.set $l7646 + local.get $l7645 + local.get $l7646 + i32.add + local.set $l7647 + local.get $l7646 + local.get $l7647 + i32.add + local.set $l7648 + local.get $l7647 + local.get $l7648 + i32.add + local.set $l7649 + local.get $l7648 + local.get $l7649 + i32.add + local.set $l7650 + local.get $l7649 + local.get $l7650 + i32.add + local.set $l7651 + local.get $l7650 + local.get $l7651 + i32.add + local.set $l7652 + local.get $l7651 + local.get $l7652 + i32.add + local.set $l7653 + local.get $l7652 + local.get $l7653 + i32.add + local.set $l7654 + local.get $l7653 + local.get $l7654 + i32.add + local.set $l7655 + local.get $l7654 + local.get $l7655 + i32.add + local.set $l7656 + local.get $l7655 + local.get $l7656 + i32.add + local.set $l7657 + local.get $l7656 + local.get $l7657 + i32.add + local.set $l7658 + local.get $l7657 + local.get $l7658 + i32.add + local.set $l7659 + local.get $l7658 + local.get $l7659 + i32.add + local.set $l7660 + local.get $l7659 + local.get $l7660 + i32.add + local.set $l7661 + local.get $l7660 + local.get $l7661 + i32.add + local.set $l7662 + local.get $l7661 + local.get $l7662 + i32.add + local.set $l7663 + local.get $l7662 + local.get $l7663 + i32.add + local.set $l7664 + local.get $l7663 + local.get $l7664 + i32.add + local.set $l7665 + local.get $l7664 + local.get $l7665 + i32.add + local.set $l7666 + local.get $l7665 + local.get $l7666 + i32.add + local.set $l7667 + local.get $l7666 + local.get $l7667 + i32.add + local.set $l7668 + local.get $l7667 + local.get $l7668 + i32.add + local.set $l7669 + local.get $l7668 + local.get $l7669 + i32.add + local.set $l7670 + local.get $l7669 + local.get $l7670 + i32.add + local.set $l7671 + local.get $l7670 + local.get $l7671 + i32.add + local.set $l7672 + local.get $l7671 + local.get $l7672 + i32.add + local.set $l7673 + local.get $l7672 + local.get $l7673 + i32.add + local.set $l7674 + local.get $l7673 + local.get $l7674 + i32.add + local.set $l7675 + local.get $l7674 + local.get $l7675 + i32.add + local.set $l7676 + local.get $l7675 + local.get $l7676 + i32.add + local.set $l7677 + local.get $l7676 + local.get $l7677 + i32.add + local.set $l7678 + local.get $l7677 + local.get $l7678 + i32.add + local.set $l7679 + local.get $l7678 + local.get $l7679 + i32.add + local.set $l7680 + local.get $l7679 + local.get $l7680 + i32.add + local.set $l7681 + local.get $l7680 + local.get $l7681 + i32.add + local.set $l7682 + local.get $l7681 + local.get $l7682 + i32.add + local.set $l7683 + local.get $l7682 + local.get $l7683 + i32.add + local.set $l7684 + local.get $l7683 + local.get $l7684 + i32.add + local.set $l7685 + local.get $l7684 + local.get $l7685 + i32.add + local.set $l7686 + local.get $l7685 + local.get $l7686 + i32.add + local.set $l7687 + local.get $l7686 + local.get $l7687 + i32.add + local.set $l7688 + local.get $l7687 + local.get $l7688 + i32.add + local.set $l7689 + local.get $l7688 + local.get $l7689 + i32.add + local.set $l7690 + local.get $l7689 + local.get $l7690 + i32.add + local.set $l7691 + local.get $l7690 + local.get $l7691 + i32.add + local.set $l7692 + local.get $l7691 + local.get $l7692 + i32.add + local.set $l7693 + local.get $l7692 + local.get $l7693 + i32.add + local.set $l7694 + local.get $l7693 + local.get $l7694 + i32.add + local.set $l7695 + local.get $l7694 + local.get $l7695 + i32.add + local.set $l7696 + local.get $l7695 + local.get $l7696 + i32.add + local.set $l7697 + local.get $l7696 + local.get $l7697 + i32.add + local.set $l7698 + local.get $l7697 + local.get $l7698 + i32.add + local.set $l7699 + local.get $l7698 + local.get $l7699 + i32.add + local.set $l7700 + local.get $l7699 + local.get $l7700 + i32.add + local.set $l7701 + local.get $l7700 + local.get $l7701 + i32.add + local.set $l7702 + local.get $l7701 + local.get $l7702 + i32.add + local.set $l7703 + local.get $l7702 + local.get $l7703 + i32.add + local.set $l7704 + local.get $l7703 + local.get $l7704 + i32.add + local.set $l7705 + local.get $l7704 + local.get $l7705 + i32.add + local.set $l7706 + local.get $l7705 + local.get $l7706 + i32.add + local.set $l7707 + local.get $l7706 + local.get $l7707 + i32.add + local.set $l7708 + local.get $l7707 + local.get $l7708 + i32.add + local.set $l7709 + local.get $l7708 + local.get $l7709 + i32.add + local.set $l7710 + local.get $l7709 + local.get $l7710 + i32.add + local.set $l7711 + local.get $l7710 + local.get $l7711 + i32.add + local.set $l7712 + local.get $l7711 + local.get $l7712 + i32.add + local.set $l7713 + local.get $l7712 + local.get $l7713 + i32.add + local.set $l7714 + local.get $l7713 + local.get $l7714 + i32.add + local.set $l7715 + local.get $l7714 + local.get $l7715 + i32.add + local.set $l7716 + local.get $l7715 + local.get $l7716 + i32.add + local.set $l7717 + local.get $l7716 + local.get $l7717 + i32.add + local.set $l7718 + local.get $l7717 + local.get $l7718 + i32.add + local.set $l7719 + local.get $l7718 + local.get $l7719 + i32.add + local.set $l7720 + local.get $l7719 + local.get $l7720 + i32.add + local.set $l7721 + local.get $l7720 + local.get $l7721 + i32.add + local.set $l7722 + local.get $l7721 + local.get $l7722 + i32.add + local.set $l7723 + local.get $l7722 + local.get $l7723 + i32.add + local.set $l7724 + local.get $l7723 + local.get $l7724 + i32.add + local.set $l7725 + local.get $l7724 + local.get $l7725 + i32.add + local.set $l7726 + local.get $l7725 + local.get $l7726 + i32.add + local.set $l7727 + local.get $l7726 + local.get $l7727 + i32.add + local.set $l7728 + local.get $l7727 + local.get $l7728 + i32.add + local.set $l7729 + local.get $l7728 + local.get $l7729 + i32.add + local.set $l7730 + local.get $l7729 + local.get $l7730 + i32.add + local.set $l7731 + local.get $l7730 + local.get $l7731 + i32.add + local.set $l7732 + local.get $l7731 + local.get $l7732 + i32.add + local.set $l7733 + local.get $l7732 + local.get $l7733 + i32.add + local.set $l7734 + local.get $l7733 + local.get $l7734 + i32.add + local.set $l7735 + local.get $l7734 + local.get $l7735 + i32.add + local.set $l7736 + local.get $l7735 + local.get $l7736 + i32.add + local.set $l7737 + local.get $l7736 + local.get $l7737 + i32.add + local.set $l7738 + local.get $l7737 + local.get $l7738 + i32.add + local.set $l7739 + local.get $l7738 + local.get $l7739 + i32.add + local.set $l7740 + local.get $l7739 + local.get $l7740 + i32.add + local.set $l7741 + local.get $l7740 + local.get $l7741 + i32.add + local.set $l7742 + local.get $l7741 + local.get $l7742 + i32.add + local.set $l7743 + local.get $l7742 + local.get $l7743 + i32.add + local.set $l7744 + local.get $l7743 + local.get $l7744 + i32.add + local.set $l7745 + local.get $l7744 + local.get $l7745 + i32.add + local.set $l7746 + local.get $l7745 + local.get $l7746 + i32.add + local.set $l7747 + local.get $l7746 + local.get $l7747 + i32.add + local.set $l7748 + local.get $l7747 + local.get $l7748 + i32.add + local.set $l7749 + local.get $l7748 + local.get $l7749 + i32.add + local.set $l7750 + local.get $l7749 + local.get $l7750 + i32.add + local.set $l7751 + local.get $l7750 + local.get $l7751 + i32.add + local.set $l7752 + local.get $l7751 + local.get $l7752 + i32.add + local.set $l7753 + local.get $l7752 + local.get $l7753 + i32.add + local.set $l7754 + local.get $l7753 + local.get $l7754 + i32.add + local.set $l7755 + local.get $l7754 + local.get $l7755 + i32.add + local.set $l7756 + local.get $l7755 + local.get $l7756 + i32.add + local.set $l7757 + local.get $l7756 + local.get $l7757 + i32.add + local.set $l7758 + local.get $l7757 + local.get $l7758 + i32.add + local.set $l7759 + local.get $l7758 + local.get $l7759 + i32.add + local.set $l7760 + local.get $l7759 + local.get $l7760 + i32.add + local.set $l7761 + local.get $l7760 + local.get $l7761 + i32.add + local.set $l7762 + local.get $l7761 + local.get $l7762 + i32.add + local.set $l7763 + local.get $l7762 + local.get $l7763 + i32.add + local.set $l7764 + local.get $l7763 + local.get $l7764 + i32.add + local.set $l7765 + local.get $l7764 + local.get $l7765 + i32.add + local.set $l7766 + local.get $l7765 + local.get $l7766 + i32.add + local.set $l7767 + local.get $l7766 + local.get $l7767 + i32.add + local.set $l7768 + local.get $l7767 + local.get $l7768 + i32.add + local.set $l7769 + local.get $l7768 + local.get $l7769 + i32.add + local.set $l7770 + local.get $l7769 + local.get $l7770 + i32.add + local.set $l7771 + local.get $l7770 + local.get $l7771 + i32.add + local.set $l7772 + local.get $l7771 + local.get $l7772 + i32.add + local.set $l7773 + local.get $l7772 + local.get $l7773 + i32.add + local.set $l7774 + local.get $l7773 + local.get $l7774 + i32.add + local.set $l7775 + local.get $l7774 + local.get $l7775 + i32.add + local.set $l7776 + local.get $l7775 + local.get $l7776 + i32.add + local.set $l7777 + local.get $l7776 + local.get $l7777 + i32.add + local.set $l7778 + local.get $l7777 + local.get $l7778 + i32.add + local.set $l7779 + local.get $l7778 + local.get $l7779 + i32.add + local.set $l7780 + local.get $l7779 + local.get $l7780 + i32.add + local.set $l7781 + local.get $l7780 + local.get $l7781 + i32.add + local.set $l7782 + local.get $l7781 + local.get $l7782 + i32.add + local.set $l7783 + local.get $l7782 + local.get $l7783 + i32.add + local.set $l7784 + local.get $l7783 + local.get $l7784 + i32.add + local.set $l7785 + local.get $l7784 + local.get $l7785 + i32.add + local.set $l7786 + local.get $l7785 + local.get $l7786 + i32.add + local.set $l7787 + local.get $l7786 + local.get $l7787 + i32.add + local.set $l7788 + local.get $l7787 + local.get $l7788 + i32.add + local.set $l7789 + local.get $l7788 + local.get $l7789 + i32.add + local.set $l7790 + local.get $l7789 + local.get $l7790 + i32.add + local.set $l7791 + local.get $l7790 + local.get $l7791 + i32.add + local.set $l7792 + local.get $l7791 + local.get $l7792 + i32.add + local.set $l7793 + local.get $l7792 + local.get $l7793 + i32.add + local.set $l7794 + local.get $l7793 + local.get $l7794 + i32.add + local.set $l7795 + local.get $l7794 + local.get $l7795 + i32.add + local.set $l7796 + local.get $l7795 + local.get $l7796 + i32.add + local.set $l7797 + local.get $l7796 + local.get $l7797 + i32.add + local.set $l7798 + local.get $l7797 + local.get $l7798 + i32.add + local.set $l7799 + local.get $l7798 + local.get $l7799 + i32.add + local.set $l7800 + local.get $l7799 + local.get $l7800 + i32.add + local.set $l7801 + local.get $l7800 + local.get $l7801 + i32.add + local.set $l7802 + local.get $l7801 + local.get $l7802 + i32.add + local.set $l7803 + local.get $l7802 + local.get $l7803 + i32.add + local.set $l7804 + local.get $l7803 + local.get $l7804 + i32.add + local.set $l7805 + local.get $l7804 + local.get $l7805 + i32.add + local.set $l7806 + local.get $l7805 + local.get $l7806 + i32.add + local.set $l7807 + local.get $l7806 + local.get $l7807 + i32.add + local.set $l7808 + local.get $l7807 + local.get $l7808 + i32.add + local.set $l7809 + local.get $l7808 + local.get $l7809 + i32.add + local.set $l7810 + local.get $l7809 + local.get $l7810 + i32.add + local.set $l7811 + local.get $l7810 + local.get $l7811 + i32.add + local.set $l7812 + local.get $l7811 + local.get $l7812 + i32.add + local.set $l7813 + local.get $l7812 + local.get $l7813 + i32.add + local.set $l7814 + local.get $l7813 + local.get $l7814 + i32.add + local.set $l7815 + local.get $l7814 + local.get $l7815 + i32.add + local.set $l7816 + local.get $l7815 + local.get $l7816 + i32.add + local.set $l7817 + local.get $l7816 + local.get $l7817 + i32.add + local.set $l7818 + local.get $l7817 + local.get $l7818 + i32.add + local.set $l7819 + local.get $l7818 + local.get $l7819 + i32.add + local.set $l7820 + local.get $l7819 + local.get $l7820 + i32.add + local.set $l7821 + local.get $l7820 + local.get $l7821 + i32.add + local.set $l7822 + local.get $l7821 + local.get $l7822 + i32.add + local.set $l7823 + local.get $l7822 + local.get $l7823 + i32.add + local.set $l7824 + local.get $l7823 + local.get $l7824 + i32.add + local.set $l7825 + local.get $l7824 + local.get $l7825 + i32.add + local.set $l7826 + local.get $l7825 + local.get $l7826 + i32.add + local.set $l7827 + local.get $l7826 + local.get $l7827 + i32.add + local.set $l7828 + local.get $l7827 + local.get $l7828 + i32.add + local.set $l7829 + local.get $l7828 + local.get $l7829 + i32.add + local.set $l7830 + local.get $l7829 + local.get $l7830 + i32.add + local.set $l7831 + local.get $l7830 + local.get $l7831 + i32.add + local.set $l7832 + local.get $l7831 + local.get $l7832 + i32.add + local.set $l7833 + local.get $l7832 + local.get $l7833 + i32.add + local.set $l7834 + local.get $l7833 + local.get $l7834 + i32.add + local.set $l7835 + local.get $l7834 + local.get $l7835 + i32.add + local.set $l7836 + local.get $l7835 + local.get $l7836 + i32.add + local.set $l7837 + local.get $l7836 + local.get $l7837 + i32.add + local.set $l7838 + local.get $l7837 + local.get $l7838 + i32.add + local.set $l7839 + local.get $l7838 + local.get $l7839 + i32.add + local.set $l7840 + local.get $l7839 + local.get $l7840 + i32.add + local.set $l7841 + local.get $l7840 + local.get $l7841 + i32.add + local.set $l7842 + local.get $l7841 + local.get $l7842 + i32.add + local.set $l7843 + local.get $l7842 + local.get $l7843 + i32.add + local.set $l7844 + local.get $l7843 + local.get $l7844 + i32.add + local.set $l7845 + local.get $l7844 + local.get $l7845 + i32.add + local.set $l7846 + local.get $l7845 + local.get $l7846 + i32.add + local.set $l7847 + local.get $l7846 + local.get $l7847 + i32.add + local.set $l7848 + local.get $l7847 + local.get $l7848 + i32.add + local.set $l7849 + local.get $l7848 + local.get $l7849 + i32.add + local.set $l7850 + local.get $l7849 + local.get $l7850 + i32.add + local.set $l7851 + local.get $l7850 + local.get $l7851 + i32.add + local.set $l7852 + local.get $l7851 + local.get $l7852 + i32.add + local.set $l7853 + local.get $l7852 + local.get $l7853 + i32.add + local.set $l7854 + local.get $l7853 + local.get $l7854 + i32.add + local.set $l7855 + local.get $l7854 + local.get $l7855 + i32.add + local.set $l7856 + local.get $l7855 + local.get $l7856 + i32.add + local.set $l7857 + local.get $l7856 + local.get $l7857 + i32.add + local.set $l7858 + local.get $l7857 + local.get $l7858 + i32.add + local.set $l7859 + local.get $l7858 + local.get $l7859 + i32.add + local.set $l7860 + local.get $l7859 + local.get $l7860 + i32.add + local.set $l7861 + local.get $l7860 + local.get $l7861 + i32.add + local.set $l7862 + local.get $l7861 + local.get $l7862 + i32.add + local.set $l7863 + local.get $l7862 + local.get $l7863 + i32.add + local.set $l7864 + local.get $l7863 + local.get $l7864 + i32.add + local.set $l7865 + local.get $l7864 + local.get $l7865 + i32.add + local.set $l7866 + local.get $l7865 + local.get $l7866 + i32.add + local.set $l7867 + local.get $l7866 + local.get $l7867 + i32.add + local.set $l7868 + local.get $l7867 + local.get $l7868 + i32.add + local.set $l7869 + local.get $l7868 + local.get $l7869 + i32.add + local.set $l7870 + local.get $l7869 + local.get $l7870 + i32.add + local.set $l7871 + local.get $l7870 + local.get $l7871 + i32.add + local.set $l7872 + local.get $l7871 + local.get $l7872 + i32.add + local.set $l7873 + local.get $l7872 + local.get $l7873 + i32.add + local.set $l7874 + local.get $l7873 + local.get $l7874 + i32.add + local.set $l7875 + local.get $l7874 + local.get $l7875 + i32.add + local.set $l7876 + local.get $l7875 + local.get $l7876 + i32.add + local.set $l7877 + local.get $l7876 + local.get $l7877 + i32.add + local.set $l7878 + local.get $l7877 + local.get $l7878 + i32.add + local.set $l7879 + local.get $l7878 + local.get $l7879 + i32.add + local.set $l7880 + local.get $l7879 + local.get $l7880 + i32.add + local.set $l7881 + local.get $l7880 + local.get $l7881 + i32.add + local.set $l7882 + local.get $l7881 + local.get $l7882 + i32.add + local.set $l7883 + local.get $l7882 + local.get $l7883 + i32.add + local.set $l7884 + local.get $l7883 + local.get $l7884 + i32.add + local.set $l7885 + local.get $l7884 + local.get $l7885 + i32.add + local.set $l7886 + local.get $l7885 + local.get $l7886 + i32.add + local.set $l7887 + local.get $l7886 + local.get $l7887 + i32.add + local.set $l7888 + local.get $l7887 + local.get $l7888 + i32.add + local.set $l7889 + local.get $l7888 + local.get $l7889 + i32.add + local.set $l7890 + local.get $l7889 + local.get $l7890 + i32.add + local.set $l7891 + local.get $l7890 + local.get $l7891 + i32.add + local.set $l7892 + local.get $l7891 + local.get $l7892 + i32.add + local.set $l7893 + local.get $l7892 + local.get $l7893 + i32.add + local.set $l7894 + local.get $l7893 + local.get $l7894 + i32.add + local.set $l7895 + local.get $l7894 + local.get $l7895 + i32.add + local.set $l7896 + local.get $l7895 + local.get $l7896 + i32.add + local.set $l7897 + local.get $l7896 + local.get $l7897 + i32.add + local.set $l7898 + local.get $l7897 + local.get $l7898 + i32.add + local.set $l7899 + local.get $l7898 + local.get $l7899 + i32.add + local.set $l7900 + local.get $l7899 + local.get $l7900 + i32.add + local.set $l7901 + local.get $l7900 + local.get $l7901 + i32.add + local.set $l7902 + local.get $l7901 + local.get $l7902 + i32.add + local.set $l7903 + local.get $l7902 + local.get $l7903 + i32.add + local.set $l7904 + local.get $l7903 + local.get $l7904 + i32.add + local.set $l7905 + local.get $l7904 + local.get $l7905 + i32.add + local.set $l7906 + local.get $l7905 + local.get $l7906 + i32.add + local.set $l7907 + local.get $l7906 + local.get $l7907 + i32.add + local.set $l7908 + local.get $l7907 + local.get $l7908 + i32.add + local.set $l7909 + local.get $l7908 + local.get $l7909 + i32.add + local.set $l7910 + local.get $l7909 + local.get $l7910 + i32.add + local.set $l7911 + local.get $l7910 + local.get $l7911 + i32.add + local.set $l7912 + local.get $l7911 + local.get $l7912 + i32.add + local.set $l7913 + local.get $l7912 + local.get $l7913 + i32.add + local.set $l7914 + local.get $l7913 + local.get $l7914 + i32.add + local.set $l7915 + local.get $l7914 + local.get $l7915 + i32.add + local.set $l7916 + local.get $l7915 + local.get $l7916 + i32.add + local.set $l7917 + local.get $l7916 + local.get $l7917 + i32.add + local.set $l7918 + local.get $l7917 + local.get $l7918 + i32.add + local.set $l7919 + local.get $l7918 + local.get $l7919 + i32.add + local.set $l7920 + local.get $l7919 + local.get $l7920 + i32.add + local.set $l7921 + local.get $l7920 + local.get $l7921 + i32.add + local.set $l7922 + local.get $l7921 + local.get $l7922 + i32.add + local.set $l7923 + local.get $l7922 + local.get $l7923 + i32.add + local.set $l7924 + local.get $l7923 + local.get $l7924 + i32.add + local.set $l7925 + local.get $l7924 + local.get $l7925 + i32.add + local.set $l7926 + local.get $l7925 + local.get $l7926 + i32.add + local.set $l7927 + local.get $l7926 + local.get $l7927 + i32.add + local.set $l7928 + local.get $l7927 + local.get $l7928 + i32.add + local.set $l7929 + local.get $l7928 + local.get $l7929 + i32.add + local.set $l7930 + local.get $l7929 + local.get $l7930 + i32.add + local.set $l7931 + local.get $l7930 + local.get $l7931 + i32.add + local.set $l7932 + local.get $l7931 + local.get $l7932 + i32.add + local.set $l7933 + local.get $l7932 + local.get $l7933 + i32.add + local.set $l7934 + local.get $l7933 + local.get $l7934 + i32.add + local.set $l7935 + local.get $l7934 + local.get $l7935 + i32.add + local.set $l7936 + local.get $l7935 + local.get $l7936 + i32.add + local.set $l7937 + local.get $l7936 + local.get $l7937 + i32.add + local.set $l7938 + local.get $l7937 + local.get $l7938 + i32.add + local.set $l7939 + local.get $l7938 + local.get $l7939 + i32.add + local.set $l7940 + local.get $l7939 + local.get $l7940 + i32.add + local.set $l7941 + local.get $l7940 + local.get $l7941 + i32.add + local.set $l7942 + local.get $l7941 + local.get $l7942 + i32.add + local.set $l7943 + local.get $l7942 + local.get $l7943 + i32.add + local.set $l7944 + local.get $l7943 + local.get $l7944 + i32.add + local.set $l7945 + local.get $l7944 + local.get $l7945 + i32.add + local.set $l7946 + local.get $l7945 + local.get $l7946 + i32.add + local.set $l7947 + local.get $l7946 + local.get $l7947 + i32.add + local.set $l7948 + local.get $l7947 + local.get $l7948 + i32.add + local.set $l7949 + local.get $l7948 + local.get $l7949 + i32.add + local.set $l7950 + local.get $l7949 + local.get $l7950 + i32.add + local.set $l7951 + local.get $l7950 + local.get $l7951 + i32.add + local.set $l7952 + local.get $l7951 + local.get $l7952 + i32.add + local.set $l7953 + local.get $l7952 + local.get $l7953 + i32.add + local.set $l7954 + local.get $l7953 + local.get $l7954 + i32.add + local.set $l7955 + local.get $l7954 + local.get $l7955 + i32.add + local.set $l7956 + local.get $l7955 + local.get $l7956 + i32.add + local.set $l7957 + local.get $l7956 + local.get $l7957 + i32.add + local.set $l7958 + local.get $l7957 + local.get $l7958 + i32.add + local.set $l7959 + local.get $l7958 + local.get $l7959 + i32.add + local.set $l7960 + local.get $l7959 + local.get $l7960 + i32.add + local.set $l7961 + local.get $l7960 + local.get $l7961 + i32.add + local.set $l7962 + local.get $l7961 + local.get $l7962 + i32.add + local.set $l7963 + local.get $l7962 + local.get $l7963 + i32.add + local.set $l7964 + local.get $l7963 + local.get $l7964 + i32.add + local.set $l7965 + local.get $l7964 + local.get $l7965 + i32.add + local.set $l7966 + local.get $l7965 + local.get $l7966 + i32.add + local.set $l7967 + local.get $l7966 + local.get $l7967 + i32.add + local.set $l7968 + local.get $l7967 + local.get $l7968 + i32.add + local.set $l7969 + local.get $l7968 + local.get $l7969 + i32.add + local.set $l7970 + local.get $l7969 + local.get $l7970 + i32.add + local.set $l7971 + local.get $l7970 + local.get $l7971 + i32.add + local.set $l7972 + local.get $l7971 + local.get $l7972 + i32.add + local.set $l7973 + local.get $l7972 + local.get $l7973 + i32.add + local.set $l7974 + local.get $l7973 + local.get $l7974 + i32.add + local.set $l7975 + local.get $l7974 + local.get $l7975 + i32.add + local.set $l7976 + local.get $l7975 + local.get $l7976 + i32.add + local.set $l7977 + local.get $l7976 + local.get $l7977 + i32.add + local.set $l7978 + local.get $l7977 + local.get $l7978 + i32.add + local.set $l7979 + local.get $l7978 + local.get $l7979 + i32.add + local.set $l7980 + local.get $l7979 + local.get $l7980 + i32.add + local.set $l7981 + local.get $l7980 + local.get $l7981 + i32.add + local.set $l7982 + local.get $l7981 + local.get $l7982 + i32.add + local.set $l7983 + local.get $l7982 + local.get $l7983 + i32.add + local.set $l7984 + local.get $l7983 + local.get $l7984 + i32.add + local.set $l7985 + local.get $l7984 + local.get $l7985 + i32.add + local.set $l7986 + local.get $l7985 + local.get $l7986 + i32.add + local.set $l7987 + local.get $l7986 + local.get $l7987 + i32.add + local.set $l7988 + local.get $l7987 + local.get $l7988 + i32.add + local.set $l7989 + local.get $l7988 + local.get $l7989 + i32.add + local.set $l7990 + local.get $l7989 + local.get $l7990 + i32.add + local.set $l7991 + local.get $l7990 + local.get $l7991 + i32.add + local.set $l7992 + local.get $l7991 + local.get $l7992 + i32.add + local.set $l7993 + local.get $l7992 + local.get $l7993 + i32.add + local.set $l7994 + local.get $l7993 + local.get $l7994 + i32.add + local.set $l7995 + local.get $l7994 + local.get $l7995 + i32.add + local.set $l7996 + local.get $l7995 + local.get $l7996 + i32.add + local.set $l7997 + local.get $l7996 + local.get $l7997 + i32.add + local.set $l7998 + local.get $l7997 + local.get $l7998 + i32.add + local.set $l7999 + local.get $l7998 + local.get $l7999 + i32.add + local.set $l8000 + local.get $l7999 + local.get $l8000 + i32.add + local.set $l8001 + local.get $l8000 + local.get $l8001 + i32.add + local.set $l8002 + local.get $l8001 + local.get $l8002 + i32.add + local.set $l8003 + local.get $l8002 + local.get $l8003 + i32.add + local.set $l8004 + local.get $l8003 + local.get $l8004 + i32.add + local.set $l8005 + local.get $l8004 + local.get $l8005 + i32.add + local.set $l8006 + local.get $l8005 + local.get $l8006 + i32.add + local.set $l8007 + local.get $l8006 + local.get $l8007 + i32.add + local.set $l8008 + local.get $l8007 + local.get $l8008 + i32.add + local.set $l8009 + local.get $l8008 + local.get $l8009 + i32.add + local.set $l8010 + local.get $l8009 + local.get $l8010 + i32.add + local.set $l8011 + local.get $l8010 + local.get $l8011 + i32.add + local.set $l8012 + local.get $l8011 + local.get $l8012 + i32.add + local.set $l8013 + local.get $l8012 + local.get $l8013 + i32.add + local.set $l8014 + local.get $l8013 + local.get $l8014 + i32.add + local.set $l8015 + local.get $l8014 + local.get $l8015 + i32.add + local.set $l8016 + local.get $l8015 + local.get $l8016 + i32.add + local.set $l8017 + local.get $l8016 + local.get $l8017 + i32.add + local.set $l8018 + local.get $l8017 + local.get $l8018 + i32.add + local.set $l8019 + local.get $l8018 + local.get $l8019 + i32.add + local.set $l8020 + local.get $l8019 + local.get $l8020 + i32.add + local.set $l8021 + local.get $l8020 + local.get $l8021 + i32.add + local.set $l8022 + local.get $l8021 + local.get $l8022 + i32.add + local.set $l8023 + local.get $l8022 + local.get $l8023 + i32.add + local.set $l8024 + local.get $l8023 + local.get $l8024 + i32.add + local.set $l8025 + local.get $l8024 + local.get $l8025 + i32.add + local.set $l8026 + local.get $l8025 + local.get $l8026 + i32.add + local.set $l8027 + local.get $l8026 + local.get $l8027 + i32.add + local.set $l8028 + local.get $l8027 + local.get $l8028 + i32.add + local.set $l8029 + local.get $l8028 + local.get $l8029 + i32.add + local.set $l8030 + local.get $l8029 + local.get $l8030 + i32.add + local.set $l8031 + local.get $l8030 + local.get $l8031 + i32.add + local.set $l8032 + local.get $l8031 + local.get $l8032 + i32.add + local.set $l8033 + local.get $l8032 + local.get $l8033 + i32.add + local.set $l8034 + local.get $l8033 + local.get $l8034 + i32.add + local.set $l8035 + local.get $l8034 + local.get $l8035 + i32.add + local.set $l8036 + local.get $l8035 + local.get $l8036 + i32.add + local.set $l8037 + local.get $l8036 + local.get $l8037 + i32.add + local.set $l8038 + local.get $l8037 + local.get $l8038 + i32.add + local.set $l8039 + local.get $l8038 + local.get $l8039 + i32.add + local.set $l8040 + local.get $l8039 + local.get $l8040 + i32.add + local.set $l8041 + local.get $l8040 + local.get $l8041 + i32.add + local.set $l8042 + local.get $l8041 + local.get $l8042 + i32.add + local.set $l8043 + local.get $l8042 + local.get $l8043 + i32.add + local.set $l8044 + local.get $l8043 + local.get $l8044 + i32.add + local.set $l8045 + local.get $l8044 + local.get $l8045 + i32.add + local.set $l8046 + local.get $l8045 + local.get $l8046 + i32.add + local.set $l8047 + local.get $l8046 + local.get $l8047 + i32.add + local.set $l8048 + local.get $l8047 + local.get $l8048 + i32.add + local.set $l8049 + local.get $l8048 + local.get $l8049 + i32.add + local.set $l8050 + local.get $l8049 + local.get $l8050 + i32.add + local.set $l8051 + local.get $l8050 + local.get $l8051 + i32.add + local.set $l8052 + local.get $l8051 + local.get $l8052 + i32.add + local.set $l8053 + local.get $l8052 + local.get $l8053 + i32.add + local.set $l8054 + local.get $l8053 + local.get $l8054 + i32.add + local.set $l8055 + local.get $l8054 + local.get $l8055 + i32.add + local.set $l8056 + local.get $l8055 + local.get $l8056 + i32.add + local.set $l8057 + local.get $l8056 + local.get $l8057 + i32.add + local.set $l8058 + local.get $l8057 + local.get $l8058 + i32.add + local.set $l8059 + local.get $l8058 + local.get $l8059 + i32.add + local.set $l8060 + local.get $l8059 + local.get $l8060 + i32.add + local.set $l8061 + local.get $l8060 + local.get $l8061 + i32.add + local.set $l8062 + local.get $l8061 + local.get $l8062 + i32.add + local.set $l8063 + local.get $l8062 + local.get $l8063 + i32.add + local.set $l8064 + local.get $l8063 + local.get $l8064 + i32.add + local.set $l8065 + local.get $l8064 + local.get $l8065 + i32.add + local.set $l8066 + local.get $l8065 + local.get $l8066 + i32.add + local.set $l8067 + local.get $l8066 + local.get $l8067 + i32.add + local.set $l8068 + local.get $l8067 + local.get $l8068 + i32.add + local.set $l8069 + local.get $l8068 + local.get $l8069 + i32.add + local.set $l8070 + local.get $l8069 + local.get $l8070 + i32.add + local.set $l8071 + local.get $l8070 + local.get $l8071 + i32.add + local.set $l8072 + local.get $l8071 + local.get $l8072 + i32.add + local.set $l8073 + local.get $l8072 + local.get $l8073 + i32.add + local.set $l8074 + local.get $l8073 + local.get $l8074 + i32.add + local.set $l8075 + local.get $l8074 + local.get $l8075 + i32.add + local.set $l8076 + local.get $l8075 + local.get $l8076 + i32.add + local.set $l8077 + local.get $l8076 + local.get $l8077 + i32.add + local.set $l8078 + local.get $l8077 + local.get $l8078 + i32.add + local.set $l8079 + local.get $l8078 + local.get $l8079 + i32.add + local.set $l8080 + local.get $l8079 + local.get $l8080 + i32.add + local.set $l8081 + local.get $l8080 + local.get $l8081 + i32.add + local.set $l8082 + local.get $l8081 + local.get $l8082 + i32.add + local.set $l8083 + local.get $l8082 + local.get $l8083 + i32.add + local.set $l8084 + local.get $l8083 + local.get $l8084 + i32.add + local.set $l8085 + local.get $l8084 + local.get $l8085 + i32.add + local.set $l8086 + local.get $l8085 + local.get $l8086 + i32.add + local.set $l8087 + local.get $l8086 + local.get $l8087 + i32.add + local.set $l8088 + local.get $l8087 + local.get $l8088 + i32.add + local.set $l8089 + local.get $l8088 + local.get $l8089 + i32.add + local.set $l8090 + local.get $l8089 + local.get $l8090 + i32.add + local.set $l8091 + local.get $l8090 + local.get $l8091 + i32.add + local.set $l8092 + local.get $l8091 + local.get $l8092 + i32.add + local.set $l8093 + local.get $l8092 + local.get $l8093 + i32.add + local.set $l8094 + local.get $l8093 + local.get $l8094 + i32.add + local.set $l8095 + local.get $l8094 + local.get $l8095 + i32.add + local.set $l8096 + local.get $l8095 + local.get $l8096 + i32.add + local.set $l8097 + local.get $l8096 + local.get $l8097 + i32.add + local.set $l8098 + local.get $l8097 + local.get $l8098 + i32.add + local.set $l8099 + local.get $l8098 + local.get $l8099 + i32.add + local.set $l8100 + local.get $l8099 + local.get $l8100 + i32.add + local.set $l8101 + local.get $l8100 + local.get $l8101 + i32.add + local.set $l8102 + local.get $l8101 + local.get $l8102 + i32.add + local.set $l8103 + local.get $l8102 + local.get $l8103 + i32.add + local.set $l8104 + local.get $l8103 + local.get $l8104 + i32.add + local.set $l8105 + local.get $l8104 + local.get $l8105 + i32.add + local.set $l8106 + local.get $l8105 + local.get $l8106 + i32.add + local.set $l8107 + local.get $l8106 + local.get $l8107 + i32.add + local.set $l8108 + local.get $l8107 + local.get $l8108 + i32.add + local.set $l8109 + local.get $l8108 + local.get $l8109 + i32.add + local.set $l8110 + local.get $l8109 + local.get $l8110 + i32.add + local.set $l8111 + local.get $l8110 + local.get $l8111 + i32.add + local.set $l8112 + local.get $l8111 + local.get $l8112 + i32.add + local.set $l8113 + local.get $l8112 + local.get $l8113 + i32.add + local.set $l8114 + local.get $l8113 + local.get $l8114 + i32.add + local.set $l8115 + local.get $l8114 + local.get $l8115 + i32.add + local.set $l8116 + local.get $l8115 + local.get $l8116 + i32.add + local.set $l8117 + local.get $l8116 + local.get $l8117 + i32.add + local.set $l8118 + local.get $l8117 + local.get $l8118 + i32.add + local.set $l8119 + local.get $l8118 + local.get $l8119 + i32.add + local.set $l8120 + local.get $l8119 + local.get $l8120 + i32.add + local.set $l8121 + local.get $l8120 + local.get $l8121 + i32.add + local.set $l8122 + local.get $l8121 + local.get $l8122 + i32.add + local.set $l8123 + local.get $l8122 + local.get $l8123 + i32.add + local.set $l8124 + local.get $l8123 + local.get $l8124 + i32.add + local.set $l8125 + local.get $l8124 + local.get $l8125 + i32.add + local.set $l8126 + local.get $l8125 + local.get $l8126 + i32.add + local.set $l8127 + local.get $l8126 + local.get $l8127 + i32.add + local.set $l8128 + local.get $l8127 + local.get $l8128 + i32.add + local.set $l8129 + local.get $l8128 + local.get $l8129 + i32.add + local.set $l8130 + local.get $l8129 + local.get $l8130 + i32.add + local.set $l8131 + local.get $l8130 + local.get $l8131 + i32.add + local.set $l8132 + local.get $l8131 + local.get $l8132 + i32.add + local.set $l8133 + local.get $l8132 + local.get $l8133 + i32.add + local.set $l8134 + local.get $l8133 + local.get $l8134 + i32.add + local.set $l8135 + local.get $l8134 + local.get $l8135 + i32.add + local.set $l8136 + local.get $l8135 + local.get $l8136 + i32.add + local.set $l8137 + local.get $l8136 + local.get $l8137 + i32.add + local.set $l8138 + local.get $l8137 + local.get $l8138 + i32.add + local.set $l8139 + local.get $l8138 + local.get $l8139 + i32.add + local.set $l8140 + local.get $l8139 + local.get $l8140 + i32.add + local.set $l8141 + local.get $l8140 + local.get $l8141 + i32.add + local.set $l8142 + local.get $l8141 + local.get $l8142 + i32.add + local.set $l8143 + local.get $l8142 + local.get $l8143 + i32.add + local.set $l8144 + local.get $l8143 + local.get $l8144 + i32.add + local.set $l8145 + local.get $l8144 + local.get $l8145 + i32.add + local.set $l8146 + local.get $l8145 + local.get $l8146 + i32.add + local.set $l8147 + local.get $l8146 + local.get $l8147 + i32.add + local.set $l8148 + local.get $l8147 + local.get $l8148 + i32.add + local.set $l8149 + local.get $l8148 + local.get $l8149 + i32.add + local.set $l8150 + local.get $l8149 + local.get $l8150 + i32.add + local.set $l8151 + local.get $l8150 + local.get $l8151 + i32.add + local.set $l8152 + local.get $l8151 + local.get $l8152 + i32.add + local.set $l8153 + local.get $l8152 + local.get $l8153 + i32.add + local.set $l8154 + local.get $l8153 + local.get $l8154 + i32.add + local.set $l8155 + local.get $l8154 + local.get $l8155 + i32.add + local.set $l8156 + local.get $l8155 + local.get $l8156 + i32.add + local.set $l8157 + local.get $l8156 + local.get $l8157 + i32.add + local.set $l8158 + local.get $l8157 + local.get $l8158 + i32.add + local.set $l8159 + local.get $l8158 + local.get $l8159 + i32.add + local.set $l8160 + local.get $l8159 + local.get $l8160 + i32.add + local.set $l8161 + local.get $l8160 + local.get $l8161 + i32.add + local.set $l8162 + local.get $l8161 + local.get $l8162 + i32.add + local.set $l8163 + local.get $l8162 + local.get $l8163 + i32.add + local.set $l8164 + local.get $l8163 + local.get $l8164 + i32.add + local.set $l8165 + local.get $l8164 + local.get $l8165 + i32.add + local.set $l8166 + local.get $l8165 + local.get $l8166 + i32.add + local.set $l8167 + local.get $l8166 + local.get $l8167 + i32.add + local.set $l8168 + local.get $l8167 + local.get $l8168 + i32.add + local.set $l8169 + local.get $l8168 + local.get $l8169 + i32.add + local.set $l8170 + local.get $l8169 + local.get $l8170 + i32.add + local.set $l8171 + local.get $l8170 + local.get $l8171 + i32.add + local.set $l8172 + local.get $l8171 + local.get $l8172 + i32.add + local.set $l8173 + local.get $l8172 + local.get $l8173 + i32.add + local.set $l8174 + local.get $l8173 + local.get $l8174 + i32.add + local.set $l8175 + local.get $l8174 + local.get $l8175 + i32.add + local.set $l8176 + local.get $l8175 + local.get $l8176 + i32.add + local.set $l8177 + local.get $l8176 + local.get $l8177 + i32.add + local.set $l8178 + local.get $l8177 + local.get $l8178 + i32.add + local.set $l8179 + local.get $l8178 + local.get $l8179 + i32.add + local.set $l8180 + local.get $l8179 + local.get $l8180 + i32.add + local.set $l8181 + local.get $l8180 + local.get $l8181 + i32.add + local.set $l8182 + local.get $l8181 + local.get $l8182 + i32.add + local.set $l8183 + local.get $l8182 + local.get $l8183 + i32.add + local.set $l8184 + local.get $l8183 + local.get $l8184 + i32.add + local.set $l8185 + local.get $l8184 + local.get $l8185 + i32.add + local.set $l8186 + local.get $l8185 + local.get $l8186 + i32.add + local.set $l8187 + local.get $l8186 + local.get $l8187 + i32.add + local.set $l8188 + local.get $l8187 + local.get $l8188 + i32.add + local.set $l8189 + local.get $l8188 + local.get $l8189 + i32.add + local.set $l8190 + local.get $l8189 + local.get $l8190 + i32.add + local.set $l8191 + local.get $l8190 + local.get $l8191 + i32.add + local.set $l8192 + local.get $l8191 + local.get $l8192 + i32.add + local.set $l8193 + local.get $l8192 + local.get $l8193 + i32.add + local.set $l8194 + local.get $l8193 + local.get $l8194 + i32.add + local.set $l8195 + local.get $l8194 + local.get $l8195 + i32.add + local.set $l8196 + local.get $l8195 + local.get $l8196 + i32.add + local.set $l8197 + local.get $l8196 + local.get $l8197 + i32.add + local.set $l8198 + local.get $l8197 + local.get $l8198 + i32.add + local.set $l8199 + local.get $l8198 + local.get $l8199 + i32.add + local.set $l8200 + local.get $l8199 + local.get $l8200 + i32.add + local.set $l8201 + local.get $l8200 + local.get $l8201 + i32.add + local.set $l8202 + local.get $l8201 + local.get $l8202 + i32.add + local.set $l8203 + local.get $l8202 + local.get $l8203 + i32.add + local.set $l8204 + local.get $l8203 + local.get $l8204 + i32.add + local.set $l8205 + local.get $l8204 + local.get $l8205 + i32.add + local.set $l8206 + local.get $l8205 + local.get $l8206 + i32.add + local.set $l8207 + local.get $l8206 + local.get $l8207 + i32.add + local.set $l8208 + local.get $l8207 + local.get $l8208 + i32.add + local.set $l8209 + local.get $l8208 + local.get $l8209 + i32.add + local.set $l8210 + local.get $l8209 + local.get $l8210 + i32.add + local.set $l8211 + local.get $l8210 + local.get $l8211 + i32.add + local.set $l8212 + local.get $l8211 + local.get $l8212 + i32.add + local.set $l8213 + local.get $l8212 + local.get $l8213 + i32.add + local.set $l8214 + local.get $l8213 + local.get $l8214 + i32.add + local.set $l8215 + local.get $l8214 + local.get $l8215 + i32.add + local.set $l8216 + local.get $l8215 + local.get $l8216 + i32.add + local.set $l8217 + local.get $l8216 + local.get $l8217 + i32.add + local.set $l8218 + local.get $l8217 + local.get $l8218 + i32.add + local.set $l8219 + local.get $l8218 + local.get $l8219 + i32.add + local.set $l8220 + local.get $l8219 + local.get $l8220 + i32.add + local.set $l8221 + local.get $l8220 + local.get $l8221 + i32.add + local.set $l8222 + local.get $l8221 + local.get $l8222 + i32.add + local.set $l8223 + local.get $l8222 + local.get $l8223 + i32.add + local.set $l8224 + local.get $l8223 + local.get $l8224 + i32.add + local.set $l8225 + local.get $l8224 + local.get $l8225 + i32.add + local.set $l8226 + local.get $l8225 + local.get $l8226 + i32.add + local.set $l8227 + local.get $l8226 + local.get $l8227 + i32.add + local.set $l8228 + local.get $l8227 + local.get $l8228 + i32.add + local.set $l8229 + local.get $l8228 + local.get $l8229 + i32.add + local.set $l8230 + local.get $l8229 + local.get $l8230 + i32.add + local.set $l8231 + local.get $l8230 + local.get $l8231 + i32.add + local.set $l8232 + local.get $l8231 + local.get $l8232 + i32.add + local.set $l8233 + local.get $l8232 + local.get $l8233 + i32.add + local.set $l8234 + local.get $l8233 + local.get $l8234 + i32.add + local.set $l8235 + local.get $l8234 + local.get $l8235 + i32.add + local.set $l8236 + local.get $l8235 + local.get $l8236 + i32.add + local.set $l8237 + local.get $l8236 + local.get $l8237 + i32.add + local.set $l8238 + local.get $l8237 + local.get $l8238 + i32.add + local.set $l8239 + local.get $l8238 + local.get $l8239 + i32.add + local.set $l8240 + local.get $l8239 + local.get $l8240 + i32.add + local.set $l8241 + local.get $l8240 + local.get $l8241 + i32.add + local.set $l8242 + local.get $l8241 + local.get $l8242 + i32.add + local.set $l8243 + local.get $l8242 + local.get $l8243 + i32.add + local.set $l8244 + local.get $l8243 + local.get $l8244 + i32.add + local.set $l8245 + local.get $l8244 + local.get $l8245 + i32.add + local.set $l8246 + local.get $l8245 + local.get $l8246 + i32.add + local.set $l8247 + local.get $l8246 + local.get $l8247 + i32.add + local.set $l8248 + local.get $l8247 + local.get $l8248 + i32.add + local.set $l8249 + local.get $l8248 + local.get $l8249 + i32.add + local.set $l8250 + local.get $l8249 + local.get $l8250 + i32.add + local.set $l8251 + local.get $l8250 + local.get $l8251 + i32.add + local.set $l8252 + local.get $l8251 + local.get $l8252 + i32.add + local.set $l8253 + local.get $l8252 + local.get $l8253 + i32.add + local.set $l8254 + local.get $l8253 + local.get $l8254 + i32.add + local.set $l8255 + local.get $l8254 + local.get $l8255 + i32.add + local.set $l8256 + local.get $l8255 + local.get $l8256 + i32.add + local.set $l8257 + local.get $l8256 + local.get $l8257 + i32.add + local.set $l8258 + local.get $l8257 + local.get $l8258 + i32.add + local.set $l8259 + local.get $l8258 + local.get $l8259 + i32.add + local.set $l8260 + local.get $l8259 + local.get $l8260 + i32.add + local.set $l8261 + local.get $l8260 + local.get $l8261 + i32.add + local.set $l8262 + local.get $l8261 + local.get $l8262 + i32.add + local.set $l8263 + local.get $l8262 + local.get $l8263 + i32.add + local.set $l8264 + local.get $l8263 + local.get $l8264 + i32.add + local.set $l8265 + local.get $l8264 + local.get $l8265 + i32.add + local.set $l8266 + local.get $l8265 + local.get $l8266 + i32.add + local.set $l8267 + local.get $l8266 + local.get $l8267 + i32.add + local.set $l8268 + local.get $l8267 + local.get $l8268 + i32.add + local.set $l8269 + local.get $l8268 + local.get $l8269 + i32.add + local.set $l8270 + local.get $l8269 + local.get $l8270 + i32.add + local.set $l8271 + local.get $l8270 + local.get $l8271 + i32.add + local.set $l8272 + local.get $l8271 + local.get $l8272 + i32.add + local.set $l8273 + local.get $l8272 + local.get $l8273 + i32.add + local.set $l8274 + local.get $l8273 + local.get $l8274 + i32.add + local.set $l8275 + local.get $l8274 + local.get $l8275 + i32.add + local.set $l8276 + local.get $l8275 + local.get $l8276 + i32.add + local.set $l8277 + local.get $l8276 + local.get $l8277 + i32.add + local.set $l8278 + local.get $l8277 + local.get $l8278 + i32.add + local.set $l8279 + local.get $l8278 + local.get $l8279 + i32.add + local.set $l8280 + local.get $l8279 + local.get $l8280 + i32.add + local.set $l8281 + local.get $l8280 + local.get $l8281 + i32.add + local.set $l8282 + local.get $l8281 + local.get $l8282 + i32.add + local.set $l8283 + local.get $l8282 + local.get $l8283 + i32.add + local.set $l8284 + local.get $l8283 + local.get $l8284 + i32.add + local.set $l8285 + local.get $l8284 + local.get $l8285 + i32.add + local.set $l8286 + local.get $l8285 + local.get $l8286 + i32.add + local.set $l8287 + local.get $l8286 + local.get $l8287 + i32.add + local.set $l8288 + local.get $l8287 + local.get $l8288 + i32.add + local.set $l8289 + local.get $l8288 + local.get $l8289 + i32.add + local.set $l8290 + local.get $l8289 + local.get $l8290 + i32.add + local.set $l8291 + local.get $l8290 + local.get $l8291 + i32.add + local.set $l8292 + local.get $l8291 + local.get $l8292 + i32.add + local.set $l8293 + local.get $l8292 + local.get $l8293 + i32.add + local.set $l8294 + local.get $l8293 + local.get $l8294 + i32.add + local.set $l8295 + local.get $l8294 + local.get $l8295 + i32.add + local.set $l8296 + local.get $l8295 + local.get $l8296 + i32.add + local.set $l8297 + local.get $l8296 + local.get $l8297 + i32.add + local.set $l8298 + local.get $l8297 + local.get $l8298 + i32.add + local.set $l8299 + local.get $l8298 + local.get $l8299 + i32.add + local.set $l8300 + local.get $l8299 + local.get $l8300 + i32.add + local.set $l8301 + local.get $l8300 + local.get $l8301 + i32.add + local.set $l8302 + local.get $l8301 + local.get $l8302 + i32.add + local.set $l8303 + local.get $l8302 + local.get $l8303 + i32.add + local.set $l8304 + local.get $l8303 + local.get $l8304 + i32.add + local.set $l8305 + local.get $l8304 + local.get $l8305 + i32.add + local.set $l8306 + local.get $l8305 + local.get $l8306 + i32.add + local.set $l8307 + local.get $l8306 + local.get $l8307 + i32.add + local.set $l8308 + local.get $l8307 + local.get $l8308 + i32.add + local.set $l8309 + local.get $l8308 + local.get $l8309 + i32.add + local.set $l8310 + local.get $l8309 + local.get $l8310 + i32.add + local.set $l8311 + local.get $l8310 + local.get $l8311 + i32.add + local.set $l8312 + local.get $l8311 + local.get $l8312 + i32.add + local.set $l8313 + local.get $l8312 + local.get $l8313 + i32.add + local.set $l8314 + local.get $l8313 + local.get $l8314 + i32.add + local.set $l8315 + local.get $l8314 + local.get $l8315 + i32.add + local.set $l8316 + local.get $l8315 + local.get $l8316 + i32.add + local.set $l8317 + local.get $l8316 + local.get $l8317 + i32.add + local.set $l8318 + local.get $l8317 + local.get $l8318 + i32.add + local.set $l8319 + local.get $l8318 + local.get $l8319 + i32.add + local.set $l8320 + local.get $l8319 + local.get $l8320 + i32.add + local.set $l8321 + local.get $l8320 + local.get $l8321 + i32.add + local.set $l8322 + local.get $l8321 + local.get $l8322 + i32.add + local.set $l8323 + local.get $l8322 + local.get $l8323 + i32.add + local.set $l8324 + local.get $l8323 + local.get $l8324 + i32.add + local.set $l8325 + local.get $l8324 + local.get $l8325 + i32.add + local.set $l8326 + local.get $l8325 + local.get $l8326 + i32.add + local.set $l8327 + local.get $l8326 + local.get $l8327 + i32.add + local.set $l8328 + local.get $l8327 + local.get $l8328 + i32.add + local.set $l8329 + local.get $l8328 + local.get $l8329 + i32.add + local.set $l8330 + local.get $l8329 + local.get $l8330 + i32.add + local.set $l8331 + local.get $l8330 + local.get $l8331 + i32.add + local.set $l8332 + local.get $l8331 + local.get $l8332 + i32.add + local.set $l8333 + local.get $l8332 + local.get $l8333 + i32.add + local.set $l8334 + local.get $l8333 + local.get $l8334 + i32.add + local.set $l8335 + local.get $l8334 + local.get $l8335 + i32.add + local.set $l8336 + local.get $l8335 + local.get $l8336 + i32.add + local.set $l8337 + local.get $l8336 + local.get $l8337 + i32.add + local.set $l8338 + local.get $l8337 + local.get $l8338 + i32.add + local.set $l8339 + local.get $l8338 + local.get $l8339 + i32.add + local.set $l8340 + local.get $l8339 + local.get $l8340 + i32.add + local.set $l8341 + local.get $l8340 + local.get $l8341 + i32.add + local.set $l8342 + local.get $l8341 + local.get $l8342 + i32.add + local.set $l8343 + local.get $l8342 + local.get $l8343 + i32.add + local.set $l8344 + local.get $l8343 + local.get $l8344 + i32.add + local.set $l8345 + local.get $l8344 + local.get $l8345 + i32.add + local.set $l8346 + local.get $l8345 + local.get $l8346 + i32.add + local.set $l8347 + local.get $l8346 + local.get $l8347 + i32.add + local.set $l8348 + local.get $l8347 + local.get $l8348 + i32.add + local.set $l8349 + local.get $l8348 + local.get $l8349 + i32.add + local.set $l8350 + local.get $l8349 + local.get $l8350 + i32.add + local.set $l8351 + local.get $l8350 + local.get $l8351 + i32.add + local.set $l8352 + local.get $l8351 + local.get $l8352 + i32.add + local.set $l8353 + local.get $l8352 + local.get $l8353 + i32.add + local.set $l8354 + local.get $l8353 + local.get $l8354 + i32.add + local.set $l8355 + local.get $l8354 + local.get $l8355 + i32.add + local.set $l8356 + local.get $l8355 + local.get $l8356 + i32.add + local.set $l8357 + local.get $l8356 + local.get $l8357 + i32.add + local.set $l8358 + local.get $l8357 + local.get $l8358 + i32.add + local.set $l8359 + local.get $l8358 + local.get $l8359 + i32.add + local.set $l8360 + local.get $l8359 + local.get $l8360 + i32.add + local.set $l8361 + local.get $l8360 + local.get $l8361 + i32.add + local.set $l8362 + local.get $l8361 + local.get $l8362 + i32.add + local.set $l8363 + local.get $l8362 + local.get $l8363 + i32.add + local.set $l8364 + local.get $l8363 + local.get $l8364 + i32.add + local.set $l8365 + local.get $l8364 + local.get $l8365 + i32.add + local.set $l8366 + local.get $l8365 + local.get $l8366 + i32.add + local.set $l8367 + local.get $l8366 + local.get $l8367 + i32.add + local.set $l8368 + local.get $l8367 + local.get $l8368 + i32.add + local.set $l8369 + local.get $l8368 + local.get $l8369 + i32.add + local.set $l8370 + local.get $l8369 + local.get $l8370 + i32.add + local.set $l8371 + local.get $l8370 + local.get $l8371 + i32.add + local.set $l8372 + local.get $l8371 + local.get $l8372 + i32.add + local.set $l8373 + local.get $l8372 + local.get $l8373 + i32.add + local.set $l8374 + local.get $l8373 + local.get $l8374 + i32.add + local.set $l8375 + local.get $l8374 + local.get $l8375 + i32.add + local.set $l8376 + local.get $l8375 + local.get $l8376 + i32.add + local.set $l8377 + local.get $l8376 + local.get $l8377 + i32.add + local.set $l8378 + local.get $l8377 + local.get $l8378 + i32.add + local.set $l8379 + local.get $l8378 + local.get $l8379 + i32.add + local.set $l8380 + local.get $l8379 + local.get $l8380 + i32.add + local.set $l8381 + local.get $l8380 + local.get $l8381 + i32.add + local.set $l8382 + local.get $l8381 + local.get $l8382 + i32.add + local.set $l8383 + local.get $l8382 + local.get $l8383 + i32.add + local.set $l8384 + local.get $l8383 + local.get $l8384 + i32.add + local.set $l8385 + local.get $l8384 + local.get $l8385 + i32.add + local.set $l8386 + local.get $l8385 + local.get $l8386 + i32.add + local.set $l8387 + local.get $l8386 + local.get $l8387 + i32.add + local.set $l8388 + local.get $l8387 + local.get $l8388 + i32.add + local.set $l8389 + local.get $l8388 + local.get $l8389 + i32.add + local.set $l8390 + local.get $l8389 + local.get $l8390 + i32.add + local.set $l8391 + local.get $l8390 + local.get $l8391 + i32.add + local.set $l8392 + local.get $l8391 + local.get $l8392 + i32.add + local.set $l8393 + local.get $l8392 + local.get $l8393 + i32.add + local.set $l8394 + local.get $l8393 + local.get $l8394 + i32.add + local.set $l8395 + local.get $l8394 + local.get $l8395 + i32.add + local.set $l8396 + local.get $l8395 + local.get $l8396 + i32.add + local.set $l8397 + local.get $l8396 + local.get $l8397 + i32.add + local.set $l8398 + local.get $l8397 + local.get $l8398 + i32.add + local.set $l8399 + local.get $l8398 + local.get $l8399 + i32.add + local.set $l8400 + local.get $l8399 + local.get $l8400 + i32.add + local.set $l8401 + local.get $l8400 + local.get $l8401 + i32.add + local.set $l8402 + local.get $l8401 + local.get $l8402 + i32.add + local.set $l8403 + local.get $l8402 + local.get $l8403 + i32.add + local.set $l8404 + local.get $l8403 + local.get $l8404 + i32.add + local.set $l8405 + local.get $l8404 + local.get $l8405 + i32.add + local.set $l8406 + local.get $l8405 + local.get $l8406 + i32.add + local.set $l8407 + local.get $l8406 + local.get $l8407 + i32.add + local.set $l8408 + local.get $l8407 + local.get $l8408 + i32.add + local.set $l8409 + local.get $l8408 + local.get $l8409 + i32.add + local.set $l8410 + local.get $l8409 + local.get $l8410 + i32.add + local.set $l8411 + local.get $l8410 + local.get $l8411 + i32.add + local.set $l8412 + local.get $l8411 + local.get $l8412 + i32.add + local.set $l8413 + local.get $l8412 + local.get $l8413 + i32.add + local.set $l8414 + local.get $l8413 + local.get $l8414 + i32.add + local.set $l8415 + local.get $l8414 + local.get $l8415 + i32.add + local.set $l8416 + local.get $l8415 + local.get $l8416 + i32.add + local.set $l8417 + local.get $l8416 + local.get $l8417 + i32.add + local.set $l8418 + local.get $l8417 + local.get $l8418 + i32.add + local.set $l8419 + local.get $l8418 + local.get $l8419 + i32.add + local.set $l8420 + local.get $l8419 + local.get $l8420 + i32.add + local.set $l8421 + local.get $l8420 + local.get $l8421 + i32.add + local.set $l8422 + local.get $l8421 + local.get $l8422 + i32.add + local.set $l8423 + local.get $l8422 + local.get $l8423 + i32.add + local.set $l8424 + local.get $l8423 + local.get $l8424 + i32.add + local.set $l8425 + local.get $l8424 + local.get $l8425 + i32.add + local.set $l8426 + local.get $l8425 + local.get $l8426 + i32.add + local.set $l8427 + local.get $l8426 + local.get $l8427 + i32.add + local.set $l8428 + local.get $l8427 + local.get $l8428 + i32.add + local.set $l8429 + local.get $l8428 + local.get $l8429 + i32.add + local.set $l8430 + local.get $l8429 + local.get $l8430 + i32.add + local.set $l8431 + local.get $l8430 + local.get $l8431 + i32.add + local.set $l8432 + local.get $l8431 + local.get $l8432 + i32.add + local.set $l8433 + local.get $l8432 + local.get $l8433 + i32.add + local.set $l8434 + local.get $l8433 + local.get $l8434 + i32.add + local.set $l8435 + local.get $l8434 + local.get $l8435 + i32.add + local.set $l8436 + local.get $l8435 + local.get $l8436 + i32.add + local.set $l8437 + local.get $l8436 + local.get $l8437 + i32.add + local.set $l8438 + local.get $l8437 + local.get $l8438 + i32.add + local.set $l8439 + local.get $l8438 + local.get $l8439 + i32.add + local.set $l8440 + local.get $l8439 + local.get $l8440 + i32.add + local.set $l8441 + local.get $l8440 + local.get $l8441 + i32.add + local.set $l8442 + local.get $l8441 + local.get $l8442 + i32.add + local.set $l8443 + local.get $l8442 + local.get $l8443 + i32.add + local.set $l8444 + local.get $l8443 + local.get $l8444 + i32.add + local.set $l8445 + local.get $l8444 + local.get $l8445 + i32.add + local.set $l8446 + local.get $l8445 + local.get $l8446 + i32.add + local.set $l8447 + local.get $l8446 + local.get $l8447 + i32.add + local.set $l8448 + local.get $l8447 + local.get $l8448 + i32.add + local.set $l8449 + local.get $l8448 + local.get $l8449 + i32.add + local.set $l8450 + local.get $l8449 + local.get $l8450 + i32.add + local.set $l8451 + local.get $l8450 + local.get $l8451 + i32.add + local.set $l8452 + local.get $l8451 + local.get $l8452 + i32.add + local.set $l8453 + local.get $l8452 + local.get $l8453 + i32.add + local.set $l8454 + local.get $l8453 + local.get $l8454 + i32.add + local.set $l8455 + local.get $l8454 + local.get $l8455 + i32.add + local.set $l8456 + local.get $l8455 + local.get $l8456 + i32.add + local.set $l8457 + local.get $l8456 + local.get $l8457 + i32.add + local.set $l8458 + local.get $l8457 + local.get $l8458 + i32.add + local.set $l8459 + local.get $l8458 + local.get $l8459 + i32.add + local.set $l8460 + local.get $l8459 + local.get $l8460 + i32.add + local.set $l8461 + local.get $l8460 + local.get $l8461 + i32.add + local.set $l8462 + local.get $l8461 + local.get $l8462 + i32.add + local.set $l8463 + local.get $l8462 + local.get $l8463 + i32.add + local.set $l8464 + local.get $l8463 + local.get $l8464 + i32.add + local.set $l8465 + local.get $l8464 + local.get $l8465 + i32.add + local.set $l8466 + local.get $l8465 + local.get $l8466 + i32.add + local.set $l8467 + local.get $l8466 + local.get $l8467 + i32.add + local.set $l8468 + local.get $l8467 + local.get $l8468 + i32.add + local.set $l8469 + local.get $l8468 + local.get $l8469 + i32.add + local.set $l8470 + local.get $l8469 + local.get $l8470 + i32.add + local.set $l8471 + local.get $l8470 + local.get $l8471 + i32.add + local.set $l8472 + local.get $l8471 + local.get $l8472 + i32.add + local.set $l8473 + local.get $l8472 + local.get $l8473 + i32.add + local.set $l8474 + local.get $l8473 + local.get $l8474 + i32.add + local.set $l8475 + local.get $l8474 + local.get $l8475 + i32.add + local.set $l8476 + local.get $l8475 + local.get $l8476 + i32.add + local.set $l8477 + local.get $l8476 + local.get $l8477 + i32.add + local.set $l8478 + local.get $l8477 + local.get $l8478 + i32.add + local.set $l8479 + local.get $l8478 + local.get $l8479 + i32.add + local.set $l8480 + local.get $l8479 + local.get $l8480 + i32.add + local.set $l8481 + local.get $l8480 + local.get $l8481 + i32.add + local.set $l8482 + local.get $l8481 + local.get $l8482 + i32.add + local.set $l8483 + local.get $l8482 + local.get $l8483 + i32.add + local.set $l8484 + local.get $l8483 + local.get $l8484 + i32.add + local.set $l8485 + local.get $l8484 + local.get $l8485 + i32.add + local.set $l8486 + local.get $l8485 + local.get $l8486 + i32.add + local.set $l8487 + local.get $l8486 + local.get $l8487 + i32.add + local.set $l8488 + local.get $l8487 + local.get $l8488 + i32.add + local.set $l8489 + local.get $l8488 + local.get $l8489 + i32.add + local.set $l8490 + local.get $l8489 + local.get $l8490 + i32.add + local.set $l8491 + local.get $l8490 + local.get $l8491 + i32.add + local.set $l8492 + local.get $l8491 + local.get $l8492 + i32.add + local.set $l8493 + local.get $l8492 + local.get $l8493 + i32.add + local.set $l8494 + local.get $l8493 + local.get $l8494 + i32.add + local.set $l8495 + local.get $l8494 + local.get $l8495 + i32.add + local.set $l8496 + local.get $l8495 + local.get $l8496 + i32.add + local.set $l8497 + local.get $l8496 + local.get $l8497 + i32.add + local.set $l8498 + local.get $l8497 + local.get $l8498 + i32.add + local.set $l8499 + local.get $l8498 + local.get $l8499 + i32.add + local.set $l8500 + local.get $l8499 + local.get $l8500 + i32.add + local.set $l8501 + local.get $l8500 + local.get $l8501 + i32.add + local.set $l8502 + local.get $l8501 + local.get $l8502 + i32.add + local.set $l8503 + local.get $l8502 + local.get $l8503 + i32.add + local.set $l8504 + local.get $l8503 + local.get $l8504 + i32.add + local.set $l8505 + local.get $l8504 + local.get $l8505 + i32.add + local.set $l8506 + local.get $l8505 + local.get $l8506 + i32.add + local.set $l8507 + local.get $l8506 + local.get $l8507 + i32.add + local.set $l8508 + local.get $l8507 + local.get $l8508 + i32.add + local.set $l8509 + local.get $l8508 + local.get $l8509 + i32.add + local.set $l8510 + local.get $l8509 + local.get $l8510 + i32.add + local.set $l8511 + local.get $l8510 + local.get $l8511 + i32.add + local.set $l8512 + local.get $l8511 + local.get $l8512 + i32.add + local.set $l8513 + local.get $l8512 + local.get $l8513 + i32.add + local.set $l8514 + local.get $l8513 + local.get $l8514 + i32.add + local.set $l8515 + local.get $l8514 + local.get $l8515 + i32.add + local.set $l8516 + local.get $l8515 + local.get $l8516 + i32.add + local.set $l8517 + local.get $l8516 + local.get $l8517 + i32.add + local.set $l8518 + local.get $l8517 + local.get $l8518 + i32.add + local.set $l8519 + local.get $l8518 + local.get $l8519 + i32.add + local.set $l8520 + local.get $l8519 + local.get $l8520 + i32.add + local.set $l8521 + local.get $l8520 + local.get $l8521 + i32.add + local.set $l8522 + local.get $l8521 + local.get $l8522 + i32.add + local.set $l8523 + local.get $l8522 + local.get $l8523 + i32.add + local.set $l8524 + local.get $l8523 + local.get $l8524 + i32.add + local.set $l8525 + local.get $l8524 + local.get $l8525 + i32.add + local.set $l8526 + local.get $l8525 + local.get $l8526 + i32.add + local.set $l8527 + local.get $l8526 + local.get $l8527 + i32.add + local.set $l8528 + local.get $l8527 + local.get $l8528 + i32.add + local.set $l8529 + local.get $l8528 + local.get $l8529 + i32.add + local.set $l8530 + local.get $l8529 + local.get $l8530 + i32.add + local.set $l8531 + local.get $l8530 + local.get $l8531 + i32.add + local.set $l8532 + local.get $l8531 + local.get $l8532 + i32.add + local.set $l8533 + local.get $l8532 + local.get $l8533 + i32.add + local.set $l8534 + local.get $l8533 + local.get $l8534 + i32.add + local.set $l8535 + local.get $l8534 + local.get $l8535 + i32.add + local.set $l8536 + local.get $l8535 + local.get $l8536 + i32.add + local.set $l8537 + local.get $l8536 + local.get $l8537 + i32.add + local.set $l8538 + local.get $l8537 + local.get $l8538 + i32.add + local.set $l8539 + local.get $l8538 + local.get $l8539 + i32.add + local.set $l8540 + local.get $l8539 + local.get $l8540 + i32.add + local.set $l8541 + local.get $l8540 + local.get $l8541 + i32.add + local.set $l8542 + local.get $l8541 + local.get $l8542 + i32.add + local.set $l8543 + local.get $l8542 + local.get $l8543 + i32.add + local.set $l8544 + local.get $l8543 + local.get $l8544 + i32.add + local.set $l8545 + local.get $l8544 + local.get $l8545 + i32.add + local.set $l8546 + local.get $l8545 + local.get $l8546 + i32.add + local.set $l8547 + local.get $l8546 + local.get $l8547 + i32.add + local.set $l8548 + local.get $l8547 + local.get $l8548 + i32.add + local.set $l8549 + local.get $l8548 + local.get $l8549 + i32.add + local.set $l8550 + local.get $l8549 + local.get $l8550 + i32.add + local.set $l8551 + local.get $l8550 + local.get $l8551 + i32.add + local.set $l8552 + local.get $l8551 + local.get $l8552 + i32.add + local.set $l8553 + local.get $l8552 + local.get $l8553 + i32.add + local.set $l8554 + local.get $l8553 + local.get $l8554 + i32.add + local.set $l8555 + local.get $l8554 + local.get $l8555 + i32.add + local.set $l8556 + local.get $l8555 + local.get $l8556 + i32.add + local.set $l8557 + local.get $l8556 + local.get $l8557 + i32.add + local.set $l8558 + local.get $l8557 + local.get $l8558 + i32.add + local.set $l8559 + local.get $l8558 + local.get $l8559 + i32.add + local.set $l8560 + local.get $l8559 + local.get $l8560 + i32.add + local.set $l8561 + local.get $l8560 + local.get $l8561 + i32.add + local.set $l8562 + local.get $l8561 + local.get $l8562 + i32.add + local.set $l8563 + local.get $l8562 + local.get $l8563 + i32.add + local.set $l8564 + local.get $l8563 + local.get $l8564 + i32.add + local.set $l8565 + local.get $l8564 + local.get $l8565 + i32.add + local.set $l8566 + local.get $l8565 + local.get $l8566 + i32.add + local.set $l8567 + local.get $l8566 + local.get $l8567 + i32.add + local.set $l8568 + local.get $l8567 + local.get $l8568 + i32.add + local.set $l8569 + local.get $l8568 + local.get $l8569 + i32.add + local.set $l8570 + local.get $l8569 + local.get $l8570 + i32.add + local.set $l8571 + local.get $l8570 + local.get $l8571 + i32.add + local.set $l8572 + local.get $l8571 + local.get $l8572 + i32.add + local.set $l8573 + local.get $l8572 + local.get $l8573 + i32.add + local.set $l8574 + local.get $l8573 + local.get $l8574 + i32.add + local.set $l8575 + local.get $l8574 + local.get $l8575 + i32.add + local.set $l8576 + local.get $l8575 + local.get $l8576 + i32.add + local.set $l8577 + local.get $l8576 + local.get $l8577 + i32.add + local.set $l8578 + local.get $l8577 + local.get $l8578 + i32.add + local.set $l8579 + local.get $l8578 + local.get $l8579 + i32.add + local.set $l8580 + local.get $l8579 + local.get $l8580 + i32.add + local.set $l8581 + local.get $l8580 + local.get $l8581 + i32.add + local.set $l8582 + local.get $l8581 + local.get $l8582 + i32.add + local.set $l8583 + local.get $l8582 + local.get $l8583 + i32.add + local.set $l8584 + local.get $l8583 + local.get $l8584 + i32.add + local.set $l8585 + local.get $l8584 + local.get $l8585 + i32.add + local.set $l8586 + local.get $l8585 + local.get $l8586 + i32.add + local.set $l8587 + local.get $l8586 + local.get $l8587 + i32.add + local.set $l8588 + local.get $l8587 + local.get $l8588 + i32.add + local.set $l8589 + local.get $l8588 + local.get $l8589 + i32.add + local.set $l8590 + local.get $l8589 + local.get $l8590 + i32.add + local.set $l8591 + local.get $l8590 + local.get $l8591 + i32.add + local.set $l8592 + local.get $l8591 + local.get $l8592 + i32.add + local.set $l8593 + local.get $l8592 + local.get $l8593 + i32.add + local.set $l8594 + local.get $l8593 + local.get $l8594 + i32.add + local.set $l8595 + local.get $l8594 + local.get $l8595 + i32.add + local.set $l8596 + local.get $l8595 + local.get $l8596 + i32.add + local.set $l8597 + local.get $l8596 + local.get $l8597 + i32.add + local.set $l8598 + local.get $l8597 + local.get $l8598 + i32.add + local.set $l8599 + local.get $l8598 + local.get $l8599 + i32.add + local.set $l8600 + local.get $l8599 + local.get $l8600 + i32.add + local.set $l8601 + local.get $l8600 + local.get $l8601 + i32.add + local.set $l8602 + local.get $l8601 + local.get $l8602 + i32.add + local.set $l8603 + local.get $l8602 + local.get $l8603 + i32.add + local.set $l8604 + local.get $l8603 + local.get $l8604 + i32.add + local.set $l8605 + local.get $l8604 + local.get $l8605 + i32.add + local.set $l8606 + local.get $l8605 + local.get $l8606 + i32.add + local.set $l8607 + local.get $l8606 + local.get $l8607 + i32.add + local.set $l8608 + local.get $l8607 + local.get $l8608 + i32.add + local.set $l8609 + local.get $l8608 + local.get $l8609 + i32.add + local.set $l8610 + local.get $l8609 + local.get $l8610 + i32.add + local.set $l8611 + local.get $l8610 + local.get $l8611 + i32.add + local.set $l8612 + local.get $l8611 + local.get $l8612 + i32.add + local.set $l8613 + local.get $l8612 + local.get $l8613 + i32.add + local.set $l8614 + local.get $l8613 + local.get $l8614 + i32.add + local.set $l8615 + local.get $l8614 + local.get $l8615 + i32.add + local.set $l8616 + local.get $l8615 + local.get $l8616 + i32.add + local.set $l8617 + local.get $l8616 + local.get $l8617 + i32.add + local.set $l8618 + local.get $l8617 + local.get $l8618 + i32.add + local.set $l8619 + local.get $l8618 + local.get $l8619 + i32.add + local.set $l8620 + local.get $l8619 + local.get $l8620 + i32.add + local.set $l8621 + local.get $l8620 + local.get $l8621 + i32.add + local.set $l8622 + local.get $l8621 + local.get $l8622 + i32.add + local.set $l8623 + local.get $l8622 + local.get $l8623 + i32.add + local.set $l8624 + local.get $l8623 + local.get $l8624 + i32.add + local.set $l8625 + local.get $l8624 + local.get $l8625 + i32.add + local.set $l8626 + local.get $l8625 + local.get $l8626 + i32.add + local.set $l8627 + local.get $l8626 + local.get $l8627 + i32.add + local.set $l8628 + local.get $l8627 + local.get $l8628 + i32.add + local.set $l8629 + local.get $l8628 + local.get $l8629 + i32.add + local.set $l8630 + local.get $l8629 + local.get $l8630 + i32.add + local.set $l8631 + local.get $l8630 + local.get $l8631 + i32.add + local.set $l8632 + local.get $l8631 + local.get $l8632 + i32.add + local.set $l8633 + local.get $l8632 + local.get $l8633 + i32.add + local.set $l8634 + local.get $l8633 + local.get $l8634 + i32.add + local.set $l8635 + local.get $l8634 + local.get $l8635 + i32.add + local.set $l8636 + local.get $l8635 + local.get $l8636 + i32.add + local.set $l8637 + local.get $l8636 + local.get $l8637 + i32.add + local.set $l8638 + local.get $l8637 + local.get $l8638 + i32.add + local.set $l8639 + local.get $l8638 + local.get $l8639 + i32.add + local.set $l8640 + local.get $l8639 + local.get $l8640 + i32.add + local.set $l8641 + local.get $l8640 + local.get $l8641 + i32.add + local.set $l8642 + local.get $l8641 + local.get $l8642 + i32.add + local.set $l8643 + local.get $l8642 + local.get $l8643 + i32.add + local.set $l8644 + local.get $l8643 + local.get $l8644 + i32.add + local.set $l8645 + local.get $l8644 + local.get $l8645 + i32.add + local.set $l8646 + local.get $l8645 + local.get $l8646 + i32.add + local.set $l8647 + local.get $l8646 + local.get $l8647 + i32.add + local.set $l8648 + local.get $l8647 + local.get $l8648 + i32.add + local.set $l8649 + local.get $l8648 + local.get $l8649 + i32.add + local.set $l8650 + local.get $l8649 + local.get $l8650 + i32.add + local.set $l8651 + local.get $l8650 + local.get $l8651 + i32.add + local.set $l8652 + local.get $l8651 + local.get $l8652 + i32.add + local.set $l8653 + local.get $l8652 + local.get $l8653 + i32.add + local.set $l8654 + local.get $l8653 + local.get $l8654 + i32.add + local.set $l8655 + local.get $l8654 + local.get $l8655 + i32.add + local.set $l8656 + local.get $l8655 + local.get $l8656 + i32.add + local.set $l8657 + local.get $l8656 + local.get $l8657 + i32.add + local.set $l8658 + local.get $l8657 + local.get $l8658 + i32.add + local.set $l8659 + local.get $l8658 + local.get $l8659 + i32.add + local.set $l8660 + local.get $l8659 + local.get $l8660 + i32.add + local.set $l8661 + local.get $l8660 + local.get $l8661 + i32.add + local.set $l8662 + local.get $l8661 + local.get $l8662 + i32.add + local.set $l8663 + local.get $l8662 + local.get $l8663 + i32.add + local.set $l8664 + local.get $l8663 + local.get $l8664 + i32.add + local.set $l8665 + local.get $l8664 + local.get $l8665 + i32.add + local.set $l8666 + local.get $l8665 + local.get $l8666 + i32.add + local.set $l8667 + local.get $l8666 + local.get $l8667 + i32.add + local.set $l8668 + local.get $l8667 + local.get $l8668 + i32.add + local.set $l8669 + local.get $l8668 + local.get $l8669 + i32.add + local.set $l8670 + local.get $l8669 + local.get $l8670 + i32.add + local.set $l8671 + local.get $l8670 + local.get $l8671 + i32.add + local.set $l8672 + local.get $l8671 + local.get $l8672 + i32.add + local.set $l8673 + local.get $l8672 + local.get $l8673 + i32.add + local.set $l8674 + local.get $l8673 + local.get $l8674 + i32.add + local.set $l8675 + local.get $l8674 + local.get $l8675 + i32.add + local.set $l8676 + local.get $l8675 + local.get $l8676 + i32.add + local.set $l8677 + local.get $l8676 + local.get $l8677 + i32.add + local.set $l8678 + local.get $l8677 + local.get $l8678 + i32.add + local.set $l8679 + local.get $l8678 + local.get $l8679 + i32.add + local.set $l8680 + local.get $l8679 + local.get $l8680 + i32.add + local.set $l8681 + local.get $l8680 + local.get $l8681 + i32.add + local.set $l8682 + local.get $l8681 + local.get $l8682 + i32.add + local.set $l8683 + local.get $l8682 + local.get $l8683 + i32.add + local.set $l8684 + local.get $l8683 + local.get $l8684 + i32.add + local.set $l8685 + local.get $l8684 + local.get $l8685 + i32.add + local.set $l8686 + local.get $l8685 + local.get $l8686 + i32.add + local.set $l8687 + local.get $l8686 + local.get $l8687 + i32.add + local.set $l8688 + local.get $l8687 + local.get $l8688 + i32.add + local.set $l8689 + local.get $l8688 + local.get $l8689 + i32.add + local.set $l8690 + local.get $l8689 + local.get $l8690 + i32.add + local.set $l8691 + local.get $l8690 + local.get $l8691 + i32.add + local.set $l8692 + local.get $l8691 + local.get $l8692 + i32.add + local.set $l8693 + local.get $l8692 + local.get $l8693 + i32.add + local.set $l8694 + local.get $l8693 + local.get $l8694 + i32.add + local.set $l8695 + local.get $l8694 + local.get $l8695 + i32.add + local.set $l8696 + local.get $l8695 + local.get $l8696 + i32.add + local.set $l8697 + local.get $l8696 + local.get $l8697 + i32.add + local.set $l8698 + local.get $l8697 + local.get $l8698 + i32.add + local.set $l8699 + local.get $l8698 + local.get $l8699 + i32.add + local.set $l8700 + local.get $l8699 + local.get $l8700 + i32.add + local.set $l8701 + local.get $l8700 + local.get $l8701 + i32.add + local.set $l8702 + local.get $l8701 + local.get $l8702 + i32.add + local.set $l8703 + local.get $l8702 + local.get $l8703 + i32.add + local.set $l8704 + local.get $l8703 + local.get $l8704 + i32.add + local.set $l8705 + local.get $l8704 + local.get $l8705 + i32.add + local.set $l8706 + local.get $l8705 + local.get $l8706 + i32.add + local.set $l8707 + local.get $l8706 + local.get $l8707 + i32.add + local.set $l8708 + local.get $l8707 + local.get $l8708 + i32.add + local.set $l8709 + local.get $l8708 + local.get $l8709 + i32.add + local.set $l8710 + local.get $l8709 + local.get $l8710 + i32.add + local.set $l8711 + local.get $l8710 + local.get $l8711 + i32.add + local.set $l8712 + local.get $l8711 + local.get $l8712 + i32.add + local.set $l8713 + local.get $l8712 + local.get $l8713 + i32.add + local.set $l8714 + local.get $l8713 + local.get $l8714 + i32.add + local.set $l8715 + local.get $l8714 + local.get $l8715 + i32.add + local.set $l8716 + local.get $l8715 + local.get $l8716 + i32.add + local.set $l8717 + local.get $l8716 + local.get $l8717 + i32.add + local.set $l8718 + local.get $l8717 + local.get $l8718 + i32.add + local.set $l8719 + local.get $l8718 + local.get $l8719 + i32.add + local.set $l8720 + local.get $l8719 + local.get $l8720 + i32.add + local.set $l8721 + local.get $l8720 + local.get $l8721 + i32.add + local.set $l8722 + local.get $l8721 + local.get $l8722 + i32.add + local.set $l8723 + local.get $l8722 + local.get $l8723 + i32.add + local.set $l8724 + local.get $l8723 + local.get $l8724 + i32.add + local.set $l8725 + local.get $l8724 + local.get $l8725 + i32.add + local.set $l8726 + local.get $l8725 + local.get $l8726 + i32.add + local.set $l8727 + local.get $l8726 + local.get $l8727 + i32.add + local.set $l8728 + local.get $l8727 + local.get $l8728 + i32.add + local.set $l8729 + local.get $l8728 + local.get $l8729 + i32.add + local.set $l8730 + local.get $l8729 + local.get $l8730 + i32.add + local.set $l8731 + local.get $l8730 + local.get $l8731 + i32.add + local.set $l8732 + local.get $l8731 + local.get $l8732 + i32.add + local.set $l8733 + local.get $l8732 + local.get $l8733 + i32.add + local.set $l8734 + local.get $l8733 + local.get $l8734 + i32.add + local.set $l8735 + local.get $l8734 + local.get $l8735 + i32.add + local.set $l8736 + local.get $l8735 + local.get $l8736 + i32.add + local.set $l8737 + local.get $l8736 + local.get $l8737 + i32.add + local.set $l8738 + local.get $l8737 + local.get $l8738 + i32.add + local.set $l8739 + local.get $l8738 + local.get $l8739 + i32.add + local.set $l8740 + local.get $l8739 + local.get $l8740 + i32.add + local.set $l8741 + local.get $l8740 + local.get $l8741 + i32.add + local.set $l8742 + local.get $l8741 + local.get $l8742 + i32.add + local.set $l8743 + local.get $l8742 + local.get $l8743 + i32.add + local.set $l8744 + local.get $l8743 + local.get $l8744 + i32.add + local.set $l8745 + local.get $l8744 + local.get $l8745 + i32.add + local.set $l8746 + local.get $l8745 + local.get $l8746 + i32.add + local.set $l8747 + local.get $l8746 + local.get $l8747 + i32.add + local.set $l8748 + local.get $l8747 + local.get $l8748 + i32.add + local.set $l8749 + local.get $l8748 + local.get $l8749 + i32.add + local.set $l8750 + local.get $l8749 + local.get $l8750 + i32.add + local.set $l8751 + local.get $l8750 + local.get $l8751 + i32.add + local.set $l8752 + local.get $l8751 + local.get $l8752 + i32.add + local.set $l8753 + local.get $l8752 + local.get $l8753 + i32.add + local.set $l8754 + local.get $l8753 + local.get $l8754 + i32.add + local.set $l8755 + local.get $l8754 + local.get $l8755 + i32.add + local.set $l8756 + local.get $l8755 + local.get $l8756 + i32.add + local.set $l8757 + local.get $l8756 + local.get $l8757 + i32.add + local.set $l8758 + local.get $l8757 + local.get $l8758 + i32.add + local.set $l8759 + local.get $l8758 + local.get $l8759 + i32.add + local.set $l8760 + local.get $l8759 + local.get $l8760 + i32.add + local.set $l8761 + local.get $l8760 + local.get $l8761 + i32.add + local.set $l8762 + local.get $l8761 + local.get $l8762 + i32.add + local.set $l8763 + local.get $l8762 + local.get $l8763 + i32.add + local.set $l8764 + local.get $l8763 + local.get $l8764 + i32.add + local.set $l8765 + local.get $l8764 + local.get $l8765 + i32.add + local.set $l8766 + local.get $l8765 + local.get $l8766 + i32.add + local.set $l8767 + local.get $l8766 + local.get $l8767 + i32.add + local.set $l8768 + local.get $l8767 + local.get $l8768 + i32.add + local.set $l8769 + local.get $l8768 + local.get $l8769 + i32.add + local.set $l8770 + local.get $l8769 + local.get $l8770 + i32.add + local.set $l8771 + local.get $l8770 + local.get $l8771 + i32.add + local.set $l8772 + local.get $l8771 + local.get $l8772 + i32.add + local.set $l8773 + local.get $l8772 + local.get $l8773 + i32.add + local.set $l8774 + local.get $l8773 + local.get $l8774 + i32.add + local.set $l8775 + local.get $l8774 + local.get $l8775 + i32.add + local.set $l8776 + local.get $l8775 + local.get $l8776 + i32.add + local.set $l8777 + local.get $l8776 + local.get $l8777 + i32.add + local.set $l8778 + local.get $l8777 + local.get $l8778 + i32.add + local.set $l8779 + local.get $l8778 + local.get $l8779 + i32.add + local.set $l8780 + local.get $l8779 + local.get $l8780 + i32.add + local.set $l8781 + local.get $l8780 + local.get $l8781 + i32.add + local.set $l8782 + local.get $l8781 + local.get $l8782 + i32.add + local.set $l8783 + local.get $l8782 + local.get $l8783 + i32.add + local.set $l8784 + local.get $l8783 + local.get $l8784 + i32.add + local.set $l8785 + local.get $l8784 + local.get $l8785 + i32.add + local.set $l8786 + local.get $l8785 + local.get $l8786 + i32.add + local.set $l8787 + local.get $l8786 + local.get $l8787 + i32.add + local.set $l8788 + local.get $l8787 + local.get $l8788 + i32.add + local.set $l8789 + local.get $l8788 + local.get $l8789 + i32.add + local.set $l8790 + local.get $l8789 + local.get $l8790 + i32.add + local.set $l8791 + local.get $l8790 + local.get $l8791 + i32.add + local.set $l8792 + local.get $l8791 + local.get $l8792 + i32.add + local.set $l8793 + local.get $l8792 + local.get $l8793 + i32.add + local.set $l8794 + local.get $l8793 + local.get $l8794 + i32.add + local.set $l8795 + local.get $l8794 + local.get $l8795 + i32.add + local.set $l8796 + local.get $l8795 + local.get $l8796 + i32.add + local.set $l8797 + local.get $l8796 + local.get $l8797 + i32.add + local.set $l8798 + local.get $l8797 + local.get $l8798 + i32.add + local.set $l8799 + local.get $l8798 + local.get $l8799 + i32.add + local.set $l8800 + local.get $l8799 + local.get $l8800 + i32.add + local.set $l8801 + local.get $l8800 + local.get $l8801 + i32.add + local.set $l8802 + local.get $l8801 + local.get $l8802 + i32.add + local.set $l8803 + local.get $l8802 + local.get $l8803 + i32.add + local.set $l8804 + local.get $l8803 + local.get $l8804 + i32.add + local.set $l8805 + local.get $l8804 + local.get $l8805 + i32.add + local.set $l8806 + local.get $l8805 + local.get $l8806 + i32.add + local.set $l8807 + local.get $l8806 + local.get $l8807 + i32.add + local.set $l8808 + local.get $l8807 + local.get $l8808 + i32.add + local.set $l8809 + local.get $l8808 + local.get $l8809 + i32.add + local.set $l8810 + local.get $l8809 + local.get $l8810 + i32.add + local.set $l8811 + local.get $l8810 + local.get $l8811 + i32.add + local.set $l8812 + local.get $l8811 + local.get $l8812 + i32.add + local.set $l8813 + local.get $l8812 + local.get $l8813 + i32.add + local.set $l8814 + local.get $l8813 + local.get $l8814 + i32.add + local.set $l8815 + local.get $l8814 + local.get $l8815 + i32.add + local.set $l8816 + local.get $l8815 + local.get $l8816 + i32.add + local.set $l8817 + local.get $l8816 + local.get $l8817 + i32.add + local.set $l8818 + local.get $l8817 + local.get $l8818 + i32.add + local.set $l8819 + local.get $l8818 + local.get $l8819 + i32.add + local.set $l8820 + local.get $l8819 + local.get $l8820 + i32.add + local.set $l8821 + local.get $l8820 + local.get $l8821 + i32.add + local.set $l8822 + local.get $l8821 + local.get $l8822 + i32.add + local.set $l8823 + local.get $l8822 + local.get $l8823 + i32.add + local.set $l8824 + local.get $l8823 + local.get $l8824 + i32.add + local.set $l8825 + local.get $l8824 + local.get $l8825 + i32.add + local.set $l8826 + local.get $l8825 + local.get $l8826 + i32.add + local.set $l8827 + local.get $l8826 + local.get $l8827 + i32.add + local.set $l8828 + local.get $l8827 + local.get $l8828 + i32.add + local.set $l8829 + local.get $l8828 + local.get $l8829 + i32.add + local.set $l8830 + local.get $l8829 + local.get $l8830 + i32.add + local.set $l8831 + local.get $l8830 + local.get $l8831 + i32.add + local.set $l8832 + local.get $l8831 + local.get $l8832 + i32.add + local.set $l8833 + local.get $l8832 + local.get $l8833 + i32.add + local.set $l8834 + local.get $l8833 + local.get $l8834 + i32.add + local.set $l8835 + local.get $l8834 + local.get $l8835 + i32.add + local.set $l8836 + local.get $l8835 + local.get $l8836 + i32.add + local.set $l8837 + local.get $l8836 + local.get $l8837 + i32.add + local.set $l8838 + local.get $l8837 + local.get $l8838 + i32.add + local.set $l8839 + local.get $l8838 + local.get $l8839 + i32.add + local.set $l8840 + local.get $l8839 + local.get $l8840 + i32.add + local.set $l8841 + local.get $l8840 + local.get $l8841 + i32.add + local.set $l8842 + local.get $l8841 + local.get $l8842 + i32.add + local.set $l8843 + local.get $l8842 + local.get $l8843 + i32.add + local.set $l8844 + local.get $l8843 + local.get $l8844 + i32.add + local.set $l8845 + local.get $l8844 + local.get $l8845 + i32.add + local.set $l8846 + local.get $l8845 + local.get $l8846 + i32.add + local.set $l8847 + local.get $l8846 + local.get $l8847 + i32.add + local.set $l8848 + local.get $l8847 + local.get $l8848 + i32.add + local.set $l8849 + local.get $l8848 + local.get $l8849 + i32.add + local.set $l8850 + local.get $l8849 + local.get $l8850 + i32.add + local.set $l8851 + local.get $l8850 + local.get $l8851 + i32.add + local.set $l8852 + local.get $l8851 + local.get $l8852 + i32.add + local.set $l8853 + local.get $l8852 + local.get $l8853 + i32.add + local.set $l8854 + local.get $l8853 + local.get $l8854 + i32.add + local.set $l8855 + local.get $l8854 + local.get $l8855 + i32.add + local.set $l8856 + local.get $l8855 + local.get $l8856 + i32.add + local.set $l8857 + local.get $l8856 + local.get $l8857 + i32.add + local.set $l8858 + local.get $l8857 + local.get $l8858 + i32.add + local.set $l8859 + local.get $l8858 + local.get $l8859 + i32.add + local.set $l8860 + local.get $l8859 + local.get $l8860 + i32.add + local.set $l8861 + local.get $l8860 + local.get $l8861 + i32.add + local.set $l8862 + local.get $l8861 + local.get $l8862 + i32.add + local.set $l8863 + local.get $l8862 + local.get $l8863 + i32.add + local.set $l8864 + local.get $l8863 + local.get $l8864 + i32.add + local.set $l8865 + local.get $l8864 + local.get $l8865 + i32.add + local.set $l8866 + local.get $l8865 + local.get $l8866 + i32.add + local.set $l8867 + local.get $l8866 + local.get $l8867 + i32.add + local.set $l8868 + local.get $l8867 + local.get $l8868 + i32.add + local.set $l8869 + local.get $l8868 + local.get $l8869 + i32.add + local.set $l8870 + local.get $l8869 + local.get $l8870 + i32.add + local.set $l8871 + local.get $l8870 + local.get $l8871 + i32.add + local.set $l8872 + local.get $l8871 + local.get $l8872 + i32.add + local.set $l8873 + local.get $l8872 + local.get $l8873 + i32.add + local.set $l8874 + local.get $l8873 + local.get $l8874 + i32.add + local.set $l8875 + local.get $l8874 + local.get $l8875 + i32.add + local.set $l8876 + local.get $l8875 + local.get $l8876 + i32.add + local.set $l8877 + local.get $l8876 + local.get $l8877 + i32.add + local.set $l8878 + local.get $l8877 + local.get $l8878 + i32.add + local.set $l8879 + local.get $l8878 + local.get $l8879 + i32.add + local.set $l8880 + local.get $l8879 + local.get $l8880 + i32.add + local.set $l8881 + local.get $l8880 + local.get $l8881 + i32.add + local.set $l8882 + local.get $l8881 + local.get $l8882 + i32.add + local.set $l8883 + local.get $l8882 + local.get $l8883 + i32.add + local.set $l8884 + local.get $l8883 + local.get $l8884 + i32.add + local.set $l8885 + local.get $l8884 + local.get $l8885 + i32.add + local.set $l8886 + local.get $l8885 + local.get $l8886 + i32.add + local.set $l8887 + local.get $l8886 + local.get $l8887 + i32.add + local.set $l8888 + local.get $l8887 + local.get $l8888 + i32.add + local.set $l8889 + local.get $l8888 + local.get $l8889 + i32.add + local.set $l8890 + local.get $l8889 + local.get $l8890 + i32.add + local.set $l8891 + local.get $l8890 + local.get $l8891 + i32.add + local.set $l8892 + local.get $l8891 + local.get $l8892 + i32.add + local.set $l8893 + local.get $l8892 + local.get $l8893 + i32.add + local.set $l8894 + local.get $l8893 + local.get $l8894 + i32.add + local.set $l8895 + local.get $l8894 + local.get $l8895 + i32.add + local.set $l8896 + local.get $l8895 + local.get $l8896 + i32.add + local.set $l8897 + local.get $l8896 + local.get $l8897 + i32.add + local.set $l8898 + local.get $l8897 + local.get $l8898 + i32.add + local.set $l8899 + local.get $l8898 + local.get $l8899 + i32.add + local.set $l8900 + local.get $l8899 + local.get $l8900 + i32.add + local.set $l8901 + local.get $l8900 + local.get $l8901 + i32.add + local.set $l8902 + local.get $l8901 + local.get $l8902 + i32.add + local.set $l8903 + local.get $l8902 + local.get $l8903 + i32.add + local.set $l8904 + local.get $l8903 + local.get $l8904 + i32.add + local.set $l8905 + local.get $l8904 + local.get $l8905 + i32.add + local.set $l8906 + local.get $l8905 + local.get $l8906 + i32.add + local.set $l8907 + local.get $l8906 + local.get $l8907 + i32.add + local.set $l8908 + local.get $l8907 + local.get $l8908 + i32.add + local.set $l8909 + local.get $l8908 + local.get $l8909 + i32.add + local.set $l8910 + local.get $l8909 + local.get $l8910 + i32.add + local.set $l8911 + local.get $l8910 + local.get $l8911 + i32.add + local.set $l8912 + local.get $l8911 + local.get $l8912 + i32.add + local.set $l8913 + local.get $l8912 + local.get $l8913 + i32.add + local.set $l8914 + local.get $l8913 + local.get $l8914 + i32.add + local.set $l8915 + local.get $l8914 + local.get $l8915 + i32.add + local.set $l8916 + local.get $l8915 + local.get $l8916 + i32.add + local.set $l8917 + local.get $l8916 + local.get $l8917 + i32.add + local.set $l8918 + local.get $l8917 + local.get $l8918 + i32.add + local.set $l8919 + local.get $l8918 + local.get $l8919 + i32.add + local.set $l8920 + local.get $l8919 + local.get $l8920 + i32.add + local.set $l8921 + local.get $l8920 + local.get $l8921 + i32.add + local.set $l8922 + local.get $l8921 + local.get $l8922 + i32.add + local.set $l8923 + local.get $l8922 + local.get $l8923 + i32.add + local.set $l8924 + local.get $l8923 + local.get $l8924 + i32.add + local.set $l8925 + local.get $l8924 + local.get $l8925 + i32.add + local.set $l8926 + local.get $l8925 + local.get $l8926 + i32.add + local.set $l8927 + local.get $l8926 + local.get $l8927 + i32.add + local.set $l8928 + local.get $l8927 + local.get $l8928 + i32.add + local.set $l8929 + local.get $l8928 + local.get $l8929 + i32.add + local.set $l8930 + local.get $l8929 + local.get $l8930 + i32.add + local.set $l8931 + local.get $l8930 + local.get $l8931 + i32.add + local.set $l8932 + local.get $l8931 + local.get $l8932 + i32.add + local.set $l8933 + local.get $l8932 + local.get $l8933 + i32.add + local.set $l8934 + local.get $l8933 + local.get $l8934 + i32.add + local.set $l8935 + local.get $l8934 + local.get $l8935 + i32.add + local.set $l8936 + local.get $l8935 + local.get $l8936 + i32.add + local.set $l8937 + local.get $l8936 + local.get $l8937 + i32.add + local.set $l8938 + local.get $l8937 + local.get $l8938 + i32.add + local.set $l8939 + local.get $l8938 + local.get $l8939 + i32.add + local.set $l8940 + local.get $l8939 + local.get $l8940 + i32.add + local.set $l8941 + local.get $l8940 + local.get $l8941 + i32.add + local.set $l8942 + local.get $l8941 + local.get $l8942 + i32.add + local.set $l8943 + local.get $l8942 + local.get $l8943 + i32.add + local.set $l8944 + local.get $l8943 + local.get $l8944 + i32.add + local.set $l8945 + local.get $l8944 + local.get $l8945 + i32.add + local.set $l8946 + local.get $l8945 + local.get $l8946 + i32.add + local.set $l8947 + local.get $l8946 + local.get $l8947 + i32.add + local.set $l8948 + local.get $l8947 + local.get $l8948 + i32.add + local.set $l8949 + local.get $l8948 + local.get $l8949 + i32.add + local.set $l8950 + local.get $l8949 + local.get $l8950 + i32.add + local.set $l8951 + local.get $l8950 + local.get $l8951 + i32.add + local.set $l8952 + local.get $l8951 + local.get $l8952 + i32.add + local.set $l8953 + local.get $l8952 + local.get $l8953 + i32.add + local.set $l8954 + local.get $l8953 + local.get $l8954 + i32.add + local.set $l8955 + local.get $l8954 + local.get $l8955 + i32.add + local.set $l8956 + local.get $l8955 + local.get $l8956 + i32.add + local.set $l8957 + local.get $l8956 + local.get $l8957 + i32.add + local.set $l8958 + local.get $l8957 + local.get $l8958 + i32.add + local.set $l8959 + local.get $l8958 + local.get $l8959 + i32.add + local.set $l8960 + local.get $l8959 + local.get $l8960 + i32.add + local.set $l8961 + local.get $l8960 + local.get $l8961 + i32.add + local.set $l8962 + local.get $l8961 + local.get $l8962 + i32.add + local.set $l8963 + local.get $l8962 + local.get $l8963 + i32.add + local.set $l8964 + local.get $l8963 + local.get $l8964 + i32.add + local.set $l8965 + local.get $l8964 + local.get $l8965 + i32.add + local.set $l8966 + local.get $l8965 + local.get $l8966 + i32.add + local.set $l8967 + local.get $l8966 + local.get $l8967 + i32.add + local.set $l8968 + local.get $l8967 + local.get $l8968 + i32.add + local.set $l8969 + local.get $l8968 + local.get $l8969 + i32.add + local.set $l8970 + local.get $l8969 + local.get $l8970 + i32.add + local.set $l8971 + local.get $l8970 + local.get $l8971 + i32.add + local.set $l8972 + local.get $l8971 + local.get $l8972 + i32.add + local.set $l8973 + local.get $l8972 + local.get $l8973 + i32.add + local.set $l8974 + local.get $l8973 + local.get $l8974 + i32.add + local.set $l8975 + local.get $l8974 + local.get $l8975 + i32.add + local.set $l8976 + local.get $l8975 + local.get $l8976 + i32.add + local.set $l8977 + local.get $l8976 + local.get $l8977 + i32.add + local.set $l8978 + local.get $l8977 + local.get $l8978 + i32.add + local.set $l8979 + local.get $l8978 + local.get $l8979 + i32.add + local.set $l8980 + local.get $l8979 + local.get $l8980 + i32.add + local.set $l8981 + local.get $l8980 + local.get $l8981 + i32.add + local.set $l8982 + local.get $l8981 + local.get $l8982 + i32.add + local.set $l8983 + local.get $l8982 + local.get $l8983 + i32.add + local.set $l8984 + local.get $l8983 + local.get $l8984 + i32.add + local.set $l8985 + local.get $l8984 + local.get $l8985 + i32.add + local.set $l8986 + local.get $l8985 + local.get $l8986 + i32.add + local.set $l8987 + local.get $l8986 + local.get $l8987 + i32.add + local.set $l8988 + local.get $l8987 + local.get $l8988 + i32.add + local.set $l8989 + local.get $l8988 + local.get $l8989 + i32.add + local.set $l8990 + local.get $l8989 + local.get $l8990 + i32.add + local.set $l8991 + local.get $l8990 + local.get $l8991 + i32.add + local.set $l8992 + local.get $l8991 + local.get $l8992 + i32.add + local.set $l8993 + local.get $l8992 + local.get $l8993 + i32.add + local.set $l8994 + local.get $l8993 + local.get $l8994 + i32.add + local.set $l8995 + local.get $l8994 + local.get $l8995 + i32.add + local.set $l8996 + local.get $l8995 + local.get $l8996 + i32.add + local.set $l8997 + local.get $l8996 + local.get $l8997 + i32.add + local.set $l8998 + local.get $l8997 + local.get $l8998 + i32.add + local.set $l8999 + local.get $l8998 + local.get $l8999 + i32.add + local.set $l9000 + local.get $l8999 + local.get $l9000 + i32.add + local.set $l9001 + local.get $l9000 + local.get $l9001 + i32.add + local.set $l9002 + local.get $l9001 + local.get $l9002 + i32.add + local.set $l9003 + local.get $l9002 + local.get $l9003 + i32.add + local.set $l9004 + local.get $l9003 + local.get $l9004 + i32.add + local.set $l9005 + local.get $l9004 + local.get $l9005 + i32.add + local.set $l9006 + local.get $l9005 + local.get $l9006 + i32.add + local.set $l9007 + local.get $l9006 + local.get $l9007 + i32.add + local.set $l9008 + local.get $l9007 + local.get $l9008 + i32.add + local.set $l9009 + local.get $l9008 + local.get $l9009 + i32.add + local.set $l9010 + local.get $l9009 + local.get $l9010 + i32.add + local.set $l9011 + local.get $l9010 + local.get $l9011 + i32.add + local.set $l9012 + local.get $l9011 + local.get $l9012 + i32.add + local.set $l9013 + local.get $l9012 + local.get $l9013 + i32.add + local.set $l9014 + local.get $l9013 + local.get $l9014 + i32.add + local.set $l9015 + local.get $l9014 + local.get $l9015 + i32.add + local.set $l9016 + local.get $l9015 + local.get $l9016 + i32.add + local.set $l9017 + local.get $l9016 + local.get $l9017 + i32.add + local.set $l9018 + local.get $l9017 + local.get $l9018 + i32.add + local.set $l9019 + local.get $l9018 + local.get $l9019 + i32.add + local.set $l9020 + local.get $l9019 + local.get $l9020 + i32.add + local.set $l9021 + local.get $l9020 + local.get $l9021 + i32.add + local.set $l9022 + local.get $l9021 + local.get $l9022 + i32.add + local.set $l9023 + local.get $l9022 + local.get $l9023 + i32.add + local.set $l9024 + local.get $l9023 + local.get $l9024 + i32.add + local.set $l9025 + local.get $l9024 + local.get $l9025 + i32.add + local.set $l9026 + local.get $l9025 + local.get $l9026 + i32.add + local.set $l9027 + local.get $l9026 + local.get $l9027 + i32.add + local.set $l9028 + local.get $l9027 + local.get $l9028 + i32.add + local.set $l9029 + local.get $l9028 + local.get $l9029 + i32.add + local.set $l9030 + local.get $l9029 + local.get $l9030 + i32.add + local.set $l9031 + local.get $l9030 + local.get $l9031 + i32.add + local.set $l9032 + local.get $l9031 + local.get $l9032 + i32.add + local.set $l9033 + local.get $l9032 + local.get $l9033 + i32.add + local.set $l9034 + local.get $l9033 + local.get $l9034 + i32.add + local.set $l9035 + local.get $l9034 + local.get $l9035 + i32.add + local.set $l9036 + local.get $l9035 + local.get $l9036 + i32.add + local.set $l9037 + local.get $l9036 + local.get $l9037 + i32.add + local.set $l9038 + local.get $l9037 + local.get $l9038 + i32.add + local.set $l9039 + local.get $l9038 + local.get $l9039 + i32.add + local.set $l9040 + local.get $l9039 + local.get $l9040 + i32.add + local.set $l9041 + local.get $l9040 + local.get $l9041 + i32.add + local.set $l9042 + local.get $l9041 + local.get $l9042 + i32.add + local.set $l9043 + local.get $l9042 + local.get $l9043 + i32.add + local.set $l9044 + local.get $l9043 + local.get $l9044 + i32.add + local.set $l9045 + local.get $l9044 + local.get $l9045 + i32.add + local.set $l9046 + local.get $l9045 + local.get $l9046 + i32.add + local.set $l9047 + local.get $l9046 + local.get $l9047 + i32.add + local.set $l9048 + local.get $l9047 + local.get $l9048 + i32.add + local.set $l9049 + local.get $l9048 + local.get $l9049 + i32.add + local.set $l9050 + local.get $l9049 + local.get $l9050 + i32.add + local.set $l9051 + local.get $l9050 + local.get $l9051 + i32.add + local.set $l9052 + local.get $l9051 + local.get $l9052 + i32.add + local.set $l9053 + local.get $l9052 + local.get $l9053 + i32.add + local.set $l9054 + local.get $l9053 + local.get $l9054 + i32.add + local.set $l9055 + local.get $l9054 + local.get $l9055 + i32.add + local.set $l9056 + local.get $l9055 + local.get $l9056 + i32.add + local.set $l9057 + local.get $l9056 + local.get $l9057 + i32.add + local.set $l9058 + local.get $l9057 + local.get $l9058 + i32.add + local.set $l9059 + local.get $l9058 + local.get $l9059 + i32.add + local.set $l9060 + local.get $l9059 + local.get $l9060 + i32.add + local.set $l9061 + local.get $l9060 + local.get $l9061 + i32.add + local.set $l9062 + local.get $l9061 + local.get $l9062 + i32.add + local.set $l9063 + local.get $l9062 + local.get $l9063 + i32.add + local.set $l9064 + local.get $l9063 + local.get $l9064 + i32.add + local.set $l9065 + local.get $l9064 + local.get $l9065 + i32.add + local.set $l9066 + local.get $l9065 + local.get $l9066 + i32.add + local.set $l9067 + local.get $l9066 + local.get $l9067 + i32.add + local.set $l9068 + local.get $l9067 + local.get $l9068 + i32.add + local.set $l9069 + local.get $l9068 + local.get $l9069 + i32.add + local.set $l9070 + local.get $l9069 + local.get $l9070 + i32.add + local.set $l9071 + local.get $l9070 + local.get $l9071 + i32.add + local.set $l9072 + local.get $l9071 + local.get $l9072 + i32.add + local.set $l9073 + local.get $l9072 + local.get $l9073 + i32.add + local.set $l9074 + local.get $l9073 + local.get $l9074 + i32.add + local.set $l9075 + local.get $l9074 + local.get $l9075 + i32.add + local.set $l9076 + local.get $l9075 + local.get $l9076 + i32.add + local.set $l9077 + local.get $l9076 + local.get $l9077 + i32.add + local.set $l9078 + local.get $l9077 + local.get $l9078 + i32.add + local.set $l9079 + local.get $l9078 + local.get $l9079 + i32.add + local.set $l9080 + local.get $l9079 + local.get $l9080 + i32.add + local.set $l9081 + local.get $l9080 + local.get $l9081 + i32.add + local.set $l9082 + local.get $l9081 + local.get $l9082 + i32.add + local.set $l9083 + local.get $l9082 + local.get $l9083 + i32.add + local.set $l9084 + local.get $l9083 + local.get $l9084 + i32.add + local.set $l9085 + local.get $l9084 + local.get $l9085 + i32.add + local.set $l9086 + local.get $l9085 + local.get $l9086 + i32.add + local.set $l9087 + local.get $l9086 + local.get $l9087 + i32.add + local.set $l9088 + local.get $l9087 + local.get $l9088 + i32.add + local.set $l9089 + local.get $l9088 + local.get $l9089 + i32.add + local.set $l9090 + local.get $l9089 + local.get $l9090 + i32.add + local.set $l9091 + local.get $l9090 + local.get $l9091 + i32.add + local.set $l9092 + local.get $l9091 + local.get $l9092 + i32.add + local.set $l9093 + local.get $l9092 + local.get $l9093 + i32.add + local.set $l9094 + local.get $l9093 + local.get $l9094 + i32.add + local.set $l9095 + local.get $l9094 + local.get $l9095 + i32.add + local.set $l9096 + local.get $l9095 + local.get $l9096 + i32.add + local.set $l9097 + local.get $l9096 + local.get $l9097 + i32.add + local.set $l9098 + local.get $l9097 + local.get $l9098 + i32.add + local.set $l9099 + local.get $l9098 + local.get $l9099 + i32.add + local.set $l9100 + local.get $l9099 + local.get $l9100 + i32.add + local.set $l9101 + local.get $l9100 + local.get $l9101 + i32.add + local.set $l9102 + local.get $l9101 + local.get $l9102 + i32.add + local.set $l9103 + local.get $l9102 + local.get $l9103 + i32.add + local.set $l9104 + local.get $l9103 + local.get $l9104 + i32.add + local.set $l9105 + local.get $l9104 + local.get $l9105 + i32.add + local.set $l9106 + local.get $l9105 + local.get $l9106 + i32.add + local.set $l9107 + local.get $l9106 + local.get $l9107 + i32.add + local.set $l9108 + local.get $l9107 + local.get $l9108 + i32.add + local.set $l9109 + local.get $l9108 + local.get $l9109 + i32.add + local.set $l9110 + local.get $l9109 + local.get $l9110 + i32.add + local.set $l9111 + local.get $l9110 + local.get $l9111 + i32.add + local.set $l9112 + local.get $l9111 + local.get $l9112 + i32.add + local.set $l9113 + local.get $l9112 + local.get $l9113 + i32.add + local.set $l9114 + local.get $l9113 + local.get $l9114 + i32.add + local.set $l9115 + local.get $l9114 + local.get $l9115 + i32.add + local.set $l9116 + local.get $l9115 + local.get $l9116 + i32.add + local.set $l9117 + local.get $l9116 + local.get $l9117 + i32.add + local.set $l9118 + local.get $l9117 + local.get $l9118 + i32.add + local.set $l9119 + local.get $l9118 + local.get $l9119 + i32.add + local.set $l9120 + local.get $l9119 + local.get $l9120 + i32.add + local.set $l9121 + local.get $l9120 + local.get $l9121 + i32.add + local.set $l9122 + local.get $l9121 + local.get $l9122 + i32.add + local.set $l9123 + local.get $l9122 + local.get $l9123 + i32.add + local.set $l9124 + local.get $l9123 + local.get $l9124 + i32.add + local.set $l9125 + local.get $l9124 + local.get $l9125 + i32.add + local.set $l9126 + local.get $l9125 + local.get $l9126 + i32.add + local.set $l9127 + local.get $l9126 + local.get $l9127 + i32.add + local.set $l9128 + local.get $l9127 + local.get $l9128 + i32.add + local.set $l9129 + local.get $l9128 + local.get $l9129 + i32.add + local.set $l9130 + local.get $l9129 + local.get $l9130 + i32.add + local.set $l9131 + local.get $l9130 + local.get $l9131 + i32.add + local.set $l9132 + local.get $l9131 + local.get $l9132 + i32.add + local.set $l9133 + local.get $l9132 + local.get $l9133 + i32.add + local.set $l9134 + local.get $l9133 + local.get $l9134 + i32.add + local.set $l9135 + local.get $l9134 + local.get $l9135 + i32.add + local.set $l9136 + local.get $l9135 + local.get $l9136 + i32.add + local.set $l9137 + local.get $l9136 + local.get $l9137 + i32.add + local.set $l9138 + local.get $l9137 + local.get $l9138 + i32.add + local.set $l9139 + local.get $l9138 + local.get $l9139 + i32.add + local.set $l9140 + local.get $l9139 + local.get $l9140 + i32.add + local.set $l9141 + local.get $l9140 + local.get $l9141 + i32.add + local.set $l9142 + local.get $l9141 + local.get $l9142 + i32.add + local.set $l9143 + local.get $l9142 + local.get $l9143 + i32.add + local.set $l9144 + local.get $l9143 + local.get $l9144 + i32.add + local.set $l9145 + local.get $l9144 + local.get $l9145 + i32.add + local.set $l9146 + local.get $l9145 + local.get $l9146 + i32.add + local.set $l9147 + local.get $l9146 + local.get $l9147 + i32.add + local.set $l9148 + local.get $l9147 + local.get $l9148 + i32.add + local.set $l9149 + local.get $l9148 + local.get $l9149 + i32.add + local.set $l9150 + local.get $l9149 + local.get $l9150 + i32.add + local.set $l9151 + local.get $l9150 + local.get $l9151 + i32.add + local.set $l9152 + local.get $l9151 + local.get $l9152 + i32.add + local.set $l9153 + local.get $l9152 + local.get $l9153 + i32.add + local.set $l9154 + local.get $l9153 + local.get $l9154 + i32.add + local.set $l9155 + local.get $l9154 + local.get $l9155 + i32.add + local.set $l9156 + local.get $l9155 + local.get $l9156 + i32.add + local.set $l9157 + local.get $l9156 + local.get $l9157 + i32.add + local.set $l9158 + local.get $l9157 + local.get $l9158 + i32.add + local.set $l9159 + local.get $l9158 + local.get $l9159 + i32.add + local.set $l9160 + local.get $l9159 + local.get $l9160 + i32.add + local.set $l9161 + local.get $l9160 + local.get $l9161 + i32.add + local.set $l9162 + local.get $l9161 + local.get $l9162 + i32.add + local.set $l9163 + local.get $l9162 + local.get $l9163 + i32.add + local.set $l9164 + local.get $l9163 + local.get $l9164 + i32.add + local.set $l9165 + local.get $l9164 + local.get $l9165 + i32.add + local.set $l9166 + local.get $l9165 + local.get $l9166 + i32.add + local.set $l9167 + local.get $l9166 + local.get $l9167 + i32.add + local.set $l9168 + local.get $l9167 + local.get $l9168 + i32.add + local.set $l9169 + local.get $l9168 + local.get $l9169 + i32.add + local.set $l9170 + local.get $l9169 + local.get $l9170 + i32.add + local.set $l9171 + local.get $l9170 + local.get $l9171 + i32.add + local.set $l9172 + local.get $l9171 + local.get $l9172 + i32.add + local.set $l9173 + local.get $l9172 + local.get $l9173 + i32.add + local.set $l9174 + local.get $l9173 + local.get $l9174 + i32.add + local.set $l9175 + local.get $l9174 + local.get $l9175 + i32.add + local.set $l9176 + local.get $l9175 + local.get $l9176 + i32.add + local.set $l9177 + local.get $l9176 + local.get $l9177 + i32.add + local.set $l9178 + local.get $l9177 + local.get $l9178 + i32.add + local.set $l9179 + local.get $l9178 + local.get $l9179 + i32.add + local.set $l9180 + local.get $l9179 + local.get $l9180 + i32.add + local.set $l9181 + local.get $l9180 + local.get $l9181 + i32.add + local.set $l9182 + local.get $l9181 + local.get $l9182 + i32.add + local.set $l9183 + local.get $l9182 + local.get $l9183 + i32.add + local.set $l9184 + local.get $l9183 + local.get $l9184 + i32.add + local.set $l9185 + local.get $l9184 + local.get $l9185 + i32.add + local.set $l9186 + local.get $l9185 + local.get $l9186 + i32.add + local.set $l9187 + local.get $l9186 + local.get $l9187 + i32.add + local.set $l9188 + local.get $l9187 + local.get $l9188 + i32.add + local.set $l9189 + local.get $l9188 + local.get $l9189 + i32.add + local.set $l9190 + local.get $l9189 + local.get $l9190 + i32.add + local.set $l9191 + local.get $l9190 + local.get $l9191 + i32.add + local.set $l9192 + local.get $l9191 + local.get $l9192 + i32.add + local.set $l9193 + local.get $l9192 + local.get $l9193 + i32.add + local.set $l9194 + local.get $l9193 + local.get $l9194 + i32.add + local.set $l9195 + local.get $l9194 + local.get $l9195 + i32.add + local.set $l9196 + local.get $l9195 + local.get $l9196 + i32.add + local.set $l9197 + local.get $l9196 + local.get $l9197 + i32.add + local.set $l9198 + local.get $l9197 + local.get $l9198 + i32.add + local.set $l9199 + local.get $l9198 + local.get $l9199 + i32.add + local.set $l9200 + local.get $l9199 + local.get $l9200 + i32.add + local.set $l9201 + local.get $l9200 + local.get $l9201 + i32.add + local.set $l9202 + local.get $l9201 + local.get $l9202 + i32.add + local.set $l9203 + local.get $l9202 + local.get $l9203 + i32.add + local.set $l9204 + local.get $l9203 + local.get $l9204 + i32.add + local.set $l9205 + local.get $l9204 + local.get $l9205 + i32.add + local.set $l9206 + local.get $l9205 + local.get $l9206 + i32.add + local.set $l9207 + local.get $l9206 + local.get $l9207 + i32.add + local.set $l9208 + local.get $l9207 + local.get $l9208 + i32.add + local.set $l9209 + local.get $l9208 + local.get $l9209 + i32.add + local.set $l9210 + local.get $l9209 + local.get $l9210 + i32.add + local.set $l9211 + local.get $l9210 + local.get $l9211 + i32.add + local.set $l9212 + local.get $l9211 + local.get $l9212 + i32.add + local.set $l9213 + local.get $l9212 + local.get $l9213 + i32.add + local.set $l9214 + local.get $l9213 + local.get $l9214 + i32.add + local.set $l9215 + local.get $l9214 + local.get $l9215 + i32.add + local.set $l9216 + local.get $l9215 + local.get $l9216 + i32.add + local.set $l9217 + local.get $l9216 + local.get $l9217 + i32.add + local.set $l9218 + local.get $l9217 + local.get $l9218 + i32.add + local.set $l9219 + local.get $l9218 + local.get $l9219 + i32.add + local.set $l9220 + local.get $l9219 + local.get $l9220 + i32.add + local.set $l9221 + local.get $l9220 + local.get $l9221 + i32.add + local.set $l9222 + local.get $l9221 + local.get $l9222 + i32.add + local.set $l9223 + local.get $l9222 + local.get $l9223 + i32.add + local.set $l9224 + local.get $l9223 + local.get $l9224 + i32.add + local.set $l9225 + local.get $l9224 + local.get $l9225 + i32.add + local.set $l9226 + local.get $l9225 + local.get $l9226 + i32.add + local.set $l9227 + local.get $l9226 + local.get $l9227 + i32.add + local.set $l9228 + local.get $l9227 + local.get $l9228 + i32.add + local.set $l9229 + local.get $l9228 + local.get $l9229 + i32.add + local.set $l9230 + local.get $l9229 + local.get $l9230 + i32.add + local.set $l9231 + local.get $l9230 + local.get $l9231 + i32.add + local.set $l9232 + local.get $l9231 + local.get $l9232 + i32.add + local.set $l9233 + local.get $l9232 + local.get $l9233 + i32.add + local.set $l9234 + local.get $l9233 + local.get $l9234 + i32.add + local.set $l9235 + local.get $l9234 + local.get $l9235 + i32.add + local.set $l9236 + local.get $l9235 + local.get $l9236 + i32.add + local.set $l9237 + local.get $l9236 + local.get $l9237 + i32.add + local.set $l9238 + local.get $l9237 + local.get $l9238 + i32.add + local.set $l9239 + local.get $l9238 + local.get $l9239 + i32.add + local.set $l9240 + local.get $l9239 + local.get $l9240 + i32.add + local.set $l9241 + local.get $l9240 + local.get $l9241 + i32.add + local.set $l9242 + local.get $l9241 + local.get $l9242 + i32.add + local.set $l9243 + local.get $l9242 + local.get $l9243 + i32.add + local.set $l9244 + local.get $l9243 + local.get $l9244 + i32.add + local.set $l9245 + local.get $l9244 + local.get $l9245 + i32.add + local.set $l9246 + local.get $l9245 + local.get $l9246 + i32.add + local.set $l9247 + local.get $l9246 + local.get $l9247 + i32.add + local.set $l9248 + local.get $l9247 + local.get $l9248 + i32.add + local.set $l9249 + local.get $l9248 + local.get $l9249 + i32.add + local.set $l9250 + local.get $l9249 + local.get $l9250 + i32.add + local.set $l9251 + local.get $l9250 + local.get $l9251 + i32.add + local.set $l9252 + local.get $l9251 + local.get $l9252 + i32.add + local.set $l9253 + local.get $l9252 + local.get $l9253 + i32.add + local.set $l9254 + local.get $l9253 + local.get $l9254 + i32.add + local.set $l9255 + local.get $l9254 + local.get $l9255 + i32.add + local.set $l9256 + local.get $l9255 + local.get $l9256 + i32.add + local.set $l9257 + local.get $l9256 + local.get $l9257 + i32.add + local.set $l9258 + local.get $l9257 + local.get $l9258 + i32.add + local.set $l9259 + local.get $l9258 + local.get $l9259 + i32.add + local.set $l9260 + local.get $l9259 + local.get $l9260 + i32.add + local.set $l9261 + local.get $l9260 + local.get $l9261 + i32.add + local.set $l9262 + local.get $l9261 + local.get $l9262 + i32.add + local.set $l9263 + local.get $l9262 + local.get $l9263 + i32.add + local.set $l9264 + local.get $l9263 + local.get $l9264 + i32.add + local.set $l9265 + local.get $l9264 + local.get $l9265 + i32.add + local.set $l9266 + local.get $l9265 + local.get $l9266 + i32.add + local.set $l9267 + local.get $l9266 + local.get $l9267 + i32.add + local.set $l9268 + local.get $l9267 + local.get $l9268 + i32.add + local.set $l9269 + local.get $l9268 + local.get $l9269 + i32.add + local.set $l9270 + local.get $l9269 + local.get $l9270 + i32.add + local.set $l9271 + local.get $l9270 + local.get $l9271 + i32.add + local.set $l9272 + local.get $l9271 + local.get $l9272 + i32.add + local.set $l9273 + local.get $l9272 + local.get $l9273 + i32.add + local.set $l9274 + local.get $l9273 + local.get $l9274 + i32.add + local.set $l9275 + local.get $l9274 + local.get $l9275 + i32.add + local.set $l9276 + local.get $l9275 + local.get $l9276 + i32.add + local.set $l9277 + local.get $l9276 + local.get $l9277 + i32.add + local.set $l9278 + local.get $l9277 + local.get $l9278 + i32.add + local.set $l9279 + local.get $l9278 + local.get $l9279 + i32.add + local.set $l9280 + local.get $l9279 + local.get $l9280 + i32.add + local.set $l9281 + local.get $l9280 + local.get $l9281 + i32.add + local.set $l9282 + local.get $l9281 + local.get $l9282 + i32.add + local.set $l9283 + local.get $l9282 + local.get $l9283 + i32.add + local.set $l9284 + local.get $l9283 + local.get $l9284 + i32.add + local.set $l9285 + local.get $l9284 + local.get $l9285 + i32.add + local.set $l9286 + local.get $l9285 + local.get $l9286 + i32.add + local.set $l9287 + local.get $l9286 + local.get $l9287 + i32.add + local.set $l9288 + local.get $l9287 + local.get $l9288 + i32.add + local.set $l9289 + local.get $l9288 + local.get $l9289 + i32.add + local.set $l9290 + local.get $l9289 + local.get $l9290 + i32.add + local.set $l9291 + local.get $l9290 + local.get $l9291 + i32.add + local.set $l9292 + local.get $l9291 + local.get $l9292 + i32.add + local.set $l9293 + local.get $l9292 + local.get $l9293 + i32.add + local.set $l9294 + local.get $l9293 + local.get $l9294 + i32.add + local.set $l9295 + local.get $l9294 + local.get $l9295 + i32.add + local.set $l9296 + local.get $l9295 + local.get $l9296 + i32.add + local.set $l9297 + local.get $l9296 + local.get $l9297 + i32.add + local.set $l9298 + local.get $l9297 + local.get $l9298 + i32.add + local.set $l9299 + local.get $l9298 + local.get $l9299 + i32.add + local.set $l9300 + local.get $l9299 + local.get $l9300 + i32.add + local.set $l9301 + local.get $l9300 + local.get $l9301 + i32.add + local.set $l9302 + local.get $l9301 + local.get $l9302 + i32.add + local.set $l9303 + local.get $l9302 + local.get $l9303 + i32.add + local.set $l9304 + local.get $l9303 + local.get $l9304 + i32.add + local.set $l9305 + local.get $l9304 + local.get $l9305 + i32.add + local.set $l9306 + local.get $l9305 + local.get $l9306 + i32.add + local.set $l9307 + local.get $l9306 + local.get $l9307 + i32.add + local.set $l9308 + local.get $l9307 + local.get $l9308 + i32.add + local.set $l9309 + local.get $l9308 + local.get $l9309 + i32.add + local.set $l9310 + local.get $l9309 + local.get $l9310 + i32.add + local.set $l9311 + local.get $l9310 + local.get $l9311 + i32.add + local.set $l9312 + local.get $l9311 + local.get $l9312 + i32.add + local.set $l9313 + local.get $l9312 + local.get $l9313 + i32.add + local.set $l9314 + local.get $l9313 + local.get $l9314 + i32.add + local.set $l9315 + local.get $l9314 + local.get $l9315 + i32.add + local.set $l9316 + local.get $l9315 + local.get $l9316 + i32.add + local.set $l9317 + local.get $l9316 + local.get $l9317 + i32.add + local.set $l9318 + local.get $l9317 + local.get $l9318 + i32.add + local.set $l9319 + local.get $l9318 + local.get $l9319 + i32.add + local.set $l9320 + local.get $l9319 + local.get $l9320 + i32.add + local.set $l9321 + local.get $l9320 + local.get $l9321 + i32.add + local.set $l9322 + local.get $l9321 + local.get $l9322 + i32.add + local.set $l9323 + local.get $l9322 + local.get $l9323 + i32.add + local.set $l9324 + local.get $l9323 + local.get $l9324 + i32.add + local.set $l9325 + local.get $l9324 + local.get $l9325 + i32.add + local.set $l9326 + local.get $l9325 + local.get $l9326 + i32.add + local.set $l9327 + local.get $l9326 + local.get $l9327 + i32.add + local.set $l9328 + local.get $l9327 + local.get $l9328 + i32.add + local.set $l9329 + local.get $l9328 + local.get $l9329 + i32.add + local.set $l9330 + local.get $l9329 + local.get $l9330 + i32.add + local.set $l9331 + local.get $l9330 + local.get $l9331 + i32.add + local.set $l9332 + local.get $l9331 + local.get $l9332 + i32.add + local.set $l9333 + local.get $l9332 + local.get $l9333 + i32.add + local.set $l9334 + local.get $l9333 + local.get $l9334 + i32.add + local.set $l9335 + local.get $l9334 + local.get $l9335 + i32.add + local.set $l9336 + local.get $l9335 + local.get $l9336 + i32.add + local.set $l9337 + local.get $l9336 + local.get $l9337 + i32.add + local.set $l9338 + local.get $l9337 + local.get $l9338 + i32.add + local.set $l9339 + local.get $l9338 + local.get $l9339 + i32.add + local.set $l9340 + local.get $l9339 + local.get $l9340 + i32.add + local.set $l9341 + local.get $l9340 + local.get $l9341 + i32.add + local.set $l9342 + local.get $l9341 + local.get $l9342 + i32.add + local.set $l9343 + local.get $l9342 + local.get $l9343 + i32.add + local.set $l9344 + local.get $l9343 + local.get $l9344 + i32.add + local.set $l9345 + local.get $l9344 + local.get $l9345 + i32.add + local.set $l9346 + local.get $l9345 + local.get $l9346 + i32.add + local.set $l9347 + local.get $l9346 + local.get $l9347 + i32.add + local.set $l9348 + local.get $l9347 + local.get $l9348 + i32.add + local.set $l9349 + local.get $l9348 + local.get $l9349 + i32.add + local.set $l9350 + local.get $l9349 + local.get $l9350 + i32.add + local.set $l9351 + local.get $l9350 + local.get $l9351 + i32.add + local.set $l9352 + local.get $l9351 + local.get $l9352 + i32.add + local.set $l9353 + local.get $l9352 + local.get $l9353 + i32.add + local.set $l9354 + local.get $l9353 + local.get $l9354 + i32.add + local.set $l9355 + local.get $l9354 + local.get $l9355 + i32.add + local.set $l9356 + local.get $l9355 + local.get $l9356 + i32.add + local.set $l9357 + local.get $l9356 + local.get $l9357 + i32.add + local.set $l9358 + local.get $l9357 + local.get $l9358 + i32.add + local.set $l9359 + local.get $l9358 + local.get $l9359 + i32.add + local.set $l9360 + local.get $l9359 + local.get $l9360 + i32.add + local.set $l9361 + local.get $l9360 + local.get $l9361 + i32.add + local.set $l9362 + local.get $l9361 + local.get $l9362 + i32.add + local.set $l9363 + local.get $l9362 + local.get $l9363 + i32.add + local.set $l9364 + local.get $l9363 + local.get $l9364 + i32.add + local.set $l9365 + local.get $l9364 + local.get $l9365 + i32.add + local.set $l9366 + local.get $l9365 + local.get $l9366 + i32.add + local.set $l9367 + local.get $l9366 + local.get $l9367 + i32.add + local.set $l9368 + local.get $l9367 + local.get $l9368 + i32.add + local.set $l9369 + local.get $l9368 + local.get $l9369 + i32.add + local.set $l9370 + local.get $l9369 + local.get $l9370 + i32.add + local.set $l9371 + local.get $l9370 + local.get $l9371 + i32.add + local.set $l9372 + local.get $l9371 + local.get $l9372 + i32.add + local.set $l9373 + local.get $l9372 + local.get $l9373 + i32.add + local.set $l9374 + local.get $l9373 + local.get $l9374 + i32.add + local.set $l9375 + local.get $l9374 + local.get $l9375 + i32.add + local.set $l9376 + local.get $l9375 + local.get $l9376 + i32.add + local.set $l9377 + local.get $l9376 + local.get $l9377 + i32.add + local.set $l9378 + local.get $l9377 + local.get $l9378 + i32.add + local.set $l9379 + local.get $l9378 + local.get $l9379 + i32.add + local.set $l9380 + local.get $l9379 + local.get $l9380 + i32.add + local.set $l9381 + local.get $l9380 + local.get $l9381 + i32.add + local.set $l9382 + local.get $l9381 + local.get $l9382 + i32.add + local.set $l9383 + local.get $l9382 + local.get $l9383 + i32.add + local.set $l9384 + local.get $l9383 + local.get $l9384 + i32.add + local.set $l9385 + local.get $l9384 + local.get $l9385 + i32.add + local.set $l9386 + local.get $l9385 + local.get $l9386 + i32.add + local.set $l9387 + local.get $l9386 + local.get $l9387 + i32.add + local.set $l9388 + local.get $l9387 + local.get $l9388 + i32.add + local.set $l9389 + local.get $l9388 + local.get $l9389 + i32.add + local.set $l9390 + local.get $l9389 + local.get $l9390 + i32.add + local.set $l9391 + local.get $l9390 + local.get $l9391 + i32.add + local.set $l9392 + local.get $l9391 + local.get $l9392 + i32.add + local.set $l9393 + local.get $l9392 + local.get $l9393 + i32.add + local.set $l9394 + local.get $l9393 + local.get $l9394 + i32.add + local.set $l9395 + local.get $l9394 + local.get $l9395 + i32.add + local.set $l9396 + local.get $l9395 + local.get $l9396 + i32.add + local.set $l9397 + local.get $l9396 + local.get $l9397 + i32.add + local.set $l9398 + local.get $l9397 + local.get $l9398 + i32.add + local.set $l9399 + local.get $l9398 + local.get $l9399 + i32.add + local.set $l9400 + local.get $l9399 + local.get $l9400 + i32.add + local.set $l9401 + local.get $l9400 + local.get $l9401 + i32.add + local.set $l9402 + local.get $l9401 + local.get $l9402 + i32.add + local.set $l9403 + local.get $l9402 + local.get $l9403 + i32.add + local.set $l9404 + local.get $l9403 + local.get $l9404 + i32.add + local.set $l9405 + local.get $l9404 + local.get $l9405 + i32.add + local.set $l9406 + local.get $l9405 + local.get $l9406 + i32.add + local.set $l9407 + local.get $l9406 + local.get $l9407 + i32.add + local.set $l9408 + local.get $l9407 + local.get $l9408 + i32.add + local.set $l9409 + local.get $l9408 + local.get $l9409 + i32.add + local.set $l9410 + local.get $l9409 + local.get $l9410 + i32.add + local.set $l9411 + local.get $l9410 + local.get $l9411 + i32.add + local.set $l9412 + local.get $l9411 + local.get $l9412 + i32.add + local.set $l9413 + local.get $l9412 + local.get $l9413 + i32.add + local.set $l9414 + local.get $l9413 + local.get $l9414 + i32.add + local.set $l9415 + local.get $l9414 + local.get $l9415 + i32.add + local.set $l9416 + local.get $l9415 + local.get $l9416 + i32.add + local.set $l9417 + local.get $l9416 + local.get $l9417 + i32.add + local.set $l9418 + local.get $l9417 + local.get $l9418 + i32.add + local.set $l9419 + local.get $l9418 + local.get $l9419 + i32.add + local.set $l9420 + local.get $l9419 + local.get $l9420 + i32.add + local.set $l9421 + local.get $l9420 + local.get $l9421 + i32.add + local.set $l9422 + local.get $l9421 + local.get $l9422 + i32.add + local.set $l9423 + local.get $l9422 + local.get $l9423 + i32.add + local.set $l9424 + local.get $l9423 + local.get $l9424 + i32.add + local.set $l9425 + local.get $l9424 + local.get $l9425 + i32.add + local.set $l9426 + local.get $l9425 + local.get $l9426 + i32.add + local.set $l9427 + local.get $l9426 + local.get $l9427 + i32.add + local.set $l9428 + local.get $l9427 + local.get $l9428 + i32.add + local.set $l9429 + local.get $l9428 + local.get $l9429 + i32.add + local.set $l9430 + local.get $l9429 + local.get $l9430 + i32.add + local.set $l9431 + local.get $l9430 + local.get $l9431 + i32.add + local.set $l9432 + local.get $l9431 + local.get $l9432 + i32.add + local.set $l9433 + local.get $l9432 + local.get $l9433 + i32.add + local.set $l9434 + local.get $l9433 + local.get $l9434 + i32.add + local.set $l9435 + local.get $l9434 + local.get $l9435 + i32.add + local.set $l9436 + local.get $l9435 + local.get $l9436 + i32.add + local.set $l9437 + local.get $l9436 + local.get $l9437 + i32.add + local.set $l9438 + local.get $l9437 + local.get $l9438 + i32.add + local.set $l9439 + local.get $l9438 + local.get $l9439 + i32.add + local.set $l9440 + local.get $l9439 + local.get $l9440 + i32.add + local.set $l9441 + local.get $l9440 + local.get $l9441 + i32.add + local.set $l9442 + local.get $l9441 + local.get $l9442 + i32.add + local.set $l9443 + local.get $l9442 + local.get $l9443 + i32.add + local.set $l9444 + local.get $l9443 + local.get $l9444 + i32.add + local.set $l9445 + local.get $l9444 + local.get $l9445 + i32.add + local.set $l9446 + local.get $l9445 + local.get $l9446 + i32.add + local.set $l9447 + local.get $l9446 + local.get $l9447 + i32.add + local.set $l9448 + local.get $l9447 + local.get $l9448 + i32.add + local.set $l9449 + local.get $l9448 + local.get $l9449 + i32.add + local.set $l9450 + local.get $l9449 + local.get $l9450 + i32.add + local.set $l9451 + local.get $l9450 + local.get $l9451 + i32.add + local.set $l9452 + local.get $l9451 + local.get $l9452 + i32.add + local.set $l9453 + local.get $l9452 + local.get $l9453 + i32.add + local.set $l9454 + local.get $l9453 + local.get $l9454 + i32.add + local.set $l9455 + local.get $l9454 + local.get $l9455 + i32.add + local.set $l9456 + local.get $l9455 + local.get $l9456 + i32.add + local.set $l9457 + local.get $l9456 + local.get $l9457 + i32.add + local.set $l9458 + local.get $l9457 + local.get $l9458 + i32.add + local.set $l9459 + local.get $l9458 + local.get $l9459 + i32.add + local.set $l9460 + local.get $l9459 + local.get $l9460 + i32.add + local.set $l9461 + local.get $l9460 + local.get $l9461 + i32.add + local.set $l9462 + local.get $l9461 + local.get $l9462 + i32.add + local.set $l9463 + local.get $l9462 + local.get $l9463 + i32.add + local.set $l9464 + local.get $l9463 + local.get $l9464 + i32.add + local.set $l9465 + local.get $l9464 + local.get $l9465 + i32.add + local.set $l9466 + local.get $l9465 + local.get $l9466 + i32.add + local.set $l9467 + local.get $l9466 + local.get $l9467 + i32.add + local.set $l9468 + local.get $l9467 + local.get $l9468 + i32.add + local.set $l9469 + local.get $l9468 + local.get $l9469 + i32.add + local.set $l9470 + local.get $l9469 + local.get $l9470 + i32.add + local.set $l9471 + local.get $l9470 + local.get $l9471 + i32.add + local.set $l9472 + local.get $l9471 + local.get $l9472 + i32.add + local.set $l9473 + local.get $l9472 + local.get $l9473 + i32.add + local.set $l9474 + local.get $l9473 + local.get $l9474 + i32.add + local.set $l9475 + local.get $l9474 + local.get $l9475 + i32.add + local.set $l9476 + local.get $l9475 + local.get $l9476 + i32.add + local.set $l9477 + local.get $l9476 + local.get $l9477 + i32.add + local.set $l9478 + local.get $l9477 + local.get $l9478 + i32.add + local.set $l9479 + local.get $l9478 + local.get $l9479 + i32.add + local.set $l9480 + local.get $l9479 + local.get $l9480 + i32.add + local.set $l9481 + local.get $l9480 + local.get $l9481 + i32.add + local.set $l9482 + local.get $l9481 + local.get $l9482 + i32.add + local.set $l9483 + local.get $l9482 + local.get $l9483 + i32.add + local.set $l9484 + local.get $l9483 + local.get $l9484 + i32.add + local.set $l9485 + local.get $l9484 + local.get $l9485 + i32.add + local.set $l9486 + local.get $l9485 + local.get $l9486 + i32.add + local.set $l9487 + local.get $l9486 + local.get $l9487 + i32.add + local.set $l9488 + local.get $l9487 + local.get $l9488 + i32.add + local.set $l9489 + local.get $l9488 + local.get $l9489 + i32.add + local.set $l9490 + local.get $l9489 + local.get $l9490 + i32.add + local.set $l9491 + local.get $l9490 + local.get $l9491 + i32.add + local.set $l9492 + local.get $l9491 + local.get $l9492 + i32.add + local.set $l9493 + local.get $l9492 + local.get $l9493 + i32.add + local.set $l9494 + local.get $l9493 + local.get $l9494 + i32.add + local.set $l9495 + local.get $l9494 + local.get $l9495 + i32.add + local.set $l9496 + local.get $l9495 + local.get $l9496 + i32.add + local.set $l9497 + local.get $l9496 + local.get $l9497 + i32.add + local.set $l9498 + local.get $l9497 + local.get $l9498 + i32.add + local.set $l9499 + local.get $l9498 + local.get $l9499 + i32.add + local.set $l9500 + local.get $l9499 + local.get $l9500 + i32.add + local.set $l9501 + local.get $l9500 + local.get $l9501 + i32.add + local.set $l9502 + local.get $l9501 + local.get $l9502 + i32.add + local.set $l9503 + local.get $l9502 + local.get $l9503 + i32.add + local.set $l9504 + local.get $l9503 + local.get $l9504 + i32.add + local.set $l9505 + local.get $l9504 + local.get $l9505 + i32.add + local.set $l9506 + local.get $l9505 + local.get $l9506 + i32.add + local.set $l9507 + local.get $l9506 + local.get $l9507 + i32.add + local.set $l9508 + local.get $l9507 + local.get $l9508 + i32.add + local.set $l9509 + local.get $l9508 + local.get $l9509 + i32.add + local.set $l9510 + local.get $l9509 + local.get $l9510 + i32.add + local.set $l9511 + local.get $l9510 + local.get $l9511 + i32.add + local.set $l9512 + local.get $l9511 + local.get $l9512 + i32.add + local.set $l9513 + local.get $l9512 + local.get $l9513 + i32.add + local.set $l9514 + local.get $l9513 + local.get $l9514 + i32.add + local.set $l9515 + local.get $l9514 + local.get $l9515 + i32.add + local.set $l9516 + local.get $l9515 + local.get $l9516 + i32.add + local.set $l9517 + local.get $l9516 + local.get $l9517 + i32.add + local.set $l9518 + local.get $l9517 + local.get $l9518 + i32.add + local.set $l9519 + local.get $l9518 + local.get $l9519 + i32.add + local.set $l9520 + local.get $l9519 + local.get $l9520 + i32.add + local.set $l9521 + local.get $l9520 + local.get $l9521 + i32.add + local.set $l9522 + local.get $l9521 + local.get $l9522 + i32.add + local.set $l9523 + local.get $l9522 + local.get $l9523 + i32.add + local.set $l9524 + local.get $l9523 + local.get $l9524 + i32.add + local.set $l9525 + local.get $l9524 + local.get $l9525 + i32.add + local.set $l9526 + local.get $l9525 + local.get $l9526 + i32.add + local.set $l9527 + local.get $l9526 + local.get $l9527 + i32.add + local.set $l9528 + local.get $l9527 + local.get $l9528 + i32.add + local.set $l9529 + local.get $l9528 + local.get $l9529 + i32.add + local.set $l9530 + local.get $l9529 + local.get $l9530 + i32.add + local.set $l9531 + local.get $l9530 + local.get $l9531 + i32.add + local.set $l9532 + local.get $l9531 + local.get $l9532 + i32.add + local.set $l9533 + local.get $l9532 + local.get $l9533 + i32.add + local.set $l9534 + local.get $l9533 + local.get $l9534 + i32.add + local.set $l9535 + local.get $l9534 + local.get $l9535 + i32.add + local.set $l9536 + local.get $l9535 + local.get $l9536 + i32.add + local.set $l9537 + local.get $l9536 + local.get $l9537 + i32.add + local.set $l9538 + local.get $l9537 + local.get $l9538 + i32.add + local.set $l9539 + local.get $l9538 + local.get $l9539 + i32.add + local.set $l9540 + local.get $l9539 + local.get $l9540 + i32.add + local.set $l9541 + local.get $l9540 + local.get $l9541 + i32.add + local.set $l9542 + local.get $l9541 + local.get $l9542 + i32.add + local.set $l9543 + local.get $l9542 + local.get $l9543 + i32.add + local.set $l9544 + local.get $l9543 + local.get $l9544 + i32.add + local.set $l9545 + local.get $l9544 + local.get $l9545 + i32.add + local.set $l9546 + local.get $l9545 + local.get $l9546 + i32.add + local.set $l9547 + local.get $l9546 + local.get $l9547 + i32.add + local.set $l9548 + local.get $l9547 + local.get $l9548 + i32.add + local.set $l9549 + local.get $l9548 + local.get $l9549 + i32.add + local.set $l9550 + local.get $l9549 + local.get $l9550 + i32.add + local.set $l9551 + local.get $l9550 + local.get $l9551 + i32.add + local.set $l9552 + local.get $l9551 + local.get $l9552 + i32.add + local.set $l9553 + local.get $l9552 + local.get $l9553 + i32.add + local.set $l9554 + local.get $l9553 + local.get $l9554 + i32.add + local.set $l9555 + local.get $l9554 + local.get $l9555 + i32.add + local.set $l9556 + local.get $l9555 + local.get $l9556 + i32.add + local.set $l9557 + local.get $l9556 + local.get $l9557 + i32.add + local.set $l9558 + local.get $l9557 + local.get $l9558 + i32.add + local.set $l9559 + local.get $l9558 + local.get $l9559 + i32.add + local.set $l9560 + local.get $l9559 + local.get $l9560 + i32.add + local.set $l9561 + local.get $l9560 + local.get $l9561 + i32.add + local.set $l9562 + local.get $l9561 + local.get $l9562 + i32.add + local.set $l9563 + local.get $l9562 + local.get $l9563 + i32.add + local.set $l9564 + local.get $l9563 + local.get $l9564 + i32.add + local.set $l9565 + local.get $l9564 + local.get $l9565 + i32.add + local.set $l9566 + local.get $l9565 + local.get $l9566 + i32.add + local.set $l9567 + local.get $l9566 + local.get $l9567 + i32.add + local.set $l9568 + local.get $l9567 + local.get $l9568 + i32.add + local.set $l9569 + local.get $l9568 + local.get $l9569 + i32.add + local.set $l9570 + local.get $l9569 + local.get $l9570 + i32.add + local.set $l9571 + local.get $l9570 + local.get $l9571 + i32.add + local.set $l9572 + local.get $l9571 + local.get $l9572 + i32.add + local.set $l9573 + local.get $l9572 + local.get $l9573 + i32.add + local.set $l9574 + local.get $l9573 + local.get $l9574 + i32.add + local.set $l9575 + local.get $l9574 + local.get $l9575 + i32.add + local.set $l9576 + local.get $l9575 + local.get $l9576 + i32.add + local.set $l9577 + local.get $l9576 + local.get $l9577 + i32.add + local.set $l9578 + local.get $l9577 + local.get $l9578 + i32.add + local.set $l9579 + local.get $l9578 + local.get $l9579 + i32.add + local.set $l9580 + local.get $l9579 + local.get $l9580 + i32.add + local.set $l9581 + local.get $l9580 + local.get $l9581 + i32.add + local.set $l9582 + local.get $l9581 + local.get $l9582 + i32.add + local.set $l9583 + local.get $l9582 + local.get $l9583 + i32.add + local.set $l9584 + local.get $l9583 + local.get $l9584 + i32.add + local.set $l9585 + local.get $l9584 + local.get $l9585 + i32.add + local.set $l9586 + local.get $l9585 + local.get $l9586 + i32.add + local.set $l9587 + local.get $l9586 + local.get $l9587 + i32.add + local.set $l9588 + local.get $l9587 + local.get $l9588 + i32.add + local.set $l9589 + local.get $l9588 + local.get $l9589 + i32.add + local.set $l9590 + local.get $l9589 + local.get $l9590 + i32.add + local.set $l9591 + local.get $l9590 + local.get $l9591 + i32.add + local.set $l9592 + local.get $l9591 + local.get $l9592 + i32.add + local.set $l9593 + local.get $l9592 + local.get $l9593 + i32.add + local.set $l9594 + local.get $l9593 + local.get $l9594 + i32.add + local.set $l9595 + local.get $l9594 + local.get $l9595 + i32.add + local.set $l9596 + local.get $l9595 + local.get $l9596 + i32.add + local.set $l9597 + local.get $l9596 + local.get $l9597 + i32.add + local.set $l9598 + local.get $l9597 + local.get $l9598 + i32.add + local.set $l9599 + local.get $l9598 + local.get $l9599 + i32.add + local.set $l9600 + local.get $l9599 + local.get $l9600 + i32.add + local.set $l9601 + local.get $l9600 + local.get $l9601 + i32.add + local.set $l9602 + local.get $l9601 + local.get $l9602 + i32.add + local.set $l9603 + local.get $l9602 + local.get $l9603 + i32.add + local.set $l9604 + local.get $l9603 + local.get $l9604 + i32.add + local.set $l9605 + local.get $l9604 + local.get $l9605 + i32.add + local.set $l9606 + local.get $l9605 + local.get $l9606 + i32.add + local.set $l9607 + local.get $l9606 + local.get $l9607 + i32.add + local.set $l9608 + local.get $l9607 + local.get $l9608 + i32.add + local.set $l9609 + local.get $l9608 + local.get $l9609 + i32.add + local.set $l9610 + local.get $l9609 + local.get $l9610 + i32.add + local.set $l9611 + local.get $l9610 + local.get $l9611 + i32.add + local.set $l9612 + local.get $l9611 + local.get $l9612 + i32.add + local.set $l9613 + local.get $l9612 + local.get $l9613 + i32.add + local.set $l9614 + local.get $l9613 + local.get $l9614 + i32.add + local.set $l9615 + local.get $l9614 + local.get $l9615 + i32.add + local.set $l9616 + local.get $l9615 + local.get $l9616 + i32.add + local.set $l9617 + local.get $l9616 + local.get $l9617 + i32.add + local.set $l9618 + local.get $l9617 + local.get $l9618 + i32.add + local.set $l9619 + local.get $l9618 + local.get $l9619 + i32.add + local.set $l9620 + local.get $l9619 + local.get $l9620 + i32.add + local.set $l9621 + local.get $l9620 + local.get $l9621 + i32.add + local.set $l9622 + local.get $l9621 + local.get $l9622 + i32.add + local.set $l9623 + local.get $l9622 + local.get $l9623 + i32.add + local.set $l9624 + local.get $l9623 + local.get $l9624 + i32.add + local.set $l9625 + local.get $l9624 + local.get $l9625 + i32.add + local.set $l9626 + local.get $l9625 + local.get $l9626 + i32.add + local.set $l9627 + local.get $l9626 + local.get $l9627 + i32.add + local.set $l9628 + local.get $l9627 + local.get $l9628 + i32.add + local.set $l9629 + local.get $l9628 + local.get $l9629 + i32.add + local.set $l9630 + local.get $l9629 + local.get $l9630 + i32.add + local.set $l9631 + local.get $l9630 + local.get $l9631 + i32.add + local.set $l9632 + local.get $l9631 + local.get $l9632 + i32.add + local.set $l9633 + local.get $l9632 + local.get $l9633 + i32.add + local.set $l9634 + local.get $l9633 + local.get $l9634 + i32.add + local.set $l9635 + local.get $l9634 + local.get $l9635 + i32.add + local.set $l9636 + local.get $l9635 + local.get $l9636 + i32.add + local.set $l9637 + local.get $l9636 + local.get $l9637 + i32.add + local.set $l9638 + local.get $l9637 + local.get $l9638 + i32.add + local.set $l9639 + local.get $l9638 + local.get $l9639 + i32.add + local.set $l9640 + local.get $l9639 + local.get $l9640 + i32.add + local.set $l9641 + local.get $l9640 + local.get $l9641 + i32.add + local.set $l9642 + local.get $l9641 + local.get $l9642 + i32.add + local.set $l9643 + local.get $l9642 + local.get $l9643 + i32.add + local.set $l9644 + local.get $l9643 + local.get $l9644 + i32.add + local.set $l9645 + local.get $l9644 + local.get $l9645 + i32.add + local.set $l9646 + local.get $l9645 + local.get $l9646 + i32.add + local.set $l9647 + local.get $l9646 + local.get $l9647 + i32.add + local.set $l9648 + local.get $l9647 + local.get $l9648 + i32.add + local.set $l9649 + local.get $l9648 + local.get $l9649 + i32.add + local.set $l9650 + local.get $l9649 + local.get $l9650 + i32.add + local.set $l9651 + local.get $l9650 + local.get $l9651 + i32.add + local.set $l9652 + local.get $l9651 + local.get $l9652 + i32.add + local.set $l9653 + local.get $l9652 + local.get $l9653 + i32.add + local.set $l9654 + local.get $l9653 + local.get $l9654 + i32.add + local.set $l9655 + local.get $l9654 + local.get $l9655 + i32.add + local.set $l9656 + local.get $l9655 + local.get $l9656 + i32.add + local.set $l9657 + local.get $l9656 + local.get $l9657 + i32.add + local.set $l9658 + local.get $l9657 + local.get $l9658 + i32.add + local.set $l9659 + local.get $l9658 + local.get $l9659 + i32.add + local.set $l9660 + local.get $l9659 + local.get $l9660 + i32.add + local.set $l9661 + local.get $l9660 + local.get $l9661 + i32.add + local.set $l9662 + local.get $l9661 + local.get $l9662 + i32.add + local.set $l9663 + local.get $l9662 + local.get $l9663 + i32.add + local.set $l9664 + local.get $l9663 + local.get $l9664 + i32.add + local.set $l9665 + local.get $l9664 + local.get $l9665 + i32.add + local.set $l9666 + local.get $l9665 + local.get $l9666 + i32.add + local.set $l9667 + local.get $l9666 + local.get $l9667 + i32.add + local.set $l9668 + local.get $l9667 + local.get $l9668 + i32.add + local.set $l9669 + local.get $l9668 + local.get $l9669 + i32.add + local.set $l9670 + local.get $l9669 + local.get $l9670 + i32.add + local.set $l9671 + local.get $l9670 + local.get $l9671 + i32.add + local.set $l9672 + local.get $l9671 + local.get $l9672 + i32.add + local.set $l9673 + local.get $l9672 + local.get $l9673 + i32.add + local.set $l9674 + local.get $l9673 + local.get $l9674 + i32.add + local.set $l9675 + local.get $l9674 + local.get $l9675 + i32.add + local.set $l9676 + local.get $l9675 + local.get $l9676 + i32.add + local.set $l9677 + local.get $l9676 + local.get $l9677 + i32.add + local.set $l9678 + local.get $l9677 + local.get $l9678 + i32.add + local.set $l9679 + local.get $l9678 + local.get $l9679 + i32.add + local.set $l9680 + local.get $l9679 + local.get $l9680 + i32.add + local.set $l9681 + local.get $l9680 + local.get $l9681 + i32.add + local.set $l9682 + local.get $l9681 + local.get $l9682 + i32.add + local.set $l9683 + local.get $l9682 + local.get $l9683 + i32.add + local.set $l9684 + local.get $l9683 + local.get $l9684 + i32.add + local.set $l9685 + local.get $l9684 + local.get $l9685 + i32.add + local.set $l9686 + local.get $l9685 + local.get $l9686 + i32.add + local.set $l9687 + local.get $l9686 + local.get $l9687 + i32.add + local.set $l9688 + local.get $l9687 + local.get $l9688 + i32.add + local.set $l9689 + local.get $l9688 + local.get $l9689 + i32.add + local.set $l9690 + local.get $l9689 + local.get $l9690 + i32.add + local.set $l9691 + local.get $l9690 + local.get $l9691 + i32.add + local.set $l9692 + local.get $l9691 + local.get $l9692 + i32.add + local.set $l9693 + local.get $l9692 + local.get $l9693 + i32.add + local.set $l9694 + local.get $l9693 + local.get $l9694 + i32.add + local.set $l9695 + local.get $l9694 + local.get $l9695 + i32.add + local.set $l9696 + local.get $l9695 + local.get $l9696 + i32.add + local.set $l9697 + local.get $l9696 + local.get $l9697 + i32.add + local.set $l9698 + local.get $l9697 + local.get $l9698 + i32.add + local.set $l9699 + local.get $l9698 + local.get $l9699 + i32.add + local.set $l9700 + local.get $l9699 + local.get $l9700 + i32.add + local.set $l9701 + local.get $l9700 + local.get $l9701 + i32.add + local.set $l9702 + local.get $l9701 + local.get $l9702 + i32.add + local.set $l9703 + local.get $l9702 + local.get $l9703 + i32.add + local.set $l9704 + local.get $l9703 + local.get $l9704 + i32.add + local.set $l9705 + local.get $l9704 + local.get $l9705 + i32.add + local.set $l9706 + local.get $l9705 + local.get $l9706 + i32.add + local.set $l9707 + local.get $l9706 + local.get $l9707 + i32.add + local.set $l9708 + local.get $l9707 + local.get $l9708 + i32.add + local.set $l9709 + local.get $l9708 + local.get $l9709 + i32.add + local.set $l9710 + local.get $l9709 + local.get $l9710 + i32.add + local.set $l9711 + local.get $l9710 + local.get $l9711 + i32.add + local.set $l9712 + local.get $l9711 + local.get $l9712 + i32.add + local.set $l9713 + local.get $l9712 + local.get $l9713 + i32.add + local.set $l9714 + local.get $l9713 + local.get $l9714 + i32.add + local.set $l9715 + local.get $l9714 + local.get $l9715 + i32.add + local.set $l9716 + local.get $l9715 + local.get $l9716 + i32.add + local.set $l9717 + local.get $l9716 + local.get $l9717 + i32.add + local.set $l9718 + local.get $l9717 + local.get $l9718 + i32.add + local.set $l9719 + local.get $l9718 + local.get $l9719 + i32.add + local.set $l9720 + local.get $l9719 + local.get $l9720 + i32.add + local.set $l9721 + local.get $l9720 + local.get $l9721 + i32.add + local.set $l9722 + local.get $l9721 + local.get $l9722 + i32.add + local.set $l9723 + local.get $l9722 + local.get $l9723 + i32.add + local.set $l9724 + local.get $l9723 + local.get $l9724 + i32.add + local.set $l9725 + local.get $l9724 + local.get $l9725 + i32.add + local.set $l9726 + local.get $l9725 + local.get $l9726 + i32.add + local.set $l9727 + local.get $l9726 + local.get $l9727 + i32.add + local.set $l9728 + local.get $l9727 + local.get $l9728 + i32.add + local.set $l9729 + local.get $l9728 + local.get $l9729 + i32.add + local.set $l9730 + local.get $l9729 + local.get $l9730 + i32.add + local.set $l9731 + local.get $l9730 + local.get $l9731 + i32.add + local.set $l9732 + local.get $l9731 + local.get $l9732 + i32.add + local.set $l9733 + local.get $l9732 + local.get $l9733 + i32.add + local.set $l9734 + local.get $l9733 + local.get $l9734 + i32.add + local.set $l9735 + local.get $l9734 + local.get $l9735 + i32.add + local.set $l9736 + local.get $l9735 + local.get $l9736 + i32.add + local.set $l9737 + local.get $l9736 + local.get $l9737 + i32.add + local.set $l9738 + local.get $l9737 + local.get $l9738 + i32.add + local.set $l9739 + local.get $l9738 + local.get $l9739 + i32.add + local.set $l9740 + local.get $l9739 + local.get $l9740 + i32.add + local.set $l9741 + local.get $l9740 + local.get $l9741 + i32.add + local.set $l9742 + local.get $l9741 + local.get $l9742 + i32.add + local.set $l9743 + local.get $l9742 + local.get $l9743 + i32.add + local.set $l9744 + local.get $l9743 + local.get $l9744 + i32.add + local.set $l9745 + local.get $l9744 + local.get $l9745 + i32.add + local.set $l9746 + local.get $l9745 + local.get $l9746 + i32.add + local.set $l9747 + local.get $l9746 + local.get $l9747 + i32.add + local.set $l9748 + local.get $l9747 + local.get $l9748 + i32.add + local.set $l9749 + local.get $l9748 + local.get $l9749 + i32.add + local.set $l9750 + local.get $l9749 + local.get $l9750 + i32.add + local.set $l9751 + local.get $l9750 + local.get $l9751 + i32.add + local.set $l9752 + local.get $l9751 + local.get $l9752 + i32.add + local.set $l9753 + local.get $l9752 + local.get $l9753 + i32.add + local.set $l9754 + local.get $l9753 + local.get $l9754 + i32.add + local.set $l9755 + local.get $l9754 + local.get $l9755 + i32.add + local.set $l9756 + local.get $l9755 + local.get $l9756 + i32.add + local.set $l9757 + local.get $l9756 + local.get $l9757 + i32.add + local.set $l9758 + local.get $l9757 + local.get $l9758 + i32.add + local.set $l9759 + local.get $l9758 + local.get $l9759 + i32.add + local.set $l9760 + local.get $l9759 + local.get $l9760 + i32.add + local.set $l9761 + local.get $l9760 + local.get $l9761 + i32.add + local.set $l9762 + local.get $l9761 + local.get $l9762 + i32.add + local.set $l9763 + local.get $l9762 + local.get $l9763 + i32.add + local.set $l9764 + local.get $l9763 + local.get $l9764 + i32.add + local.set $l9765 + local.get $l9764 + local.get $l9765 + i32.add + local.set $l9766 + local.get $l9765 + local.get $l9766 + i32.add + local.set $l9767 + local.get $l9766 + local.get $l9767 + i32.add + local.set $l9768 + local.get $l9767 + local.get $l9768 + i32.add + local.set $l9769 + local.get $l9768 + local.get $l9769 + i32.add + local.set $l9770 + local.get $l9769 + local.get $l9770 + i32.add + local.set $l9771 + local.get $l9770 + local.get $l9771 + i32.add + local.set $l9772 + local.get $l9771 + local.get $l9772 + i32.add + local.set $l9773 + local.get $l9772 + local.get $l9773 + i32.add + local.set $l9774 + local.get $l9773 + local.get $l9774 + i32.add + local.set $l9775 + local.get $l9774 + local.get $l9775 + i32.add + local.set $l9776 + local.get $l9775 + local.get $l9776 + i32.add + local.set $l9777 + local.get $l9776 + local.get $l9777 + i32.add + local.set $l9778 + local.get $l9777 + local.get $l9778 + i32.add + local.set $l9779 + local.get $l9778 + local.get $l9779 + i32.add + local.set $l9780 + local.get $l9779 + local.get $l9780 + i32.add + local.set $l9781 + local.get $l9780 + local.get $l9781 + i32.add + local.set $l9782 + local.get $l9781 + local.get $l9782 + i32.add + local.set $l9783 + local.get $l9782 + local.get $l9783 + i32.add + local.set $l9784 + local.get $l9783 + local.get $l9784 + i32.add + local.set $l9785 + local.get $l9784 + local.get $l9785 + i32.add + local.set $l9786 + local.get $l9785 + local.get $l9786 + i32.add + local.set $l9787 + local.get $l9786 + local.get $l9787 + i32.add + local.set $l9788 + local.get $l9787 + local.get $l9788 + i32.add + local.set $l9789 + local.get $l9788 + local.get $l9789 + i32.add + local.set $l9790 + local.get $l9789 + local.get $l9790 + i32.add + local.set $l9791 + local.get $l9790 + local.get $l9791 + i32.add + local.set $l9792 + local.get $l9791 + local.get $l9792 + i32.add + local.set $l9793 + local.get $l9792 + local.get $l9793 + i32.add + local.set $l9794 + local.get $l9793 + local.get $l9794 + i32.add + local.set $l9795 + local.get $l9794 + local.get $l9795 + i32.add + local.set $l9796 + local.get $l9795 + local.get $l9796 + i32.add + local.set $l9797 + local.get $l9796 + local.get $l9797 + i32.add + local.set $l9798 + local.get $l9797 + local.get $l9798 + i32.add + local.set $l9799 + local.get $l9798 + local.get $l9799 + i32.add + local.set $l9800 + local.get $l9799 + local.get $l9800 + i32.add + local.set $l9801 + local.get $l9800 + local.get $l9801 + i32.add + local.set $l9802 + local.get $l9801 + local.get $l9802 + i32.add + local.set $l9803 + local.get $l9802 + local.get $l9803 + i32.add + local.set $l9804 + local.get $l9803 + local.get $l9804 + i32.add + local.set $l9805 + local.get $l9804 + local.get $l9805 + i32.add + local.set $l9806 + local.get $l9805 + local.get $l9806 + i32.add + local.set $l9807 + local.get $l9806 + local.get $l9807 + i32.add + local.set $l9808 + local.get $l9807 + local.get $l9808 + i32.add + local.set $l9809 + local.get $l9808 + local.get $l9809 + i32.add + local.set $l9810 + local.get $l9809 + local.get $l9810 + i32.add + local.set $l9811 + local.get $l9810 + local.get $l9811 + i32.add + local.set $l9812 + local.get $l9811 + local.get $l9812 + i32.add + local.set $l9813 + local.get $l9812 + local.get $l9813 + i32.add + local.set $l9814 + local.get $l9813 + local.get $l9814 + i32.add + local.set $l9815 + local.get $l9814 + local.get $l9815 + i32.add + local.set $l9816 + local.get $l9815 + local.get $l9816 + i32.add + local.set $l9817 + local.get $l9816 + local.get $l9817 + i32.add + local.set $l9818 + local.get $l9817 + local.get $l9818 + i32.add + local.set $l9819 + local.get $l9818 + local.get $l9819 + i32.add + local.set $l9820 + local.get $l9819 + local.get $l9820 + i32.add + local.set $l9821 + local.get $l9820 + local.get $l9821 + i32.add + local.set $l9822 + local.get $l9821 + local.get $l9822 + i32.add + local.set $l9823 + local.get $l9822 + local.get $l9823 + i32.add + local.set $l9824 + local.get $l9823 + local.get $l9824 + i32.add + local.set $l9825 + local.get $l9824 + local.get $l9825 + i32.add + local.set $l9826 + local.get $l9825 + local.get $l9826 + i32.add + local.set $l9827 + local.get $l9826 + local.get $l9827 + i32.add + local.set $l9828 + local.get $l9827 + local.get $l9828 + i32.add + local.set $l9829 + local.get $l9828 + local.get $l9829 + i32.add + local.set $l9830 + local.get $l9829 + local.get $l9830 + i32.add + local.set $l9831 + local.get $l9830 + local.get $l9831 + i32.add + local.set $l9832 + local.get $l9831 + local.get $l9832 + i32.add + local.set $l9833 + local.get $l9832 + local.get $l9833 + i32.add + local.set $l9834 + local.get $l9833 + local.get $l9834 + i32.add + local.set $l9835 + local.get $l9834 + local.get $l9835 + i32.add + local.set $l9836 + local.get $l9835 + local.get $l9836 + i32.add + local.set $l9837 + local.get $l9836 + local.get $l9837 + i32.add + local.set $l9838 + local.get $l9837 + local.get $l9838 + i32.add + local.set $l9839 + local.get $l9838 + local.get $l9839 + i32.add + local.set $l9840 + local.get $l9839 + local.get $l9840 + i32.add + local.set $l9841 + local.get $l9840 + local.get $l9841 + i32.add + local.set $l9842 + local.get $l9841 + local.get $l9842 + i32.add + local.set $l9843 + local.get $l9842 + local.get $l9843 + i32.add + local.set $l9844 + local.get $l9843 + local.get $l9844 + i32.add + local.set $l9845 + local.get $l9844 + local.get $l9845 + i32.add + local.set $l9846 + local.get $l9845 + local.get $l9846 + i32.add + local.set $l9847 + local.get $l9846 + local.get $l9847 + i32.add + local.set $l9848 + local.get $l9847 + local.get $l9848 + i32.add + local.set $l9849 + local.get $l9848 + local.get $l9849 + i32.add + local.set $l9850 + local.get $l9849 + local.get $l9850 + i32.add + local.set $l9851 + local.get $l9850 + local.get $l9851 + i32.add + local.set $l9852 + local.get $l9851 + local.get $l9852 + i32.add + local.set $l9853 + local.get $l9852 + local.get $l9853 + i32.add + local.set $l9854 + local.get $l9853 + local.get $l9854 + i32.add + local.set $l9855 + local.get $l9854 + local.get $l9855 + i32.add + local.set $l9856 + local.get $l9855 + local.get $l9856 + i32.add + local.set $l9857 + local.get $l9856 + local.get $l9857 + i32.add + local.set $l9858 + local.get $l9857 + local.get $l9858 + i32.add + local.set $l9859 + local.get $l9858 + local.get $l9859 + i32.add + local.set $l9860 + local.get $l9859 + local.get $l9860 + i32.add + local.set $l9861 + local.get $l9860 + local.get $l9861 + i32.add + local.set $l9862 + local.get $l9861 + local.get $l9862 + i32.add + local.set $l9863 + local.get $l9862 + local.get $l9863 + i32.add + local.set $l9864 + local.get $l9863 + local.get $l9864 + i32.add + local.set $l9865 + local.get $l9864 + local.get $l9865 + i32.add + local.set $l9866 + local.get $l9865 + local.get $l9866 + i32.add + local.set $l9867 + local.get $l9866 + local.get $l9867 + i32.add + local.set $l9868 + local.get $l9867 + local.get $l9868 + i32.add + local.set $l9869 + local.get $l9868 + local.get $l9869 + i32.add + local.set $l9870 + local.get $l9869 + local.get $l9870 + i32.add + local.set $l9871 + local.get $l9870 + local.get $l9871 + i32.add + local.set $l9872 + local.get $l9871 + local.get $l9872 + i32.add + local.set $l9873 + local.get $l9872 + local.get $l9873 + i32.add + local.set $l9874 + local.get $l9873 + local.get $l9874 + i32.add + local.set $l9875 + local.get $l9874 + local.get $l9875 + i32.add + local.set $l9876 + local.get $l9875 + local.get $l9876 + i32.add + local.set $l9877 + local.get $l9876 + local.get $l9877 + i32.add + local.set $l9878 + local.get $l9877 + local.get $l9878 + i32.add + local.set $l9879 + local.get $l9878 + local.get $l9879 + i32.add + local.set $l9880 + local.get $l9879 + local.get $l9880 + i32.add + local.set $l9881 + local.get $l9880 + local.get $l9881 + i32.add + local.set $l9882 + local.get $l9881 + local.get $l9882 + i32.add + local.set $l9883 + local.get $l9882 + local.get $l9883 + i32.add + local.set $l9884 + local.get $l9883 + local.get $l9884 + i32.add + local.set $l9885 + local.get $l9884 + local.get $l9885 + i32.add + local.set $l9886 + local.get $l9885 + local.get $l9886 + i32.add + local.set $l9887 + local.get $l9886 + local.get $l9887 + i32.add + local.set $l9888 + local.get $l9887 + local.get $l9888 + i32.add + local.set $l9889 + local.get $l9888 + local.get $l9889 + i32.add + local.set $l9890 + local.get $l9889 + local.get $l9890 + i32.add + local.set $l9891 + local.get $l9890 + local.get $l9891 + i32.add + local.set $l9892 + local.get $l9891 + local.get $l9892 + i32.add + local.set $l9893 + local.get $l9892 + local.get $l9893 + i32.add + local.set $l9894 + local.get $l9893 + local.get $l9894 + i32.add + local.set $l9895 + local.get $l9894 + local.get $l9895 + i32.add + local.set $l9896 + local.get $l9895 + local.get $l9896 + i32.add + local.set $l9897 + local.get $l9896 + local.get $l9897 + i32.add + local.set $l9898 + local.get $l9897 + local.get $l9898 + i32.add + local.set $l9899 + local.get $l9898 + local.get $l9899 + i32.add + local.set $l9900 + local.get $l9899 + local.get $l9900 + i32.add + local.set $l9901 + local.get $l9900 + local.get $l9901 + i32.add + local.set $l9902 + local.get $l9901 + local.get $l9902 + i32.add + local.set $l9903 + local.get $l9902 + local.get $l9903 + i32.add + local.set $l9904 + local.get $l9903 + local.get $l9904 + i32.add + local.set $l9905 + local.get $l9904 + local.get $l9905 + i32.add + local.set $l9906 + local.get $l9905 + local.get $l9906 + i32.add + local.set $l9907 + local.get $l9906 + local.get $l9907 + i32.add + local.set $l9908 + local.get $l9907 + local.get $l9908 + i32.add + local.set $l9909 + local.get $l9908 + local.get $l9909 + i32.add + local.set $l9910 + local.get $l9909 + local.get $l9910 + i32.add + local.set $l9911 + local.get $l9910 + local.get $l9911 + i32.add + local.set $l9912 + local.get $l9911 + local.get $l9912 + i32.add + local.set $l9913 + local.get $l9912 + local.get $l9913 + i32.add + local.set $l9914 + local.get $l9913 + local.get $l9914 + i32.add + local.set $l9915 + local.get $l9914 + local.get $l9915 + i32.add + local.set $l9916 + local.get $l9915 + local.get $l9916 + i32.add + local.set $l9917 + local.get $l9916 + local.get $l9917 + i32.add + local.set $l9918 + local.get $l9917 + local.get $l9918 + i32.add + local.set $l9919 + local.get $l9918 + local.get $l9919 + i32.add + local.set $l9920 + local.get $l9919 + local.get $l9920 + i32.add + local.set $l9921 + local.get $l9920 + local.get $l9921 + i32.add + local.set $l9922 + local.get $l9921 + local.get $l9922 + i32.add + local.set $l9923 + local.get $l9922 + local.get $l9923 + i32.add + local.set $l9924 + local.get $l9923 + local.get $l9924 + i32.add + local.set $l9925 + local.get $l9924 + local.get $l9925 + i32.add + local.set $l9926 + local.get $l9925 + local.get $l9926 + i32.add + local.set $l9927 + local.get $l9926 + local.get $l9927 + i32.add + local.set $l9928 + local.get $l9927 + local.get $l9928 + i32.add + local.set $l9929 + local.get $l9928 + local.get $l9929 + i32.add + local.set $l9930 + local.get $l9929 + local.get $l9930 + i32.add + local.set $l9931 + local.get $l9930 + local.get $l9931 + i32.add + local.set $l9932 + local.get $l9931 + local.get $l9932 + i32.add + local.set $l9933 + local.get $l9932 + local.get $l9933 + i32.add + local.set $l9934 + local.get $l9933 + local.get $l9934 + i32.add + local.set $l9935 + local.get $l9934 + local.get $l9935 + i32.add + local.set $l9936 + local.get $l9935 + local.get $l9936 + i32.add + local.set $l9937 + local.get $l9936 + local.get $l9937 + i32.add + local.set $l9938 + local.get $l9937 + local.get $l9938 + i32.add + local.set $l9939 + local.get $l9938 + local.get $l9939 + i32.add + local.set $l9940 + local.get $l9939 + local.get $l9940 + i32.add + local.set $l9941 + local.get $l9940 + local.get $l9941 + i32.add + local.set $l9942 + local.get $l9941 + local.get $l9942 + i32.add + local.set $l9943 + local.get $l9942 + local.get $l9943 + i32.add + local.set $l9944 + local.get $l9943 + local.get $l9944 + i32.add + local.set $l9945 + local.get $l9944 + local.get $l9945 + i32.add + local.set $l9946 + local.get $l9945 + local.get $l9946 + i32.add + local.set $l9947 + local.get $l9946 + local.get $l9947 + i32.add + local.set $l9948 + local.get $l9947 + local.get $l9948 + i32.add + local.set $l9949 + local.get $l9948 + local.get $l9949 + i32.add + local.set $l9950 + local.get $l9949 + local.get $l9950 + i32.add + local.set $l9951 + local.get $l9950 + local.get $l9951 + i32.add + local.set $l9952 + local.get $l9951 + local.get $l9952 + i32.add + local.set $l9953 + local.get $l9952 + local.get $l9953 + i32.add + local.set $l9954 + local.get $l9953 + local.get $l9954 + i32.add + local.set $l9955 + local.get $l9954 + local.get $l9955 + i32.add + local.set $l9956 + local.get $l9955 + local.get $l9956 + i32.add + local.set $l9957 + local.get $l9956 + local.get $l9957 + i32.add + local.set $l9958 + local.get $l9957 + local.get $l9958 + i32.add + local.set $l9959 + local.get $l9958 + local.get $l9959 + i32.add + local.set $l9960 + local.get $l9959 + local.get $l9960 + i32.add + local.set $l9961 + local.get $l9960 + local.get $l9961 + i32.add + local.set $l9962 + local.get $l9961 + local.get $l9962 + i32.add + local.set $l9963 + local.get $l9962 + local.get $l9963 + i32.add + local.set $l9964 + local.get $l9963 + local.get $l9964 + i32.add + local.set $l9965 + local.get $l9964 + local.get $l9965 + i32.add + local.set $l9966 + local.get $l9965 + local.get $l9966 + i32.add + local.set $l9967 + local.get $l9966 + local.get $l9967 + i32.add + local.set $l9968 + local.get $l9967 + local.get $l9968 + i32.add + local.set $l9969 + local.get $l9968 + local.get $l9969 + i32.add + local.set $l9970 + local.get $l9969 + local.get $l9970 + i32.add + local.set $l9971 + local.get $l9970 + local.get $l9971 + i32.add + local.set $l9972 + local.get $l9971 + local.get $l9972 + i32.add + local.set $l9973 + local.get $l9972 + local.get $l9973 + i32.add + local.set $l9974 + local.get $l9973 + local.get $l9974 + i32.add + local.set $l9975 + local.get $l9974 + local.get $l9975 + i32.add + local.set $l9976 + local.get $l9975 + local.get $l9976 + i32.add + local.set $l9977 + local.get $l9976 + local.get $l9977 + i32.add + local.set $l9978 + local.get $l9977 + local.get $l9978 + i32.add + local.set $l9979 + local.get $l9978 + local.get $l9979 + i32.add + local.set $l9980 + local.get $l9979 + local.get $l9980 + i32.add + local.set $l9981 + local.get $l9980 + local.get $l9981 + i32.add + local.set $l9982 + local.get $l9981 + local.get $l9982 + i32.add + local.set $l9983 + local.get $l9982 + local.get $l9983 + i32.add + local.set $l9984 + local.get $l9983 + local.get $l9984 + i32.add + local.set $l9985 + local.get $l9984 + local.get $l9985 + i32.add + local.set $l9986 + local.get $l9985 + local.get $l9986 + i32.add + local.set $l9987 + local.get $l9986 + local.get $l9987 + i32.add + local.set $l9988 + local.get $l9987 + local.get $l9988 + i32.add + local.set $l9989 + local.get $l9988 + local.get $l9989 + i32.add + local.set $l9990 + local.get $l9989 + local.get $l9990 + i32.add + local.set $l9991 + local.get $l9990 + local.get $l9991 + i32.add + local.set $l9992 + local.get $l9991 + local.get $l9992 + i32.add + local.set $l9993 + local.get $l9992 + local.get $l9993 + i32.add + local.set $l9994 + local.get $l9993 + local.get $l9994 + i32.add + local.set $l9995 + local.get $l9994 + local.get $l9995 + i32.add + local.set $l9996 + local.get $l9995 + local.get $l9996 + i32.add + local.set $l9997 + local.get $l9996 + local.get $l9997 + i32.add + local.set $l9998 + local.get $l9997 + local.get $l9998 + i32.add + local.set $l9999 + ;; Return the last local variable + local.get $l9999 + ) +) diff --git a/src/test/app/wasm_fixtures/wat/opc_reserved.wat b/src/test/app/wasm_fixtures/wat/opc_reserved.wat new file mode 100644 index 0000000000..0bc61b52c3 --- /dev/null +++ b/src/test/app/wasm_fixtures/wat/opc_reserved.wat @@ -0,0 +1,98 @@ +(module + + ;; Type for call_indirect + (type (func (result i32))) + + ;; Memory and table declarations + (memory 1) + (table 1 funcref) + (data (i32.const 0) "test") + (elem (i32.const 0) $test_func) + + ;; Global declarations + (global $g0 (mut i32) (i32.const 0)) + (global $g1 (mut i64) (i64.const 0)) + + ;; Test function for call/call_indirect + (func $test_func (result i32) + i32.const 42 + ) + + + ;; Main function with all instructions in hex order + (func $all_instructions (export "all_instructions") (result i32) + (local $l0 i32) + (local $l1 i64) + + ;; 0x01: nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + i32.const 11 + ) +) diff --git a/src/test/basics/Buffer_test.cpp b/src/test/basics/Buffer_test.cpp index 908650982c..f5f626133f 100644 --- a/src/test/basics/Buffer_test.cpp +++ b/src/test/basics/Buffer_test.cpp @@ -12,7 +12,7 @@ struct Buffer_test : beast::unit_test::suite bool sane(Buffer const& b) const { - if (b.size() == 0) + if (b.empty()) return b.data() == nullptr; return b.data() != nullptr; @@ -21,9 +21,10 @@ struct Buffer_test : beast::unit_test::suite void run() override { - std::uint8_t const data[] = {0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a, - 0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, - 0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; + std::uint8_t const data[] = {0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, + 0x71, 0x6d, 0x2a, 0x18, 0xb4, 0x70, 0xcb, 0xf5, + 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, 0xf0, 0x2c, + 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; Buffer b0; BEAST_EXPECT(sane(b0)); @@ -105,18 +106,18 @@ struct Buffer_test : beast::unit_test::suite { // Move-construct from empty buf Buffer x; Buffer y{std::move(x)}; - BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.empty()); + BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y.empty()); - BEAST_EXPECT(x == y); + BEAST_EXPECT(x == y); // NOLINT(bugprone-use-after-move) } { // Move-construct from non-empty buf Buffer x{b1}; Buffer y{std::move(x)}; - BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.empty()); + BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(sane(y)); BEAST_EXPECT(y == b1); } @@ -128,8 +129,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to empty buf @@ -139,8 +140,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x == b1); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign empty buf to non-empty buf @@ -150,8 +151,8 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) } { // Move assign non-empty buf to non-empty buf @@ -162,14 +163,14 @@ struct Buffer_test : beast::unit_test::suite x = std::move(y); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); - BEAST_EXPECT(sane(y)); - BEAST_EXPECT(y.empty()); + BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move) x = std::move(z); BEAST_EXPECT(sane(x)); BEAST_EXPECT(!x.empty()); - BEAST_EXPECT(sane(z)); - BEAST_EXPECT(z.empty()); + BEAST_EXPECT(sane(z)); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(z.empty()); // NOLINT(bugprone-use-after-move) } } @@ -240,13 +241,13 @@ struct Buffer_test : beast::unit_test::suite // Try to clear: x.clear(); BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.size() == 0); + BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); // Try to clear again: x.clear(); BEAST_EXPECT(sane(x)); - BEAST_EXPECT(x.size() == 0); + BEAST_EXPECT(x.empty()); BEAST_EXPECT(x.data() == nullptr); }; diff --git a/src/test/basics/Expected_test.cpp b/src/test/basics/Expected_test.cpp index 186e34d2e0..2caa15816f 100644 --- a/src/test/basics/Expected_test.cpp +++ b/src/test/basics/Expected_test.cpp @@ -85,7 +85,9 @@ struct Expected_test : beast::unit_test::suite } // Test error construction from rvalue. { - auto const expected = []() -> Expected { return Unexpected(telLOCAL_ERROR); }(); + auto const expected = []() -> Expected { + return Unexpected(telLOCAL_ERROR); + }(); BEAST_EXPECT(!expected); BEAST_EXPECT(!expected.has_value()); BEAST_EXPECT(expected.error() == telLOCAL_ERROR); @@ -126,14 +128,18 @@ struct Expected_test : beast::unit_test::suite } // Test error construction from const char*. { - auto const expected = []() -> Expected { return Unexpected("Not what is expected!"); }(); + auto const expected = []() -> Expected { + return Unexpected("Not what is expected!"); + }(); BEAST_EXPECT(!expected); BEAST_EXPECT(!expected.has_value()); BEAST_EXPECT(expected.error() == std::string("Not what is expected!")); } // Test error construction of string from const char*. { - auto expected = []() -> Expected { return Unexpected("Not what is expected!"); }(); + auto expected = []() -> Expected { + return Unexpected("Not what is expected!"); + }(); BEAST_EXPECT(!expected); BEAST_EXPECT(!expected.has_value()); BEAST_EXPECT(expected.error() == "Not what is expected!"); @@ -176,13 +182,17 @@ struct Expected_test : beast::unit_test::suite } // Test error const construction of Expected. { - auto const expected = []() -> Expected { return Unexpected("Not what is expected!"); }(); + auto const expected = []() -> Expected { + return Unexpected("Not what is expected!"); + }(); BEAST_EXPECT(!expected); BEAST_EXPECT(expected.error() == "Not what is expected!"); } // Test error non-const construction of Expected. { - auto expected = []() -> Expected { return Unexpected("Not what is expected!"); }(); + auto expected = []() -> Expected { + return Unexpected("Not what is expected!"); + }(); BEAST_EXPECT(!expected); BEAST_EXPECT(expected.error() == "Not what is expected!"); std::string const s(std::move(expected.error())); diff --git a/src/test/basics/FileUtilities_test.cpp b/src/test/basics/FileUtilities_test.cpp index d8664448d6..3d6f9b754b 100644 --- a/src/test/basics/FileUtilities_test.cpp +++ b/src/test/basics/FileUtilities_test.cpp @@ -17,7 +17,8 @@ public: constexpr char const* expectedContents = "This file is very short. That's all we need."; - FileDirGuard file(*this, "test_file", "test.txt", "This is temporary text that should get overwritten"); + FileDirGuard file( + *this, "test_file", "test.txt", "This is temporary text that should get overwritten"); error_code ec; auto const path = file.file(); diff --git a/src/test/basics/IntrusiveShared_test.cpp b/src/test/basics/IntrusiveShared_test.cpp index d0aa4c75e9..5a460a6044 100644 --- a/src/test/basics/IntrusiveShared_test.cpp +++ b/src/test/basics/IntrusiveShared_test.cpp @@ -144,7 +144,7 @@ public: } void - partialDestructor() + partialDestructor() const { using enum TrackedState; @@ -245,7 +245,7 @@ public: while (!weak.empty()) { weak.resize(weak.size() - 1); - if (weak.size()) + if (!weak.empty()) BEAST_EXPECT(TIBase::getState(id) == partiallyDeleted); } BEAST_EXPECT(TIBase::getState(id) == deleted); @@ -494,8 +494,12 @@ public: int s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; - auto setDestructorRan = [&]() -> void { destructionState.fetch_or(1, std::memory_order_acq_rel); }; - auto setPartialDeleteRan = [&]() -> void { destructionState.fetch_or(2, std::memory_order_acq_rel); }; + auto setDestructorRan = [&]() -> void { + destructionState.fetch_or(1, std::memory_order_acq_rel); + }; + auto setPartialDeleteRan = [&]() -> void { + destructionState.fetch_or(2, std::memory_order_acq_rel); + }; auto tracingCallback = [&](TrackedState cur, std::optional next) { using enum TrackedState; auto [destructorRan, partialDeleteRan] = getDestructorState(); @@ -623,8 +627,12 @@ public: int s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; - auto setDestructorRan = [&]() -> void { destructionState.fetch_or(1, std::memory_order_acq_rel); }; - auto setPartialDeleteRan = [&]() -> void { destructionState.fetch_or(2, std::memory_order_acq_rel); }; + auto setDestructorRan = [&]() -> void { + destructionState.fetch_or(1, std::memory_order_acq_rel); + }; + auto setPartialDeleteRan = [&]() -> void { + destructionState.fetch_or(2, std::memory_order_acq_rel); + }; auto tracingCallback = [&](TrackedState cur, std::optional next) { using enum TrackedState; auto [destructorRan, partialDeleteRan] = getDestructorState(); @@ -639,8 +647,9 @@ public: setDestructorRan(); } }; - auto createVecOfPointers = [&](auto const& toClone, - std::default_random_engine& eng) -> std::vector> { + auto createVecOfPointers = + [&](auto const& toClone, + std::default_random_engine& eng) -> std::vector> { std::vector> result; std::uniform_int_distribution<> toCreateDist(4, 64); auto numToCreate = toCreateDist(eng); @@ -758,8 +767,12 @@ public: int s = destructionState.load(std::memory_order_relaxed); return {(s & 1) != 0, (s & 2) != 0}; }; - auto setDestructorRan = [&]() -> void { destructionState.fetch_or(1, std::memory_order_acq_rel); }; - auto setPartialDeleteRan = [&]() -> void { destructionState.fetch_or(2, std::memory_order_acq_rel); }; + auto setDestructorRan = [&]() -> void { + destructionState.fetch_or(1, std::memory_order_acq_rel); + }; + auto setPartialDeleteRan = [&]() -> void { + destructionState.fetch_or(2, std::memory_order_acq_rel); + }; auto tracingCallback = [&](TrackedState cur, std::optional next) { using enum TrackedState; auto [destructorRan, partialDeleteRan] = getDestructorState(); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 5838a98a15..02a1e49232 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -66,17 +66,20 @@ public: test( Number{std::numeric_limits::min()}, - scale == MantissaRange::small ? Number{-9'223'372'036'854'776, 3} - : Number{true, 9'223'372'036'854'775'808ULL, 0, Number::normalized{}}, + scale == MantissaRange::small + ? Number{-9'223'372'036'854'776, 3} + : Number{true, 9'223'372'036'854'775'808ULL, 0, Number::normalized{}}, __LINE__); test( Number{std::numeric_limits::min() + 1}, - scale == MantissaRange::small ? Number{-9'223'372'036'854'776, 3} : Number{-9'223'372'036'854'775'807}, + scale == MantissaRange::small ? Number{-9'223'372'036'854'776, 3} + : Number{-9'223'372'036'854'775'807}, __LINE__); test( Number{std::numeric_limits::max()}, Number{ - scale == MantissaRange::small ? 9'223'372'036'854'776 : std::numeric_limits::max(), + scale == MantissaRange::small ? 9'223'372'036'854'776 + : std::numeric_limits::max(), 18 - Number::mantissaLog()}, __LINE__); caught = false; @@ -114,7 +117,9 @@ public: Number{9'999'999'999'999'344, -16}}, {Number{}, Number{5}, Number{5}}, {Number{5}, Number{}, Number{5}}, - {Number{5'555'555'555'555'555, -32768}, Number{-5'555'555'555'555'554, -32768}, Number{0}}, + {Number{5'555'555'555'555'555, -32768}, + Number{-5'555'555'555'555'554, -32768}, + Number{0}}, {Number{-9'999'999'999'999'999, -31}, Number{1'000'000'000'000'000, -15}, Number{9'999'999'999'999'990, -16}}}); @@ -137,7 +142,9 @@ public: Number{false, 9'999'999'999'999'344'444ULL, -19, Number::normalized{}}}, {Number{}, Number{5}, Number{5}}, {Number{5}, Number{}, Number{5}}, - {Number{5'555'555'555'555'555'000, -32768}, Number{-5'555'555'555'555'554'000, -32768}, Number{0}}, + {Number{5'555'555'555'555'555'000, -32768}, + Number{-5'555'555'555'555'554'000, -32768}, + Number{0}}, {Number{-9'999'999'999'999'999, -31}, Number{1'000'000'000'000'000, -15}, Number{9'999'999'999'999'990, -16}}, @@ -155,7 +162,9 @@ public: Number{1'000'000'000'000'000'000, -18}, Number{false, 9'999'999'999'999'999'344ULL, -19, Number::normalized{}}}, {Number{}, Number{5}, Number{5}}, - {Number{5'555'555'555'555'555'555, -32768}, Number{-5'555'555'555'555'555'554, -32768}, Number{0}}, + {Number{5'555'555'555'555'555'555, -32768}, + Number{-5'555'555'555'555'555'554, -32768}, + Number{0}}, {Number{true, 9'999'999'999'999'999'999ULL, -37, Number::normalized{}}, Number{1'000'000'000'000'000'000, -18}, Number{false, 9'999'999'999'999'999'990ULL, -19, Number::normalized{}}}, @@ -190,9 +199,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } { bool caught = false; try @@ -254,7 +267,9 @@ public: {Number{6'555'555'555'555'555'555, -32}, Number{1'000'000'000'000'000'000, -18}, Number{true, 9'999'999'999'999'344'444ULL, -19, Number::normalized{}}}, - {Number{1'000'000'000'000'000'000, -18}, Number{1'000'000'000'000'000'000, -18}, Number{0}}, + {Number{1'000'000'000'000'000'000, -18}, + Number{1'000'000'000'000'000'000, -18}, + Number{0}}, {Number{1'000'000'000'000'000'000, -18}, Number{1'000'000'000'000'000'001, -18}, Number{-1'000'000'000'000'000'000, -36}}, @@ -265,7 +280,9 @@ public: {Number{false, Number::maxRep + 1, 0, Number::normalized{}}, Number{1, 0}, Number{Number::maxRep / 10 + 1, 1}}, - {Number{false, Number::maxRep + 1, 0, Number::normalized{}}, Number{3, 0}, Number{Number::maxRep}}, + {Number{false, Number::maxRep + 1, 0, Number::normalized{}}, + Number{3, 0}, + Number{Number::maxRep}}, {power(2, 63), Number{3, 0}, Number{Number::maxRep}}, }); auto test = [this](auto const& c) { @@ -278,9 +295,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } } void @@ -301,9 +322,13 @@ public: }; auto tests = [&](auto const& cSmall, auto const& cLarge) { if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } }; auto const maxMantissa = Number::maxMantissa(); @@ -311,30 +336,50 @@ public: { auto const cSmall = std::to_array({ {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{2000000000000000, -15}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-2000000000000000, -15}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{2000000000000000, -15}}, - {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{1000000000000000, -14}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{2000000000000000, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{1000000000000000, -14}}, {Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}, // Maximum mantissa range - {Number{9'999'999'999'999'999, 0}, Number{9'999'999'999'999'999, 0}, Number{9'999'999'999'999'998, 16}}, + {Number{9'999'999'999'999'999, 0}, + Number{9'999'999'999'999'999, 0}, + Number{9'999'999'999'999'998, 16}}, }); auto const cLarge = std::to_array({ // Note that items with extremely large mantissas need to be // calculated, because otherwise they overflow uint64. Items // from C with larger mantissa {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999862, -18}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999862, -18}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999862, -18}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999862, -18}}, {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}}, - {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, // Items from cSmall expanded for the larger mantissa, // except duplicates. Sadly, it looks like sqrt(2)^2 != 2 // with higher precision - {Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2000000000000000001, -18}}, + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2000000000000000001, -18}}, {Number{-1414213562373095048, -18}, Number{1414213562373095048, -18}, Number{-1999999999999999998, -18}}, @@ -347,7 +392,9 @@ public: Number{false, maxMantissa, 0, Number::normalized{}}, Number{1, 38}}, // Maximum int64 range - {Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'85, 19}}, + {Number{Number::maxRep, 0}, + Number{Number::maxRep, 0}, + Number{85'070'591'730'234'615'85, 19}}, }); tests(cSmall, cLarge); } @@ -356,10 +403,18 @@ public: { auto const cSmall = std::to_array( {{Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999, -15}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999, -15}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999, -15}}, - {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{9999999999999999, -15}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999, -15}}, {Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}}); auto const cLarge = std::to_array( // Note that items with extremely large mantissas need to be @@ -367,24 +422,36 @@ public: // from C with larger mantissa { {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999861, -18}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999861, -18}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999861, -18}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999861, -18}}, {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{false, 9999999999999999579ULL, -18, Number::normalized{}}}, - {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, // Items from cSmall expanded for the larger mantissa, // except duplicates. Sadly, it looks like sqrt(2)^2 != 2 // with higher precision - {Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}}, + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2, 0}}, {Number{-1414213562373095048, -18}, Number{1414213562373095048, -18}, Number{-1999999999999999997, -18}}, {Number{-1414213562373095048, -18}, Number{-1414213562373095049, -18}, Number{1999999999999999999, -18}}, - {Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{10, 0}}, // Maximum mantissa range - rounds down to maxMantissa/10e1 // 99'999'999'999'999'999'800'000'000'000'000'000'100 {Number{false, maxMantissa, 0, Number::normalized{}}, @@ -392,7 +459,9 @@ public: Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}}, // Maximum int64 range // 85'070'591'730'234'615'847'396'907'784'232'501'249 - {Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'84, 19}}, + {Number{Number::maxRep, 0}, + Number{Number::maxRep, 0}, + Number{85'070'591'730'234'615'84, 19}}, }); tests(cSmall, cLarge); } @@ -401,10 +470,18 @@ public: { auto const cSmall = std::to_array( {{Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999, -15}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-2000000000000000, -15}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999, -15}}, - {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{9999999999999999, -15}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999, -15}}, {Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}}); auto const cLarge = std::to_array( // Note that items with extremely large mantissas need to be @@ -412,24 +489,36 @@ public: // from C with larger mantissa { {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999861, -18}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999862, -18}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999861, -18}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999861, -18}}, {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}}, - {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, // Items from cSmall expanded for the larger mantissa, // except duplicates. Sadly, it looks like sqrt(2)^2 != 2 // with higher precision - {Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}}, + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2, 0}}, {Number{-1414213562373095048, -18}, Number{1414213562373095048, -18}, Number{-1999999999999999998, -18}}, {Number{-1414213562373095048, -18}, Number{-1414213562373095049, -18}, Number{1999999999999999999, -18}}, - {Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{10, 0}}, // Maximum mantissa range - rounds down to maxMantissa/10e1 // 99'999'999'999'999'999'800'000'000'000'000'000'100 {Number{false, maxMantissa, 0, Number::normalized{}}, @@ -437,7 +526,9 @@ public: Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}}, // Maximum int64 range // 85'070'591'730'234'615'847'396'907'784'232'501'249 - {Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'84, 19}}, + {Number{Number::maxRep, 0}, + Number{Number::maxRep, 0}, + Number{85'070'591'730'234'615'84, 19}}, }); tests(cSmall, cLarge); } @@ -446,10 +537,18 @@ public: { auto const cSmall = std::to_array( {{Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{2000000000000000, -15}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999, -15}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{2000000000000000, -15}}, - {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{1000000000000000, -14}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{2000000000000000, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{1000000000000000, -14}}, {Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}}); auto const cLarge = std::to_array( // Note that items with extremely large mantissas need to be @@ -457,11 +556,21 @@ public: // from C with larger mantissa { {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999862, -18}}, - {Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999861, -18}}, - {Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999862, -18}}, - {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{999999999999999958, -17}}, - {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{999999999999999958, -17}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, // Items from cSmall expanded for the larger mantissa, // except duplicates. Sadly, it looks like sqrt(2)^2 != 2 // with higher precision @@ -471,7 +580,9 @@ public: {Number{-1414213562373095048, -18}, Number{1414213562373095048, -18}, Number{-1999999999999999997, -18}}, - {Number{-1414213562373095048, -18}, Number{-1414213562373095049, -18}, Number{2, 0}}, + {Number{-1414213562373095048, -18}, + Number{-1414213562373095049, -18}, + Number{2, 0}}, {Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{1000000000000000001, -17}}, @@ -482,7 +593,9 @@ public: Number{1, 38}}, // Maximum int64 range // 85'070'591'730'234'615'847'396'907'784'232'501'249 - {Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'85, 19}}, + {Number{Number::maxRep, 0}, + Number{Number::maxRep, 0}, + Number{85'070'591'730'234'615'85, 19}}, }); tests(cSmall, cLarge); } @@ -521,9 +634,13 @@ public: auto const maxMantissa = Number::maxMantissa(); auto tests = [&](auto const& cSmall, auto const& cLarge) { if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } }; saveNumberRoundMode save{Number::setround(Number::to_nearest)}; { @@ -533,7 +650,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); @@ -546,7 +665,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666'667, -19}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666'667, -19}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, @@ -567,7 +688,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); @@ -580,7 +703,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666'666, -19}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666'666, -19}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, @@ -601,7 +726,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); @@ -614,7 +741,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666'666, -19}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666'667, -19}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, @@ -635,7 +764,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}, {Number{1}, Number{7}, Number{1'428'571'428'571'429, -16}}}); @@ -648,7 +779,9 @@ public: {Number{1}, Number{-10}, Number{-1, -1}}, {Number{0}, Number{100}, Number{0}}, {Number{1414213562373095, -10}, Number{1414213562373095, -10}, Number{1}}, - {Number{9'999'999'999'999'999}, Number{1'000'000'000'000'000}, Number{9'999'999'999'999'999, -15}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, {Number{2}, Number{3}, Number{6'666'666'666'666'666'667, -19}}, {Number{-2}, Number{3}, Number{-6'666'666'666'666'666'666, -19}}, {Number{1}, Number{7}, Number{1'428'571'428'571'428'572, -19}}, @@ -714,8 +847,12 @@ public: {Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}}, 2, Number{false, 3'162'277'660'168'379'330, -9, Number::normalized{}}}, - {Number{Number::maxRep}, 2, Number{false, 3'037'000'499'976049692, -9, Number::normalized{}}}, - {Number{Number::maxRep}, 4, Number{false, 55'108'98747006743627, -14, Number::normalized{}}}, + {Number{Number::maxRep}, + 2, + Number{false, 3'037'000'499'976049692, -9, Number::normalized{}}}, + {Number{Number::maxRep}, + 4, + Number{false, 55'108'98747006743627, -14, Number::normalized{}}}, }); test(cSmall); if (Number::getMantissaScale() != MantissaRange::small) @@ -1100,14 +1237,23 @@ public: auto const maxMantissa = Number::maxMantissa(); BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999); - test(Number{false, maxMantissa * 1000 + 999, -3, Number::normalized()}, "9999999999999999"); - test(Number{true, maxMantissa * 1000 + 999, -3, Number::normalized()}, "-9999999999999999"); + test( + Number{false, maxMantissa * 1000 + 999, -3, Number::normalized()}, + "9999999999999999"); + test( + Number{true, maxMantissa * 1000 + 999, -3, Number::normalized()}, + "-9999999999999999"); test(Number{std::numeric_limits::max(), -3}, "9223372036854775"); - test(-(Number{std::numeric_limits::max(), -3}), "-9223372036854775"); + test( + -(Number{std::numeric_limits::max(), -3}), + "-9223372036854775"); - test(Number{std::numeric_limits::min(), 0}, "-9223372036854775e3"); - test(-(Number{std::numeric_limits::min(), 0}), "9223372036854775e3"); + test( + Number{std::numeric_limits::min(), 0}, "-9223372036854775e3"); + test( + -(Number{std::numeric_limits::min(), 0}), + "9223372036854775e3"); } break; case MantissaRange::large: @@ -1121,21 +1267,33 @@ public: auto const maxMantissa = Number::maxMantissa(); BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999'999ULL); - test(Number{false, maxMantissa, 0, Number::normalized{}}, "9999999999999999990"); - test(Number{true, maxMantissa, 0, Number::normalized{}}, "-9999999999999999990"); + test( + Number{false, maxMantissa, 0, Number::normalized{}}, "9999999999999999990"); + test( + Number{true, maxMantissa, 0, Number::normalized{}}, "-9999999999999999990"); - test(Number{std::numeric_limits::max(), 0}, "9223372036854775807"); - test(-(Number{std::numeric_limits::max(), 0}), "-9223372036854775807"); + test( + Number{std::numeric_limits::max(), 0}, "9223372036854775807"); + test( + -(Number{std::numeric_limits::max(), 0}), + "-9223372036854775807"); // Because the absolute value of min is larger than max, it // will be scaled down to fit under max. Since we're // rounding towards zero, the 8 at the end is dropped. - test(Number{std::numeric_limits::min(), 0}, "-9223372036854775800"); - test(-(Number{std::numeric_limits::min(), 0}), "9223372036854775800"); + test( + Number{std::numeric_limits::min(), 0}, + "-9223372036854775800"); + test( + -(Number{std::numeric_limits::min(), 0}), + "9223372036854775800"); } - test(Number{std::numeric_limits::max(), 0} + 1, "9223372036854775810"); - test(-(Number{std::numeric_limits::max(), 0} + 1), "-9223372036854775810"); + test( + Number{std::numeric_limits::max(), 0} + 1, "9223372036854775810"); + test( + -(Number{std::numeric_limits::max(), 0} + 1), + "-9223372036854775810"); break; default: BEAST_EXPECT(false); @@ -1233,39 +1391,87 @@ public: std::map const expected{ // Positive numbers {Number{13, -1}, - {{Number::to_nearest, 1}, {Number::towards_zero, 1}, {Number::downward, 1}, {Number::upward, 2}}}, + {{Number::to_nearest, 1}, + {Number::towards_zero, 1}, + {Number::downward, 1}, + {Number::upward, 2}}}, {Number{23, -1}, - {{Number::to_nearest, 2}, {Number::towards_zero, 2}, {Number::downward, 2}, {Number::upward, 3}}}, + {{Number::to_nearest, 2}, + {Number::towards_zero, 2}, + {Number::downward, 2}, + {Number::upward, 3}}}, {Number{15, -1}, - {{Number::to_nearest, 2}, {Number::towards_zero, 1}, {Number::downward, 1}, {Number::upward, 2}}}, + {{Number::to_nearest, 2}, + {Number::towards_zero, 1}, + {Number::downward, 1}, + {Number::upward, 2}}}, {Number{25, -1}, - {{Number::to_nearest, 2}, {Number::towards_zero, 2}, {Number::downward, 2}, {Number::upward, 3}}}, + {{Number::to_nearest, 2}, + {Number::towards_zero, 2}, + {Number::downward, 2}, + {Number::upward, 3}}}, {Number{152, -2}, - {{Number::to_nearest, 2}, {Number::towards_zero, 1}, {Number::downward, 1}, {Number::upward, 2}}}, + {{Number::to_nearest, 2}, + {Number::towards_zero, 1}, + {Number::downward, 1}, + {Number::upward, 2}}}, {Number{252, -2}, - {{Number::to_nearest, 3}, {Number::towards_zero, 2}, {Number::downward, 2}, {Number::upward, 3}}}, + {{Number::to_nearest, 3}, + {Number::towards_zero, 2}, + {Number::downward, 2}, + {Number::upward, 3}}}, {Number{17, -1}, - {{Number::to_nearest, 2}, {Number::towards_zero, 1}, {Number::downward, 1}, {Number::upward, 2}}}, + {{Number::to_nearest, 2}, + {Number::towards_zero, 1}, + {Number::downward, 1}, + {Number::upward, 2}}}, {Number{27, -1}, - {{Number::to_nearest, 3}, {Number::towards_zero, 2}, {Number::downward, 2}, {Number::upward, 3}}}, + {{Number::to_nearest, 3}, + {Number::towards_zero, 2}, + {Number::downward, 2}, + {Number::upward, 3}}}, // Negative numbers {Number{-13, -1}, - {{Number::to_nearest, -1}, {Number::towards_zero, -1}, {Number::downward, -2}, {Number::upward, -1}}}, + {{Number::to_nearest, -1}, + {Number::towards_zero, -1}, + {Number::downward, -2}, + {Number::upward, -1}}}, {Number{-23, -1}, - {{Number::to_nearest, -2}, {Number::towards_zero, -2}, {Number::downward, -3}, {Number::upward, -2}}}, + {{Number::to_nearest, -2}, + {Number::towards_zero, -2}, + {Number::downward, -3}, + {Number::upward, -2}}}, {Number{-15, -1}, - {{Number::to_nearest, -2}, {Number::towards_zero, -1}, {Number::downward, -2}, {Number::upward, -1}}}, + {{Number::to_nearest, -2}, + {Number::towards_zero, -1}, + {Number::downward, -2}, + {Number::upward, -1}}}, {Number{-25, -1}, - {{Number::to_nearest, -2}, {Number::towards_zero, -2}, {Number::downward, -3}, {Number::upward, -2}}}, + {{Number::to_nearest, -2}, + {Number::towards_zero, -2}, + {Number::downward, -3}, + {Number::upward, -2}}}, {Number{-152, -2}, - {{Number::to_nearest, -2}, {Number::towards_zero, -1}, {Number::downward, -2}, {Number::upward, -1}}}, + {{Number::to_nearest, -2}, + {Number::towards_zero, -1}, + {Number::downward, -2}, + {Number::upward, -1}}}, {Number{-252, -2}, - {{Number::to_nearest, -3}, {Number::towards_zero, -2}, {Number::downward, -3}, {Number::upward, -2}}}, + {{Number::to_nearest, -3}, + {Number::towards_zero, -2}, + {Number::downward, -3}, + {Number::upward, -2}}}, {Number{-17, -1}, - {{Number::to_nearest, -2}, {Number::towards_zero, -1}, {Number::downward, -2}, {Number::upward, -1}}}, + {{Number::to_nearest, -2}, + {Number::towards_zero, -1}, + {Number::downward, -2}, + {Number::upward, -1}}}, {Number{-27, -1}, - {{Number::to_nearest, -3}, {Number::towards_zero, -2}, {Number::downward, -3}, {Number::upward, -2}}}, + {{Number::to_nearest, -3}, + {Number::towards_zero, -2}, + {Number::downward, -3}, + {Number::upward, -2}}}, }; for (auto const& [num, roundings] : expected) @@ -1276,8 +1482,8 @@ public: auto const res = static_cast(num); BEAST_EXPECTS( res == val, - to_string(num) + " with mode " + std::to_string(mode) + " expected " + std::to_string(val) + - " got " + std::to_string(res)); + to_string(num) + " with mode " + std::to_string(mode) + " expected " + + std::to_string(val) + " got " + std::to_string(res)); } } } @@ -1330,7 +1536,8 @@ public: BEAST_EXPECT(max.exponent() == 1); // 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38 // digits - BEAST_EXPECT((power(max, 2) == Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}})); + BEAST_EXPECT( + (power(max, 2) == Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}})); } } @@ -1365,7 +1572,8 @@ public: Number{false, 1'746'901'684'478'673'451ll, -17, Number::normalized{}}}, {Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}}, Number{false, 1'846'901'684'478'673'451ll, -17, Number::normalized{}}}, - {Number{Number::maxRep}, Number{false, 1'861'728'612'932'620'011ll, -17, Number::normalized{}}}}); + {Number{Number::maxRep}, + Number{false, 1'861'728'612'932'620'011ll, -17, Number::normalized{}}}}); if (Number::getMantissaScale() == MantissaRange::small) { diff --git a/src/test/basics/PerfLog_test.cpp b/src/test/basics/PerfLog_test.cpp index 57eed2aed9..ce0109c53d 100644 --- a/src/test/basics/PerfLog_test.cpp +++ b/src/test/basics/PerfLog_test.cpp @@ -85,8 +85,12 @@ class PerfLog_test : public beast::unit_test::suite std::unique_ptr perfLog(WithFile withFile) { - perf::PerfLog::Setup const setup{withFile == WithFile::no ? "" : logFile(), logInterval()}; - return perf::make_PerfLog(setup, app_, j_, [this]() { return signalStop(); }); + perf::PerfLog::Setup const setup{ + withFile == WithFile::no ? "" : logFile(), logInterval()}; + return perf::make_PerfLog(setup, app_, j_, [this]() { + signalStop(); + return; + }); } // Block until the log file has grown in size, indicating that the @@ -235,7 +239,9 @@ public: return; boost::filesystem::permissions( - fixture.logFile(), perms::remove_perms | perms::owner_write | perms::others_write | perms::group_write); + fixture.logFile(), + perms::remove_perms | perms::owner_write | perms::others_write | + perms::group_write); // If the test is running as root, then the write protect may have // no effect. Make sure write protect worked before proceeding. @@ -260,7 +266,8 @@ public: // Fix file permissions so the file can be cleaned up. boost::filesystem::permissions( - fixture.logFile(), perms::add_perms | perms::owner_write | perms::others_write | perms::group_write); + fixture.logFile(), + perms::add_perms | perms::owner_write | perms::others_write | perms::group_write); } } @@ -284,13 +291,13 @@ public: std::vector ids; ids.reserve(labels.size() * 2); std::generate_n( - std::back_inserter(ids), labels.size(), [i = std::numeric_limits::min()]() mutable { - return i++; - }); + std::back_inserter(ids), + labels.size(), + [i = std::numeric_limits::min()]() mutable { return i++; }); std::generate_n( - std::back_inserter(ids), labels.size(), [i = std::numeric_limits::max()]() mutable { - return i--; - }); + std::back_inserter(ids), + labels.size(), + [i = std::numeric_limits::max()]() mutable { return i--; }); std::shuffle(ids.begin(), ids.end(), default_prng()); // Start all of the RPC commands twice to show they can all be tracked @@ -447,8 +454,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. validateFinalCounters(parsedLastLine[jss::counters]); @@ -579,7 +588,8 @@ public: BEAST_EXPECT(total[jss::finished] == "0"); // Total queued duration is triangle number of (i + 1). - BEAST_EXPECT(jsonToUint64(total[jss::queued_duration_us]) == (((i * i) + 3 * i + 2) / 2)); + BEAST_EXPECT( + jsonToUint64(total[jss::queued_duration_us]) == (((i * i) + 3 * i + 2) / 2)); BEAST_EXPECT(total[jss::running_duration_us] == "0"); } @@ -765,8 +775,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. validateFinalCounters(parsedLastLine[jss::counters]); @@ -788,7 +800,7 @@ public: perfLog->start(); // Randomly select a job type and its name. - JobType jobType; + JobType jobType = jtINVALID; std::string jobTypeName; { auto const& jobTypes = JobTypes::instance(); @@ -805,31 +817,34 @@ public: perfLog->resizeJobs(1); // Lambda to validate countersJson for this test. - auto verifyCounters = - [this, jobTypeName]( - Json::Value const& countersJson, int started, int finished, int queued_us, int running_us) { - BEAST_EXPECT(countersJson.isObject()); - BEAST_EXPECT(countersJson.size() == 2); + auto verifyCounters = [this, jobTypeName]( + Json::Value const& countersJson, + int started, + int finished, + int queued_us, + int running_us) { + BEAST_EXPECT(countersJson.isObject()); + BEAST_EXPECT(countersJson.size() == 2); - BEAST_EXPECT(countersJson.isMember(jss::rpc)); - BEAST_EXPECT(countersJson[jss::rpc].isObject()); - BEAST_EXPECT(countersJson[jss::rpc].size() == 0); + BEAST_EXPECT(countersJson.isMember(jss::rpc)); + BEAST_EXPECT(countersJson[jss::rpc].isObject()); + BEAST_EXPECT(countersJson[jss::rpc].size() == 0); - BEAST_EXPECT(countersJson.isMember(jss::job_queue)); - BEAST_EXPECT(countersJson[jss::job_queue].isObject()); - BEAST_EXPECT(countersJson[jss::job_queue].size() == 1); - { - Json::Value const& job{countersJson[jss::job_queue][jobTypeName]}; + BEAST_EXPECT(countersJson.isMember(jss::job_queue)); + BEAST_EXPECT(countersJson[jss::job_queue].isObject()); + BEAST_EXPECT(countersJson[jss::job_queue].size() == 1); + { + Json::Value const& job{countersJson[jss::job_queue][jobTypeName]}; - BEAST_EXPECT(job.isObject()); - BEAST_EXPECT(jsonToUint64(job[jss::queued]) == 0); - BEAST_EXPECT(jsonToUint64(job[jss::started]) == started); - BEAST_EXPECT(jsonToUint64(job[jss::finished]) == finished); + BEAST_EXPECT(job.isObject()); + BEAST_EXPECT(jsonToUint64(job[jss::queued]) == 0); + BEAST_EXPECT(jsonToUint64(job[jss::started]) == started); + BEAST_EXPECT(jsonToUint64(job[jss::finished]) == finished); - BEAST_EXPECT(jsonToUint64(job[jss::queued_duration_us]) == queued_us); - BEAST_EXPECT(jsonToUint64(job[jss::running_duration_us]) == running_us); - } - }; + BEAST_EXPECT(jsonToUint64(job[jss::queued_duration_us]) == queued_us); + BEAST_EXPECT(jsonToUint64(job[jss::running_duration_us]) == running_us); + } + }; // Lambda to validate currentJson (always empty) for this test. auto verifyEmptyCurrent = [this](Json::Value const& currentJson) { @@ -900,8 +915,10 @@ public: Json::Value parsedLastLine; Json::Reader().parse(lastLine, parsedLastLine); if (!BEAST_EXPECT(!RPC::contains_error(parsedLastLine))) + { // Avoid cascade of failures return; + } // Validate the contents of the last line of the log. verifyCounters(parsedLastLine[jss::counters], 2, 2, 24, 36); diff --git a/src/test/basics/StringUtilities_test.cpp b/src/test/basics/StringUtilities_test.cpp index 65134da8e5..78719e47c6 100644 --- a/src/test/basics/StringUtilities_test.cpp +++ b/src/test/basics/StringUtilities_test.cpp @@ -13,6 +13,8 @@ public: { auto rv = strUnHex(strIn); BEAST_EXPECT(rv); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(makeSlice(*rv) == makeSlice(strExpected)); } @@ -92,7 +94,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 234); + BEAST_EXPECT(*pUrl.port == 234); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/"); } @@ -114,7 +116,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "::1"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/path"); } @@ -125,7 +127,7 @@ public: BEAST_EXPECT(pUrl.username == "user"); BEAST_EXPECT(pUrl.password == "pass"); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -136,7 +138,7 @@ public: BEAST_EXPECT(pUrl.username == "user"); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -147,7 +149,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password == "pass"); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } @@ -158,7 +160,7 @@ public: BEAST_EXPECT(pUrl.username.empty()); BEAST_EXPECT(pUrl.password.empty()); BEAST_EXPECT(pUrl.domain == "domain"); - BEAST_EXPECT(*pUrl.port == 123); + BEAST_EXPECT(*pUrl.port == 123); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(pUrl.path == "/abc:321"); } diff --git a/src/test/basics/Units_test.cpp b/src/test/basics/Units_test.cpp index 9094730870..9693c6d181 100644 --- a/src/test/basics/Units_test.cpp +++ b/src/test/basics/Units_test.cpp @@ -31,8 +31,10 @@ private: auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); - BEAST_EXPECT((std::is_same_v::unit_type, unit::dropTag>)); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT((std::is_same_v< + std::remove_reference_t::unit_type, + unit::dropTag>)); BEAST_EXPECT((std::is_same_v, XRPAmount>)); } @@ -50,8 +52,10 @@ private: auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); - BEAST_EXPECT((std::is_same_v::unit_type, unit::dropTag>)); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT((std::is_same_v< + std::remove_reference_t::unit_type, + unit::dropTag>)); BEAST_EXPECT((std::is_same_v, XRPAmount>)); } { @@ -69,8 +73,10 @@ private: auto drops = mulDiv(x, basefee, referencefee); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 40); - BEAST_EXPECT((std::is_same_v::unit_type, unit::dropTag>)); + BEAST_EXPECT(drops.value() == 40); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT((std::is_same_v< + std::remove_reference_t::unit_type, + unit::dropTag>)); BEAST_EXPECT((std::is_same_v, XRPAmount>)); } } diff --git a/src/test/basics/XRPAmount_test.cpp b/src/test/basics/XRPAmount_test.cpp index 391917e678..58b15b5d2d 100644 --- a/src/test/basics/XRPAmount_test.cpp +++ b/src/test/basics/XRPAmount_test.cpp @@ -16,11 +16,17 @@ public: XRPAmount const x(i); if (i < 0) + { BEAST_EXPECT(x.signum() < 0); + } else if (i > 0) + { BEAST_EXPECT(x.signum() > 0); + } else + { BEAST_EXPECT(x.signum() == 0); + } } } @@ -121,7 +127,7 @@ public: // since some of them are templated, but not used anywhere else. auto make = [&](auto x) -> XRPAmount { return XRPAmount{x}; }; - XRPAmount defaulted; + XRPAmount defaulted{}; (void)defaulted; XRPAmount test{0}; BEAST_EXPECT(test.drops() == 0); @@ -154,7 +160,7 @@ public: BEAST_EXPECT(test.drops() == 200); auto testOther = test.dropsAs(); BEAST_EXPECT(testOther); - BEAST_EXPECT(*testOther == 200); + BEAST_EXPECT(*testOther == 200); // NOLINT(bugprone-unchecked-optional-access) test = std::numeric_limits::max(); testOther = test.dropsAs(); BEAST_EXPECT(!testOther); diff --git a/src/test/basics/base58_test.cpp b/src/test/basics/base58_test.cpp index 5da22d533e..ca62ac02ef 100644 --- a/src/test/basics/base58_test.cpp +++ b/src/test/basics/base58_test.cpp @@ -73,7 +73,8 @@ randomTokenTypeAndSize() -> std::tuple // Return the token type and subspan of `d` to use as test data. [[nodiscard]] inline auto -randomB256TestData(std::span d) -> std::tuple> +randomB256TestData(std::span d) + -> std::tuple> { auto& rng = randEngine(); std::uniform_int_distribution dist(0, 255); @@ -164,14 +165,14 @@ class base58_test : public beast::unit_test::suite if (!d) continue; auto bigInt = multiprecision_utils::randomBigInt(); - auto const boostBigInt = - multiprecision_utils::toBoostMP(std::span(bigInt.data(), bigInt.size())); + auto const boostBigInt = multiprecision_utils::toBoostMP( + std::span(bigInt.data(), bigInt.size())); auto const refDiv = boostBigInt / d; auto const refMod = boostBigInt % d; - auto const mod = - b58_fast::detail::inplace_bigint_div_rem(std::span(bigInt.data(), bigInt.size()), d); + auto const mod = b58_fast::detail::inplace_bigint_div_rem( + std::span(bigInt.data(), bigInt.size()), d); auto const foundDiv = multiprecision_utils::toBoostMP(bigInt); BEAST_EXPECT(refMod.convert_to() == mod); BEAST_EXPECT(foundDiv == refDiv); @@ -184,13 +185,13 @@ class base58_test : public beast::unit_test::suite { bigInt[bigInt.size() - 1] -= 1; // Prevent overflow } - auto const boostBigInt = - multiprecision_utils::toBoostMP(std::span(bigInt.data(), bigInt.size())); + auto const boostBigInt = multiprecision_utils::toBoostMP( + std::span(bigInt.data(), bigInt.size())); auto const refAdd = boostBigInt + d; - auto const result = - b58_fast::detail::inplace_bigint_add(std::span(bigInt.data(), bigInt.size()), d); + auto const result = b58_fast::detail::inplace_bigint_add( + std::span(bigInt.data(), bigInt.size()), d); BEAST_EXPECT(result == TokenCodecErrc::success); auto const foundAdd = multiprecision_utils::toBoostMP(bigInt); BEAST_EXPECT(refAdd == foundAdd); @@ -201,13 +202,13 @@ class base58_test : public beast::unit_test::suite // Force overflow std::vector bigInt(5, std::numeric_limits::max()); - auto const boostBigInt = - multiprecision_utils::toBoostMP(std::span(bigInt.data(), bigInt.size())); + auto const boostBigInt = multiprecision_utils::toBoostMP( + std::span(bigInt.data(), bigInt.size())); auto const refAdd = boostBigInt + d; - auto const result = - b58_fast::detail::inplace_bigint_add(std::span(bigInt.data(), bigInt.size()), d); + auto const result = b58_fast::detail::inplace_bigint_add( + std::span(bigInt.data(), bigInt.size()), d); BEAST_EXPECT(result == TokenCodecErrc::overflowAdd); auto const foundAdd = multiprecision_utils::toBoostMP(bigInt); BEAST_EXPECT(refAdd != foundAdd); @@ -219,13 +220,13 @@ class base58_test : public beast::unit_test::suite // inplace mul requires the most significant coeff to be zero to // hold the result. bigInt[bigInt.size() - 1] = 0; - auto const boostBigInt = - multiprecision_utils::toBoostMP(std::span(bigInt.data(), bigInt.size())); + auto const boostBigInt = multiprecision_utils::toBoostMP( + std::span(bigInt.data(), bigInt.size())); auto const refMul = boostBigInt * d; - auto const result = - b58_fast::detail::inplace_bigint_mul(std::span(bigInt.data(), bigInt.size()), d); + auto const result = b58_fast::detail::inplace_bigint_mul( + std::span(bigInt.data(), bigInt.size()), d); BEAST_EXPECT(result == TokenCodecErrc::success); auto const foundMul = multiprecision_utils::toBoostMP(bigInt); BEAST_EXPECT(refMul == foundMul); @@ -235,13 +236,13 @@ class base58_test : public beast::unit_test::suite std::uint64_t const d = dist1(eng); // Force overflow std::vector bigInt(5, std::numeric_limits::max()); - auto const boostBigInt = - multiprecision_utils::toBoostMP(std::span(bigInt.data(), bigInt.size())); + auto const boostBigInt = multiprecision_utils::toBoostMP( + std::span(bigInt.data(), bigInt.size())); auto const refMul = boostBigInt * d; - auto const result = - b58_fast::detail::inplace_bigint_mul(std::span(bigInt.data(), bigInt.size()), d); + auto const result = b58_fast::detail::inplace_bigint_mul( + std::span(bigInt.data(), bigInt.size()), d); BEAST_EXPECT(result == TokenCodecErrc::inputTooLarge); auto const foundMul = multiprecision_utils::toBoostMP(bigInt); BEAST_EXPECT(refMul != foundMul); @@ -269,7 +270,7 @@ class base58_test : public beast::unit_test::suite } else { - std::array tmpBuf; + std::array tmpBuf{}; std::string const s = xrpl::b58_ref::detail::encodeBase58( b256Data.data(), b256Data.size(), tmpBuf.data(), tmpBuf.size()); BEAST_EXPECT(s.size()); @@ -279,7 +280,8 @@ class base58_test : public beast::unit_test::suite } if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size())) { - if (!BEAST_EXPECT(memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0)) + if (!BEAST_EXPECT( + memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0)) { printAsChar(b58Result[0], b58Result[1]); } @@ -290,7 +292,8 @@ class base58_test : public beast::unit_test::suite std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()}; if (i == 0) { - std::string const in(b58Result[i].data(), b58Result[i].data() + b58Result[i].size()); + std::string const in( + b58Result[i].data(), b58Result[i].data() + b58Result[i].size()); auto const r = xrpl::b58_fast::detail::b58_to_b256_be(in, outBuf); BEAST_EXPECT(r); b256Result[i] = r.value(); @@ -307,14 +310,17 @@ class base58_test : public beast::unit_test::suite if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size())) { - if (!BEAST_EXPECT(memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == 0)) + if (!BEAST_EXPECT( + memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == + 0)) { printAsInt(b256Result[0], b256Result[1]); } } }; - auto testTokenEncode = [&](xrpl::TokenType const tokType, std::span const& b256Data) { + auto testTokenEncode = [&](xrpl::TokenType const tokType, + std::span const& b256Data) { std::array b58ResultBuf[2]; std::array, 2> b58Result; @@ -331,7 +337,8 @@ class base58_test : public beast::unit_test::suite } else { - std::string const s = xrpl::b58_ref::encodeBase58Token(tokType, b256Data.data(), b256Data.size()); + std::string const s = + xrpl::b58_ref::encodeBase58Token(tokType, b256Data.data(), b256Data.size()); BEAST_EXPECT(s.size()); b58Result[i] = outBuf.subspan(0, s.size()); std::copy(s.begin(), s.end(), b58Result[i].begin()); @@ -339,7 +346,8 @@ class base58_test : public beast::unit_test::suite } if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size())) { - if (!BEAST_EXPECT(memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0)) + if (!BEAST_EXPECT( + memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0)) { printAsChar(b58Result[0], b58Result[1]); } @@ -350,7 +358,8 @@ class base58_test : public beast::unit_test::suite std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()}; if (i == 0) { - std::string const in(b58Result[i].data(), b58Result[i].data() + b58Result[i].size()); + std::string const in( + b58Result[i].data(), b58Result[i].data() + b58Result[i].size()); auto const r = xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf); BEAST_EXPECT(r); b256Result[i] = r.value(); @@ -367,7 +376,9 @@ class base58_test : public beast::unit_test::suite if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size())) { - if (!BEAST_EXPECT(memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == 0)) + if (!BEAST_EXPECT( + memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == + 0)) { printAsInt(b256Result[0], b256Result[1]); } @@ -383,7 +394,7 @@ class base58_test : public beast::unit_test::suite // bytes range from 0-255 for (int i = 0; i < numTokenTypeIndexes; ++i) { - std::array b256DataBuf; + std::array b256DataBuf{}; auto const [tokType, tokSize] = tokenTypeAndSize(i); for (int d = 0; d <= 255; ++d) { @@ -396,7 +407,7 @@ class base58_test : public beast::unit_test::suite constexpr std::size_t iters = 100000; for (int i = 0; i < iters; ++i) { - std::array b256DataBuf; + std::array b256DataBuf{}; auto const [tokType, b256Data] = randomB256TestData(b256DataBuf); testIt(tokType, b256Data); } diff --git a/src/test/basics/base_uint_test.cpp b/src/test/basics/base_uint_test.cpp index 9fdc2c7f29..c8f931e2b5 100644 --- a/src/test/basics/base_uint_test.cpp +++ b/src/test/basics/base_uint_test.cpp @@ -76,14 +76,15 @@ struct base_uint_test : beast::unit_test::suite } { - static constexpr std::array, 6> test_args{{ - {"000000000000000000000000", "000000000000000000000001"}, - {"000000000000000000000000", "ffffffffffffffffffffffff"}, - {"0123456789ab0123456789ab", "123456789abc123456789abc"}, - {"555555555555555555555555", "55555555555a555555555555"}, - {"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"}, - {"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"}, - }}; + static constexpr std::array, 6> test_args{ + { + {"000000000000000000000000", "000000000000000000000001"}, + {"000000000000000000000000", "ffffffffffffffffffffffff"}, + {"0123456789ab0123456789ab", "123456789abc123456789abc"}, + {"555555555555555555555555", "55555555555a555555555555"}, + {"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"}, + {"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"}, + }}; for (auto const& arg : test_args) { @@ -141,7 +142,7 @@ struct base_uint_test : beast::unit_test::suite // Test hash_append by "hashing" with a no-op hasher (h) // and then extracting the bytes that were written during hashing // back into another base_uint (w) for comparison with the original - nonhash<96> h; + nonhash<96> h{}; hash_append(h, u); test96 w{std::vector(h.data_.begin(), h.data_.end())}; BEAST_EXPECT(w == u); diff --git a/src/test/basics/join_test.cpp b/src/test/basics/join_test.cpp index 9739624c63..36d34eee04 100644 --- a/src/test/basics/join_test.cpp +++ b/src/test/basics/join_test.cpp @@ -50,7 +50,9 @@ struct join_test : beast::unit_test::suite { // vector with one item edge case using namespace jtx; - test(CollectionAndDelimiter(std::vector{Account::master}, "xxx"), Account::master.human()); + test( + CollectionAndDelimiter(std::vector{Account::master}, "xxx"), + Account::master.human()); } // empty vector edge case test(CollectionAndDelimiter(std::vector{}, ","), ""); diff --git a/src/test/beast/IPEndpointCommon.h b/src/test/beast/IPEndpointCommon.h index 3ca023188e..73cbe7d95b 100644 --- a/src/test/beast/IPEndpointCommon.h +++ b/src/test/beast/IPEndpointCommon.h @@ -34,7 +34,9 @@ randomEP(bool v4 = true) static_cast(rand_int(1, UINT8_MAX)), static_cast(rand_int(1, UINT8_MAX))}}; }; - return Endpoint{v4 ? Address{AddressV4{dv4()}} : Address{AddressV6{dv6()}}, rand_int(1, UINT16_MAX)}; + return Endpoint{ + v4 ? Address{AddressV4{dv4()}} : Address{AddressV6{dv6()}}, + rand_int(1, UINT16_MAX)}; } } // namespace IP diff --git a/src/test/beast/IPEndpoint_test.cpp b/src/test/beast/IPEndpoint_test.cpp index 67e3f8ed7d..6276f7cd96 100644 --- a/src/test/beast/IPEndpoint_test.cpp +++ b/src/test/beast/IPEndpoint_test.cpp @@ -152,7 +152,7 @@ public: std::string const& normal = "") { auto const result = Endpoint::from_string_checked(s); - if (!BEAST_EXPECT(result)) + if (BEAST_EXPECT(result); !result.has_value()) return; if (!BEAST_EXPECT(result->address().is_v4())) return; @@ -171,7 +171,7 @@ public: std::string const& normal = "") { auto result = Endpoint::from_string_checked(s); - if (!BEAST_EXPECT(result)) + if (BEAST_EXPECT(result); !result.has_value()) return; if (!BEAST_EXPECT(result->address().is_v6())) return; @@ -209,8 +209,14 @@ public: shouldParseEPV4("1.2.3.4:5 ", {{1, 2, 3, 4}}, 5, "1.2.3.4:5"); shouldParseEPV4("1.2.3.4 ", {{1, 2, 3, 4}}, 0, "1.2.3.4"); shouldParseEPV4(" 1.2.3.4", {{1, 2, 3, 4}}, 0, "1.2.3.4"); - shouldParseEPV6("2001:db8:a0b:12f0::1", {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, 0); - shouldParseEPV6("[2001:db8:a0b:12f0::1]:8", {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, 8); + shouldParseEPV6( + "2001:db8:a0b:12f0::1", + {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, + 0); + shouldParseEPV6( + "[2001:db8:a0b:12f0::1]:8", + {{32, 01, 13, 184, 10, 11, 18, 240, 0, 0, 0, 0, 0, 0, 0, 1}}, + 8); shouldParseEPV6( "[2001:2002:2003:2004:2005:2006:2007:2008]:65535", {{32, 1, 32, 2, 32, 3, 32, 4, 32, 5, 32, 6, 32, 7, 32, 8}}, @@ -232,7 +238,8 @@ public: BEAST_EXPECT(is_loopback(ep)); BEAST_EXPECT(to_string(ep) == "127.0.0.1:80"); // same address as v4 mapped in ipv6 - ep = Endpoint(boost::asio::ip::make_address_v6(boost::asio::ip::v4_mapped, AddressV4{d}), 80); + ep = Endpoint( + boost::asio::ip::make_address_v6(boost::asio::ip::v4_mapped, AddressV4{d}), 80); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(!is_public(ep)); BEAST_EXPECT(is_private(ep)); @@ -251,7 +258,9 @@ public: BEAST_EXPECT(to_string(ep) == "10.0.0.1"); // same address as v4 mapped in ipv6 ep = Endpoint(boost::asio::ip::make_address_v6(boost::asio::ip::v4_mapped, AddressV4{d})); - BEAST_EXPECT(get_class(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, ep.to_v6())) == 'A'); + BEAST_EXPECT( + get_class(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, ep.to_v6())) == + 'A'); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(!is_public(ep)); BEAST_EXPECT(is_private(ep)); @@ -334,9 +343,9 @@ public: #if BOOST_OS_WINDOWS // windows asio bugs...false positives - shouldParseEPV4("255", {{ 0, 0, 0, 255 }}, 0, "0.0.0.255"); - shouldParseEPV4("512", {{ 0, 0, 2, 0 }}, 0, "0.0.2.0"); - shouldParseEPV4("1.2.3:80", {{ 1, 2, 0, 3 }}, 80, "1.2.0.3:80"); + shouldParseEPV4("255", {{0, 0, 0, 255}}, 0, "0.0.0.255"); + shouldParseEPV4("512", {{0, 0, 2, 0}}, 0, "0.0.2.0"); + shouldParseEPV4("1.2.3:80", {{1, 2, 0, 3}}, 80, "1.2.0.3:80"); #else failParseEP("255"); failParseEP("512"); @@ -385,7 +394,8 @@ public: using namespace std::literals; T t; BEAST_EXPECT(parse(text, t)); - BEAST_EXPECTS(to_string(t) == (normal.empty() ? text : normal), "string mismatch for "s + text); + BEAST_EXPECTS( + to_string(t) == (normal.empty() ? text : normal), "string mismatch for "s + text); } template diff --git a/src/test/beast/LexicalCast_test.cpp b/src/test/beast/LexicalCast_test.cpp index 4d55b95278..5cfc73a6f6 100644 --- a/src/test/beast/LexicalCast_test.cpp +++ b/src/test/beast/LexicalCast_test.cpp @@ -198,7 +198,7 @@ public: testcase("zero conversion"); { - std::int32_t out; + std::int32_t out = 0; expect(lexicalCastChecked(out, "-0"), "0"); expect(lexicalCastChecked(out, "0"), "0"); @@ -206,7 +206,7 @@ public: } { - std::uint32_t out; + std::uint32_t out = 0; expect(!lexicalCastChecked(out, "-0"), "0"); expect(lexicalCastChecked(out, "0"), "0"); @@ -220,7 +220,7 @@ public: testcase("entire range"); std::int32_t i = std::numeric_limits::min(); - std::string const empty(""); + std::string const empty; while (i <= std::numeric_limits::max()) { diff --git a/src/test/beast/SemanticVersion_test.cpp b/src/test/beast/SemanticVersion_test.cpp index b3394235d1..cae10497af 100644 --- a/src/test/beast/SemanticVersion_test.cpp +++ b/src/test/beast/SemanticVersion_test.cpp @@ -198,7 +198,12 @@ public: checkValues("1.2.3+full.prod", 1, 2, 3, ids(), ids("full", "prod")); checkValues("1.2.3+full.prod.x86", 1, 2, 3, ids(), ids("full", "prod", "x86")); checkValues( - "1.2.3-rc1.debug.asm+full.prod.x86", 1, 2, 3, ids("rc1", "debug", "asm"), ids("full", "prod", "x86")); + "1.2.3-rc1.debug.asm+full.prod.x86", + 1, + 2, + 3, + ids("rc1", "debug", "asm"), + ids("full", "prod", "x86")); } // makes sure the left version is less than the right diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp index 3af559c1cb..690f03cd49 100644 --- a/src/test/beast/aged_associative_container_test.cpp +++ b/src/test/beast/aged_associative_container_test.cpp @@ -227,7 +227,7 @@ public: static typename Base::Key const& extract(Value const& value) { - return value; + return value; // NOLINT(bugprone-return-const-ref-from-parameter) } static Values @@ -292,7 +292,9 @@ public: template struct ContType { - template , class Allocator = std::allocator> + template < + class Compare = std::less, + class Allocator = std::allocator> using Cont = detail::aged_ordered_container< Base::is_multi::value, Base::is_map::value, @@ -332,10 +334,12 @@ public: }; template - struct TestTraitsHelper : MaybeUnordered, IsMulti>, IsUnordered> + struct TestTraitsHelper + : MaybeUnordered, IsMulti>, IsUnordered> { private: - using Base = MaybeUnordered, IsMulti>, IsUnordered>; + using Base = + MaybeUnordered, IsMulti>, IsUnordered>; public: using typename Base::Key; @@ -350,7 +354,8 @@ public: static std::string name() { - return std::string("aged_") + Base::name_ordered_part() + Base::name_multi_part() + Base::name_map_part(); + return std::string("aged_") + Base::name_ordered_part() + Base::name_multi_part() + + Base::name_map_part(); } }; @@ -629,7 +634,8 @@ typename std::enable_if::type::is_unordered::value>::ty aged_associative_container_test_base::checkUnorderedContentsRefRef(C&& c, Values const& v) { using Cont = typename std::remove_reference::type; - using Traits = TestTraits; + using Traits = + TestTraits; using size_type = typename Cont::size_type; auto const hash(c.hash_function()); auto const key_eq(c.key_eq()); @@ -638,9 +644,10 @@ aged_associative_container_test_base::checkUnorderedContentsRefRef(C&& c, Values auto const last(c.end(i)); for (auto iter(c.begin(i)); iter != last; ++iter) { - auto const match(std::find_if(v.begin(), v.end(), [iter](typename Values::value_type const& e) { - return Traits::extract(*iter) == Traits::extract(e); - })); + auto const match( + std::find_if(v.begin(), v.end(), [iter](typename Values::value_type const& e) { + return Traits::extract(*iter) == Traits::extract(e); + })); BEAST_EXPECT(match != v.end()); BEAST_EXPECT(key_eq(Traits::extract(*iter), Traits::extract(*match))); BEAST_EXPECT(hash(Traits::extract(*iter)) == hash(Traits::extract(*match))); @@ -658,10 +665,14 @@ aged_associative_container_test_base::checkContentsRefRef(C&& c, Values const& v BEAST_EXPECT(c.size() == v.size()); BEAST_EXPECT(size_type(std::distance(c.begin(), c.end())) == v.size()); BEAST_EXPECT(size_type(std::distance(c.cbegin(), c.cend())) == v.size()); - BEAST_EXPECT(size_type(std::distance(c.chronological.begin(), c.chronological.end())) == v.size()); - BEAST_EXPECT(size_type(std::distance(c.chronological.cbegin(), c.chronological.cend())) == v.size()); - BEAST_EXPECT(size_type(std::distance(c.chronological.rbegin(), c.chronological.rend())) == v.size()); - BEAST_EXPECT(size_type(std::distance(c.chronological.crbegin(), c.chronological.crend())) == v.size()); + BEAST_EXPECT( + size_type(std::distance(c.chronological.begin(), c.chronological.end())) == v.size()); + BEAST_EXPECT( + size_type(std::distance(c.chronological.cbegin(), c.chronological.cend())) == v.size()); + BEAST_EXPECT( + size_type(std::distance(c.chronological.rbegin(), c.chronological.rend())) == v.size()); + BEAST_EXPECT( + size_type(std::distance(c.chronological.crbegin(), c.chronological.crend())) == v.size()); checkUnorderedContentsRefRef(c, v); } @@ -679,7 +690,8 @@ template void aged_associative_container_test_base::checkContents(Cont& c) { - using Traits = TestTraits; + using Traits = + TestTraits; using Values = typename Traits::Values; checkContents(c, Values()); } @@ -778,7 +790,8 @@ aged_associative_container_test_base::testConstructEmpty() } { - typename Traits::template Cont c(clock, MyHash(1), MyEqual(1), MyAlloc(1)); + typename Traits::template Cont c( + clock, MyHash(1), MyEqual(1), MyAlloc(1)); checkContents(c); } } @@ -815,7 +828,8 @@ aged_associative_container_test_base::testConstructRange() } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyComp(1), MyAlloc(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyComp(1), MyAlloc(1)); checkContents(c, v); } @@ -853,32 +867,38 @@ aged_associative_container_test_base::testConstructRange() } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyHash(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyHash(1)); checkContents(c, v); } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyEqual(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyEqual(1)); checkContents(c, v); } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyAlloc(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyAlloc(1)); checkContents(c, v); } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyHash(1), MyEqual(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyHash(1), MyEqual(1)); checkContents(c, v); } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyHash(1), MyAlloc(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyHash(1), MyAlloc(1)); checkContents(c, v); } { - typename Traits::template Cont c(v.begin(), v.end(), clock, MyEqual(1), MyAlloc(1)); + typename Traits::template Cont c( + v.begin(), v.end(), clock, MyEqual(1), MyAlloc(1)); checkContents(c, v); } @@ -1266,7 +1286,12 @@ aged_associative_container_test_base::testChronological() typename Traits::template Cont<> c(v.begin(), v.end(), clock); BEAST_EXPECT( - std::equal(c.chronological.cbegin(), c.chronological.cend(), v.begin(), v.end(), equal_value())); + std::equal( + c.chronological.cbegin(), + c.chronological.cend(), + v.begin(), + v.end(), + equal_value())); // Test touch() with a non-const iterator. for (auto iter(v.crbegin()); iter != v.crend(); ++iter) @@ -1281,7 +1306,12 @@ aged_associative_container_test_base::testChronological() } BEAST_EXPECT( - std::equal(c.chronological.cbegin(), c.chronological.cend(), v.crbegin(), v.crend(), equal_value())); + std::equal( + c.chronological.cbegin(), + c.chronological.cend(), + v.crbegin(), + v.crend(), + equal_value())); // Test touch() with a const_iterator for (auto iter(v.cbegin()); iter != v.cend(); ++iter) @@ -1296,7 +1326,12 @@ aged_associative_container_test_base::testChronological() } BEAST_EXPECT( - std::equal(c.chronological.cbegin(), c.chronological.cend(), v.cbegin(), v.cend(), equal_value())); + std::equal( + c.chronological.cbegin(), + c.chronological.cend(), + v.cbegin(), + v.cend(), + equal_value())); { // Because touch (reverse_iterator pos) is not allowed, the following @@ -1407,7 +1442,10 @@ aged_associative_container_test_base::nextToEndIter(Iter beginIter, Iter const e // the whole test. template bool -aged_associative_container_test_base::doElementErase(Container& c, Iter const beginItr, Iter const endItr) +aged_associative_container_test_base::doElementErase( + Container& c, + Iter const beginItr, + Iter const endItr) { auto it(beginItr); size_t count = c.size(); @@ -1486,7 +1524,9 @@ aged_associative_container_test_base::testElementErase() auto tempContainer(c); BEAST_EXPECT(tempContainer.size() > 2); if (!doElementErase( - tempContainer, ++tempContainer.begin(), nextToEndIter(tempContainer.begin(), tempContainer.end()))) + tempContainer, + ++tempContainer.begin(), + nextToEndIter(tempContainer.begin(), tempContainer.end()))) return; // Test failed BEAST_EXPECT(tempContainer.size() == 2); @@ -1497,7 +1537,8 @@ aged_associative_container_test_base::testElementErase() auto tempContainer(c); BEAST_EXPECT(tempContainer.size() > 2); auto& chron(tempContainer.chronological); - if (!doElementErase(tempContainer, ++chron.begin(), nextToEndIter(chron.begin(), chron.end()))) + if (!doElementErase( + tempContainer, ++chron.begin(), nextToEndIter(chron.begin(), chron.end()))) return; // Test failed BEAST_EXPECT(tempContainer.size() == 2); @@ -1696,7 +1737,8 @@ public: "bad alias: aged_set"); static_assert( - std::is_same, detail::aged_ordered_container>::value, + std::is_same, detail::aged_ordered_container>:: + value, "bad alias: aged_multiset"); static_assert( @@ -1704,23 +1746,32 @@ public: "bad alias: aged_map"); static_assert( - std::is_same, detail::aged_ordered_container>::value, + std::is_same, detail::aged_ordered_container>:: + value, "bad alias: aged_multimap"); static_assert( - std::is_same, detail::aged_unordered_container>::value, + std::is_same< + aged_unordered_set, + detail::aged_unordered_container>::value, "bad alias: aged_unordered_set"); static_assert( - std::is_same, detail::aged_unordered_container>::value, + std::is_same< + aged_unordered_multiset, + detail::aged_unordered_container>::value, "bad alias: aged_unordered_multiset"); static_assert( - std::is_same, detail::aged_unordered_container>::value, + std::is_same< + aged_unordered_map, + detail::aged_unordered_container>::value, "bad alias: aged_unordered_map"); static_assert( - std::is_same, detail::aged_unordered_container>::value, + std::is_same< + aged_unordered_multimap, + detail::aged_unordered_container>::value, "bad alias: aged_unordered_multimap"); void diff --git a/src/test/beast/beast_Journal_test.cpp b/src/test/beast/beast_Journal_test.cpp index 407e9ea7aa..cb190e57a0 100644 --- a/src/test/beast/beast_Journal_test.cpp +++ b/src/test/beast/beast_Journal_test.cpp @@ -9,10 +9,10 @@ public: class TestSink : public Journal::Sink { private: - int m_count; + int m_count{0}; public: - TestSink() : Sink(severities::kWarning, false), m_count(0) + TestSink() : Sink(severities::kWarning, false) { } diff --git a/src/test/beast/beast_PropertyStream_test.cpp b/src/test/beast/beast_PropertyStream_test.cpp index c2e6ddc22b..35aa91d18e 100644 --- a/src/test/beast/beast_PropertyStream_test.cpp +++ b/src/test/beast/beast_PropertyStream_test.cpp @@ -9,7 +9,10 @@ public: using Source = PropertyStream::Source; void - test_peel_name(std::string s, std::string const& expected, std::string const& expected_remainder) + test_peel_name( + std::string s, + std::string const& expected, + std::string const& expected_remainder) { try { @@ -41,7 +44,10 @@ public: } void - test_peel_trailing_slashstar(std::string s, std::string const& expected_remainder, bool should_be_found) + test_peel_trailing_slashstar( + std::string s, + std::string const& expected_remainder, + bool should_be_found) { try { diff --git a/src/test/beast/beast_abstract_clock_test.cpp b/src/test/beast/beast_abstract_clock_test.cpp index 94b7909820..43a210e128 100644 --- a/src/test/beast/beast_abstract_clock_test.cpp +++ b/src/test/beast/beast_abstract_clock_test.cpp @@ -37,8 +37,6 @@ public: using clock_type = manual_clock; clock_type c; - std::stringstream ss; - auto c1 = c.now().time_since_epoch(); c.set(clock_type::time_point(std::chrono::seconds(1))); auto c2 = c.now().time_since_epoch(); diff --git a/src/test/beast/beast_io_latency_probe_test.cpp b/src/test/beast/beast_io_latency_probe_test.cpp index 4a78eb77fe..f2423550d3 100644 --- a/src/test/beast/beast_io_latency_probe_test.cpp +++ b/src/test/beast/beast_io_latency_probe_test.cpp @@ -39,8 +39,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test { using namespace std::chrono; boost::asio::io_context ios; - std::optional> work{ - boost::asio::make_work_guard(ios)}; + std::optional> + work{boost::asio::make_work_guard(ios)}; std::thread worker{[&] { ios.run(); }}; boost::asio::basic_waitable_timer timer{ios}; elapsed_times_.reserve(num_samples); @@ -88,7 +88,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test auto getMax() { - return std::chrono::duration_cast(*std::max_element(elapsed_times_.begin(), elapsed_times_.end())) + return std::chrono::duration_cast( + *std::max_element(elapsed_times_.begin(), elapsed_times_.end())) .count(); } @@ -96,7 +97,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test auto getMin() { - return std::chrono::duration_cast(*std::min_element(elapsed_times_.begin(), elapsed_times_.end())) + return std::chrono::duration_cast( + *std::min_element(elapsed_times_.begin(), elapsed_times_.end())) .count(); } }; @@ -107,7 +109,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test beast::io_latency_probe probe_; std::vector durations_; - test_sampler(std::chrono::milliseconds interval, boost::asio::io_context& ios) : probe_(interval, ios) + test_sampler(std::chrono::milliseconds interval, boost::asio::io_context& ios) + : probe_(interval, ios) { } @@ -162,7 +165,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test measure_asio_timers tt{interval}; log << "measured mean for timers: " << tt.getMean() << "ms\n"; log << "measured max for timers: " << tt.getMax() << "ms\n"; - expected_probe_count_min = static_cast(duration_cast(probe_duration).count()) / + expected_probe_count_min = + static_cast(duration_cast(probe_duration).count()) / static_cast(tt.getMean()); #endif test_sampler io_probe{interval, get_io_context()}; @@ -173,7 +177,8 @@ class io_latency_probe_test : public beast::unit_test::suite, public beast::test return; auto probes_seen = io_probe.durations_.size(); BEAST_EXPECTS( - probes_seen >= (expected_probe_count_min - 1) && probes_seen <= (expected_probe_count_max + 1), + probes_seen >= (expected_probe_count_min - 1) && + probes_seen <= (expected_probe_count_max + 1), std::string("probe count is ") + std::to_string(probes_seen)); io_probe.probe_.cancel_async(); // wait again in order to flush the remaining diff --git a/src/test/conditions/PreimageSha256_test.cpp b/src/test/conditions/PreimageSha256_test.cpp index 71ba526be3..973f714ba4 100644 --- a/src/test/conditions/PreimageSha256_test.cpp +++ b/src/test/conditions/PreimageSha256_test.cpp @@ -1,12 +1,11 @@ -#include -#include -#include - #include #include #include #include #include +#include +#include +#include #include #include @@ -18,12 +17,12 @@ namespace cryptoconditions { class PreimageSha256_test : public beast::unit_test::suite { - inline Buffer + Buffer hexblob(std::string const& s) { auto blob = strUnHex(s); BEAST_EXPECT(blob); - return {blob->data(), blob->size()}; + return {blob->data(), blob->size()}; // NOLINT(bugprone-unchecked-optional-access) } void diff --git a/src/test/consensus/ByzantineFailureSim_test.cpp b/src/test/consensus/ByzantineFailureSim_test.cpp index 62899730c2..245c52d9e3 100644 --- a/src/test/consensus/ByzantineFailureSim_test.cpp +++ b/src/test/consensus/ByzantineFailureSim_test.cpp @@ -48,7 +48,8 @@ class ByzantineFailureSim_test : public beast::unit_test::suite for (TrustGraph::ForkInfo const& fi : sim.trustGraph.forkablePairs(0.8)) { std::cout << "Can fork " << PeerGroup{fi.unlA} << " " - << " " << PeerGroup{fi.unlB} << " overlap " << fi.overlap << " required " << fi.required << "\n"; + << " " << PeerGroup{fi.unlB} << " overlap " << fi.overlap << " required " + << fi.required << "\n"; }; // set prior state diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp index 3227ad4d1c..a096b82365 100644 --- a/src/test/consensus/Consensus_test.cpp +++ b/src/test/consensus/Consensus_test.cpp @@ -61,61 +61,79 @@ public: // Disputes still in doubt // // Not enough time has elapsed - BEAST_EXPECT(ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, false, p, true, journal_)); // If not enough peers have proposed, ensure // more time for proposals - BEAST_EXPECT(ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, false, p, true, journal_)); // Enough time has elapsed and we all agree - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, false, p, true, journal_)); // Enough time has elapsed and we don't yet agree - BEAST_EXPECT(ConsensusState::No == checkConsensus(10, 2, 1, 0, 3s, 10s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(10, 2, 1, 0, 3s, 10s, false, p, true, journal_)); // Our peers have moved on // Enough time has elapsed and we all agree - BEAST_EXPECT(ConsensusState::MovedOn == checkConsensus(10, 2, 1, 8, 3s, 10s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::MovedOn == + checkConsensus(10, 2, 1, 8, 3s, 10s, false, p, true, journal_)); // If no peers, don't agree until time has passed. - BEAST_EXPECT(ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, false, p, true, journal_)); // Agree if no peers and enough time has passed. - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, false, p, true, journal_)); // Expire if too much time has passed without agreement - BEAST_EXPECT(ConsensusState::Expired == checkConsensus(10, 8, 1, 0, 1s, 19s, false, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Expired == + checkConsensus(10, 8, 1, 0, 1s, 19s, false, p, true, journal_)); /////////////// // Stalled // // Not enough time has elapsed - BEAST_EXPECT(ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 2s, true, p, true, journal_)); // If not enough peers have proposed, ensure // more time for proposals - BEAST_EXPECT(ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(10, 2, 2, 0, 3s, 4s, true, p, true, journal_)); // Enough time has elapsed and we all agree - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(10, 2, 2, 0, 3s, 10s, true, p, true, journal_)); // Enough time has elapsed and we don't yet agree, but there's nothing // left to dispute - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(10, 2, 1, 0, 3s, 10s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(10, 2, 1, 0, 3s, 10s, true, p, true, journal_)); // Our peers have moved on // Enough time has elapsed and we all agree, nothing left to dispute - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(10, 2, 1, 8, 3s, 10s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(10, 2, 1, 8, 3s, 10s, true, p, true, journal_)); // If no peers, don't agree until time has passed. - BEAST_EXPECT(ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::No == checkConsensus(0, 0, 0, 0, 3s, 10s, true, p, true, journal_)); // Agree if no peers and enough time has passed. - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(0, 0, 0, 0, 3s, 16s, true, p, true, journal_)); // We are done if there's nothing left to dispute, no matter how much // time has passed - BEAST_EXPECT(ConsensusState::Yes == checkConsensus(10, 8, 1, 0, 1s, 19s, true, p, true, journal_)); + BEAST_EXPECT( + ConsensusState::Yes == checkConsensus(10, 8, 1, 0, 1s, 19s, true, p, true, journal_)); } void @@ -139,7 +157,7 @@ public: BEAST_EXPECT(peer->prevLedgerID() == lcl.id()); BEAST_EXPECT(lcl.seq() == Ledger::Seq{1}); BEAST_EXPECT(lcl.txs().size() == 1); - BEAST_EXPECT(lcl.txs().find(Tx{1}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{1})); BEAST_EXPECT(peer->prevProposers == 0); } @@ -175,7 +193,7 @@ public: BEAST_EXPECT(peer->prevProposers == peers.size() - 1); // All transactions were accepted for (std::uint32_t i = 0; i < peers.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); } } } @@ -226,12 +244,12 @@ public: BEAST_EXPECT(peer->prevProposers == network.size() - 1); BEAST_EXPECT(peer->prevRoundTime == network[0]->prevRoundTime); - BEAST_EXPECT(lcl.txs().find(Tx{0}) == lcl.txs().end()); + BEAST_EXPECT(not lcl.txs().contains(Tx{0})); for (std::uint32_t i = 2; i < network.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); // Tx 0 didn't make it - BEAST_EXPECT(peer->openTxs.find(Tx{0}) != peer->openTxs.end()); + BEAST_EXPECT(peer->openTxs.contains(Tx{0})); } } } @@ -279,21 +297,25 @@ public: // Closed ledger has all but transaction 0,1 auto const& lcl = peer->lastClosedLedger; BEAST_EXPECT(lcl.seq() == Ledger::Seq{1}); - BEAST_EXPECT(lcl.txs().find(Tx{0}) == lcl.txs().end()); - BEAST_EXPECT(lcl.txs().find(Tx{1}) == lcl.txs().end()); + BEAST_EXPECT(not lcl.txs().contains(Tx{0})); + BEAST_EXPECT(not lcl.txs().contains(Tx{1})); for (std::uint32_t i = slow.size(); i < network.size(); ++i) - BEAST_EXPECT(lcl.txs().find(Tx{i}) != lcl.txs().end()); + BEAST_EXPECT(lcl.txs().contains(Tx{i})); // Tx 0-1 didn't make it - BEAST_EXPECT(peer->openTxs.find(Tx{0}) != peer->openTxs.end()); - BEAST_EXPECT(peer->openTxs.find(Tx{1}) != peer->openTxs.end()); + BEAST_EXPECT(peer->openTxs.contains(Tx{0})); + BEAST_EXPECT(peer->openTxs.contains(Tx{1})); } Peer const* slowPeer = slow[0]; if (isParticipant) + { BEAST_EXPECT(slowPeer->prevProposers == network.size() - 1); + } else + { BEAST_EXPECT(slowPeer->prevProposers == fast.size()); + } for (Peer* peer : fast) { @@ -708,7 +730,9 @@ public: // Since the overlapped nodes have a UNL that is the union of the // two cliques, the maximum sized UNL list is the number of peers if (overlap > 0.4 * numPeers) + { BEAST_EXPECT(sim.synchronized()); + } else { // Even if we do fork, there shouldn't be more than 3 ledgers @@ -1005,8 +1029,8 @@ public: // Simulate clients submitting 1 tx every 5 seconds to a random // validator Rate const rate{1, 5s}; - auto peerSelector = - makeSelector(network.begin(), network.end(), std::vector(network.size(), 1.), sim.rng); + auto peerSelector = makeSelector( + network.begin(), network.end(), std::vector(network.size(), 1.), sim.rng); auto txSubmitter = makeSubmitter( ConstantDistribution{rate.inv()}, sim.scheduler.now(), @@ -1082,7 +1106,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // I'm in the majority, my vote should not change BEAST_EXPECT(!proposingTrue.updateVote(5, true, p)); @@ -1100,7 +1124,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // Right now, the vote is 51%. The requirement is about to jump to // 65% @@ -1194,7 +1218,7 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // Threshold jumps to 95% BEAST_EXPECT(proposingTrue.updateVote(220, true, p)); @@ -1235,22 +1259,39 @@ public: BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged, j, clog)); BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged, j, clog)); BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); } - auto expectStalled = - [this, &clog](int txid, bool ourVote, int ourTime, int peerTime, int support, std::uint32_t line) { - using namespace std::string_literals; + auto expectStalled = [this, &clog]( + int txid, + bool ourVote, + int ourTime, + int peerTime, + int support, + std::uint32_t line) { + using namespace std::string_literals; - auto const s = clog->str(); - expect(s.find("stalled"), s, __FILE__, line); - expect(s.starts_with("Transaction "s + std::to_string(txid)), s, __FILE__, line); - expect(s.find("voting "s + (ourVote ? "YES" : "NO")) != s.npos, s, __FILE__, line); - expect(s.find("for "s + std::to_string(ourTime) + " rounds."s) != s.npos, s, __FILE__, line); - expect(s.find("votes in "s + std::to_string(peerTime) + " rounds.") != s.npos, s, __FILE__, line); - expect(s.ends_with("has "s + std::to_string(support) + "% support. "s), s, __FILE__, line); - clog = std::make_unique(); - }; + auto const s = clog->str(); + expect(s.find("stalled"), s, __FILE__, line); + expect(s.starts_with("Transaction "s + std::to_string(txid)), s, __FILE__, line); + expect(s.find("voting "s + (ourVote ? "YES" : "NO")) != s.npos, s, __FILE__, line); + expect( + s.find("for "s + std::to_string(ourTime) + " rounds."s) != s.npos, + s, + __FILE__, + line); + expect( + s.find("votes in "s + std::to_string(peerTime) + " rounds.") != s.npos, + s, + __FILE__, + line); + expect( + s.ends_with("has "s + std::to_string(support) + "% support. "s), + s, + __FILE__, + line); + clog = std::make_unique(); + }; for (int i = 0; i < 1; ++i) { @@ -1266,7 +1307,7 @@ public: // true vote has changed recently, so not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog)); - BEAST_EXPECT(clog->str() == ""); + BEAST_EXPECT(clog->str().empty()); // remaining votes have been unchanged in so long that we only // need to hit the second round at 95% to be stalled, regardless // of peers @@ -1279,7 +1320,7 @@ public: // true vote has changed recently, so not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged, j, clog)); - BEAST_EXPECTS(clog->str() == "", clog->str()); + BEAST_EXPECTS(clog->str().empty(), clog->str()); // remaining votes have been unchanged in so long that we only // need to hit the second round at 95% to be stalled, regardless // of peers @@ -1305,7 +1346,7 @@ public: // true vote changed 2 rounds ago, and peers are changing, so // not stalled BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog)); - BEAST_EXPECTS(clog->str() == "", clog->str()); + BEAST_EXPECTS(clog->str().empty(), clog->str()); // still stalled BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog)); expectStalled(98, false, 11 + i, 0, 2, __LINE__); diff --git a/src/test/consensus/DistributedValidatorsSim_test.cpp b/src/test/consensus/DistributedValidatorsSim_test.cpp index 49d2c6e7e2..b430a63880 100644 --- a/src/test/consensus/DistributedValidatorsSim_test.cpp +++ b/src/test/consensus/DistributedValidatorsSim_test.cpp @@ -67,7 +67,8 @@ class DistributedValidators_test : public beast::unit_test::suite HeartbeatTimer heart(sim.scheduler); // txs, start/stop/step, target - auto peerSelector = makeSelector(peers.begin(), peers.end(), std::vector(numPeers, 1.), sim.rng); + auto peerSelector = + makeSelector(peers.begin(), peers.end(), std::vector(numPeers, 1.), sim.rng); auto txSubmitter = makeSubmitter( ConstantDistribution{rate.inv()}, sim.scheduler.now() + quiet, @@ -85,7 +86,8 @@ class DistributedValidators_test : public beast::unit_test::suite log << std::right; log << "| Peers: " << std::setw(2) << peers.size(); - log << " | Duration: " << std::setw(6) << duration_cast(simDuration).count() << " ms"; + log << " | Duration: " << std::setw(6) << duration_cast(simDuration).count() + << " ms"; log << " | Branches: " << std::setw(1) << sim.branches(); log << " | Synchronized: " << std::setw(1) << (sim.synchronized() ? "Y" : "N"); log << " |" << std::endl; @@ -136,7 +138,12 @@ class DistributedValidators_test : public beast::unit_test::suite // scale-free connect graph with fixed delay std::vector const ranks = sample(peers.size(), PowerLawDistribution{1, 3}, sim.rng); randomRankedConnect( - peers, ranks, numCNLs, std::uniform_int_distribution<>{minCNLSize, maxCNLSize}, sim.rng, delay); + peers, + ranks, + numCNLs, + std::uniform_int_distribution<>{minCNLSize, maxCNLSize}, + sim.rng, + delay); // Initialize collectors to track statistics to report TxCollector txCollector; @@ -156,7 +163,8 @@ class DistributedValidators_test : public beast::unit_test::suite HeartbeatTimer heart(sim.scheduler); // txs, start/stop/step, target - auto peerSelector = makeSelector(peers.begin(), peers.end(), std::vector(numPeers, 1.), sim.rng); + auto peerSelector = + makeSelector(peers.begin(), peers.end(), std::vector(numPeers, 1.), sim.rng); auto txSubmitter = makeSubmitter( ConstantDistribution{rate.inv()}, sim.scheduler.now() + quiet, @@ -174,7 +182,8 @@ class DistributedValidators_test : public beast::unit_test::suite log << std::right; log << "| Peers: " << std::setw(2) << peers.size(); - log << " | Duration: " << std::setw(6) << duration_cast(simDuration).count() << " ms"; + log << " | Duration: " << std::setw(6) << duration_cast(simDuration).count() + << " ms"; log << " | Branches: " << std::setw(1) << sim.branches(); log << " | Synchronized: " << std::setw(1) << (sim.synchronized() ? "Y" : "N"); log << " |" << std::endl; diff --git a/src/test/consensus/LedgerTiming_test.cpp b/src/test/consensus/LedgerTiming_test.cpp index ac00cd1f3a..945f6b4ff0 100644 --- a/src/test/consensus/LedgerTiming_test.cpp +++ b/src/test/consensus/LedgerTiming_test.cpp @@ -26,13 +26,20 @@ class LedgerTiming_test : public beast::unit_test::suite std::uint32_t round = 0; do { - nextCloseResolution = getNextLedgerTimeResolution(closeResolution, previousAgree, ++round); + nextCloseResolution = + getNextLedgerTimeResolution(closeResolution, previousAgree, ++round); if (nextCloseResolution < closeResolution) + { ++res.decrease; + } else if (nextCloseResolution > closeResolution) + { ++res.increase; + } else + { ++res.equal; + } std::swap(nextCloseResolution, closeResolution); } while (round < rounds); return res; diff --git a/src/test/consensus/LedgerTrie_test.cpp b/src/test/consensus/LedgerTrie_test.cpp index 586b9231f6..7fd8c71b64 100644 --- a/src/test/consensus/LedgerTrie_test.cpp +++ b/src/test/consensus/LedgerTrie_test.cpp @@ -354,6 +354,8 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; Ledger genesis = h[""]; t.insert(genesis); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{0})->id == genesis.id()); BEAST_EXPECT(t.remove(genesis)); BEAST_EXPECT(t.getPreferred(Seq{0}) == std::nullopt); @@ -364,6 +366,8 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerTrie t; LedgerHistoryHelper h; t.insert(h["abc"]); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); } // Single node smaller child support @@ -372,7 +376,11 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; t.insert(h["abc"]); t.insert(h["abcd"]); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); } // Single node larger child @@ -381,7 +389,11 @@ class LedgerTrie_test : public beast::unit_test::suite LedgerHistoryHelper h; t.insert(h["abc"]); t.insert(h["abcd"], 2); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); } // Single node smaller children support @@ -391,12 +403,16 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"]); t.insert(h["abce"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); t.insert(h["abc"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Single node larger children { @@ -405,12 +421,16 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"], 2); t.insert(h["abce"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); t.insert(h["abcd"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Tie-breaker by id { @@ -420,10 +440,14 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abce"], 2); BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id()); t.insert(h["abcd"]); BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id()); } @@ -436,14 +460,18 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abce"], 2); // abce only has a margin of 1, but it owns the tie-breaker BEAST_EXPECT(h["abce"].id() > h["abcd"].id()); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abce"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id()); // Switch support from abce to abcd, tie-breaker now needed t.remove(h["abce"]); t.insert(h["abcd"]); + BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Single node larger grand child @@ -453,9 +481,12 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abc"]); t.insert(h["abcd"], 2); t.insert(h["abcde"], 4); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abcde"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Too much uncommitted support from competing branches @@ -466,6 +497,7 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abcde"], 2); t.insert(h["abcfg"], 2); // 'de' and 'fg' are tied without 'abc' vote + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id()); @@ -473,8 +505,7 @@ class LedgerTrie_test : public beast::unit_test::suite t.remove(h["abc"]); t.insert(h["abcd"]); - // 'de' branch has 3 votes to 2, so earlier sequences see it as - // preferred + // 'de' branch has 3 votes to 2, so earlier sequences see it as preferred BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id()); @@ -482,6 +513,7 @@ class LedgerTrie_test : public beast::unit_test::suite // a different branch, you do not yet know if they chose abcd // or abcf because of you, so abc remains preferred BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } // Changing largestSeq perspective changes preferred branch @@ -505,12 +537,15 @@ class LedgerTrie_test : public beast::unit_test::suite t.insert(h["abde"], 2); // B has more branch support + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); + // But if you last validated D,F or E, you do not yet know // if someone used that validation to commit to B or C BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** One of E advancing to G doesn't change anything A @@ -526,11 +561,13 @@ class LedgerTrie_test : public beast::unit_test::suite t.remove(h["abde"]); t.insert(h["abdeg"]); + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** C advancing to H does advance the seq 3 preferred ledger A @@ -545,11 +582,14 @@ class LedgerTrie_test : public beast::unit_test::suite */ t.remove(h["ac"]); t.insert(h["abh"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) /** F advancing to E also moves the preferred ledger forward A @@ -564,11 +604,14 @@ class LedgerTrie_test : public beast::unit_test::suite */ t.remove(h["acf"]); t.insert(h["abde"]); + + // NOLINTBEGIN(bugprone-unchecked-optional-access) BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abde"].id()); BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["ab"].id()); BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["ab"].id()); + // NOLINTEND(bugprone-unchecked-optional-access) } } @@ -626,7 +669,7 @@ class LedgerTrie_test : public beast::unit_test::suite for (std::uint32_t i = 0; i < iterations; ++i) { // pick a random ledger history - std::string curr = ""; + std::string curr; char depth = depthDist(gen); char offset = 0; for (char d = 0; d < depth; ++d) @@ -638,9 +681,13 @@ class LedgerTrie_test : public beast::unit_test::suite // 50-50 to add remove if (flip(gen) == 0) + { t.insert(h[curr]); + } else + { t.remove(h[curr]); + } if (!BEAST_EXPECT(t.checkInvariants())) return; } diff --git a/src/test/consensus/NegativeUNL_test.cpp b/src/test/consensus/NegativeUNL_test.cpp index 9e77f158a3..46c6936809 100644 --- a/src/test/consensus/NegativeUNL_test.cpp +++ b/src/test/consensus/NegativeUNL_test.cpp @@ -4,10 +4,10 @@ #include #include #include -#include #include -#include +#include +#include namespace xrpl { namespace test { @@ -34,7 +34,11 @@ namespace test { * @return true if meet all three expectation */ bool -negUnlSizeTest(std::shared_ptr const& l, size_t size, bool hasToDisable, bool hasToReEnable); +negUnlSizeTest( + std::shared_ptr const& l, + size_t size, + bool hasToDisable, + bool hasToReEnable); /** * Try to apply a ttUNL_MODIFY Tx, and test the apply result @@ -58,7 +62,9 @@ applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass); * @return true if meet the expectation */ bool -VerifyPubKeyAndSeq(std::shared_ptr const& l, hash_map nUnlLedgerSeq); +VerifyPubKeyAndSeq( + std::shared_ptr const& l, + hash_map nUnlLedgerSeq); /** * Count the number of Tx in a TxSet @@ -536,7 +542,10 @@ struct NetworkHistory { static uint256 fake_amendment; // So we have different genesis ledgers auto l = std::make_shared( - create_genesis, env.app().config(), std::vector{fake_amendment++}, env.app().getNodeFamily()); + create_genesis, + env.app().config(), + std::vector{fake_amendment++}, + env.app().getNodeFamily()); history.push_back(l); // When putting validators into the negative UNL, we start with @@ -660,11 +669,16 @@ auto defaultPreVote = [](NegativeUNLVote& vote) {}; */ template bool -voteAndCheck(NetworkHistory& history, NodeID const& myId, std::size_t expect, PreVote const& pre = defaultPreVote) +voteAndCheck( + NetworkHistory& history, + NodeID const& myId, + std::size_t expect, + PreVote const& pre = defaultPreVote) { NegativeUNLVote vote(myId, history.env.journal); pre(vote); - auto txSet = std::make_shared(SHAMapType::TRANSACTION, history.env.app().getNodeFamily()); + auto txSet = + std::make_shared(SHAMapType::TRANSACTION, history.env.app().getNodeFamily()); vote.doVoting(history.lastLedger(), history.UNLKeySet, history.validations, txSet); return countTx(txSet) == expect; } @@ -741,7 +755,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite if (history.goodHistory) { NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); - BEAST_EXPECT(!vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations)); + BEAST_EXPECT(!vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations)); } } @@ -752,7 +767,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite if (history.goodHistory) { NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); - BEAST_EXPECT(!vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations)); + BEAST_EXPECT(!vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations)); } } @@ -769,7 +785,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite return !(history.UNLNodeIDs[idx] == myId && l->seq() % 2 == 0); }); NegativeUNLVote vote(myId, history.env.journal); - BEAST_EXPECT(!vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations)); + BEAST_EXPECT(!vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations)); } } @@ -810,22 +827,28 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite // local node still on wrong chain, can build a scoreTable, // but all other nodes' scores are zero - auto scoreTable = vote.buildScoreTable(wrongChain.back(), history.UNLNodeIDSet, history.validations); + auto scoreTable = vote.buildScoreTable( + wrongChain.back(), history.UNLNodeIDSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { for (auto const& [n, score] : *scoreTable) { if (n == myId) + { BEAST_EXPECT(score == 256); + } else + { BEAST_EXPECT(score == 0); + } } } // if local node switched to right history, but cannot build // scoreTable because not enough local validations - BEAST_EXPECT(!vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations)); + BEAST_EXPECT(!vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations)); } } @@ -836,9 +859,12 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return true; + }); NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); - auto scoreTable = vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations); + auto scoreTable = vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { @@ -873,7 +899,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite std::size_t numDisable, std::size_t numReEnable) { - auto [disableCandidates, reEnableCandidates] = vote.findAllCandidates(unl, negUnl, scoreTable); + auto [disableCandidates, reEnableCandidates] = + vote.findAllCandidates(unl, negUnl, scoreTable); bool rightDisable = disableCandidates.size() == numDisable; bool rightReEnable = reEnableCandidates.size() == numReEnable; return rightDisable && rightReEnable; @@ -914,28 +941,32 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite { // all good scores - BEAST_EXPECT(checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, goodScoreTable, 0, 3)); + BEAST_EXPECT( + checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, goodScoreTable, 0, 3)); } { // all bad scores hash_map scoreTable; for (auto& n : history.UNLNodeIDs) scoreTable[n] = NegativeUNLVote::negativeUNLLowWaterMark - 1; - BEAST_EXPECT(checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 35 - 3, 0)); + BEAST_EXPECT( + checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 35 - 3, 0)); } { // all between watermarks hash_map scoreTable; for (auto& n : history.UNLNodeIDs) scoreTable[n] = NegativeUNLVote::negativeUNLLowWaterMark + 1; - BEAST_EXPECT(checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 0)); + BEAST_EXPECT( + checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 0)); } { // 2 good scorers in negUnl auto scoreTable = goodScoreTable; scoreTable[*negUnl_012.begin()] = NegativeUNLVote::negativeUNLLowWaterMark + 1; - BEAST_EXPECT(checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 2)); + BEAST_EXPECT( + checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 2)); } { @@ -943,7 +974,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite auto scoreTable = goodScoreTable; scoreTable[history.UNLNodeIDs[11]] = NegativeUNLVote::negativeUNLLowWaterMark - 1; scoreTable[history.UNLNodeIDs[12]] = NegativeUNLVote::negativeUNLLowWaterMark - 1; - BEAST_EXPECT(checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 2, 3)); + BEAST_EXPECT( + checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 2, 3)); } { @@ -984,7 +1016,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite hash_set negUnl_temp = negUnl_012; negUnl_temp.insert(new_1); negUnl_temp.insert(new_2); - BEAST_EXPECT(checkCandidateSizes(vote, UNL_temp, negUnl_temp, scoreTable, 0, 3 + 2)); + BEAST_EXPECT( + checkCandidateSizes(vote, UNL_temp, negUnl_temp, scoreTable, 0, 3 + 2)); } { // 2 new validators have bad scores, not in negUnl @@ -1098,8 +1131,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite toReEnable_expect = us; } } - BEAST_EXPECT( - checkCandidateSizes(vote, unl, negUnl, scoreTable, toDisable_expect, toReEnable_expect)); + BEAST_EXPECT(checkCandidateSizes( + vote, unl, negUnl, scoreTable, toDisable_expect, toReEnable_expect)); } } } @@ -1168,8 +1201,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite { toReEnable_expect = negUnl.size() - 12; } - BEAST_EXPECT( - checkCandidateSizes(vote, unl, negUnl, scoreTable, toDisable_expect, toReEnable_expect)); + BEAST_EXPECT(checkCandidateSizes( + vote, unl, negUnl, scoreTable, toDisable_expect, toReEnable_expect)); } } } @@ -1227,7 +1260,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite vote.purgeNewValidators(NegativeUNLVote::newValidatorDisableSkip + 4); BEAST_EXPECT(vote.newValidators_.size() == 1); BEAST_EXPECT(vote.newValidators_.begin()->first == n3); - BEAST_EXPECT(vote.newValidators_.begin()->second == NegativeUNLVote::newValidatorDisableSkip); + BEAST_EXPECT( + vote.newValidators_.begin()->second == NegativeUNLVote::newValidatorDisableSkip); } void @@ -1274,13 +1308,19 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite NodeID myId = history.UNLNodeIDs[3]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { - std::size_t k; + std::size_t k = 0; if (idx < 2) + { k = 0; + } else if (idx < 4) + { k = 1; + } else + { k = 2; + } bool add_50 = scorePattern[sp][k] == 50 && l->seq() % 2 == 0; bool add_100 = scorePattern[sp][k] == 100; @@ -1289,8 +1329,8 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite }); NegativeUNLVote vote(myId, history.env.journal); - auto scoreTable = - vote.buildScoreTable(history.lastLedger(), history.UNLNodeIDSet, history.validations); + auto scoreTable = vote.buildScoreTable( + history.lastLedger(), history.UNLNodeIDSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { @@ -1303,9 +1343,11 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite if (scorePattern[sp][k] == 50) return score == 256 / 2; if (scorePattern[sp][k] == 100) + { return score == 256; - else - return false; + } + + return false; }; for (; i < 2; ++i) { @@ -1379,7 +1421,9 @@ class NegativeUNLVoteGoodScore_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return true; + }); BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0)); } } @@ -1392,7 +1436,9 @@ class NegativeUNLVoteGoodScore_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return true; + }); BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 1)); } } @@ -1436,6 +1482,7 @@ class NegativeUNLVoteOffline_test : public beast::unit_test::suite if (history.goodHistory) { NodeID n1 = calcNodeID(*history.lastLedger()->negativeUNL().begin()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) NodeID n2 = calcNodeID(*history.lastLedger()->validatorToDisable()); history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { @@ -1500,7 +1547,9 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return idx > 1; }); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return idx > 1; + }); BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0)); } } @@ -1513,7 +1562,9 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return idx > 1; }); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return idx > 1; + }); BEAST_EXPECT(voteAndCheck(history, NodeID(0xdeadbeef), 0)); } } @@ -1526,11 +1577,14 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return idx > 1; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs.back(), 1, [&](NegativeUNLVote& vote) { - history.UNLKeySet.erase(history.UNLKeys[0]); - history.UNLKeySet.erase(history.UNLKeys[1]); - })); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return idx > 1; + }); + BEAST_EXPECT( + voteAndCheck(history, history.UNLNodeIDs.back(), 1, [&](NegativeUNLVote& vote) { + history.UNLKeySet.erase(history.UNLKeys[0]); + history.UNLKeySet.erase(history.UNLKeys[1]); + })); } } } @@ -1557,39 +1611,46 @@ class NegativeUNLVoteNewValidator_test : public beast::unit_test::suite if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0, [&](NegativeUNLVote& vote) { - auto extra_key_1 = randomKeyPair(KeyType::ed25519).first; - auto extra_key_2 = randomKeyPair(KeyType::ed25519).first; - history.UNLKeySet.insert(extra_key_1); - history.UNLKeySet.insert(extra_key_2); - hash_set nowTrusted; - nowTrusted.insert(calcNodeID(extra_key_1)); - nowTrusted.insert(calcNodeID(extra_key_2)); - vote.newValidators(history.lastLedger()->seq(), nowTrusted); - })); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return true; + }); + BEAST_EXPECT( + voteAndCheck(history, history.UNLNodeIDs[0], 0, [&](NegativeUNLVote& vote) { + auto extra_key_1 = randomKeyPair(KeyType::ed25519).first; + auto extra_key_2 = randomKeyPair(KeyType::ed25519).first; + history.UNLKeySet.insert(extra_key_1); + history.UNLKeySet.insert(extra_key_2); + hash_set nowTrusted; + nowTrusted.insert(calcNodeID(extra_key_1)); + nowTrusted.insert(calcNodeID(extra_key_2)); + vote.newValidators(history.lastLedger()->seq(), nowTrusted); + })); } } { //== 2 expired new validators have bad scores //-- txSet.size = 1 - NetworkHistory history = {*this, {21, 0, false, false, NegativeUNLVote::newValidatorDisableSkip * 2}}; + NetworkHistory history = { + *this, {21, 0, false, false, NegativeUNLVote::newValidatorDisableSkip * 2}}; BEAST_EXPECT(history.goodHistory); if (history.goodHistory) { history.walkHistoryAndAddValidations( - [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 1, [&](NegativeUNLVote& vote) { - auto extra_key_1 = randomKeyPair(KeyType::ed25519).first; - auto extra_key_2 = randomKeyPair(KeyType::ed25519).first; - history.UNLKeySet.insert(extra_key_1); - history.UNLKeySet.insert(extra_key_2); - hash_set nowTrusted; - nowTrusted.insert(calcNodeID(extra_key_1)); - nowTrusted.insert(calcNodeID(extra_key_2)); - vote.newValidators(256, nowTrusted); - })); + [&](std::shared_ptr const& l, std::size_t idx) -> bool { + return true; + }); + BEAST_EXPECT( + voteAndCheck(history, history.UNLNodeIDs[0], 1, [&](NegativeUNLVote& vote) { + auto extra_key_1 = randomKeyPair(KeyType::ed25519).first; + auto extra_key_2 = randomKeyPair(KeyType::ed25519).first; + history.UNLKeySet.insert(extra_key_1); + history.UNLKeySet.insert(extra_key_2); + hash_set nowTrusted; + nowTrusted.insert(calcNodeID(extra_key_1)); + nowTrusted.insert(calcNodeID(extra_key_2)); + vote.newValidators(256, nowTrusted); + })); } } } @@ -1613,7 +1674,11 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::suite auto createSTVal = [&](std::pair const& keys) { return std::make_shared( - env.app().timeKeeper().now(), keys.first, keys.second, calcNodeID(keys.first), [&](STValidation& v) { + env.app().timeKeeper().now(), + keys.first, + keys.second, + calcNodeID(keys.first), + [&](STValidation& v) { v.setFieldH256(sfLedgerHash, l->header().hash); v.setFieldU32(sfLedgerSequence, l->seq()); v.setFlag(vfFullValidation); @@ -1682,7 +1747,11 @@ BEAST_DEFINE_TESTSUITE(NegativeUNLVoteFilterValidations, consensus, xrpl); /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// bool -negUnlSizeTest(std::shared_ptr const& l, size_t size, bool hasToDisable, bool hasToReEnable) +negUnlSizeTest( + std::shared_ptr const& l, + size_t size, + bool hasToDisable, + bool hasToReEnable) { bool sameSize = l->negativeUNL().size() == size; bool sameToDisable = (l->validatorToDisable() != std::nullopt) == hasToDisable; @@ -1696,13 +1765,17 @@ applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass) { auto const res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal); if (pass) - return res.ter == tesSUCCESS; - else - return res.ter == tefFAILURE || res.ter == temDISABLED; + { + return isTesSuccess(res.ter); + } + + return res.ter == tefFAILURE || res.ter == temDISABLED; } bool -VerifyPubKeyAndSeq(std::shared_ptr const& l, hash_map nUnlLedgerSeq) +VerifyPubKeyAndSeq( + std::shared_ptr const& l, + hash_map nUnlLedgerSeq) { auto sle = l->read(keylet::negativeUNL()); if (!sle) @@ -1732,7 +1805,7 @@ VerifyPubKeyAndSeq(std::shared_ptr const& l, hash_map const ranks = sample(network.size(), PowerLawDistribution{1, 3}, sim.rng); + std::vector const ranks = + sample(network.size(), PowerLawDistribution{1, 3}, sim.rng); // generate scale-free trust graph - randomRankedTrust(network, ranks, numUNLs, std::uniform_int_distribution<>{minUNLSize, maxUNLSize}, sim.rng); + randomRankedTrust( + network, + ranks, + numUNLs, + std::uniform_int_distribution<>{minUNLSize, maxUNLSize}, + sim.rng); // nodes with a trust line in either direction are network-connected network.connectFromTrust(round(0.2 * parms.ledgerGRANULARITY)); @@ -74,7 +80,8 @@ class ScaleFreeSim_test : public beast::unit_test::suite // TODO: Clean up this formatting mess!! log << "Peers: " << network.size() << std::endl; - log << "Simulated Duration: " << duration_cast(simDuration).count() << " ms" << std::endl; + log << "Simulated Duration: " << duration_cast(simDuration).count() << " ms" + << std::endl; log << "Branches: " << sim.branches() << std::endl; log << "Synchronized: " << (sim.synchronized() ? "Y" : "N") << std::endl; log << std::endl; diff --git a/src/test/consensus/Validations_test.cpp b/src/test/consensus/Validations_test.cpp index 34e0c23bad..cf4498bd52 100644 --- a/src/test/consensus/Validations_test.cpp +++ b/src/test/consensus/Validations_test.cpp @@ -25,7 +25,8 @@ class Validations_test : public beast::unit_test::suite // generated NetClock time to be well past its epoch to ensure // any subtractions are positive using namespace std::chrono; - return NetClock::time_point(duration_cast(c.now().time_since_epoch() + 86400s)); + return NetClock::time_point( + duration_cast(c.now().time_since_epoch() + 86400s)); } // Represents a node that can issue validations @@ -99,7 +100,15 @@ class Validations_test : public beast::unit_test::suite NetClock::duration seenOffset, bool full) const { - Validation v{id, seq, now() + signOffset, now() + seenOffset, currKey(), nodeID_, full, loadFee_}; + Validation v{ + id, + seq, + now() + signOffset, + now() + seenOffset, + currKey(), + nodeID_, + full, + loadFee_}; if (trusted_) v.setTrusted(); return v; @@ -114,13 +123,15 @@ class Validations_test : public beast::unit_test::suite Validation validate(Ledger ledger) const { - return validate(ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, true); + return validate( + ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, true); } Validation partial(Ledger ledger) const { - return validate(ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, false); + return validate( + ledger.id(), ledger.seq(), NetClock::duration{0}, NetClock::duration{0}, false); } }; @@ -304,13 +315,16 @@ class Validations_test : public beast::unit_test::suite Node n = harness.makeNode(); BEAST_EXPECT( - ValStatus::stale == harness.add(n.validate(ledgerA, -harness.parms().validationCURRENT_EARLY, 0s))); + ValStatus::stale == + harness.add(n.validate(ledgerA, -harness.parms().validationCURRENT_EARLY, 0s))); BEAST_EXPECT( - ValStatus::stale == harness.add(n.validate(ledgerA, harness.parms().validationCURRENT_WALL, 0s))); + ValStatus::stale == + harness.add(n.validate(ledgerA, harness.parms().validationCURRENT_WALL, 0s))); BEAST_EXPECT( - ValStatus::stale == harness.add(n.validate(ledgerA, 0s, harness.parms().validationCURRENT_LOCAL))); + ValStatus::stale == + harness.add(n.validate(ledgerA, 0s, harness.parms().validationCURRENT_LOCAL))); } { @@ -367,7 +381,9 @@ class Validations_test : public beast::unit_test::suite BEAST_EXPECT(ValStatus::current == harness.add(n.validate(ledgerAB))); trigger(harness.vals()); BEAST_EXPECT(harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 1); - BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id())); + BEAST_EXPECT( + harness.vals().getPreferred(genesisLedger) == + std::make_pair(ledgerAB.seq(), ledgerAB.id())); harness.clock().advance(harness.parms().validationCURRENT_LOCAL); // trigger check for stale @@ -395,7 +411,8 @@ class Validations_test : public beast::unit_test::suite Ledger ledgerAD = h["ad"]; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode(); + Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), + d = harness.makeNode(); c.untrust(); // first round a,b,c agree, d has is partial @@ -520,8 +537,8 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode(), - e = harness.makeNode(); + Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), + d = harness.makeNode(), e = harness.makeNode(); c.untrust(); // Mix of load fees @@ -546,7 +563,9 @@ class Validations_test : public beast::unit_test::suite auto const& expectedValidations = it.second; BEAST_EXPECT(harness.vals().numTrustedForLedger(id) == expectedValidations.size()); - BEAST_EXPECT(sorted(harness.vals().getTrustedForLedger(id, seq)) == sorted(expectedValidations)); + BEAST_EXPECT( + sorted(harness.vals().getTrustedForLedger(id, seq)) == + sorted(expectedValidations)); std::uint32_t baseFee = 0; std::vector expectedFees; @@ -641,7 +660,8 @@ class Validations_test : public beast::unit_test::suite // change toKeep harness.vals().setSeqToKeep(ledgerB.seq() + one, ledgerB.seq() + two); // advance clock slowly - int const loops = harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1; + int const loops = + harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1; for (int i = 0; i < loops; ++i) { harness.clock().advance(harness.parms().validationFRESHNESS); @@ -698,7 +718,8 @@ class Validations_test : public beast::unit_test::suite LedgerHistoryHelper h; TestHarness harness(h.oracle); - Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), d = harness.makeNode(); + Node a = harness.makeNode(), b = harness.makeNode(), c = harness.makeNode(), + d = harness.makeNode(); c.untrust(); Ledger ledgerA = h["a"]; @@ -841,20 +862,26 @@ class Validations_test : public beast::unit_test::suite Validation val2 = a.validate(ID{4}, Seq{4}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(val2)); BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 1); - BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id())); + BEAST_EXPECT( + harness.vals().getPreferred(genesisLedger) == + std::make_pair(ledgerAB.seq(), ledgerAB.id())); // Another node requesting that ledger still doesn't change things Validation val3 = b.validate(ID{4}, Seq{4}, 0s, 0s, true); BEAST_EXPECT(ValStatus::current == harness.add(val3)); BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{4}) == 2); - BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerAB.seq(), ledgerAB.id())); + BEAST_EXPECT( + harness.vals().getPreferred(genesisLedger) == + std::make_pair(ledgerAB.seq(), ledgerAB.id())); // Switch to validation that is available harness.clock().advance(5s); Ledger ledgerABCDE = h["abcde"]; BEAST_EXPECT(ValStatus::current == harness.add(a.partial(ledgerABCDE))); BEAST_EXPECT(ValStatus::current == harness.add(b.partial(ledgerABCDE))); - BEAST_EXPECT(harness.vals().getPreferred(genesisLedger) == std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id())); + BEAST_EXPECT( + harness.vals().getPreferred(genesisLedger) == + std::make_pair(ledgerABCDE.seq(), ledgerABCDE.id())); } void @@ -902,20 +929,29 @@ class Validations_test : public beast::unit_test::suite testcase("TrustChanged"); using namespace std::chrono; - auto checker = - [this](TestValidations& vals, hash_set const& listed, std::vector const& trustedVals) { - Ledger::ID testID = trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID(); - Ledger::Seq testSeq = trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq(); - BEAST_EXPECT(vals.currentTrusted() == trustedVals); - BEAST_EXPECT(vals.getCurrentNodeIDs() == listed); - BEAST_EXPECT(vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size()); - if (trustedVals.empty()) - BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt); - else - BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID); - BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals); - BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size()); - }; + auto checker = [this]( + TestValidations& vals, + hash_set const& listed, + std::vector const& trustedVals) { + Ledger::ID testID = + trustedVals.empty() ? this->genesisLedger.id() : trustedVals[0].ledgerID(); + Ledger::Seq testSeq = + trustedVals.empty() ? this->genesisLedger.seq() : trustedVals[0].seq(); + BEAST_EXPECT(vals.currentTrusted() == trustedVals); + BEAST_EXPECT(vals.getCurrentNodeIDs() == listed); + BEAST_EXPECT( + vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) == trustedVals.size()); + if (trustedVals.empty()) + { + BEAST_EXPECT(vals.getPreferred(this->genesisLedger) == std::nullopt); + } + else + { + BEAST_EXPECT(vals.getPreferred(this->genesisLedger)->second == testID); + } + BEAST_EXPECT(vals.getTrustedForLedger(testID, testSeq) == trustedVals); + BEAST_EXPECT(vals.numTrustedForLedger(testID) == trustedVals.size()); + }; { // Trusted to untrusted @@ -966,6 +1002,8 @@ class Validations_test : public beast::unit_test::suite std::vector trustedVals({v}); auto& vals = harness.vals(); BEAST_EXPECT(vals.currentTrusted() == trustedVals); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(vals.getPreferred(genesisLedger)->second == v.ledgerID()); BEAST_EXPECT(vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0); diff --git a/src/test/core/ClosureCounter_test.cpp b/src/test/core/ClosureCounter_test.cpp index f5e9dc7880..fe3f0a219f 100644 --- a/src/test/core/ClosureCounter_test.cpp +++ b/src/test/core/ClosureCounter_test.cpp @@ -36,9 +36,9 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with no arguments. - (*wrapped)(); + (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 1); - (*wrapped)(); + (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 2); // Destroying the contents of wrapped should decrement voidCounter. @@ -60,9 +60,9 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with one integer argument. - (*wrapped)(5); + (*wrapped)(5); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 5); - (*wrapped)(11); + (*wrapped)(11); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(evidence == 11); // Destroying the contents of wrapped should decrement setCounter. @@ -82,8 +82,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); // wrapped() should be callable with two integers. - BEAST_EXPECT((*wrapped)(5, 2) == 7); - BEAST_EXPECT((*wrapped)(2, -8) == -6); + BEAST_EXPECT((*wrapped)(5, 2) == 7); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT((*wrapped)(2, -8) == -6); // NOLINT(bugprone-unchecked-optional-access) // Destroying the contents of wrapped should decrement sumCounter. wrapped = std::nullopt; @@ -106,12 +106,14 @@ class ClosureCounter_test : public beast::unit_test::suite } // Copy constructor - TrackedString(TrackedString const& rhs) : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str) + TrackedString(TrackedString const& rhs) + : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str) { } // Move constructor - TrackedString(TrackedString&& rhs) noexcept : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str)) + TrackedString(TrackedString&& rhs) noexcept + : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str)) { } @@ -152,7 +154,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString const strValue("value"); - TrackedString const result = (*wrapped)(strValue); + TrackedString const result = + (*wrapped)(strValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 2); BEAST_EXPECT(result.moves == 1); BEAST_EXPECT(result.str == "value!"); @@ -169,7 +172,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString const strConstLValue("const lvalue"); - TrackedString const result = (*wrapped)(strConstLValue); + TrackedString const result = + (*wrapped)(strConstLValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 1); // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0 BEAST_EXPECT(result.str == "const lvalue!"); @@ -186,7 +190,8 @@ class ClosureCounter_test : public beast::unit_test::suite BEAST_EXPECT(wrapped); TrackedString strLValue("lvalue"); - TrackedString const result = (*wrapped)(strLValue); + TrackedString const result = + (*wrapped)(strLValue); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 1); BEAST_EXPECT(result.moves == 0); BEAST_EXPECT(result.str == "lvalue!"); @@ -202,20 +207,21 @@ class ClosureCounter_test : public beast::unit_test::suite // leaving scope. So, without intervention, they would // do a copy for the return (June 2017). An explicit // std::move() was required. - return std::move(in += "!"); + in += "!"; + return std::move(in); }); BEAST_EXPECT(strCounter.count() == 1); BEAST_EXPECT(wrapped); - // Make the string big enough to (probably) avoid the small string - // optimization. + // Make the string big enough to (probably) avoid the small string optimization. TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz"); - TrackedString const result = (*wrapped)(std::move(strRValue)); + TrackedString const result = + (*wrapped)(std::move(strRValue)); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(result.copies == 0); BEAST_EXPECT(result.moves == 1); BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!"); - BEAST_EXPECT(strRValue.str.size() == 0); + BEAST_EXPECT(strRValue.str.empty()); // NOLINT(bugprone-use-after-move) } } diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index d5f3bb556c..e9f7354482 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -101,7 +101,8 @@ backend=sqlite )xrpldConfig"); std::string dbPathSection = dbPath.empty() ? "" : "[database_path]\n" + dbPath; - std::string valFileSection = validatorsFile.empty() ? "" : "[validators_file]\n" + validatorsFile; + std::string valFileSection = + validatorsFile.empty() ? "" : "[validators_file]\n" + validatorsFile; return boost::str(configContentsTemplate % dbPathSection % valFileSection); } @@ -130,7 +131,8 @@ public: test, std::move(subDir), configFile, - confContents.empty() ? configContents(dbPath.string(), validatorsFile.string()) : confContents, + confContents.empty() ? configContents(dbPath.string(), validatorsFile.string()) + : confContents, useCounter) , dataDir_(dbPath) { @@ -282,7 +284,7 @@ port_wss_admin expectException([&c] { c.legacy("server"); }); // not a single line // set a legacy value - BEAST_EXPECT(c.legacy("not_in_file") == ""); + BEAST_EXPECT(c.legacy("not_in_file").empty()); c.legacy("not_in_file", "new_value"); BEAST_EXPECT(c.legacy("not_in_file") == "new_value"); } @@ -312,7 +314,9 @@ port_wss_admin Config c; c.setup("", true, false, true); BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); + BEAST_EXPECT( + c.section(SECTION_DEBUG_LOGFILE).values()[0] == + "/Users/dummy/xrpld/config/log/debug.log"); } // Config file in HOME or XDG_CONFIG_HOME directory. @@ -349,7 +353,9 @@ port_wss_admin Config c; c.setup("", true, false, true); BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); + BEAST_EXPECT( + c.section(SECTION_DEBUG_LOGFILE).values()[0] == + "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. h ? setenv("HOME", h, 1) : unsetenv("HOME"); @@ -383,7 +389,9 @@ port_wss_admin Config c; c.setup("", true, false, true); BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); + BEAST_EXPECT( + c.section(SECTION_DEBUG_LOGFILE).values()[0] == + "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. h ? setenv("HOME", h, 1) : unsetenv("HOME"); @@ -426,7 +434,7 @@ port_wss_admin // load will not. Config c; c.loadFromString(""); - BEAST_EXPECT(c.legacy("database_path") == ""); + BEAST_EXPECT(c.legacy("database_path").empty()); } } { @@ -435,7 +443,8 @@ port_wss_admin detail::DirGuard const g0(*this, "test_db"); path const dataDirRel("test_data_dir"); path const dataDirAbs(cwd / g0.subdir() / dataDirRel); - detail::FileCfgGuard const g(*this, g0.subdir(), dataDirAbs, Config::configFileName, "", false); + detail::FileCfgGuard const g( + *this, g0.subdir(), dataDirAbs, Config::configFileName, "", false); auto const& c(g.config()); BEAST_EXPECT(g.dataDirExists()); BEAST_EXPECT(g.configFileExists()); @@ -455,7 +464,8 @@ port_wss_admin // read from file no path detail::FileCfgGuard const g(*this, "test_db", "", Config::configFileName, ""); auto const& c(g.config()); - std::string const nativeDbPath = absolute(g.subdir() / path(Config::databaseDirName)).string(); + std::string const nativeDbPath = + absolute(g.subdir() / path(Config::databaseDirName)).string(); BEAST_EXPECT(g.dataDirExists()); BEAST_EXPECT(g.configFileExists()); BEAST_EXPECT(c.legacy("database_path") == nativeDbPath); @@ -523,7 +533,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -536,7 +546,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -551,7 +561,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 255); try @@ -566,7 +576,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 10000); } @@ -581,7 +591,8 @@ main boost::format cc("[validators_file]\n%1%\n"); std::string error; std::string const missingPath = "/no/way/this/path/exists"; - auto const expectedError = "The file specified in [validators_file] does not exist: " + missingPath; + auto const expectedError = + "The file specified in [validators_file] does not exist: " + missingPath; try { Config c; @@ -599,7 +610,8 @@ main path const invalidFile = current_path() / vtg.subdir(); boost::format cc("[validators_file]\n%1%\n"); std::string error; - auto const expectedError = "Invalid file specified in [validators_file]: " + invalidFile.string(); + auto const expectedError = + "Invalid file specified in [validators_file]: " + invalidFile.string(); try { Config c; @@ -645,8 +657,11 @@ trust-these-validators.gov )xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == "trust-these-validators.gov"); + BEAST_EXPECT( + c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); + BEAST_EXPECT( + c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == + "trust-these-validators.gov"); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1); BEAST_EXPECT( c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] == @@ -672,8 +687,11 @@ trust-these-validators.gov )xrpldConfig"); c.loadFromString(toLoad); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == "trust-these-validators.gov"); + BEAST_EXPECT( + c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); + BEAST_EXPECT( + c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == + "trust-these-validators.gov"); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1); BEAST_EXPECT( c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] == @@ -809,7 +827,8 @@ trust-these-validators.gov // in config directory std::string const valFileName = "validators.txt"; detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", valFileName); - detail::FileCfgGuard const rcg(*this, vtg.subdir(), "", Config::configFileName, valFileName, false); + detail::FileCfgGuard const rcg( + *this, vtg.subdir(), "", Config::configFileName, valFileName, false); BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); @@ -825,7 +844,8 @@ trust-these-validators.gov // to config directory detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.txt"); auto const valFilePath = ".." / vtg.subdir() / "validators.txt"; - detail::FileCfgGuard const rcg(*this, vtg.subdir(), "", Config::configFileName, valFilePath, false); + detail::FileCfgGuard const rcg( + *this, vtg.subdir(), "", Config::configFileName, valFilePath, false); BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); @@ -839,7 +859,8 @@ trust-these-validators.gov { // load from validators file in default location detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.txt"); - detail::FileCfgGuard const rcg(*this, vtg.subdir(), "", Config::configFileName, "", false); + detail::FileCfgGuard const rcg( + *this, vtg.subdir(), "", Config::configFileName, "", false); BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); @@ -855,7 +876,8 @@ trust-these-validators.gov // of default location detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg"); BEAST_EXPECT(vtg.validatorsFileExists()); - detail::ValidatorsTxtGuard const vtgDefault(*this, vtg.subdir(), "validators.txt", false); + detail::ValidatorsTxtGuard const vtgDefault( + *this, vtg.subdir(), "validators.txt", false); BEAST_EXPECT(vtgDefault.validatorsFileExists()); detail::FileCfgGuard const rcg( *this, vtg.subdir(), "", Config::configFileName, vtg.validatorsFile(), false); @@ -963,7 +985,8 @@ trust-these-validators.gov void testSetup(bool explicitPath) { - detail::FileCfgGuard const cfg(*this, "testSetup", explicitPath ? "test_db" : "", Config::configFileName, ""); + detail::FileCfgGuard const cfg( + *this, "testSetup", explicitPath ? "test_db" : "", Config::configFileName, ""); /* FileCfgGuard has a Config object that gets loaded on construction, but Config::setup is not reentrant, so we need a fresh config for every test case, so ignore it. @@ -1096,17 +1119,19 @@ trust-these-validators.gov void testZeroPort() { - auto const contents = - std::regex_replace(detail::configContents("", ""), std::regex("port\\s*=\\s*\\d+"), "port = 0"); + auto const contents = std::regex_replace( + detail::configContents("", ""), std::regex("port\\s*=\\s*\\d+"), "port = 0"); try { - detail::FileCfgGuard const cfg(*this, "testPort", "", Config::configFileName, "", true, contents); + detail::FileCfgGuard const cfg( + *this, "testPort", "", Config::configFileName, "", true, contents); BEAST_EXPECT(false); } catch (std::exception const& ex) { - BEAST_EXPECT(std::string_view(ex.what()).starts_with("Invalid value '0' for key 'port'")); + BEAST_EXPECT( + std::string_view(ex.what()).starts_with("Invalid value '0' for key 'port'")); } } @@ -1116,23 +1141,24 @@ trust-these-validators.gov Config cfg; /* NOTE: this string includes some explicit * space chars in order to verify proper trimming */ - std::string toLoad(R"( + std::string toLoad( + R"( [port_rpc])" - "\x20" - R"( + "\x20" + R"( # comment # indented comment )" - "\x20\x20" - R"( + "\x20\x20" + R"( [ips])" - "\x20" - R"( + "\x20" + R"( r.ripple.com 51235 [ips_fixed])" - "\x20\x20" - R"( + "\x20\x20" + R"( # COMMENT s1.ripple.com 51235 s2.ripple.com 51235 @@ -1156,23 +1182,24 @@ r.ripple.com 51235 Config cfg; /* NOTE: this string includes some explicit * space chars in order to verify proper trimming */ - std::string toLoad(R"( + std::string toLoad( + R"( [port_rpc])" - "\x20" - R"( + "\x20" + R"( # comment # indented comment )" - "\x20\x20" - R"( + "\x20\x20" + R"( [ips])" - "\x20" - R"( + "\x20" + R"( r.ripple.com:51235 [ips_fixed])" - "\x20\x20" - R"( + "\x20\x20" + R"( # COMMENT s1.ripple.com:51235 s2.ripple.com 51235 @@ -1253,7 +1280,7 @@ r.ripple.com:51235 for (auto const& t : tests) { Section s; - s.append(t.line.data()); + s.append(std::string(t.line)); BEAST_EXPECT(s.had_trailing_comments() == t.had_comment); if (t.field.empty()) { @@ -1262,7 +1289,7 @@ r.ripple.com:51235 else { std::string field; - BEAST_EXPECTS(set(field, t.field.data(), s), t.line); + BEAST_EXPECTS(set(field, std::string(t.field), s), t.line); BEAST_EXPECTS(field == t.expect, t.line); } } @@ -1272,6 +1299,7 @@ r.ripple.com:51235 s.append("online_delete = 3000"); std::uint32_t od = 0; BEAST_EXPECT(set(od, "online_delete", s)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECTS(od == 3000, *(s.get("online_delete"))); } @@ -1280,6 +1308,7 @@ r.ripple.com:51235 s.append("online_delete = 2000 #my comment on this"); std::uint32_t od = 0; BEAST_EXPECT(set(od, "online_delete", s)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECTS(od == 2000, *(s.get("online_delete"))); } } @@ -1308,7 +1337,7 @@ r.ripple.com:51235 auto val_3 = get(s, "a_string"); BEAST_EXPECT(val_3 == "mystring"); auto val_4 = get(s, "not_a_key"); - BEAST_EXPECT(val_4 == ""); + BEAST_EXPECT(val_4.empty()); auto val_5 = get(s, "not_a_key", "default"); BEAST_EXPECT(val_5 == "default"); @@ -1400,7 +1429,7 @@ r.ripple.com:51235 {"months", 2592000, 1, false}, {"years", 31536000, 1, false}}; - std::string space = ""; + std::string space; for (auto& [unit, sec, val, shouldPass] : units) { Config c; @@ -1408,22 +1437,30 @@ r.ripple.com:51235 [amendment_majority_time] )xrpldConfig"); toLoad += std::to_string(val) + space + unit; - space = space == "" ? " " : ""; + space = space.empty() ? " " : ""; try { c.loadFromString(toLoad); if (shouldPass) + { BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec); + } else + { fail(); + } } catch (std::runtime_error&) { if (!shouldPass) + { pass(); + } else + { fail(); + } } } } diff --git a/src/test/core/JobQueue_test.cpp b/src/test/core/JobQueue_test.cpp index 0eee9ce6f2..bf6a5590e6 100644 --- a/src/test/core/JobQueue_test.cpp +++ b/src/test/core/JobQueue_test.cpp @@ -19,7 +19,8 @@ class JobQueue_test : public beast::unit_test::suite { // addJob() should run the Job (and return true). std::atomic jobRan{false}; - BEAST_EXPECT(jQueue.addJob(jtCLIENT, "JobAddTest1", [&jobRan]() { jobRan = true; }) == true); + BEAST_EXPECT( + jQueue.addJob(jtCLIENT, "JobAddTest1", [&jobRan]() { jobRan = true; }) == true); // Wait for the Job to run. while (jobRan == false) @@ -35,8 +36,10 @@ class JobQueue_test : public beast::unit_test::suite // The Job should never run, so having the Job access this // unprotected variable on the stack should be completely safe. // Not recommended for the faint of heart... - bool unprotected; - BEAST_EXPECT(jQueue.addJob(jtCLIENT, "JobAddTest2", [&unprotected]() { unprotected = false; }) == false); + bool unprotected = false; + BEAST_EXPECT(jQueue.addJob(jtCLIENT, "JobAddTest2", [&unprotected]() { + unprotected = false; + }) == false); } } @@ -50,7 +53,9 @@ class JobQueue_test : public beast::unit_test::suite // Test repeated post()s until the Coro completes. std::atomic yieldCount{0}; auto const coro = jQueue.postCoro( - jtCLIENT, "PostCoroTest1", [&yieldCount](std::shared_ptr const& coroCopy) { + jtCLIENT, + "PostCoroTest1", + [&yieldCount](std::shared_ptr const& coroCopy) { while (++yieldCount < 4) coroCopy->yield(); }); @@ -77,7 +82,9 @@ class JobQueue_test : public beast::unit_test::suite // Test repeated resume()s until the Coro completes. int yieldCount{0}; auto const coro = jQueue.postCoro( - jtCLIENT, "PostCoroTest2", [&yieldCount](std::shared_ptr const& coroCopy) { + jtCLIENT, + "PostCoroTest2", + [&yieldCount](std::shared_ptr const& coroCopy) { while (++yieldCount < 4) coroCopy->yield(); }); @@ -111,9 +118,9 @@ class JobQueue_test : public beast::unit_test::suite // The Coro should never run, so having the Coro access this // unprotected variable on the stack should be completely safe. // Not recommended for the faint of heart... - bool unprotected; - auto const coro = - jQueue.postCoro(jtCLIENT, "PostCoroTest3", [&unprotected](std::shared_ptr const&) { + bool unprotected = false; + auto const coro = jQueue.postCoro( + jtCLIENT, "PostCoroTest3", [&unprotected](std::shared_ptr const&) { unprotected = false; }); BEAST_EXPECT(coro == nullptr); diff --git a/src/test/core/SociDB_test.cpp b/src/test/core/SociDB_test.cpp index 001022aa95..66b368176d 100644 --- a/src/test/core/SociDB_test.cpp +++ b/src/test/core/SociDB_test.cpp @@ -1,9 +1,8 @@ #include -#include - #include #include +#include #include #include @@ -59,7 +58,7 @@ public: { setupDatabaseDir(getDatabasePath()); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } } @@ -69,7 +68,7 @@ public: { cleanupDatabaseDir(getDatabasePath()); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } } @@ -81,7 +80,10 @@ public: BasicConfig c; setupSQLiteConfig(c, getDatabasePath()); std::vector> const d( - {{"peerfinder", ".sqlite"}, {"state", ".db"}, {"random", ".db"}, {"validators", ".sqlite"}}); + {{"peerfinder", ".sqlite"}, + {"state", ".db"}, + {"random", ".db"}, + {"validators", ".sqlite"}}); for (auto const& i : d) { @@ -102,13 +104,17 @@ public: // Check values in db std::vector stringResult(20 * stringData.size()); std::vector intResult(20 * intData.size()); - s << "SELECT StringData, IntData FROM SociTestTable;", soci::into(stringResult), soci::into(intResult); - BEAST_EXPECT(stringResult.size() == stringData.size() && intResult.size() == intData.size()); + s << "SELECT StringData, IntData FROM SociTestTable;", soci::into(stringResult), + soci::into(intResult); + BEAST_EXPECT( + stringResult.size() == stringData.size() && intResult.size() == intData.size()); for (int i = 0; i < stringResult.size(); ++i) { - auto si = - std::distance(stringData.begin(), std::find(stringData.begin(), stringData.end(), stringResult[i])); - auto ii = std::distance(intData.begin(), std::find(intData.begin(), intData.end(), intResult[i])); + auto si = std::distance( + stringData.begin(), + std::find(stringData.begin(), stringData.end(), stringResult[i])); + auto ii = std::distance( + intData.begin(), std::find(intData.begin(), intData.end(), intResult[i])); BEAST_EXPECT(si == ii && si < stringResult.size()); } }; @@ -149,7 +155,8 @@ public: BasicConfig c; setupSQLiteConfig(c, getDatabasePath()); DBConfig sc(c, "SociTestDB"); - std::vector const ubid({(std::uint64_t)std::numeric_limits::max(), 20, 30}); + std::vector const ubid( + {(std::uint64_t)std::numeric_limits::max(), 20, 30}); std::vector const bid({-10, -20, -30}); std::vector const uid({std::numeric_limits::max(), 2, 3}); std::vector const id({-1, -2, -3}); @@ -177,8 +184,8 @@ public: std::uint32_t uig = 0; std::int64_t big = 0; std::uint64_t ubig = 0; - s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig), soci::into(big), - soci::into(ubig); + s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig), + soci::into(big), soci::into(ubig); BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] && ubig == ubid[0]); } catch (std::exception&) @@ -195,8 +202,8 @@ public: uint32_t uig = 0; boost::optional big; boost::optional ubig; - s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig), soci::into(big), - soci::into(ubig); + s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig), + soci::into(big), soci::into(ubig); BEAST_EXPECT(*ig == id[0] && uig == uid[0] && *big == bid[0] && *ubig == ubid[0]); } catch (std::exception&) diff --git a/src/test/core/Workers_test.cpp b/src/test/core/Workers_test.cpp index 1f1c16b12b..65245b8c94 100644 --- a/src/test/core/Workers_test.cpp +++ b/src/test/core/Workers_test.cpp @@ -101,7 +101,9 @@ public: void testThreads(int const tc1, int const tc2, int const tc3) { - testcase("threadCounts: " + std::to_string(tc1) + " -> " + std::to_string(tc2) + " -> " + std::to_string(tc3)); + testcase( + "threadCounts: " + std::to_string(tc1) + " -> " + std::to_string(tc2) + " -> " + + std::to_string(tc3)); TestCallback cb; std::unique_ptr perfLog = std::make_unique(); diff --git a/src/test/csf/BasicNetwork_test.cpp b/src/test/csf/BasicNetwork_test.cpp index d66f5c9891..eee16c2ce1 100644 --- a/src/test/csf/BasicNetwork_test.cpp +++ b/src/test/csf/BasicNetwork_test.cpp @@ -33,7 +33,10 @@ public: if (id == 0) { for (auto const link : net.links(this)) - net.send(this, link.target, [&, to = link.target] { to->receive(net, this, 1); }); + { + net.send( + this, link.target, [&, to = link.target] { to->receive(net, this, 1); }); + } } else { @@ -50,7 +53,11 @@ public: if (m < 5) { for (auto const link : net.links(this)) - net.send(this, link.target, [&, mm = m, to = link.target] { to->receive(net, this, mm); }); + { + net.send(this, link.target, [&, mm = m, to = link.target] { + to->receive(net, this, mm); + }); + } } } }; diff --git a/src/test/csf/Digraph.h b/src/test/csf/Digraph.h index 672445ca95..66ef73390a 100644 --- a/src/test/csf/Digraph.h +++ b/src/test/csf/Digraph.h @@ -128,7 +128,8 @@ public: auto outVertices() const { - return boost::adaptors::transform(graph_, [](typename Graph::value_type const& v) { return v.first; }); + return boost::adaptors::transform( + graph_, [](typename Graph::value_type const& v) { return v.first; }); } /** Range over target vertices diff --git a/src/test/csf/Digraph_test.cpp b/src/test/csf/Digraph_test.cpp index a0cb35323d..a183234903 100644 --- a/src/test/csf/Digraph_test.cpp +++ b/src/test/csf/Digraph_test.cpp @@ -24,13 +24,15 @@ public: BEAST_EXPECT(graph.connect('a', 'b', "foobar")); BEAST_EXPECT(graph.connected('a', 'b')); - BEAST_EXPECT(*graph.edge('a', 'b') == "foobar"); + BEAST_EXPECT( + *graph.edge('a', 'b') == "foobar"); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!graph.connect('a', 'b', "repeat")); BEAST_EXPECT(graph.disconnect('a', 'b')); BEAST_EXPECT(graph.connect('a', 'b', "repeat")); BEAST_EXPECT(graph.connected('a', 'b')); - BEAST_EXPECT(*graph.edge('a', 'b') == "repeat"); + BEAST_EXPECT( + *graph.edge('a', 'b') == "repeat"); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(graph.connect('a', 'c', "tree")); diff --git a/src/test/csf/Peer.h b/src/test/csf/Peer.h index 7b19c977d6..c36d600e6c 100644 --- a/src/test/csf/Peer.h +++ b/src/test/csf/Peer.h @@ -489,7 +489,9 @@ struct Peer issue(CloseLedger{prevLedger, openTxs}); return Result( - TxSet{openTxs}, Proposal(prevLedger.id(), Proposal::seqJoin, TxSet::calcID(openTxs), closeTime, now(), id)); + TxSet{openTxs}, + Proposal( + prevLedger.id(), Proposal::seqJoin, TxSet::calcID(openTxs), closeTime, now(), id)); } void @@ -501,7 +503,14 @@ struct Peer ConsensusMode const& mode, Json::Value&& consensusJson) { - onAccept(result, prevLedger, closeResolution, rawCloseTimes, mode, std::move(consensusJson), validating()); + onAccept( + result, + prevLedger, + closeResolution, + rawCloseTimes, + mode, + std::move(consensusJson), + validating()); } void @@ -519,8 +528,8 @@ struct Peer bool const consensusFail = result.state == ConsensusState::MovedOn; TxSet const acceptedTxs = injectTxs(prevLedger, result.txns); - Ledger const newLedger = - oracle.accept(prevLedger, acceptedTxs.txs(), closeResolution, result.position.closeTime()); + Ledger const newLedger = oracle.accept( + prevLedger, acceptedTxs.txs(), closeResolution, result.position.closeTime()); ledgers[newLedger.id()] = newLedger; issue(AcceptLedger{newLedger, lastClosedLedger}); @@ -528,8 +537,9 @@ struct Peer prevRoundTime = result.roundTime.read(); lastClosedLedger = newLedger; - auto const it = std::remove_if( - openTxs.begin(), openTxs.end(), [&](Tx const& tx) { return acceptedTxs.exists(tx.id()); }); + auto const it = std::remove_if(openTxs.begin(), openTxs.end(), [&](Tx const& tx) { + return acceptedTxs.exists(tx.id()); + }); openTxs.erase(it, openTxs.end()); // Only send validation if the new ledger is compatible with our @@ -537,7 +547,8 @@ struct Peer bool const isCompatible = newLedger.isAncestor(fullyValidatedLedger); // Can only send one validated ledger per seq - if (runAsValidator && isCompatible && !consensusFail && validations.canValidateSeq(newLedger.seq())) + if (runAsValidator && isCompatible && !consensusFail && + validations.canValidateSeq(newLedger.seq())) { bool isFull = proposing; @@ -709,7 +720,9 @@ struct Peer if (link.target->router.lastObservedSeq[bm.origin] < bm.seq) { issue(Relay{link.target->id, bm.msg}); - net.send(this, link.target, [to = link.target, bm, id = this->id] { to->receive(bm, id); }); + net.send(this, link.target, [to = link.target, bm, id = this->id] { + to->receive(bm, id); + }); } } } @@ -892,7 +905,8 @@ struct Peer using namespace std::chrono; using namespace std::chrono_literals; return NetClock::time_point( - duration_cast(scheduler.now().time_since_epoch() + 86400s + clockSkew)); + duration_cast( + scheduler.now().time_since_epoch() + 86400s + clockSkew)); } Ledger::ID diff --git a/src/test/csf/PeerGroup.h b/src/test/csf/PeerGroup.h index ce7325f659..e900ab9934 100644 --- a/src/test/csf/PeerGroup.h +++ b/src/test/csf/PeerGroup.h @@ -85,7 +85,9 @@ public: bool contains(PeerID id) { - return std::find_if(peers_.begin(), peers_.end(), [id](Peer const* p) { return p->id == id; }) != peers_.end(); + return std::find_if(peers_.begin(), peers_.end(), [id](Peer const* p) { + return p->id == id; + }) != peers_.end(); } std::size_t @@ -214,7 +216,11 @@ public: { PeerGroup res; std::set_union( - a.peers_.begin(), a.peers_.end(), b.peers_.begin(), b.peers_.end(), std::back_inserter(res.peers_)); + a.peers_.begin(), + a.peers_.end(), + b.peers_.begin(), + b.peers_.end(), + std::back_inserter(res.peers_)); return res; } @@ -225,7 +231,11 @@ public: PeerGroup res; std::set_difference( - a.peers_.begin(), a.peers_.end(), b.peers_.begin(), b.peers_.end(), std::back_inserter(res.peers_)); + a.peers_.begin(), + a.peers_.end(), + b.peers_.begin(), + b.peers_.end(), + std::back_inserter(res.peers_)); return res; } diff --git a/src/test/csf/Scheduler.h b/src/test/csf/Scheduler.h index 2f7535ef16..61ec8f62ff 100644 --- a/src/test/csf/Scheduler.h +++ b/src/test/csf/Scheduler.h @@ -34,7 +34,8 @@ public: using time_point = typename clock_type::time_point; private: - using by_when_hook = boost::intrusive::set_base_hook>; + using by_when_hook = + boost::intrusive::set_base_hook>; struct event : by_when_hook { @@ -73,7 +74,8 @@ private: operator=(event_impl const&) = delete; template - event_impl(time_point when_, DeducedHandler&& h) : event(when_), h_(std::forward(h)) + event_impl(time_point when_, DeducedHandler&& h) + : event(when_), h_(std::forward(h)) { } @@ -87,8 +89,8 @@ private: class queue_type { private: - using by_when_set = - typename boost::intrusive::make_multiset>::type; + using by_when_set = typename boost::intrusive:: + make_multiset>::type; // alloc_ is owned by the scheduler boost::container::pmr::monotonic_buffer_resource* alloc_; by_when_set by_when_; @@ -251,7 +253,8 @@ public: //------------------------------------------------------------------------------ -inline Scheduler::queue_type::queue_type(boost::container::pmr::monotonic_buffer_resource* alloc) : alloc_(alloc) +inline Scheduler::queue_type::queue_type(boost::container::pmr::monotonic_buffer_resource* alloc) + : alloc_(alloc) { } diff --git a/src/test/csf/Sim.h b/src/test/csf/Sim.h index 709226b216..3525258c59 100644 --- a/src/test/csf/Sim.h +++ b/src/test/csf/Sim.h @@ -23,7 +23,8 @@ class BasicSink : public beast::Journal::Sink Scheduler::clock_type const& clock_; public: - BasicSink(Scheduler::clock_type const& clock) : Sink(beast::severities::kDisabled, false), clock_{clock} + BasicSink(Scheduler::clock_type const& clock) + : Sink(beast::severities::kDisabled, false), clock_{clock} { } @@ -89,7 +90,13 @@ public: for (std::size_t i = 0; i < numPeers; ++i) { peers.emplace_back( - PeerID{static_cast(peers.size())}, scheduler, oracle, net, trustGraph, collectors, j); + PeerID{static_cast(peers.size())}, + scheduler, + oracle, + net, + trustGraph, + collectors, + j); newPeers.emplace_back(&peers.back()); } PeerGroup res{newPeers}; diff --git a/src/test/csf/TrustGraph.h b/src/test/csf/TrustGraph.h index d25925ad38..bae0be4af7 100644 --- a/src/test/csf/TrustGraph.h +++ b/src/test/csf/TrustGraph.h @@ -124,8 +124,8 @@ public: auto const& unlB = uniqueUNLs[j]; double rhs = 2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size()); - int intersectionSize = - std::count_if(unlA.begin(), unlA.end(), [&](Peer p) { return unlB.find(p) != unlB.end(); }); + int intersectionSize = std::count_if( + unlA.begin(), unlA.end(), [&](Peer p) { return unlB.find(p) != unlB.end(); }); if (intersectionSize < rhs) { diff --git a/src/test/csf/Tx.h b/src/test/csf/Tx.h index 20c90cbac5..f919b650db 100644 --- a/src/test/csf/Tx.h +++ b/src/test/csf/Tx.h @@ -144,7 +144,11 @@ public: auto populate_diffs = [&res](auto const& a, auto const& b, bool s) { auto populator = [&](auto const& tx) { res[tx.id()] = s; }; std::set_difference( - a.begin(), a.end(), b.begin(), b.end(), boost::make_function_output_iterator(std::ref(populator))); + a.begin(), + a.end(), + b.begin(), + b.end(), + boost::make_function_output_iterator(std::ref(populator))); }; populate_diffs(txs_, other.txs_, true); diff --git a/src/test/csf/collectors.h b/src/test/csf/collectors.h index 20fc31a01f..cfe40330ff 100644 --- a/src/test/csf/collectors.h +++ b/src/test/csf/collectors.h @@ -229,14 +229,16 @@ struct TxCollector std::size_t orphaned() const { - return std::count_if(txs.begin(), txs.end(), [](auto const& it) { return !it.second.accepted; }); + return std::count_if( + txs.begin(), txs.end(), [](auto const& it) { return !it.second.accepted; }); } // Returns the number of txs which were never validated std::size_t unvalidated() const { - return std::count_if(txs.begin(), txs.end(), [](auto const& it) { return !it.second.validated; }); + return std::count_if( + txs.begin(), txs.end(), [](auto const& it) { return !it.second.validated; }); } template @@ -252,47 +254,49 @@ struct TxCollector if (printBreakline) { - log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" - << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) + << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" + << "-" << std::setw(36) << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); } - log << std::left << std::setw(11) << "TxStats" << "|" << std::setw(7) << "Count" << "|" << std::setw(7) - << "Per Sec" << "|" << std::setw(15) << "Latency (sec)" << std::right << std::setw(7) << "10-ile" - << std::setw(7) << "50-ile" << std::setw(7) << "90-ile" << std::left << std::endl; + log << std::left << std::setw(11) << "TxStats" << "|" << std::setw(7) << "Count" << "|" + << std::setw(7) << "Per Sec" << "|" << std::setw(15) << "Latency (sec)" << std::right + << std::setw(7) << "10-ile" << std::setw(7) << "50-ile" << std::setw(7) << "90-ile" + << std::left << std::endl; - log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" - << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-') + << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36) + << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); - log << std::left << std::setw(11) << "Submit " << "|" << std::right << std::setw(7) << submitted << "|" - << std::setw(7) << std::setprecision(2) << perSec(submitted) << "|" << std::setw(36) << "" << std::endl; + log << std::left << std::setw(11) << "Submit " << "|" << std::right << std::setw(7) + << submitted << "|" << std::setw(7) << std::setprecision(2) << perSec(submitted) << "|" + << std::setw(36) << "" << std::endl; - log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7) << accepted << "|" - << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|" << std::setw(15) << std::left - << "From Submit" << std::right << std::setw(7) << std::setprecision(2) - << fmtS(submitToAccept.percentile(0.1f)) << std::setw(7) << std::setprecision(2) - << fmtS(submitToAccept.percentile(0.5f)) << std::setw(7) << std::setprecision(2) - << fmtS(submitToAccept.percentile(0.9f)) << std::endl; + log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7) + << accepted << "|" << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|" + << std::setw(15) << std::left << "From Submit" << std::right << std::setw(7) + << std::setprecision(2) << fmtS(submitToAccept.percentile(0.1f)) << std::setw(7) + << std::setprecision(2) << fmtS(submitToAccept.percentile(0.5f)) << std::setw(7) + << std::setprecision(2) << fmtS(submitToAccept.percentile(0.9f)) << std::endl; - log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7) << validated << "|" - << std::setw(7) << std::setprecision(2) << perSec(validated) << "|" << std::setw(15) << std::left - << "From Submit" << std::right << std::setw(7) << std::setprecision(2) - << fmtS(submitToValidate.percentile(0.1f)) << std::setw(7) << std::setprecision(2) - << fmtS(submitToValidate.percentile(0.5f)) << std::setw(7) << std::setprecision(2) - << fmtS(submitToValidate.percentile(0.9f)) << std::endl; + log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7) + << validated << "|" << std::setw(7) << std::setprecision(2) << perSec(validated) << "|" + << std::setw(15) << std::left << "From Submit" << std::right << std::setw(7) + << std::setprecision(2) << fmtS(submitToValidate.percentile(0.1f)) << std::setw(7) + << std::setprecision(2) << fmtS(submitToValidate.percentile(0.5f)) << std::setw(7) + << std::setprecision(2) << fmtS(submitToValidate.percentile(0.9f)) << std::endl; - log << std::left << std::setw(11) << "Orphan" << "|" << std::right << std::setw(7) << orphaned() << "|" - << std::setw(7) << "" << "|" << std::setw(36) << std::endl; + log << std::left << std::setw(11) << "Orphan" << "|" << std::right << std::setw(7) + << orphaned() << "|" << std::setw(7) << "" << "|" << std::setw(36) << std::endl; - log << std::left << std::setw(11) << "Unvalidated" << "|" << std::right << std::setw(7) << unvalidated() << "|" - << std::setw(7) << "" << "|" << std::setw(43) << std::endl; + log << std::left << std::setw(11) << "Unvalidated" << "|" << std::right << std::setw(7) + << unvalidated() << "|" << std::setw(7) << "" << "|" << std::setw(43) << std::endl; - log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" - << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') + << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) + << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); } @@ -453,8 +457,9 @@ struct LedgerCollector std::size_t unvalidated() const { - return std::count_if( - ledgers_.begin(), ledgers_.end(), [](auto const& it) { return !it.second.fullyValidated; }); + return std::count_if(ledgers_.begin(), ledgers_.end(), [](auto const& it) { + return !it.second.fullyValidated; + }); } template @@ -470,39 +475,40 @@ struct LedgerCollector if (printBreakline) { - log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" - << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) + << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" + << "-" << std::setw(36) << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); } - log << std::left << std::setw(11) << "LedgerStats" << "|" << std::setw(7) << "Count" << "|" << std::setw(7) - << "Per Sec" - << "|" << std::setw(15) << "Latency (sec)" << std::right << std::setw(7) << "10-ile" << std::setw(7) - << "50-ile" << std::setw(7) << "90-ile" << std::left << std::endl; + log << std::left << std::setw(11) << "LedgerStats" << "|" << std::setw(7) << "Count" << "|" + << std::setw(7) << "Per Sec" + << "|" << std::setw(15) << "Latency (sec)" << std::right << std::setw(7) << "10-ile" + << std::setw(7) << "50-ile" << std::setw(7) << "90-ile" << std::left << std::endl; - log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" - << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-') + << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36) + << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); - log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7) << accepted << "|" - << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|" << std::setw(15) << std::left - << "From Accept" << std::right << std::setw(7) << std::setprecision(2) - << fmtS(acceptToAccept.percentile(0.1f)) << std::setw(7) << std::setprecision(2) - << fmtS(acceptToAccept.percentile(0.5f)) << std::setw(7) << std::setprecision(2) - << fmtS(acceptToAccept.percentile(0.9f)) << std::endl; + log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7) + << accepted << "|" << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|" + << std::setw(15) << std::left << "From Accept" << std::right << std::setw(7) + << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.1f)) << std::setw(7) + << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.5f)) << std::setw(7) + << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.9f)) << std::endl; - log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7) << fullyValidated << "|" - << std::setw(7) << std::setprecision(2) << perSec(fullyValidated) << "|" << std::setw(15) << std::left - << "From Validate " << std::right << std::setw(7) << std::setprecision(2) + log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7) + << fullyValidated << "|" << std::setw(7) << std::setprecision(2) + << perSec(fullyValidated) << "|" << std::setw(15) << std::left << "From Validate " + << std::right << std::setw(7) << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.1f)) << std::setw(7) << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.5f)) << std::setw(7) << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.9f)) << std::endl; - log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" - << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) << std::setfill('-') << "-" - << std::endl; + log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') + << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36) + << std::setfill('-') << "-" << std::endl; log << std::setfill(' '); } @@ -583,15 +589,15 @@ struct StreamCollector void on(PeerID who, SimTime when, AcceptLedger const& e) { - out << when.time_since_epoch().count() << ": Node " << who << " accepted " << "L" << e.ledger.id() << " " - << e.ledger.txs() << "\n"; + out << when.time_since_epoch().count() << ": Node " << who << " accepted " << "L" + << e.ledger.id() << " " << e.ledger.txs() << "\n"; } void on(PeerID who, SimTime when, FullyValidateLedger const& e) { - out << when.time_since_epoch().count() << ": Node " << who << " fully-validated " << "L" << e.ledger.id() << " " - << e.ledger.txs() << "\n"; + out << when.time_since_epoch().count() << ": Node " << who << " fully-validated " << "L" + << e.ledger.id() << " " << e.ledger.txs() << "\n"; } }; diff --git a/src/test/csf/impl/ledgers.cpp b/src/test/csf/impl/ledgers.cpp index 12d461a214..33e9b123c5 100644 --- a/src/test/csf/impl/ledgers.cpp +++ b/src/test/csf/impl/ledgers.cpp @@ -58,7 +58,9 @@ mismatch(Ledger const& a, Ledger const& b) count -= step + Seq{1}; } else + { count = step; + } } return start; } @@ -88,9 +90,13 @@ LedgerOracle::accept( next.closeTimeResolution = closeTimeResolution; next.closeTimeAgree = consensusCloseTime != NetClock::time_point{}; if (next.closeTimeAgree) + { next.closeTime = effCloseTime(consensusCloseTime, closeTimeResolution, parent.closeTime()); + } else + { next.closeTime = parent.closeTime() + 1s; + } next.parentCloseTime = parent.closeTime(); next.parentID = parent.id(); diff --git a/src/test/csf/ledgers.h b/src/test/csf/ledgers.h index d78c7c29fa..9da1c2182a 100644 --- a/src/test/csf/ledgers.h +++ b/src/test/csf/ledgers.h @@ -93,7 +93,14 @@ private: auto asTie() const { - return std::tie(seq, txs, closeTimeResolution, closeTime, closeTimeAgree, parentID, parentCloseTime); + return std::tie( + seq, + txs, + closeTimeResolution, + closeTime, + closeTimeAgree, + parentID, + parentCloseTime); } friend bool diff --git a/src/test/csf/submitters.h b/src/test/csf/submitters.h index 2300b91388..ae81b3e89c 100644 --- a/src/test/csf/submitters.h +++ b/src/test/csf/submitters.h @@ -79,7 +79,13 @@ class Submitter } public: - Submitter(Distribution dist, SimTime start, SimTime end, Selector& selector, Scheduler& s, Generator& g) + Submitter( + Distribution dist, + SimTime start, + SimTime end, + Selector& selector, + Scheduler& s, + Generator& g) : dist_{dist}, stop_{end}, selector_{selector}, scheduler_{s}, g_{g} { scheduler_.at(start, [&]() { submit(); }); @@ -88,7 +94,13 @@ public: template Submitter -makeSubmitter(Distribution dist, SimTime start, SimTime end, Selector& sel, Scheduler& s, Generator& g) +makeSubmitter( + Distribution dist, + SimTime start, + SimTime end, + Selector& sel, + Scheduler& s, + Generator& g) { return Submitter(dist, start, end, sel, s, g); } diff --git a/src/test/csf/timers.h b/src/test/csf/timers.h index a071844a41..c8d71d5b7a 100644 --- a/src/test/csf/timers.h +++ b/src/test/csf/timers.h @@ -25,7 +25,10 @@ class HeartbeatTimer SimTime startSimTime_; public: - HeartbeatTimer(Scheduler& sched, SimDuration interval = std::chrono::seconds{60}, std::ostream& out = std::cerr) + HeartbeatTimer( + Scheduler& sched, + SimDuration interval = std::chrono::seconds{60}, + std::ostream& out = std::cerr) : scheduler_{sched} , interval_{interval} , out_{out} diff --git a/src/test/jtx.h b/src/test/jtx.h index 903b029f29..d4b88b0b9e 100644 --- a/src/test/jtx.h +++ b/src/test/jtx.h @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include diff --git a/src/test/jtx/AMM.h b/src/test/jtx/AMM.h index 4d8059d172..210138f290 100644 --- a/src/test/jtx/AMM.h +++ b/src/test/jtx/AMM.h @@ -141,7 +141,11 @@ public: ter const& ter, bool log = false, bool close = true); - AMM(Env& env, Account const& account, STAmount const& asset1, STAmount const& asset2, CreateArg const& arg); + AMM(Env& env, + Account const& account, + STAmount const& asset1, + STAmount const& asset2, + CreateArg const& arg); /** Send amm_info RPC command */ @@ -167,7 +171,10 @@ public: /** Get AMM balances for the token pair. */ std::tuple - balances(Issue const& issue1, Issue const& issue2, std::optional const& account = std::nullopt) const; + balances( + Issue const& issue1, + Issue const& issue2, + std::optional const& account = std::nullopt) const; std::tuple balances(std::optional const& account = std::nullopt) const @@ -184,7 +191,10 @@ public: * @param expectedPrice expected slot price */ [[nodiscard]] bool - expectAuctionSlot(std::uint32_t fee, std::optional timeSlot, IOUAmount expectedPrice) const; + expectAuctionSlot( + std::uint32_t fee, + std::optional timeSlot, + IOUAmount expectedPrice) const; [[nodiscard]] bool expectAuctionSlot(std::vector const& authAccount) const; @@ -252,7 +262,11 @@ public: std::optional const& ter = std::nullopt) { return withdraw( - account, std::nullopt, asset1OutDetails, asset1OutDetails ? tfOneAssetWithdrawAll : tfWithdrawAll, ter); + account, + std::nullopt, + asset1OutDetails, + asset1OutDetails ? tfOneAssetWithdrawAll : tfWithdrawAll, + ter); } IOUAmount @@ -383,11 +397,17 @@ private: } [[nodiscard]] bool - expectAmmInfo(STAmount const& asset1, STAmount const& asset2, IOUAmount const& balance, Json::Value const& jv) - const; + expectAmmInfo( + STAmount const& asset1, + STAmount const& asset2, + IOUAmount const& balance, + Json::Value const& jv) const; void - submit(Json::Value const& jv, std::optional const& seq, std::optional const& ter); + submit( + Json::Value const& jv, + std::optional const& seq, + std::optional const& ter); [[nodiscard]] bool expectAuctionSlot(auto&& cb) const; diff --git a/src/test/jtx/AMMTest.h b/src/test/jtx/AMMTest.h index be6e228c0a..78e26b9e40 100644 --- a/src/test/jtx/AMMTest.h +++ b/src/test/jtx/AMMTest.h @@ -83,14 +83,14 @@ protected: */ void testAMM( - std::function&& cb, + std::function const& cb, std::optional> const& pool = std::nullopt, std::uint16_t tfee = 0, std::optional const& ter = std::nullopt, std::vector const& features = {testable_amendments()}); void - testAMM(std::function&& cb, TestAMMArg const& arg); + testAMM(std::function const& cb, TestAMMArg const& arg); }; class AMMTest : public jtx::AMMTestBase diff --git a/src/test/jtx/CaptureLogs.h b/src/test/jtx/CaptureLogs.h index f8d0449977..8419c4dc70 100644 --- a/src/test/jtx/CaptureLogs.h +++ b/src/test/jtx/CaptureLogs.h @@ -26,7 +26,10 @@ class CaptureLogs : public Logs std::stringstream& strm_; public: - CaptureSink(beast::severities::Severity threshold, std::mutex& mutex, std::stringstream& strm) + CaptureSink( + beast::severities::Severity threshold, + std::mutex& mutex, + std::stringstream& strm) : beast::Journal::Sink(threshold, false), strmMutex_(mutex), strm_(strm) { } diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 7d7dcd2cb8..2ac0ca7435 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -59,7 +60,8 @@ struct WithSourceLocation // Non-explicit constructor allows implicit conversion. // The default argument for loc is evaluated at the call site. - WithSourceLocation(T v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l) + WithSourceLocation(T v, std::source_location l = std::source_location::current()) + : value(std::move(v)), loc(l) { } }; @@ -99,7 +101,8 @@ class SuiteLogs : public Logs beast::unit_test::suite& suite_; public: - explicit SuiteLogs(beast::unit_test::suite& suite) : Logs(beast::severities::kError), suite_(suite) + explicit SuiteLogs(beast::unit_test::suite& suite) + : Logs(beast::severities::kError), suite_(suite) { } @@ -190,7 +193,9 @@ public: { memoize(Account::master); Pathfinder::initPathTable(); - foreachFeature(features, [&appFeats = app().config().features](uint256 const& f) { appFeats.insert(f); }); + foreachFeature(features, [&appFeats = app().config().features](uint256 const& f) { + appFeats.insert(f); + }); } /** @@ -206,7 +211,9 @@ public: * @param args collection of features * */ - Env(beast::unit_test::suite& suite_, FeatureBitset features, std::unique_ptr logs = nullptr) + Env(beast::unit_test::suite& suite_, + FeatureBitset features, + std::unique_ptr logs = nullptr) : Env(suite_, envconfig(), features, std::move(logs)) { } @@ -240,7 +247,8 @@ public: * * @param suite_ the current unit_test::suite */ - Env(beast::unit_test::suite& suite_, beast::severities::Severity thresh = beast::severities::kError) + Env(beast::unit_test::suite& suite_, + beast::severities::Severity thresh = beast::severities::kError) : Env(suite_, envconfig(), nullptr, thresh) { } @@ -301,7 +309,9 @@ public: template Json::Value - rpc(std::unordered_map const& headers, std::string const& cmd, Args&&... args); + rpc(std::unordered_map const& headers, + std::string const& cmd, + Args&&... args); template Json::Value @@ -351,7 +361,9 @@ public: @return true if no error, false if error */ bool - close(NetClock::time_point closeTime, std::optional consensusDelay = std::nullopt); + close( + NetClock::time_point closeTime, + std::optional consensusDelay = std::nullopt); /** Close and advance the ledger. @@ -382,6 +394,48 @@ public: return close(std::chrono::seconds(5)); } + /** Close and advance the ledger, then synchronize with the server's + io_context to ensure all async operations initiated by the close have + been started. + + This function performs the same ledger close as close(), but additionally + ensures that all tasks posted to the server's io_context (such as + WebSocket subscription message sends) have been initiated before returning. + + What it guarantees: + - All async operations posted before syncClose() have been STARTED + - For WebSocket sends: async_write_some() has been called + - The actual I/O completion may still be pending (async) + + What it does NOT guarantee: + - Async operations have COMPLETED + - WebSocket messages have been received by clients + - However, for localhost connections, the remaining latency is typically + microseconds, making tests reliable + + Use this instead of close() when: + - Test code immediately checks for subscription messages + - Race conditions between test and worker threads must be avoided + - Deterministic test behavior is required + + @param timeout Maximum time to wait for the barrier task to execute + @return true if close succeeded and barrier executed within timeout, + false otherwise + */ + [[nodiscard]] bool + syncClose(std::chrono::steady_clock::duration timeout = std::chrono::seconds{1}) + { + XRPL_ASSERT( + app().getNumberOfThreads() == 1, + "syncClose() is only useful on an application with a single thread"); + auto const result = close(); + auto serverBarrier = std::make_shared>(); + auto future = serverBarrier->get_future(); + boost::asio::post(app().getIOContext(), [serverBarrier]() { serverBarrier->set_value(); }); + auto const status = future.wait_for(timeout); + return result && status == std::future_status::ready; + } + /** Turn on JSON tracing. With no arguments, trace all */ @@ -802,14 +856,24 @@ template Json::Value Env::rpc(unsigned apiVersion, std::string const& cmd, Args&&... args) { - return rpc(apiVersion, std::unordered_map(), cmd, std::forward(args)...); + return rpc( + apiVersion, + std::unordered_map(), + cmd, + std::forward(args)...); } template Json::Value -Env::rpc(std::unordered_map const& headers, std::string const& cmd, Args&&... args) +Env::rpc( + std::unordered_map const& headers, + std::string const& cmd, + Args&&... args) { - return do_rpc(RPC::apiCommandLineVersion, std::vector{cmd, std::forward(args)...}, headers); + return do_rpc( + RPC::apiCommandLineVersion, + std::vector{cmd, std::forward(args)...}, + headers); } template diff --git a/src/test/jtx/Env_ss.h b/src/test/jtx/Env_ss.h index 9c178a1c13..9db96d5907 100644 --- a/src/test/jtx/Env_ss.h +++ b/src/test/jtx/Env_ss.h @@ -23,7 +23,8 @@ private: SignSubmitRunner& operator=(SignSubmitRunner&&) = delete; - SignSubmitRunner(Env& env, JTx&& jt, std::source_location loc) : env_(env), jt_(jt), loc_(loc) + SignSubmitRunner(Env& env, JTx&& jt, std::source_location loc) + : env_(env), jt_(jt), loc_(loc) { } diff --git a/src/test/jtx/Env_test.cpp b/src/test/jtx/Env_test.cpp index 0f43691b86..bd40ac9a81 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -1,6 +1,5 @@ #include -#include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include @@ -39,9 +39,9 @@ public: a = std::move(b); Account c(std::move(a)); } - Account("alice"); - Account("alice", KeyType::secp256k1); - Account("alice", KeyType::ed25519); + Account("alice"); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::secp256k1); // NOLINT(bugprone-unused-raii) + Account("alice", KeyType::ed25519); // NOLINT(bugprone-unused-raii) auto const gw = Account("gw"); [](AccountID) {}(gw); auto const USD = gw["USD"]; @@ -56,11 +56,11 @@ public: { using namespace jtx; - PrettyAmount(0); - PrettyAmount(1); - PrettyAmount(0u); - PrettyAmount(1u); - PrettyAmount(-1); + PrettyAmount(0); // NOLINT(bugprone-unused-raii) + PrettyAmount(1); // NOLINT(bugprone-unused-raii) + PrettyAmount(0u); // NOLINT(bugprone-unused-raii) + PrettyAmount(1u); // NOLINT(bugprone-unused-raii) + PrettyAmount(-1); // NOLINT(bugprone-unused-raii) static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); static_assert(!std::is_trivially_constructible::value, ""); @@ -415,7 +415,10 @@ public: env(noop("alice"), msig("bob"), fee(2 * baseFee)); env(noop("alice"), msig("carol"), fee(2 * baseFee)); env(noop("alice"), msig("bob", "carol"), fee(3 * baseFee)); - env(noop("alice"), msig("bob", "carol", "dilbert"), fee(4 * baseFee), ter(tefBAD_SIGNATURE)); + env(noop("alice"), + msig("bob", "carol", "dilbert"), + fee(4 * baseFee), + ter(tefBAD_SIGNATURE)); env(signers("alice", none)); } @@ -530,14 +533,14 @@ public: BEAST_EXPECT(*jt1.get() == 7); BEAST_EXPECT(!jt1.get()); JTx jt2(std::move(jt1)); - BEAST_EXPECT(!jt1.get()); - BEAST_EXPECT(!jt1.get()); + BEAST_EXPECT(!jt1.get()); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(!jt1.get()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(jt2.get()); BEAST_EXPECT(*jt2.get() == 7); BEAST_EXPECT(!jt2.get()); jt1 = std::move(jt2); - BEAST_EXPECT(!jt2.get()); - BEAST_EXPECT(!jt2.get()); + BEAST_EXPECT(!jt2.get()); // NOLINT(bugprone-use-after-move) + BEAST_EXPECT(!jt2.get()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(jt1.get()); BEAST_EXPECT(*jt1.get() == 7); BEAST_EXPECT(!jt1.get()); @@ -704,8 +707,10 @@ public: auto const neverSupportedFeat = [&]() -> std::optional { auto const n = supported.size(); for (size_t i = 0; i < n; ++i) + { if (!supported[i]) return bitsetIndexToFeature(i); + } return std::nullopt; }(); @@ -718,14 +723,15 @@ public: } auto hasFeature = [](Env& env, uint256 const& f) { - return (env.app().config().features.find(f) != env.app().config().features.end()); + return (env.app().config().features.contains(f)); }; { // default Env has all supported features Env env{*this}; BEAST_EXPECT(supported.count() == env.app().config().features.size()); - foreachFeature(supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); }); + foreachFeature( + supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); }); } { @@ -738,7 +744,8 @@ public: }); } - auto const missingSomeFeatures = testable_amendments() - featureDynamicMPT - featureTokenEscrow; + auto const missingSomeFeatures = + testable_amendments() - featureDynamicMPT - featureTokenEscrow; BEAST_EXPECT(missingSomeFeatures.count() == (supported.count() - 2)); { // a Env supported_features_except is missing *only* those features @@ -755,7 +762,8 @@ public: // along with a list of explicit amendments // the unsupported feature should be enabled along with // the two supported ones - Env env{*this, FeatureBitset{featureDynamicMPT, featureTokenEscrow, *neverSupportedFeat}}; + Env env{ + *this, FeatureBitset{featureDynamicMPT, featureTokenEscrow, *neverSupportedFeat}}; // this app will have just 2 supported amendments and // one additional never supported feature flag @@ -794,7 +802,8 @@ public: // one additional never supported feature flag BEAST_EXPECT(env.app().config().features.size() == (supported.count() + 1)); BEAST_EXPECT(hasFeature(env, *neverSupportedFeat)); - foreachFeature(supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); }); + foreachFeature( + supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); }); } } diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index 90fbb8a6b6..bf43d0aa75 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -38,9 +38,6 @@ struct JTx // Functions that sign something else after the mainSigners, such as // sfCounterpartySignature std::vector> postSigners; - // Metadata about the unit test itself - // The line where the JTx was constructed - std::optional testLine = std::nullopt; JTx() = default; JTx(JTx const&) = default; diff --git a/src/test/jtx/Oracle.h b/src/test/jtx/Oracle.h index da241c0ce8..8efd17802c 100644 --- a/src/test/jtx/Oracle.h +++ b/src/test/jtx/Oracle.h @@ -31,8 +31,9 @@ void toJsonHex(Json::Value& jv, AnyValue const& v); // base asset, quote asset, price, scale -using DataSeries = - std::vector, std::optional>>; +using DataSeries = std::vector< + std:: + tuple, std::optional>>; // Typical defaults for Create struct CreateArg diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index 3f30d7193f..c522ed635e 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { @@ -11,7 +12,11 @@ namespace test { /** Count offer */ inline std::size_t -countOffers(jtx::Env& env, jtx::Account const& account, Issue const& takerPays, Issue const& takerGets) +countOffers( + jtx::Env& env, + jtx::Account const& account, + Issue const& takerPays, + Issue const& takerGets) { size_t count = 0; forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { @@ -23,7 +28,11 @@ countOffers(jtx::Env& env, jtx::Account const& account, Issue const& takerPays, } inline std::size_t -countOffers(jtx::Env& env, jtx::Account const& account, STAmount const& takerPays, STAmount const& takerGets) +countOffers( + jtx::Env& env, + jtx::Account const& account, + STAmount const& takerPays, + STAmount const& takerGets) { size_t count = 0; forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { @@ -37,7 +46,11 @@ countOffers(jtx::Env& env, jtx::Account const& account, STAmount const& takerPay /** An offer exists */ inline bool -isOffer(jtx::Env& env, jtx::Account const& account, STAmount const& takerPays, STAmount const& takerGets) +isOffer( + jtx::Env& env, + jtx::Account const& account, + STAmount const& takerPays, + STAmount const& takerGets) { return countOffers(env, account, takerPays, takerGets) > 0; } @@ -93,7 +106,11 @@ Path::push_back(STPathElement const& pe) inline Path& Path::push_back(Issue const& iss) { - path.emplace_back(STPathElement::typeCurrency | STPathElement::typeIssuer, beast::zero, iss.currency, iss.account); + path.emplace_back( + STPathElement::typeCurrency | STPathElement::typeIssuer, + beast::zero, + iss.currency, + iss.account); return *this; } diff --git a/src/test/jtx/SignerUtils.h b/src/test/jtx/SignerUtils.h index 37b1d85ec7..994868e4a2 100644 --- a/src/test/jtx/SignerUtils.h +++ b/src/test/jtx/SignerUtils.h @@ -40,7 +40,9 @@ struct Reg inline void sortSigners(std::vector& signers) { - std::sort(signers.begin(), signers.end(), [](Reg const& lhs, Reg const& rhs) { return lhs.acct < rhs.acct; }); + std::sort(signers.begin(), signers.end(), [](Reg const& lhs, Reg const& rhs) { + return lhs.acct < rhs.acct; + }); } } // namespace jtx diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index c0e54900f6..2836748ec3 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -26,7 +26,10 @@ namespace jtx { Not every helper will be able to use this because of conversions and other issues, but for classes where it's straightforward, this can simplify things. */ -template +template < + class SField, + class StoredValue = typename SField::type::value_type, + class OutputValue = StoredValue> struct JTxField { using SF = SField; @@ -178,7 +181,8 @@ struct blobField : public JTxField } template - explicit blobField(SF const& sfield, std::array const& c) : blobField(sfield, makeSlice(c)) + explicit blobField(SF const& sfield, std::array const& c) + : blobField(sfield, makeSlice(c)) { } }; @@ -433,7 +437,11 @@ PrettyAmount xrpMinusFee(Env const& env, std::int64_t xrpAmount); bool -expectHolding(Env& env, AccountID const& account, STAmount const& value, bool defaultLimits = false); +expectHolding( + Env& env, + AccountID const& account, + STAmount const& value, + bool defaultLimits = false); template bool @@ -446,13 +454,21 @@ bool expectHolding(Env& env, AccountID const& account, None const& value); bool -expectOffers(Env& env, AccountID const& account, std::uint16_t size, std::vector const& toMatch = {}); +expectOffers( + Env& env, + AccountID const& account, + std::uint16_t size, + std::vector const& toMatch = {}); Json::Value ledgerEntryRoot(Env& env, Account const& acct); Json::Value -ledgerEntryState(Env& env, Account const& acct_a, Account const& acct_b, std::string const& currency); +ledgerEntryState( + Env& env, + Account const& acct_a, + Account const& acct_b, + std::string const& currency); Json::Value accountBalance(Env& env, Account const& acct); @@ -589,55 +605,64 @@ checkMetrics( auto const metrics = env.app().getTxQ().getMetrics(*env.current()); using namespace std::string_literals; - metrics.referenceFeeLevel == baseFeeLevel ? test.pass() - : test.fail( - "reference: "s + std::to_string(metrics.referenceFeeLevel.value()) + - "/" + std::to_string(baseFeeLevel.value()), - file, - line); + metrics.referenceFeeLevel == baseFeeLevel + ? test.pass() + : test.fail( + "reference: "s + std::to_string(metrics.referenceFeeLevel.value()) + "/" + + std::to_string(baseFeeLevel.value()), + file, + line); metrics.txCount == expectedCount ? test.pass() - : test.fail("txCount: "s + std::to_string(metrics.txCount) + "/" + std::to_string(expectedCount), file, line); + : test.fail( + "txCount: "s + std::to_string(metrics.txCount) + "/" + std::to_string(expectedCount), + file, + line); - metrics.txQMaxSize == expectedMaxCount ? test.pass() - : test.fail( - "txQMaxSize: "s + std::to_string(metrics.txQMaxSize.value_or(0)) + - "/" + std::to_string(expectedMaxCount.value_or(0)), - file, - line); + metrics.txQMaxSize == expectedMaxCount + ? test.pass() + : test.fail( + "txQMaxSize: "s + std::to_string(metrics.txQMaxSize.value_or(0)) + "/" + + std::to_string(expectedMaxCount.value_or(0)), + file, + line); metrics.txInLedger == expectedInLedger ? test.pass() : test.fail( - "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" + std::to_string(expectedInLedger), + "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" + + std::to_string(expectedInLedger), file, line); metrics.txPerLedger == expectedPerLedger ? test.pass() : test.fail( - "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" + std::to_string(expectedPerLedger), + "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" + + std::to_string(expectedPerLedger), file, line); metrics.minProcessingFeeLevel == expectedMin ? test.pass() : test.fail( - "minProcessingFeeLevel: "s + std::to_string(metrics.minProcessingFeeLevel.value()) + "/" + - std::to_string(expectedMin.value()), + "minProcessingFeeLevel: "s + std::to_string(metrics.minProcessingFeeLevel.value()) + + "/" + std::to_string(expectedMin.value()), file, line); - metrics.medFeeLevel == expectedMed ? test.pass() - : test.fail( - "medFeeLevel: "s + std::to_string(metrics.medFeeLevel.value()) + "/" + - std::to_string(expectedMed.value()), - file, - line); + metrics.medFeeLevel == expectedMed + ? test.pass() + : test.fail( + "medFeeLevel: "s + std::to_string(metrics.medFeeLevel.value()) + "/" + + std::to_string(expectedMed.value()), + file, + line); auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger - ? expectedMed * expectedInLedger * expectedInLedger / (expectedPerLedger * expectedPerLedger) + ? expectedMed * expectedInLedger * expectedInLedger / + (expectedPerLedger * expectedPerLedger) : metrics.referenceFeeLevel; metrics.openLedgerFeeLevel == expectedCurFeeLevel @@ -662,10 +687,18 @@ Json::Value del(AccountID const& account, uint256 const& brokerID, std::uint32_t flags = 0); Json::Value -coverDeposit(AccountID const& account, uint256 const& brokerID, STAmount const& amount, std::uint32_t flags = 0); +coverDeposit( + AccountID const& account, + uint256 const& brokerID, + STAmount const& amount, + std::uint32_t flags = 0); Json::Value -coverWithdraw(AccountID const& account, uint256 const& brokerID, STAmount const& amount, std::uint32_t flags = 0); +coverWithdraw( + AccountID const& account, + uint256 const& brokerID, + STAmount const& amount, + std::uint32_t flags = 0); // Must specify at least one of loanBrokerID or amount. Json::Value @@ -679,7 +712,8 @@ auto const debtMaximum = simpleField(sfDebtMaximum); auto const coverRateMinimum = valueUnitWrapper(sfCoverRateMinimum); -auto const coverRateLiquidation = valueUnitWrapper(sfCoverRateLiquidation); +auto const coverRateLiquidation = + valueUnitWrapper(sfCoverRateLiquidation); auto const destination = JTxFieldWrapper(sfDestination); @@ -690,7 +724,10 @@ auto const destination = JTxFieldWrapper(sfDestination); namespace loan { Json::Value -set(AccountID const& account, uint256 const& loanBrokerID, Number principalRequested, std::uint32_t flags = 0); +set(AccountID const& account, + uint256 const& loanBrokerID, + Number principalRequested, + std::uint32_t flags = 0); auto const counterparty = JTxFieldWrapper(sfCounterparty); @@ -712,7 +749,8 @@ auto const lateInterestRate = valueUnitWrapper(sf auto const closeInterestRate = valueUnitWrapper(sfCloseInterestRate); -auto const overpaymentInterestRate = valueUnitWrapper(sfOverpaymentInterestRate); +auto const overpaymentInterestRate = + valueUnitWrapper(sfOverpaymentInterestRate); auto const paymentTotal = simpleField(sfPaymentTotal); @@ -727,7 +765,10 @@ Json::Value del(AccountID const& account, uint256 const& loanID, std::uint32_t flags = 0); Json::Value -pay(AccountID const& account, uint256 const& loanID, STAmount const& amount, std::uint32_t flags = 0); +pay(AccountID const& account, + uint256 const& loanID, + STAmount const& amount, + std::uint32_t flags = 0); } // namespace loan diff --git a/src/test/jtx/TestSuite.h b/src/test/jtx/TestSuite.h index 1558f68d39..604557c155 100644 --- a/src/test/jtx/TestSuite.h +++ b/src/test/jtx/TestSuite.h @@ -47,7 +47,10 @@ public: template bool - expectCollectionEquals(Collection const& actual, Collection const& expected, std::string const& message = "") + expectCollectionEquals( + Collection const& actual, + Collection const& expected, + std::string const& message = "") { auto msg = addPrefix(message); bool success = expectEquals(actual.size(), expected.size(), msg + "Sizes are different"); diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index a46e98ad50..dc7682bb72 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -67,7 +67,8 @@ class TrustedPublisherServer : public std::enable_shared_from_this blobInfo; blobInfo.reserve(futures.size() + 1); auto const [data, blob] = [&]() -> std::pair { // Builds the validator list, then encodes it into a blob. std::string data = "{\"sequence\":" + std::to_string(sequence) + - ",\"expiration\":" + std::to_string(validUntil.time_since_epoch().count()) + ",\"validators\":["; + ",\"expiration\":" + std::to_string(validUntil.time_since_epoch().count()) + + ",\"validators\":["; for (auto const& val : validators) { - data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + "\",\"manifest\":\"" + - val.manifest + "\"},"; + data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + + "\",\"manifest\":\"" + val.manifest + "\"},"; } data.pop_back(); data += "]}"; @@ -170,8 +178,8 @@ public: getList_ = [blob = blob, sig, manifest, version](int interval) { // Build the contents of a version 1 format UNL file std::stringstream l; - l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig << "\"" << ",\"manifest\":\"" << manifest - << "\"" + l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig << "\"" + << ",\"manifest\":\"" << manifest << "\"" << ",\"refresh_interval\": " << interval << ",\"version\":" << version << '}'; return l.str(); }; @@ -179,13 +187,14 @@ public: { std::string data = "{\"sequence\":" + std::to_string(++sequence) + ",\"effective\":" + std::to_string(future.first.time_since_epoch().count()) + - ",\"expiration\":" + std::to_string(future.second.time_since_epoch().count()) + ",\"validators\":["; + ",\"expiration\":" + std::to_string(future.second.time_since_epoch().count()) + + ",\"validators\":["; // Use the same set of validators for simplicity for (auto const& val : validators) { - data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + "\",\"manifest\":\"" + - val.manifest + "\"},"; + data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + + "\",\"manifest\":\"" + val.manifest + "\"},"; } data.pop_back(); data += "]}"; @@ -200,7 +209,8 @@ public: std::stringstream l; for (auto const& info : blobInfo) { - l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\"" << info.signature << "\"},"; + l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\"" << info.signature + << "\"},"; } std::string blobs = l.str(); blobs.pop_back(); @@ -225,12 +235,13 @@ public: acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), ec); acceptor_.bind(ep_); acceptor_.listen(boost::asio::socket_base::max_listen_connections); - acceptor_.async_accept(sock_, [wp = std::weak_ptr{shared_from_this()}](error_code ec) { - if (auto p = wp.lock()) - { - p->on_accept(ec); - } - }); + acceptor_.async_accept( + sock_, [wp = std::weak_ptr{shared_from_this()}](error_code ec) { + if (auto p = wp.lock()) + { + p->on_accept(ec); + } + }); } void @@ -238,7 +249,7 @@ public: { error_code ec; acceptor_.close(ec); - // TODO consider making this join + // TODO: consider making this join // any running do_peer threads } @@ -460,12 +471,13 @@ private: static int id_ = 0; std::thread{lambda{++id_, *this, std::move(sock_), useSSL_}}.detach(); - acceptor_.async_accept(sock_, [wp = std::weak_ptr{shared_from_this()}](error_code ec) { - if (auto p = wp.lock()) - { - p->on_accept(ec); - } - }); + acceptor_.async_accept( + sock_, [wp = std::weak_ptr{shared_from_this()}](error_code ec) { + if (auto p = wp.lock()) + { + p->on_accept(ec); + } + }); } void @@ -521,7 +533,8 @@ private: int refresh = 5; constexpr char const* refreshPrefix = "/validators2/refresh/"; if (boost::starts_with(path, refreshPrefix)) - refresh = boost::lexical_cast(path.substr(strlen(refreshPrefix))); + refresh = boost::lexical_cast( + path.substr(strlen(refreshPrefix))); res.body() = getList2_(refresh); } } @@ -538,7 +551,8 @@ private: int refresh = 5; constexpr char const* refreshPrefix = "/validators/refresh/"; if (boost::starts_with(path, refreshPrefix)) - refresh = boost::lexical_cast(path.substr(strlen(refreshPrefix))); + refresh = boost::lexical_cast( + path.substr(strlen(refreshPrefix))); res.body() = getList_(refresh); } } @@ -548,14 +562,16 @@ private: res.result(http::status::ok); res.insert("Content-Type", "text/example"); // if huge was requested, lie about content length - std::uint64_t cl = - boost::starts_with(path, "/textfile/huge") ? std::numeric_limits::max() : 1024; + std::uint64_t cl = boost::starts_with(path, "/textfile/huge") + ? std::numeric_limits::max() + : 1024; res.content_length(cl); if (req.method() == http::verb::get) { std::stringstream body; for (auto i = 0; i < 1024; ++i) - body << static_cast(rand_int(32, 126)), res.body() = body.str(); + body << static_cast(rand_int(32, 126)), + res.body() = body.str(); } } else if (boost::starts_with(path, "/sleep/")) @@ -582,7 +598,9 @@ private: else if (!boost::starts_with(path, "/redirect_nolo")) { location << (ssl ? "https://" : "http://") << local_endpoint() - << (boost::starts_with(path, "/redirect_forever/") ? path : "/validators"); + << (boost::starts_with(path, "/redirect_forever/") + ? path + : "/validators"); } if (!location.str().empty()) res.insert("Location", location.str()); @@ -637,8 +655,8 @@ make_TrustedPublisherServer( bool immediateStart = true, int sequence = 1) { - auto const r = - std::make_shared(ioc, validators, validUntil, futures, useSSL, version, sequence); + auto const r = std::make_shared( + ioc, validators, validUntil, futures, useSSL, version, sequence); if (immediateStart) r->start(); return r; diff --git a/src/test/jtx/WSClient.h b/src/test/jtx/WSClient.h index 86b81ac0ac..d1801cb4a9 100644 --- a/src/test/jtx/WSClient.h +++ b/src/test/jtx/WSClient.h @@ -20,7 +20,9 @@ public: /** Retrieve a message that meets the predicate criteria. */ virtual std::optional - findMsg(std::chrono::milliseconds const& timeout, std::function pred) = 0; + findMsg( + std::chrono::milliseconds const& timeout, + std::function pred) = 0; }; /** Returns a client operating through WebSockets/S. */ diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index c7b66504d3..c4e97502db 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -81,14 +81,18 @@ public: template PrettyAmount( T v, - std::enable_if_t= sizeof(int) && std::is_integral_v && std::is_signed_v>* = nullptr) + std::enable_if_t< + sizeof(T) >= sizeof(int) && std::is_integral_v && std::is_signed_v>* = nullptr) : amount_((v > 0) ? v : -v, v < 0) { } /** drops */ template - PrettyAmount(T v, std::enable_if_t= sizeof(int) && std::is_unsigned_v>* = nullptr) : amount_(v) + PrettyAmount( + T v, + std::enable_if_t= sizeof(int) && std::is_unsigned_v>* = nullptr) + : amount_(v) { } @@ -230,7 +234,8 @@ struct BookSpec AccountID account; xrpl::Currency currency; - BookSpec(AccountID const& account_, xrpl::Currency const& currency_) : account(account_), currency(currency_) + BookSpec(AccountID const& account_, xrpl::Currency const& currency_) + : account(account_), currency(currency_) { } }; @@ -378,7 +383,8 @@ public: Account account; xrpl::Currency currency; - IOU(Account const& account_, xrpl::Currency const& currency_) : account(account_), currency(currency_) + IOU(Account const& account_, xrpl::Currency const& currency_) + : account(account_), currency(currency_) { } @@ -412,7 +418,9 @@ public: return asset(); } - template = sizeof(int) && std::is_arithmetic::value>> + template < + class T, + class = std::enable_if_t= sizeof(int) && std::is_arithmetic::value>> PrettyAmount operator()(T v) const { diff --git a/src/test/jtx/balance.h b/src/test/jtx/balance.h index 83e2e3ea03..2181429908 100644 --- a/src/test/jtx/balance.h +++ b/src/test/jtx/balance.h @@ -27,11 +27,13 @@ public: { } - balance(Account const& account, None const& value) : none_(true), account_(account), value_(value.asset) + balance(Account const& account, None const& value) + : none_(true), account_(account), value_(value.asset) { } - balance(Account const& account, STAmount const& value) : none_(false), account_(account), value_(value) + balance(Account const& account, STAmount const& value) + : none_(false), account_(account), value_(value) { } diff --git a/src/test/jtx/batch.h b/src/test/jtx/batch.h index c11a9fe17e..f81bed3c6e 100644 --- a/src/test/jtx/batch.h +++ b/src/test/jtx/batch.h @@ -109,7 +109,8 @@ public: Account master; std::vector signers; - msig(Account const& masterAccount, std::vector signers_) : master(masterAccount), signers(std::move(signers_)) + msig(Account const& masterAccount, std::vector signers_) + : master(masterAccount), signers(std::move(signers_)) { sortSigners(signers); } @@ -117,7 +118,8 @@ public: template requires std::convertible_to explicit msig(Account const& masterAccount, AccountType&& a0, Accounts&&... aN) - : master(masterAccount), signers{std::forward(a0), std::forward(aN)...} + : master(masterAccount) + , signers{std::forward(a0), std::forward(aN)...} { sortSigners(signers); } diff --git a/src/test/jtx/credentials.h b/src/test/jtx/credentials.h index 29ecacb11b..56d127fca9 100644 --- a/src/test/jtx/credentials.h +++ b/src/test/jtx/credentials.h @@ -11,7 +11,10 @@ namespace jtx { namespace credentials { inline Keylet -keylet(test::jtx::Account const& subject, test::jtx::Account const& issuer, std::string_view credType) +keylet( + test::jtx::Account const& subject, + test::jtx::Account const& issuer, + std::string_view credType) { return keylet::credential(subject.id(), issuer.id(), Slice(credType.data(), credType.size())); } @@ -61,10 +64,18 @@ Json::Value accept(jtx::Account const& subject, jtx::Account const& issuer, std::string_view credType); Json::Value -deleteCred(jtx::Account const& acc, jtx::Account const& subject, jtx::Account const& issuer, std::string_view credType); +deleteCred( + jtx::Account const& acc, + jtx::Account const& subject, + jtx::Account const& issuer, + std::string_view credType); Json::Value -ledgerEntry(jtx::Env& env, jtx::Account const& subject, jtx::Account const& issuer, std::string_view credType); +ledgerEntry( + jtx::Env& env, + jtx::Account const& subject, + jtx::Account const& issuer, + std::string_view credType); Json::Value ledgerEntry(jtx::Env& env, std::string const& credIdx); diff --git a/src/test/jtx/delegate.h b/src/test/jtx/delegate.h index d965ab580a..7aecb54922 100644 --- a/src/test/jtx/delegate.h +++ b/src/test/jtx/delegate.h @@ -10,7 +10,9 @@ namespace jtx { namespace delegate { Json::Value -set(jtx::Account const& account, jtx::Account const& authorize, std::vector const& permissions); +set(jtx::Account const& account, + jtx::Account const& authorize, + std::vector const& permissions); Json::Value entry(jtx::Env& env, jtx::Account const& account, jtx::Account const& authorize); diff --git a/src/test/jtx/did.h b/src/test/jtx/did.h index 9cd98a1324..e6b06f8f7c 100644 --- a/src/test/jtx/did.h +++ b/src/test/jtx/did.h @@ -53,7 +53,7 @@ public: } }; -/** Sets the optional Attestation on a DIDSet. */ +/** Sets the optional Data on a DIDSet. */ class data { private: diff --git a/src/test/jtx/envconfig.h b/src/test/jtx/envconfig.h index dc0c48e064..e4a1975e74 100644 --- a/src/test/jtx/envconfig.h +++ b/src/test/jtx/envconfig.h @@ -73,6 +73,8 @@ std::unique_ptr admin_localnet(std::unique_ptr); std::unique_ptr secure_gateway_localnet(std::unique_ptr); +std::unique_ptr single_thread_io(std::unique_ptr); + /// @brief adjust configuration with params needed to be a validator /// /// this is intended for use with envconfig, as in @@ -106,7 +108,9 @@ std::unique_ptr addGrpcConfigWithSecureGateway(std::unique_ptr, std::string const& secureGateway); std::unique_ptr -makeConfig(std::map extraTxQ = {}, std::map extraVoting = {}); +makeConfig( + std::map extraTxQ = {}, + std::map extraVoting = {}); } // namespace jtx } // namespace test diff --git a/src/test/jtx/escrow.h b/src/test/jtx/escrow.h index a352d39f74..803777df80 100644 --- a/src/test/jtx/escrow.h +++ b/src/test/jtx/escrow.h @@ -48,26 +48,26 @@ rate(Env& env, Account const& account, std::uint32_t const& seq); // A PreimageSha256 fulfillments and its associated condition. std::array const fb1 = {{0xA0, 0x02, 0x80, 0x00}}; -std::array const cb1 = {{0xA0, 0x25, 0x80, 0x20, 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, - 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, - 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, - 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55, 0x81, 0x01, 0x00}}; +std::array const cb1 = { + {0xA0, 0x25, 0x80, 0x20, 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, + 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, + 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55, 0x81, 0x01, 0x00}}; // Another PreimageSha256 fulfillments and its associated condition. std::array const fb2 = {{0xA0, 0x05, 0x80, 0x03, 0x61, 0x61, 0x61}}; -std::array const cb2 = {{0xA0, 0x25, 0x80, 0x20, 0x98, 0x34, 0x87, 0x6D, 0xCF, 0xB0, - 0x5C, 0xB1, 0x67, 0xA5, 0xC2, 0x49, 0x53, 0xEB, 0xA5, 0x8C, - 0x4A, 0xC8, 0x9B, 0x1A, 0xDF, 0x57, 0xF2, 0x8F, 0x2F, 0x9D, - 0x09, 0xAF, 0x10, 0x7E, 0xE8, 0xF0, 0x81, 0x01, 0x03}}; +std::array const cb2 = { + {0xA0, 0x25, 0x80, 0x20, 0x98, 0x34, 0x87, 0x6D, 0xCF, 0xB0, 0x5C, 0xB1, 0x67, + 0xA5, 0xC2, 0x49, 0x53, 0xEB, 0xA5, 0x8C, 0x4A, 0xC8, 0x9B, 0x1A, 0xDF, 0x57, + 0xF2, 0x8F, 0x2F, 0x9D, 0x09, 0xAF, 0x10, 0x7E, 0xE8, 0xF0, 0x81, 0x01, 0x03}}; // Another PreimageSha256 fulfillment and its associated condition. std::array const fb3 = {{0xA0, 0x06, 0x80, 0x04, 0x6E, 0x69, 0x6B, 0x62}}; -std::array const cb3 = {{0xA0, 0x25, 0x80, 0x20, 0x6E, 0x4C, 0x71, 0x45, 0x30, 0xC0, - 0xA4, 0x26, 0x8B, 0x3F, 0xA6, 0x3B, 0x1B, 0x60, 0x6F, 0x2D, - 0x26, 0x4A, 0x2D, 0x85, 0x7B, 0xE8, 0xA0, 0x9C, 0x1D, 0xFD, - 0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4, 0x81, 0x01, 0x04}}; +std::array const cb3 = { + {0xA0, 0x25, 0x80, 0x20, 0x6E, 0x4C, 0x71, 0x45, 0x30, 0xC0, 0xA4, 0x26, 0x8B, + 0x3F, 0xA6, 0x3B, 0x1B, 0x60, 0x6F, 0x2D, 0x26, 0x4A, 0x2D, 0x85, 0x7B, 0xE8, + 0xA0, 0x9C, 0x1D, 0xFD, 0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4, 0x81, 0x01, 0x04}}; /** Set the "FinishAfter" time tag on a JTx */ auto const finish_time = JTxFieldWrapper(sfFinishAfter); diff --git a/src/test/jtx/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp index f920858ea5..6b2042efda 100644 --- a/src/test/jtx/impl/AMM.cpp +++ b/src/test/jtx/impl/AMM.cpp @@ -1,13 +1,13 @@ #include #include -#include -#include - +#include #include #include #include #include +#include +#include namespace xrpl { namespace test { @@ -53,8 +53,6 @@ AMM::AMM( , log_(log) , doClose_(close) , lastPurchasePrice_(0) - , bidMin_() - , bidMax_() , msig_(ms) , fee_(fee) , ammAccount_(create(tfee, flags, seq, ter)) @@ -71,12 +69,39 @@ AMM::AMM( ter const& ter, bool log, bool close) - : AMM(env, account, asset1, asset2, log, 0, 0, std::nullopt, std::nullopt, std::nullopt, ter, close) + : AMM(env, + account, + asset1, + asset2, + log, + 0, + 0, + std::nullopt, + std::nullopt, + std::nullopt, + ter, + close) { } -AMM::AMM(Env& env, Account const& account, STAmount const& asset1, STAmount const& asset2, CreateArg const& arg) - : AMM(env, account, asset1, asset2, arg.log, arg.tfee, arg.fee, arg.flags, arg.seq, arg.ms, arg.err, arg.close) +AMM::AMM( + Env& env, + Account const& account, + STAmount const& asset1, + STAmount const& asset2, + CreateArg const& arg) + : AMM(env, + account, + asset1, + asset2, + arg.log, + arg.tfee, + arg.fee, + arg.flags, + arg.seq, + arg.ms, + arg.err, + arg.close) { } @@ -96,9 +121,13 @@ AMM::create( if (flags) jv[jss::Flags] = *flags; if (fee_ != 0) + { jv[sfFee] = std::to_string(fee_); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } submit(jv, seq, ter); if (!ter || env_.ter() == tesSUCCESS) @@ -144,23 +173,31 @@ AMM::ammRpcInfo( jv[jss::amm_account] = to_string(*ammAccount); } auto jr = - (apiVersion == RPC::apiInvalidVersion ? env_.rpc("json", "amm_info", to_string(jv)) - : env_.rpc(apiVersion, "json", "amm_info", to_string(jv))); + (apiVersion == RPC::apiInvalidVersion + ? env_.rpc("json", "amm_info", to_string(jv)) + : env_.rpc(apiVersion, "json", "amm_info", to_string(jv))); if (jr.isObject() && jr.isMember(jss::result) && jr[jss::result].isMember(jss::status)) return jr[jss::result]; return Json::nullValue; } std::tuple -AMM::balances(Issue const& issue1, Issue const& issue2, std::optional const& account) const +AMM::balances(Issue const& issue1, Issue const& issue2, std::optional const& account) + const { if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) { auto const ammAccountID = amm->getAccountID(sfAccount); - auto const [asset1Balance, asset2Balance] = - ammPoolHolds(*env_.current(), ammAccountID, issue1, issue2, FreezeHandling::fhIGNORE_FREEZE, env_.journal); - auto const lptAMMBalance = - account ? ammLPHolds(*env_.current(), *amm, *account, env_.journal) : amm->getFieldAmount(sfLPTokenBalance); + auto const [asset1Balance, asset2Balance] = ammPoolHolds( + *env_.current(), + ammAccountID, + issue1, + issue2, + FreezeHandling::fhIGNORE_FREEZE, + env_.journal); + auto const lptAMMBalance = account + ? ammLPHolds(*env_.current(), *amm, *account, env_.journal) + : amm->getFieldAmount(sfLPTokenBalance); return {asset1Balance, asset2Balance, lptAMMBalance}; } return {STAmount{}, STAmount{}, STAmount{}}; @@ -173,15 +210,25 @@ AMM::expectBalances( IOUAmount const& lpt, std::optional const& account) const { - auto const [asset1Balance, asset2Balance, lptAMMBalance] = balances(asset1.issue(), asset2.issue(), account); - return asset1 == asset1Balance && asset2 == asset2Balance && lptAMMBalance == STAmount{lpt, lptIssue_}; + auto const [asset1Balance, asset2Balance, lptAMMBalance] = + balances(asset1.issue(), asset2.issue(), account); + return asset1 == asset1Balance && asset2 == asset2Balance && + lptAMMBalance == STAmount{lpt, lptIssue_}; } IOUAmount AMM::getLPTokensBalance(std::optional const& account) const { if (account) - return accountHolds(*env_.current(), *account, lptIssue_, FreezeHandling::fhZERO_IF_FROZEN, env_.journal).iou(); + { + return accountHolds( + *env_.current(), + *account, + lptIssue_, + FreezeHandling::fhZERO_IF_FROZEN, + env_.journal) + .iou(); + } if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) return amm->getFieldAmount(sfLPTokenBalance).iou(); return IOUAmount{0}; @@ -199,15 +246,21 @@ AMM::expectLPTokens(AccountID const& account, IOUAmount const& expTokens) const } bool -AMM::expectAuctionSlot(std::uint32_t fee, std::optional timeSlot, IOUAmount expectedPrice) const +AMM::expectAuctionSlot( + std::uint32_t fee, + std::optional timeSlot, + IOUAmount expectedPrice) const { - return expectAuctionSlot( - [&](std::uint32_t slotFee, std::optional slotInterval, IOUAmount const& slotPrice, auto const&) { - return slotFee == fee && - // Auction slot might be expired, in which case slotInterval is - // 0 - ((!timeSlot && slotInterval == 0) || slotInterval == timeSlot) && slotPrice == expectedPrice; - }); + return expectAuctionSlot([&](std::uint32_t slotFee, + std::optional slotInterval, + IOUAmount const& slotPrice, + auto const&) { + return slotFee == fee && + // Auction slot might be expired, in which case slotInterval is + // 0 + ((!timeSlot && slotInterval == 0) || slotInterval == timeSlot) && + slotPrice == expectedPrice; + }); } bool @@ -217,8 +270,10 @@ AMM::expectAuctionSlot(std::vector const& authAccounts) const [&](std::uint32_t, std::optional, IOUAmount const&, STArray const& accounts) { for (auto const& account : accounts) { - if (std::find(authAccounts.cbegin(), authAccounts.cend(), account.getAccountID(sfAccount)) == - authAccounts.end()) + if (std::find( + authAccounts.cbegin(), + authAccounts.cend(), + account.getAccountID(sfAccount)) == authAccounts.end()) return false; } return true; @@ -253,8 +308,11 @@ AMM::expectAmmRpcInfo( } bool -AMM::expectAmmInfo(STAmount const& asset1, STAmount const& asset2, IOUAmount const& balance, Json::Value const& jvRes) - const +AMM::expectAmmInfo( + STAmount const& asset1, + STAmount const& asset2, + IOUAmount const& balance, + Json::Value const& jvRes) const { if (!jvRes.isMember(jss::amm)) return false; @@ -273,7 +331,8 @@ AMM::expectAmmInfo(STAmount const& asset1, STAmount const& asset2, IOUAmount con // ammRpcInfo returns unordered assets if (asset1Info.issue() != asset1.issue()) std::swap(asset1Info, asset2Info); - return asset1 == asset1Info && asset2 == asset2Info && lptBalance == STAmount{balance, lptIssue_}; + return asset1 == asset1Info && asset2 == asset2Info && + lptBalance == STAmount{balance, lptIssue_}; } void @@ -319,7 +378,16 @@ AMM::deposit( std::optional const& ter) { return deposit( - account, tokens, asset1In, std::nullopt, std::nullopt, flags, std::nullopt, std::nullopt, std::nullopt, ter); + account, + tokens, + asset1In, + std::nullopt, + std::nullopt, + flags, + std::nullopt, + std::nullopt, + std::nullopt, + ter); } IOUAmount @@ -333,7 +401,16 @@ AMM::deposit( { assert(!(asset2In && maxEP)); return deposit( - account, std::nullopt, asset1In, asset2In, maxEP, flags, std::nullopt, std::nullopt, std::nullopt, ter); + account, + std::nullopt, + asset1In, + asset2In, + maxEP, + flags, + std::nullopt, + std::nullopt, + std::nullopt, + ter); } IOUAmount @@ -369,15 +446,25 @@ AMM::deposit( if (!(jvFlags & tfDepositSubTx)) { if (tokens && !asset1In) + { jvFlags |= tfLPToken; + } else if (tokens && asset1In) + { jvFlags |= tfOneAssetLPToken; + } else if (asset1In && asset2In) + { jvFlags |= tfTwoAsset; + } else if (maxEP && asset1In) + { jvFlags |= tfLimitLPToken; + } else if (asset1In) + { jvFlags |= tfSingleAsset; + } } jv[jss::Flags] = jvFlags; return deposit(account, jv, assets, seq, ter); @@ -426,7 +513,16 @@ AMM::withdraw( std::optional const& flags, std::optional const& ter) { - return withdraw(account, tokens, asset1Out, std::nullopt, std::nullopt, flags, std::nullopt, std::nullopt, ter); + return withdraw( + account, + tokens, + asset1Out, + std::nullopt, + std::nullopt, + flags, + std::nullopt, + std::nullopt, + ter); } IOUAmount @@ -438,7 +534,16 @@ AMM::withdraw( std::optional const& ter) { assert(!(asset2Out && maxEP)); - return withdraw(account, std::nullopt, asset1Out, asset2Out, maxEP, std::nullopt, std::nullopt, std::nullopt, ter); + return withdraw( + account, + std::nullopt, + asset1Out, + asset2Out, + maxEP, + std::nullopt, + std::nullopt, + std::nullopt, + ter); } IOUAmount @@ -471,15 +576,25 @@ AMM::withdraw( if (!(jvFlags & tfWithdrawSubTx)) { if (tokens && !asset1Out) + { jvFlags |= tfLPToken; + } else if (asset1Out && asset2Out) + { jvFlags |= tfTwoAsset; + } else if (tokens && asset1Out) + { jvFlags |= tfOneAssetLPToken; + } else if (asset1Out && maxEP) + { jvFlags |= tfLimitLPToken; + } else if (asset1Out) + { jvFlags |= tfSingleAsset; + } } jv[jss::Flags] = jvFlags; return withdraw(account, jv, seq, assets, ter); @@ -489,7 +604,15 @@ IOUAmount AMM::withdraw(WithdrawArg const& arg) { return withdraw( - arg.account, arg.tokens, arg.asset1Out, arg.asset2Out, arg.maxEP, arg.flags, arg.assets, arg.seq, arg.err); + arg.account, + arg.tokens, + arg.asset1Out, + arg.asset2Out, + arg.maxEP, + arg.flags, + arg.assets, + arg.seq, + arg.err); } void @@ -516,7 +639,7 @@ AMM::vote( void AMM::vote(VoteArg const& arg) { - return vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err); + vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err); } Json::Value @@ -524,10 +647,13 @@ AMM::bid(BidArg const& arg) { if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) { - assert(!env_.current()->rules().enabled(fixInnerObjTemplate) || amm->isFieldPresent(sfAuctionSlot)); + assert( + !env_.current()->rules().enabled(fixInnerObjTemplate) || + amm->isFieldPresent(sfAuctionSlot)); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = + safe_downcast(amm->peekAtField(sfAuctionSlot)); lastPurchasePrice_ = auctionSlot[sfPrice].iou(); } } @@ -539,11 +665,15 @@ AMM::bid(BidArg const& arg) setTokens(jv, arg.assets); auto getBid = [&](auto const& bid) { if (std::holds_alternative(bid)) + { return STAmount{lptIssue_, std::get(bid)}; - else if (std::holds_alternative(bid)) + } + if (std::holds_alternative(bid)) + { return toSTAmount(std::get(bid), lptIssue_); - else - return std::get(bid); + } + + return std::get(bid); }; if (arg.bidMin) { @@ -557,7 +687,7 @@ AMM::bid(BidArg const& arg) saTokens.setJson(jv[jss::BidMax]); bidMax_ = saTokens.iou(); } - if (arg.authAccounts.size() > 0) + if (!arg.authAccounts.empty()) { Json::Value accounts(Json::arrayValue); for (auto const& account : arg.authAccounts) @@ -579,29 +709,48 @@ AMM::bid(BidArg const& arg) } void -AMM::submit(Json::Value const& jv, std::optional const& seq, std::optional const& ter) +AMM::submit( + Json::Value const& jv, + std::optional const& seq, + std::optional const& ter) { if (log_) std::cout << jv.toStyledString(); if (msig_) { if (seq && ter) + { env_(jv, *msig_, *seq, *ter); + } else if (seq) + { env_(jv, *msig_, *seq); + } else if (ter) + { env_(jv, *msig_, *ter); + } else + { env_(jv, *msig_); + } } else if (seq && ter) + { env_(jv, *seq, *ter); + } else if (seq) + { env_(jv, *seq); + } else if (ter) + { env_(jv, *ter); + } else + { env_(jv); + } if (doClose_) env_.close(); } @@ -611,10 +760,13 @@ AMM::expectAuctionSlot(auto&& cb) const { if (auto const amm = env_.current()->read(keylet::amm(asset1_.issue(), asset2_.issue()))) { - assert(!env_.current()->rules().enabled(fixInnerObjTemplate) || amm->isFieldPresent(sfAuctionSlot)); + assert( + !env_.current()->rules().enabled(fixInnerObjTemplate) || + amm->isFieldPresent(sfAuctionSlot)); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = + safe_downcast(amm->peekAtField(sfAuctionSlot)); if (auctionSlot.isFieldPresent(sfAccount)) { // This could fail in pre-fixInnerObjTemplate tests @@ -622,8 +774,8 @@ AMM::expectAuctionSlot(auto&& cb) const // the failure scenarios. Access as optional // to avoid the failure. auto const slotFee = auctionSlot[~sfDiscountedFee].value_or(0); - auto const slotInterval = - ammAuctionTimeSlot(env_.app().timeKeeper().now().time_since_epoch().count(), auctionSlot); + auto const slotInterval = ammAuctionTimeSlot( + env_.app().timeKeeper().now().time_since_epoch().count(), auctionSlot); auto const slotPrice = auctionSlot[sfPrice].iou(); auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts); return cb(slotFee, slotInterval, slotPrice, authAccounts); diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 18359ad201..ab8c1578a3 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -82,17 +82,17 @@ AMMTestBase::AMMTestBase() void AMMTestBase::testAMM( - std::function&& cb, + std::function const& cb, std::optional> const& pool, std::uint16_t tfee, std::optional const& ter, std::vector const& vfeatures) { - testAMM(std::move(cb), TestAMMArg{.pool = pool, .tfee = tfee, .ter = ter, .features = vfeatures}); + testAMM(cb, TestAMMArg{.pool = pool, .tfee = tfee, .ter = ter, .features = vfeatures}); } void -AMMTestBase::testAMM(std::function&& cb, TestAMMArg const& arg) +AMMTestBase::testAMM(std::function const& cb, TestAMMArg const& arg) { using namespace jtx; @@ -129,13 +129,20 @@ AMMTestBase::testAMM(std::function&& cb, TestAMMArg BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2); if (!asset1.native() && !asset2.native()) + { fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All); + } else if (asset1.native()) + { fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All); + } else if (asset2.native()) + { fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All); + } - AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = arg.tfee, .err = arg.ter}); + AMM ammAlice( + env, alice, asset1, asset2, CreateArg{.log = false, .tfee = arg.tfee, .err = arg.ter}); if (BEAST_EXPECT(ammAlice.expectBalances(asset1, asset2, ammAlice.tokens()))) cb(ammAlice, env); } @@ -262,6 +269,7 @@ AMMTest::find_paths( Json::Value p; p["Paths"] = path[jss::paths_computed]; STParsedJSONObject po("generic", p); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) paths = po.object->getFieldPathSet(sfPaths); } } diff --git a/src/test/jtx/impl/Account.cpp b/src/test/jtx/impl/Account.cpp index 31798abdc7..a7b71ea6eb 100644 --- a/src/test/jtx/impl/Account.cpp +++ b/src/test/jtx/impl/Account.cpp @@ -14,8 +14,15 @@ Account const Account::master( generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")), Account::privateCtorTag{}); -Account::Account(std::string name, std::pair const& keys, Account::privateCtorTag) - : name_(std::move(name)), pk_(keys.first), sk_(keys.second), id_(calcAccountID(pk_)), human_(toBase58(id_)) +Account::Account( + std::string name, + std::pair const& keys, + Account::privateCtorTag) + : name_(std::move(name)) + , pk_(keys.first) + , sk_(keys.second) + , id_(calcAccountID(pk_)) + , human_(toBase58(id_)) { } @@ -46,7 +53,8 @@ Account::fromCache(AcctStringType stringType, std::string name, KeyType type) return r.first->second; } -Account::Account(std::string name, KeyType type) : Account(fromCache(Account::other, std::move(name), type)) +Account::Account(std::string name, KeyType type) + : Account(fromCache(Account::other, std::move(name), type)) { } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index d8bcec84ee..3217f67cbe 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -10,12 +10,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -56,7 +57,8 @@ Env::AppBundle::AppBundle( auto timeKeeper_ = std::make_unique(); timeKeeper = timeKeeper_.get(); // Hack so we don't have to call Config::setup - HTTPClient::initializeSSLContext(config->SSL_VERIFY_DIR, config->SSL_VERIFY_FILE, config->SSL_VERIFY, debugLog()); + HTTPClient::initializeSSLContext( + config->SSL_VERIFY_DIR, config->SSL_VERIFY_FILE, config->SSL_VERIFY, debugLog()); owned = make_Application(std::move(config), std::move(logs), std::move(timeKeeper_)); app = owned.get(); app->logs().threshold(thresh); @@ -105,7 +107,9 @@ Env::close(NetClock::time_point closeTime, std::optionalgetFieldU64(sfOutstandingAmount), 0, true}; return {amount, lookup(issuer).name()}; } - else - { - // Holder balance - auto const sle = le(keylet::mptoken(id, account)); - if (!sle) - return {STAmount(mptIssue, 0), account.name()}; - STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)}; - return {amount, lookup(issuer).name()}; - } + // Holder balance + auto const sle = le(keylet::mptoken(id, account)); + if (!sle) + return {STAmount(mptIssue, 0), account.name()}; + + STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)}; + return {amount, lookup(issuer).name()}; } PrettyAmount @@ -269,12 +277,20 @@ Env::fund(bool setDefaultRipple, STAmount const& amount, Account const& account) jtx::seq(jtx::autofill), fee(jtx::autofill), sig(jtx::autofill)); - apply(fset(account, asfDefaultRipple), jtx::seq(jtx::autofill), fee(jtx::autofill), sig(jtx::autofill)); + apply( + fset(account, asfDefaultRipple), + jtx::seq(jtx::autofill), + fee(jtx::autofill), + sig(jtx::autofill)); require(flags(account, asfDefaultRipple)); } else { - apply(pay(master, account, amount), jtx::seq(jtx::autofill), fee(jtx::autofill), sig(jtx::autofill)); + apply( + pay(master, account, amount), + jtx::seq(jtx::autofill), + fee(jtx::autofill), + sig(jtx::autofill)); require(nflags(account, asfDefaultRipple)); } require(jtx::balance(account, amount)); @@ -284,7 +300,11 @@ void Env::trust(STAmount const& amount, Account const& account) { auto const start = balance(account); - apply(jtx::trust(account, amount), jtx::seq(jtx::autofill), fee(jtx::autofill), sig(jtx::autofill)); + apply( + jtx::trust(account, amount), + jtx::seq(jtx::autofill), + fee(jtx::autofill), + sig(jtx::autofill)); apply( pay(master, account, drops(current()->fees().base)), jtx::seq(jtx::autofill), @@ -322,10 +342,14 @@ Env::parseResult(Json::Value const& jr) parsed.rpcCode.emplace(rpcSUCCESS); } else + { error(parsed, result); + } } else + { error(parsed, jr); + } return parsed; } @@ -348,16 +372,14 @@ Env::submit(JTx const& jt, std::source_location const& loc) return jr; } - else - { - // Parsing failed or the JTx is - // otherwise missing the stx field. - parsedResult.ter = ter_ = temMALFORMED; - return Json::Value(); - } + // Parsing failed or the JTx is + // otherwise missing the stx field. + parsedResult.ter = ter_ = temMALFORMED; + + return Json::Value(); }(); - return postconditions(jt, parsedResult, jr, loc); + postconditions(jt, parsedResult, jr, loc); } void @@ -378,8 +400,9 @@ Env::sign_and_submit(JTx const& jt, Json::Value params, std::source_location con // Use the provided parameters, and go straight // to the (RPC) client. assert(params.isObject()); - if (!params.isMember(jss::secret) && !params.isMember(jss::key_type) && !params.isMember(jss::seed) && - !params.isMember(jss::seed_hex) && !params.isMember(jss::passphrase)) + if (!params.isMember(jss::secret) && !params.isMember(jss::key_type) && + !params.isMember(jss::seed) && !params.isMember(jss::seed_hex) && + !params.isMember(jss::passphrase)) { params[jss::secret] = passphrase; } @@ -394,41 +417,47 @@ Env::sign_and_submit(JTx const& jt, Json::Value params, std::source_location con test.expect(parsedResult.ter, "ter uninitialized!"); ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED); - return postconditions(jt, parsedResult, jr, loc); + postconditions(jt, parsedResult, jr, loc); } void -Env::postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const& jr, std::source_location const& loc) +Env::postconditions( + JTx const& jt, + ParsedResult const& parsed, + Json::Value const& jr, + std::source_location const& loc) { - auto const line = jt.testLine ? " (" + to_string(*jt.testLine) + ")" : ""; auto const locStr = std::string("(") + loc.file_name() + ":" + to_string(loc.line()) + ")"; - bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!" + line); + bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!"); bad = (jt.ter && parsed.ter && !test.expect( *parsed.ter == *jt.ter, - "apply " + locStr + ": Got " + transToken(*parsed.ter) + " (" + transHuman(*parsed.ter) + "); Expected " + - transToken(*jt.ter) + " (" + transHuman(*jt.ter) + ")" + line)); + "apply " + locStr + ": Got " + transToken(*parsed.ter) + " (" + + transHuman(*parsed.ter) + "); Expected " + transToken(*jt.ter) + " (" + + transHuman(*jt.ter) + ")")); using namespace std::string_literals; bad = (jt.rpcCode && !test.expect( parsed.rpcCode == jt.rpcCode->first && parsed.rpcMessage == jt.rpcCode->second, "apply " + locStr + ": Got RPC result "s + - (parsed.rpcCode ? RPC::get_error_info(*parsed.rpcCode).token.c_str() : "NO RESULT") + " (" + - parsed.rpcMessage + "); Expected " + RPC::get_error_info(jt.rpcCode->first).token.c_str() + " (" + - jt.rpcCode->second + ")" + line)) || + (parsed.rpcCode ? RPC::get_error_info(*parsed.rpcCode).token.c_str() + : "NO RESULT") + + " (" + parsed.rpcMessage + "); Expected " + + RPC::get_error_info(jt.rpcCode->first).token.c_str() + " (" + + jt.rpcCode->second + ")")) || bad; // If we have an rpcCode (just checked), then the rpcException check is // optional - the 'error' field may not be defined, but if it is, it must // match rpcError. - bad = - (jt.rpcException && - !test.expect( - (jt.rpcCode && parsed.rpcError.empty()) || - (parsed.rpcError == jt.rpcException->first && - (!jt.rpcException->second || parsed.rpcException == *jt.rpcException->second)), - "apply " + locStr + ": Got RPC result "s + parsed.rpcError + " (" + parsed.rpcException + "); Expected " + - jt.rpcException->first + " (" + jt.rpcException->second.value_or("n/a") + ")" + line)) || + bad = (jt.rpcException && + !test.expect( + (jt.rpcCode && parsed.rpcError.empty()) || + (parsed.rpcError == jt.rpcException->first && + (!jt.rpcException->second || parsed.rpcException == *jt.rpcException->second)), + "apply " + locStr + ": Got RPC result "s + parsed.rpcError + " (" + + parsed.rpcException + "); Expected " + jt.rpcException->first + " (" + + jt.rpcException->second.value_or("n/a") + ")")) || bad; if (bad) { @@ -499,8 +528,9 @@ Env::autofill_sig(JTx& jt) // If the sig is still needed, get it here. if (!jt.fill_sig) return; - auto const account = jv.isMember(sfDelegate.jsonName) ? lookup(jv[sfDelegate.jsonName].asString()) - : lookup(jv[jss::Account].asString()); + auto const account = jv.isMember(sfDelegate.jsonName) + ? lookup(jv[sfDelegate.jsonName].asString()) + : lookup(jv[jss::Account].asString()); if (!app().checkSigs()) { jv[jss::SigningPubKey] = strHex(account.pk().slice()); @@ -510,9 +540,13 @@ Env::autofill_sig(JTx& jt) } auto const ar = le(account); if (ar && ar->isFieldPresent(sfRegularKey)) + { jtx::sign(jv, lookup(ar->getAccountID(sfRegularKey))); + } else + { jtx::sign(jv, account); + } } void @@ -526,7 +560,7 @@ Env::autofill(JTx& jt) if (jt.fill_netid) { - uint32_t networkID = app().config().NETWORK_ID; + uint32_t networkID = app().getNetworkIDService().getNetworkID(); if (!jv.isMember(jss::NetworkID) && networkID > 1024) jv[jss::NetworkID] = std::to_string(networkID); } @@ -564,10 +598,10 @@ Env::st(JTx const& jt) { return sterilize(STTx{std::move(*obj)}); } - catch (std::exception const&) + catch (...) { + return nullptr; } - return nullptr; } std::shared_ptr @@ -590,10 +624,10 @@ Env::ust(JTx const& jt) { return std::make_shared(std::move(*obj)); } - catch (std::exception const&) + catch (...) { + return nullptr; } - return nullptr; } Json::Value diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index 34d0ea2f68..6b60744aad 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -35,12 +35,15 @@ class JSONRPCClient : public AbstractClient continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) - *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; + { + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} + : address{address_v4::loopback()}; + } if (!pp.port) Throw("Use fixConfigPorts with auto ports"); - return {*pp.ip, *pp.port}; + return {*pp.ip, *pp.port}; // NOLINT(bugprone-unchecked-optional-access) } Throw("Missing HTTP port"); return {}; // Silence compiler control paths return value warning diff --git a/src/test/jtx/impl/Oracle.cpp b/src/test/jtx/impl/Oracle.cpp index c696011da5..8bc456cd4e 100644 --- a/src/test/jtx/impl/Oracle.cpp +++ b/src/test/jtx/impl/Oracle.cpp @@ -12,7 +12,7 @@ namespace test { namespace jtx { namespace oracle { -Oracle::Oracle(Env& env, CreateArg const& arg, bool submit) : env_(env), owner_{}, documentID_{} +Oracle::Oracle(Env& env, CreateArg const& arg, bool submit) : env_(env), documentID_{} { // LastUpdateTime is checked to be in range // {close-maxLastUpdateTimeDelta, close+maxLastUpdateTimeDelta}. @@ -38,11 +38,17 @@ Oracle::remove(RemoveArg const& arg) jv[jss::Account] = to_string(arg.owner.value_or(owner_)); toJson(jv[jss::OracleDocumentID], arg.documentID.value_or(documentID_)); if (Oracle::fee != 0) + { jv[jss::Fee] = std::to_string(Oracle::fee); + } else if (arg.fee != 0) + { jv[jss::Fee] = std::to_string(arg.fee); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } if (arg.flags != 0) jv[jss::Flags] = arg.flags; submit(jv, arg.msig, arg.seq, arg.err); @@ -58,22 +64,38 @@ Oracle::submit( if (msig) { if (seq && err) + { env_(jv, *msig, *seq, *err); + } else if (seq) + { env_(jv, *msig, *seq); + } else if (err) + { env_(jv, *msig, *err); + } else + { env_(jv, *msig); + } } else if (seq && err) + { env_(jv, *seq, *err); + } else if (seq) + { env_(jv, *seq); + } else if (err) + { env_(jv, *err); + } else + { env_(jv); + } env_.close(); } @@ -90,7 +112,7 @@ Oracle::expectPrice(DataSeries const& series) const if (auto const sle = env_.le(keylet::oracle(owner_, documentID_))) { auto const& leSeries = sle->getFieldArray(sfPriceDataSeries); - if (leSeries.size() == 0 || leSeries.size() != series.size()) + if (leSeries.empty() || leSeries.size() != series.size()) return false; for (auto const& data : series) { @@ -99,8 +121,9 @@ Oracle::expectPrice(DataSeries const& series) const auto const& quoteAsset = o.getFieldCurrency(sfQuoteAsset); auto const& price = o.getFieldU64(sfAssetPrice); auto const& scale = o.getFieldU8(sfScale); - return baseAsset.getText() == std::get<0>(data) && quoteAsset.getText() == std::get<1>(data) && - price == std::get<2>(data) && scale == std::get<3>(data); + return baseAsset.getText() == std::get<0>(data) && + quoteAsset.getText() == std::get<1>(data) && price == std::get<2>(data) && + scale == std::get<3>(data); }) == leSeries.end()) return false; } @@ -156,9 +179,13 @@ Oracle::aggregatePrice( if (jr.isObject()) { if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status)) + { return jr[jss::result]; - else if (jr.isMember(jss::error)) + } + if (jr.isMember(jss::error)) + { return jr; + } } return Json::nullValue; } @@ -176,9 +203,13 @@ Oracle::set(UpdateArg const& arg) jv[jss::OracleDocumentID] = documentID_; } else if (arg.documentID) + { toJson(jv[jss::OracleDocumentID], *arg.documentID); + } else + { jv[jss::OracleDocumentID] = documentID_; + } jv[jss::TransactionType] = jss::OracleSet; jv[jss::Account] = to_string(owner_); if (arg.assetClass) @@ -190,23 +221,36 @@ Oracle::set(UpdateArg const& arg) if (arg.flags != 0) jv[jss::Flags] = arg.flags; if (Oracle::fee != 0) + { jv[jss::Fee] = std::to_string(Oracle::fee); + } else if (arg.fee != 0) + { jv[jss::Fee] = std::to_string(arg.fee); + } else + { jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); + } // lastUpdateTime if provided is offset from testStartTime if (arg.lastUpdateTime) { if (std::holds_alternative(*arg.lastUpdateTime)) - jv[jss::LastUpdateTime] = to_string(testStartTime.count() + std::get(*arg.lastUpdateTime)); + { + jv[jss::LastUpdateTime] = + to_string(testStartTime.count() + std::get(*arg.lastUpdateTime)); + } else + { toJson(jv[jss::LastUpdateTime], *arg.lastUpdateTime); + } } else + { jv[jss::LastUpdateTime] = to_string( duration_cast(env_.current()->header().closeTime.time_since_epoch()).count() + epoch_offset.count()); + } Json::Value dataSeries(Json::arrayValue); auto assetToStr = [](std::string const& s) { // assume standard currency @@ -214,7 +258,6 @@ Oracle::set(UpdateArg const& arg) return s; assert(s.size() <= 20); // anything else must be 160-bit hex string - std::string h = strHex(s); return strHex(s).append(40 - s.size() * 2, '0'); }; for (auto const& data : arg.series) @@ -224,9 +267,12 @@ Oracle::set(UpdateArg const& arg) price[jss::BaseAsset] = assetToStr(std::get<0>(data)); price[jss::QuoteAsset] = assetToStr(std::get<1>(data)); if (std::get<2>(data)) - price[jss::AssetPrice] = *std::get<2>(data); + { + price[jss::AssetPrice] = + *std::get<2>(data); // NOLINT(bugprone-unchecked-optional-access) + } if (std::get<3>(data)) - price[jss::Scale] = *std::get<3>(data); + price[jss::Scale] = *std::get<3>(data); // NOLINT(bugprone-unchecked-optional-access) priceData[jss::PriceData] = price; dataSeries.append(priceData); } @@ -263,19 +309,27 @@ Oracle::ledgerEntry( if (account) { if (std::holds_alternative(*account)) + { jvParams[jss::oracle][jss::account] = to_string(std::get(*account)); + } else + { jvParams[jss::oracle][jss::account] = std::get(*account); + } } if (documentID) toJson(jvParams[jss::oracle][jss::oracle_document_id], *documentID); if (index) { - std::uint32_t i; + std::uint32_t i = 0; if (boost::conversion::try_lexical_convert(*index, i)) + { jvParams[jss::oracle][jss::ledger_index] = i; + } else + { jvParams[jss::oracle][jss::ledger_index] = *index; + } } // Convert "%None%" to None auto str = to_string(jvParams); @@ -306,12 +360,18 @@ toJsonHex(Json::Value& jv, AnyValue const& v) if constexpr (std::is_same_v) { if (arg.starts_with("##")) + { jv = arg.substr(2); + } else + { jv = strHex(arg); + } } else + { jv = arg; + } }, v); } @@ -337,8 +397,8 @@ validDocumentID(AnyValue const& v) } catch (...) { + return false; } - return false; } } // namespace oracle diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index d89dec456b..e10a0c46d0 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace xrpl { @@ -62,7 +63,10 @@ STPathElement IPE(Issue const& iss) { return STPathElement( - STPathElement::typeCurrency | STPathElement::typeIssuer, xrpAccount(), iss.currency, iss.account); + STPathElement::typeCurrency | STPathElement::typeIssuer, + xrpAccount(), + iss.currency, + iss.account); } /******************************************************************************/ @@ -97,7 +101,8 @@ expectHolding(Env& env, AccountID const& account, STAmount const& value, bool de low.setIssuer(accountLow ? account : issue.account); high.setIssuer(accountLow ? issue.account : account); - expectDefaultTrustLine = sle->getFieldAmount(sfLowLimit) == low && sle->getFieldAmount(sfHighLimit) == high; + expectDefaultTrustLine = + sle->getFieldAmount(sfLowLimit) == low && sle->getFieldAmount(sfHighLimit) == high; } auto amount = sle->getFieldAmount(sfBalance); @@ -125,11 +130,16 @@ expectHolding(Env& env, AccountID const& account, None const&, MPTIssue const& m expectHolding(Env& env, AccountID const& account, None const& value) { return std::visit( - [&](auto const& issue) { return expectHolding(env, account, value, issue); }, value.asset.value()); + [&](auto const& issue) { return expectHolding(env, account, value, issue); }, + value.asset.value()); } [[nodiscard]] bool -expectOffers(Env& env, AccountID const& account, std::uint16_t size, std::vector const& toMatch) +expectOffers( + Env& env, + AccountID const& account, + std::uint16_t size, + std::vector const& toMatch) { std::uint16_t cnt = 0; std::uint16_t matched = 0; @@ -140,7 +150,8 @@ expectOffers(Env& env, AccountID const& account, std::uint16_t size, std::vector { ++cnt; if (std::find_if(toMatch.begin(), toMatch.end(), [&](auto const& a) { - return a.in == sle->getFieldAmount(sfTakerPays) && a.out == sle->getFieldAmount(sfTakerGets); + return a.in == sle->getFieldAmount(sfTakerPays) && + a.out == sle->getFieldAmount(sfTakerGets); }) != toMatch.end()) ++matched; } @@ -159,7 +170,11 @@ ledgerEntryRoot(Env& env, Account const& acct) } Json::Value -ledgerEntryState(Env& env, Account const& acct_a, Account const& acct_b, std::string const& currency) +ledgerEntryState( + Env& env, + Account const& acct_a, + Account const& acct_b, + std::string const& currency) { Json::Value jvParams; jvParams[jss::ledger_index] = "current"; @@ -341,7 +356,11 @@ del(AccountID const& account, uint256 const& brokerID, uint32_t flags) } Json::Value -coverDeposit(AccountID const& account, uint256 const& brokerID, STAmount const& amount, uint32_t flags) +coverDeposit( + AccountID const& account, + uint256 const& brokerID, + STAmount const& amount, + uint32_t flags) { Json::Value jv; jv[sfTransactionType] = jss::LoanBrokerCoverDeposit; @@ -353,7 +372,11 @@ coverDeposit(AccountID const& account, uint256 const& brokerID, STAmount const& } Json::Value -coverWithdraw(AccountID const& account, uint256 const& brokerID, STAmount const& amount, uint32_t flags) +coverWithdraw( + AccountID const& account, + uint256 const& brokerID, + STAmount const& amount, + uint32_t flags) { Json::Value jv; jv[sfTransactionType] = jss::LoanBrokerCoverWithdraw; @@ -381,7 +404,10 @@ coverClawback(AccountID const& account, std::uint32_t flags) namespace loan { Json::Value -set(AccountID const& account, uint256 const& loanBrokerID, Number principalRequested, std::uint32_t flags) +set(AccountID const& account, + uint256 const& loanBrokerID, + Number principalRequested, + std::uint32_t flags) { Json::Value jv; jv[sfTransactionType] = jss::LoanSet; diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index c1012b57a4..ebe4721d60 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -27,7 +27,7 @@ class WSClientImpl : public WSClient { Json::Value jv; - explicit msg(Json::Value&& jv_) : jv(jv_) + explicit msg(Json::Value&& jv_) : jv(std::move(jv_)) { } }; @@ -49,12 +49,15 @@ class WSClientImpl : public WSClient continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) - *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} : address{address_v4::loopback()}; + { + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} + : address{address_v4::loopback()}; + } if (!pp.port) Throw("Use fixConfigPorts with auto ports"); - return {*pp.ip, *pp.port}; + return {*pp.ip, *pp.port}; // NOLINT(bugprone-unchecked-optional-access) } Throw("Missing WebSocket port"); return {}; // Silence compiler control paths return value warning @@ -100,16 +103,18 @@ class WSClientImpl : public WSClient boost::asio::post(ios_, boost::asio::bind_executor(strand_, [this] { if (!peerClosed_) { - ws_.async_close({}, boost::asio::bind_executor(strand_, [&](error_code) { - try - { - stream_.cancel(); - } - catch (boost::system::system_error const&) - { - // ignored - } - })); + ws_.async_close( + {}, boost::asio::bind_executor(strand_, [&](error_code) { + try + { + stream_.cancel(); + } + // NOLINTNEXTLINE(bugprone-empty-catch) + catch (boost::system::system_error const&) + { + // ignored + } + })); } })); work_ = std::nullopt; @@ -134,10 +139,11 @@ public: auto const ep = getEndpoint(cfg, v2); stream_.connect(ep); ws_.set_option( - boost::beast::websocket::stream_base::decorator([&](boost::beast::websocket::request_type& req) { - for (auto const& h : headers) - req.set(h.first, h.second); - })); + boost::beast::websocket::stream_base::decorator( + [&](boost::beast::websocket::request_type& req) { + for (auto const& h : headers) + req.set(h.first, h.second); + })); ws_.handshake(ep.address().to_string() + ":" + std::to_string(ep.port()), "/"); ws_.async_read( rb_, @@ -174,12 +180,15 @@ public: jp[jss::id] = 5; } else + { jp[jss::command] = cmd; + } auto const s = to_string(jp); ws_.write_some(true, buffer(s)); } - auto jv = findMsg(5s, [&](Json::Value const& jval) { return jval[jss::type] == jss::response; }); + auto jv = + findMsg(5s, [&](Json::Value const& jval) { return jval[jss::type] == jss::response; }); if (jv) { // Normalize JSON output @@ -215,7 +224,8 @@ public: } std::optional - findMsg(std::chrono::milliseconds const& timeout, std::function pred) override + findMsg(std::chrono::milliseconds const& timeout, std::function pred) + override { std::shared_ptr m; { @@ -268,7 +278,8 @@ private: } ws_.async_read( rb_, - boost::asio::bind_executor(strand_, std::bind(&WSClientImpl::on_read_msg, this, std::placeholders::_1))); + boost::asio::bind_executor( + strand_, std::bind(&WSClientImpl::on_read_msg, this, std::placeholders::_1))); } // Called when the read op terminates diff --git a/src/test/jtx/impl/amount.cpp b/src/test/jtx/impl/amount.cpp index 73dad7f48e..25fcb5c8f0 100644 --- a/src/test/jtx/impl/amount.cpp +++ b/src/test/jtx/impl/amount.cpp @@ -28,7 +28,8 @@ operator<<(std::ostream&& os, } #endif -PrettyAmount::operator AnyAmount() const +PrettyAmount:: +operator AnyAmount() const { return {amount_}; } @@ -61,9 +62,13 @@ operator<<(std::ostream& os, PrettyAmount const& amount) if (n < c) { if (amount.value().negative()) + { os << "-" << n << " drops"; + } else + { os << n << " drops"; + } return os; } auto const d = double(n) / dropsPerXRP.drops(); @@ -74,8 +79,8 @@ operator<<(std::ostream& os, PrettyAmount const& amount) } else if (amount.value().holds()) { - os << amount.value().getText() << "/" << to_string(amount.value().issue().currency) << "(" << amount.name() - << ")"; + os << amount.value().getText() << "/" << to_string(amount.value().issue().currency) << "(" + << amount.name() << ")"; } else { diff --git a/src/test/jtx/impl/attester.cpp b/src/test/jtx/impl/attester.cpp index 04b2a037c6..d4693e69fb 100644 --- a/src/test/jtx/impl/attester.cpp +++ b/src/test/jtx/impl/attester.cpp @@ -40,7 +40,14 @@ sign_create_account_attestation( AccountID const& dst) { auto const toSign = Attestations::AttestationCreateAccount::message( - bridge, sendingAccount, sendingAmount, rewardAmount, rewardAccount, wasLockingChainSend, createCount, dst); + bridge, + sendingAccount, + sendingAmount, + rewardAmount, + rewardAccount, + wasLockingChainSend, + createCount, + dst); return sign(pk, sk, makeSlice(toSign)); } diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index 1ebeeed437..1c3cc0b3c3 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -44,7 +44,12 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, } void -doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, MPTIssue const& mptIssue) +doBalance( + Env& env, + AccountID const& account, + bool none, + STAmount const& value, + MPTIssue const& mptIssue) { auto const sle = env.le(keylet::mptoken(mptIssue.getMptID(), account)); if (none) @@ -61,8 +66,9 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, void balance::operator()(Env& env) const { - return std::visit( - [&](auto const& issue) { doBalance(env, account_.id(), none_, value_, issue); }, value_.asset().value()); + std::visit( + [&](auto const& issue) { doBalance(env, account_.id(), none_, value_, issue); }, + value_.asset().value()); } } // namespace jtx diff --git a/src/test/jtx/impl/batch.cpp b/src/test/jtx/impl/batch.cpp index 3f993ddea1..65aca2a935 100644 --- a/src/test/jtx/impl/batch.cpp +++ b/src/test/jtx/impl/batch.cpp @@ -74,6 +74,7 @@ sig::operator()(Env& env, JTx& jt) const Serializer msg; serializeBatch(msg, stx.getFlags(), stx.getBatchTransactionIDs()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const sig = xrpl::sign(*publicKeyType(e.sig.pk().slice()), e.sig.sk(), msg.slice()); jo[sfTxnSignature.getJsonName()] = strHex(Slice{sig.data(), sig.size()}); } @@ -112,6 +113,7 @@ msig::operator()(Env& env, JTx& jt) const Serializer msg; serializeBatch(msg, stx.getFlags(), stx.getBatchTransactionIDs()); finishMultiSigningData(e.acct.id(), msg); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const sig = xrpl::sign(*publicKeyType(e.sig.pk().slice()), e.sig.sk(), msg.slice()); iso[sfTxnSignature.getJsonName()] = strHex(Slice{sig.data(), sig.size()}); } diff --git a/src/test/jtx/impl/creds.cpp b/src/test/jtx/impl/creds.cpp index 5a9ecfec59..e1018cc0e5 100644 --- a/src/test/jtx/impl/creds.cpp +++ b/src/test/jtx/impl/creds.cpp @@ -34,7 +34,11 @@ accept(jtx::Account const& subject, jtx::Account const& issuer, std::string_view } Json::Value -deleteCred(jtx::Account const& acc, jtx::Account const& subject, jtx::Account const& issuer, std::string_view credType) +deleteCred( + jtx::Account const& acc, + jtx::Account const& subject, + jtx::Account const& issuer, + std::string_view credType) { Json::Value jv; jv[jss::TransactionType] = jss::CredentialDelete; @@ -46,7 +50,11 @@ deleteCred(jtx::Account const& acc, jtx::Account const& subject, jtx::Account co } Json::Value -ledgerEntry(jtx::Env& env, jtx::Account const& subject, jtx::Account const& issuer, std::string_view credType) +ledgerEntry( + jtx::Env& env, + jtx::Account const& subject, + jtx::Account const& issuer, + std::string_view credType) { Json::Value jvParams; jvParams[jss::ledger_index] = jss::validated; diff --git a/src/test/jtx/impl/delegate.cpp b/src/test/jtx/impl/delegate.cpp index 312a3f2403..7cca9aa738 100644 --- a/src/test/jtx/impl/delegate.cpp +++ b/src/test/jtx/impl/delegate.cpp @@ -9,7 +9,9 @@ namespace jtx { namespace delegate { Json::Value -set(jtx::Account const& account, jtx::Account const& authorize, std::vector const& permissions) +set(jtx::Account const& account, + jtx::Account const& authorize, + std::vector const& permissions) { Json::Value jv; jv[jss::TransactionType] = jss::DelegateSet; diff --git a/src/test/jtx/impl/directory.cpp b/src/test/jtx/impl/directory.cpp index 7231389d1e..bf63bc523b 100644 --- a/src/test/jtx/impl/directory.cpp +++ b/src/test/jtx/impl/directory.cpp @@ -72,7 +72,9 @@ bumpLastPage( // Adjust root previous and previous node's next sleRoot->setFieldU64(sfIndexPrevious, newLastPage); if (prevIndex.value_or(0) == 0) + { sleRoot->setFieldU64(sfIndexNext, newLastPage); + } else { auto slePrev = sb.peek(keylet::page(directory, *prevIndex)); @@ -88,6 +90,7 @@ bumpLastPage( // Fixup page numbers in the objects referred by indexes if (adjust) + { for (auto const key : indexes) { if (!adjust(sb, key, newLastPage)) @@ -96,6 +99,7 @@ bumpLastPage( return false; } } + } sb.apply(view); return true; diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index d3d5bccd49..7b9be5adc8 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -92,6 +92,12 @@ secure_gateway_localnet(std::unique_ptr cfg) (*cfg)[PORT_WS].set("secure_gateway", "127.0.0.0/8"); return cfg; } +std::unique_ptr +single_thread_io(std::unique_ptr cfg) +{ + cfg->IO_WORKERS = 1; + return cfg; +} auto constexpr defaultseed = "shUwVw52ofnCUX5m7kPTKzJdr4HEH"; @@ -99,7 +105,8 @@ std::unique_ptr validator(std::unique_ptr cfg, std::string const& seed) { // If the config has valid validation keys then we run as a validator. - cfg->section(SECTION_VALIDATION_SEED).append(std::vector{seed.empty() ? defaultseed : seed}); + cfg->section(SECTION_VALIDATION_SEED) + .append(std::vector{seed.empty() ? defaultseed : seed}); return cfg; } @@ -124,7 +131,9 @@ addGrpcConfigWithSecureGateway(std::unique_ptr cfg, std::string const& s } std::unique_ptr -makeConfig(std::map extraTxQ, std::map extraVoting) +makeConfig( + std::map extraTxQ, + std::map extraVoting) { auto p = test::jtx::envconfig(); auto& section = p->section("transaction_queue"); diff --git a/src/test/jtx/impl/fee.cpp b/src/test/jtx/impl/fee.cpp index 2b1ac6c398..fc05afcb46 100644 --- a/src/test/jtx/impl/fee.cpp +++ b/src/test/jtx/impl/fee.cpp @@ -14,9 +14,13 @@ fee::operator()(Env& env, JTx& jt) const jt.fill_fee = false; assert(!increment_ || !amount_); if (increment_) + { jt[sfFee] = STAmount(env.current()->fees().increment).getJson(); + } else if (amount_) + { jt[sfFee] = amount_->getJson(JsonOptions::none); + } } } // namespace jtx diff --git a/src/test/jtx/impl/flags.cpp b/src/test/jtx/impl/flags.cpp index 11e7831f55..aee01a107e 100644 --- a/src/test/jtx/impl/flags.cpp +++ b/src/test/jtx/impl/flags.cpp @@ -24,11 +24,17 @@ flags::operator()(Env& env) const { auto const sle = env.le(account_); if (!sle) + { env.test.fail(); + } else if (sle->isFieldPresent(sfFlags)) + { env.test.expect((sle->getFieldU32(sfFlags) & mask_) == mask_); + } else + { env.test.expect(mask_ == 0); + } } void @@ -36,11 +42,17 @@ nflags::operator()(Env& env) const { auto const sle = env.le(account_); if (!sle) + { env.test.fail(); + } else if (sle->isFieldPresent(sfFlags)) + { env.test.expect((sle->getFieldU32(sfFlags) & mask_) == 0); + } else + { env.test.pass(); + } } } // namespace jtx diff --git a/src/test/jtx/impl/ledgerStateFixes.cpp b/src/test/jtx/impl/ledgerStateFixes.cpp index f1e6b6eda1..e5a7495a44 100644 --- a/src/test/jtx/impl/ledgerStateFixes.cpp +++ b/src/test/jtx/impl/ledgerStateFixes.cpp @@ -1,9 +1,8 @@ #include -#include - #include #include +#include namespace xrpl { namespace test { diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index f505bff740..a6283bb6f9 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -63,7 +64,12 @@ MPTTester::MPTTester(Env& env, Account const& issuer, MPTInit const& arg) create(*arg.create); } -MPTTester::MPTTester(Env& env, Account const& issuer, MPTID const& id, std::vector const& holders, bool close) +MPTTester::MPTTester( + Env& env, + Account const& issuer, + MPTID const& id, + std::vector const& holders, + bool close) : env_(env), issuer_(issuer), holders_(makeHolders(holders)), id_(id), close_(close) { } @@ -72,12 +78,14 @@ static MPTCreate makeMPTCreate(MPTInitDef const& arg) { if (arg.pay) + { return { .maxAmt = arg.maxAmt, .transferFee = arg.transferFee, .pay = {{arg.holders, *arg.pay}}, .flags = arg.flags, .authHolder = arg.authHolder}; + } return { .maxAmt = arg.maxAmt, .transferFee = arg.transferFee, @@ -87,11 +95,15 @@ makeMPTCreate(MPTInitDef const& arg) } MPTTester::MPTTester(MPTInitDef const& arg) - : MPTTester{arg.env, arg.issuer, MPTInit{.fund = arg.fund, .close = arg.close, .create = makeMPTCreate(arg)}} + : MPTTester{ + arg.env, + arg.issuer, + MPTInit{.fund = arg.fund, .close = arg.close, .create = makeMPTCreate(arg)}} { } -MPTTester::operator MPT() const +MPTTester:: +operator MPT() const { if (!id_) Throw("MPT has not been created"); @@ -136,10 +148,11 @@ MPTTester::create(MPTCreate const& arg) .metadata = arg.metadata, .mutableFlags = arg.mutableFlags, .domainID = arg.domainID}); - if (submit(arg, jv) != tesSUCCESS) + if (!isTesSuccess(submit(arg, jv))) { // Verify issuance doesn't exist - env_.require(requireAny([&]() -> bool { return env_.le(keylet::mptIssuance(*id_)) == nullptr; })); + env_.require( + requireAny([&]() -> bool { return env_.le(keylet::mptIssuance(*id_)) == nullptr; })); id_.reset(); } @@ -164,16 +177,24 @@ MPTTester::create(MPTCreate const& arg) if (arg.authorize) { if (arg.authorize->empty()) + { authAndPay(holders_, [](auto const& it) { return it.second; }); + } else + { authAndPay(*arg.authorize, [](auto const& it) { return it; }); + } } else if (arg.pay) { if (arg.pay->first.empty()) + { authAndPay(holders_, [](auto const& it) { return it.second; }); + } else + { authAndPay(arg.pay->first, [](auto const& it) { return it; }); + } } } } @@ -196,7 +217,8 @@ MPTTester::destroy(MPTDestroy const& arg) { if (!arg.id && !id_) Throw("MPT has not been created"); - Json::Value jv = destroyJV({.issuer = arg.issuer ? arg.issuer : issuer_, .id = arg.id ? arg.id : id_}); + Json::Value jv = + destroyJV({.issuer = arg.issuer ? arg.issuer : issuer_, .id = arg.id ? arg.id : id_}); submit(arg, jv); } @@ -234,7 +256,7 @@ MPTTester::authorize(MPTAuthorize const& arg) .holder = arg.holder, .id = arg.id ? arg.id : id_, }); - if (auto const result = submit(arg, jv); result == tesSUCCESS) + if (auto const result = submit(arg, jv); isTesSuccess(result)) { // Issuer authorizes if (!arg.account || *arg.account == issuer_) @@ -242,10 +264,14 @@ MPTTester::authorize(MPTAuthorize const& arg) auto const flags = getFlags(arg.holder); // issuer un-authorizes the holder if (arg.flags.value_or(0) == tfMPTUnauthorize) + { env_.require(mptflags(*this, flags, arg.holder)); - // issuer authorizes the holder + // issuer authorizes the holder + } else + { env_.require(mptflags(*this, flags | lsfMPTAuthorized, arg.holder)); + } } // Holder authorizes else if (arg.flags.value_or(0) != tfMPTUnauthorize) @@ -261,20 +287,23 @@ MPTTester::authorize(MPTAuthorize const& arg) forObject([&](SLEP const& sle) { return env_.test.BEAST_EXPECT(!sle); }, arg.account); } } - else if (arg.account && *arg.account != issuer_ && arg.flags.value_or(0) != tfMPTUnauthorize && id_) + else if ( + arg.account && *arg.account != issuer_ && arg.flags.value_or(0) != tfMPTUnauthorize && id_) { if (result == tecDUPLICATE) { // Verify that MPToken already exists - env_.require( - requireAny([&]() -> bool { return env_.le(keylet::mptoken(*id_, arg.account->id())) != nullptr; })); + env_.require(requireAny([&]() -> bool { + return env_.le(keylet::mptoken(*id_, arg.account->id())) != nullptr; + })); } else { // Verify MPToken doesn't exist if holder failed authorizing(unless // it already exists) - env_.require( - requireAny([&]() -> bool { return env_.le(keylet::mptoken(*id_, arg.account->id())) == nullptr; })); + env_.require(requireAny([&]() -> bool { + return env_.le(keylet::mptoken(*id_, arg.account->id())) == nullptr; + })); } } } @@ -301,9 +330,13 @@ MPTTester::setJV(MPTSet const& arg) std::visit( [&jv](T const& holder) { if constexpr (std::is_same_v) + { jv[sfHolder] = holder.human(); + } else if constexpr (std::is_same_v) + { jv[sfHolder] = toBase58(holder); + } }, *arg.holder); } @@ -346,42 +379,70 @@ MPTTester::set(MPTSet const& arg) if (arg.flags) { if (*arg.flags & tfMPTLock) + { flags |= lsfMPTLocked; + } else if (*arg.flags & tfMPTUnlock) + { flags &= ~lsfMPTLocked; + } } if (arg.mutableFlags) { if (*arg.mutableFlags & tmfMPTSetCanLock) + { flags |= lsfMPTCanLock; + } else if (*arg.mutableFlags & tmfMPTClearCanLock) + { flags &= ~lsfMPTCanLock; + } if (*arg.mutableFlags & tmfMPTSetRequireAuth) + { flags |= lsfMPTRequireAuth; + } else if (*arg.mutableFlags & tmfMPTClearRequireAuth) + { flags &= ~lsfMPTRequireAuth; + } if (*arg.mutableFlags & tmfMPTSetCanEscrow) + { flags |= lsfMPTCanEscrow; + } else if (*arg.mutableFlags & tmfMPTClearCanEscrow) + { flags &= ~lsfMPTCanEscrow; + } if (*arg.mutableFlags & tmfMPTSetCanClawback) + { flags |= lsfMPTCanClawback; + } else if (*arg.mutableFlags & tmfMPTClearCanClawback) + { flags &= ~lsfMPTCanClawback; + } if (*arg.mutableFlags & tmfMPTSetCanTrade) + { flags |= lsfMPTCanTrade; + } else if (*arg.mutableFlags & tmfMPTClearCanTrade) + { flags &= ~lsfMPTCanTrade; + } if (*arg.mutableFlags & tmfMPTSetCanTransfer) + { flags |= lsfMPTCanTransfer; + } else if (*arg.mutableFlags & tmfMPTClearCanTransfer) + { flags &= ~lsfMPTCanTransfer; + } } } env_.require(mptflags(*this, flags, holder)); @@ -394,7 +455,9 @@ MPTTester::set(MPTSet const& arg) } bool -MPTTester::forObject(std::function const& cb, std::optional const& holder_) const +MPTTester::forObject( + std::function const& cb, + std::optional const& holder_) const { if (!id_) Throw("MPT has not been created"); @@ -417,13 +480,15 @@ MPTTester::checkDomainID(std::optional expected) const [[nodiscard]] bool MPTTester::checkMPTokenAmount(Account const& holder_, std::int64_t expectedAmount) const { - return forObject([&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; }, holder_); + return forObject( + [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; }, holder_); } [[nodiscard]] bool MPTTester::checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const { - return forObject([&](SLEP const& sle) { return expectedAmount == (*sle)[sfOutstandingAmount]; }); + return forObject( + [&](SLEP const& sle) { return expectedAmount == (*sle)[sfOutstandingAmount]; }); } [[nodiscard]] bool @@ -445,7 +510,8 @@ MPTTester::checkMetadata(std::string const& metadata) const [[nodiscard]] bool MPTTester::isMetadataPresent() const { - return forObject([&](SLEP const& sle) -> bool { return sle->isFieldPresent(sfMPTokenMetadata); }); + return forObject( + [&](SLEP const& sle) -> bool { return sle->isFieldPresent(sfMPTokenMetadata); }); } [[nodiscard]] bool @@ -479,11 +545,18 @@ MPTTester::pay( auto const outstandingAmt = getBalance(issuer_); if (credentials) - env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)), credentials::ids(*credentials)); + { + env_( + jtx::pay(src, dest, mpt(amount)), + ter(err.value_or(tesSUCCESS)), + credentials::ids(*credentials)); + } else + { env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS))); + } - if (env_.ter() != tesSUCCESS) + if (!isTesSuccess(env_.ter())) amount = 0; if (close_) env_.close(); @@ -510,14 +583,18 @@ MPTTester::pay( } void -MPTTester::claw(Account const& issuer, Account const& holder, std::int64_t amount, std::optional err) +MPTTester::claw( + Account const& issuer, + Account const& holder, + std::int64_t amount, + std::optional err) { if (!id_) Throw("MPT has not been created"); auto const issuerAmt = getBalance(issuer); auto const holderAmt = getBalance(holder); env_(jtx::claw(issuer, mpt(amount), holder), ter(err.value_or(tesSUCCESS))); - if (env_.ter() != tesSUCCESS) + if (!isTesSuccess(env_.ter())) amount = 0; if (close_) env_.close(); @@ -534,7 +611,8 @@ MPTTester::mpt(std::int64_t amount) const return xrpl::test::jtx::MPT(issuer_.name(), *id_)(amount); } -MPTTester::operator Asset() const +MPTTester:: +operator Asset() const { if (!id_) Throw("MPT has not been created"); diff --git a/src/test/jtx/impl/multisign.cpp b/src/test/jtx/impl/multisign.cpp index 54bde3ebb9..ae39382824 100644 --- a/src/test/jtx/impl/multisign.cpp +++ b/src/test/jtx/impl/multisign.cpp @@ -53,9 +53,13 @@ msig::operator()(Env& env, JTx& jt) const // The signing pub key is only required at the top level. if (!subField) + { sigObject[sfSigningPubKey] = ""; + } else if (sigObject.isNull()) + { sigObject = Json::Value(Json::objectValue); + } std::optional st; try { @@ -80,9 +84,13 @@ msig::operator()(Env& env, JTx& jt) const } }; if (!subField) + { jt.mainSigners.emplace_back(callback); + } else + { jt.postSigners.emplace_back(callback); + } } } // namespace jtx diff --git a/src/test/jtx/impl/offer.cpp b/src/test/jtx/impl/offer.cpp index bbcfbf07c1..251b659f3b 100644 --- a/src/test/jtx/impl/offer.cpp +++ b/src/test/jtx/impl/offer.cpp @@ -7,7 +7,11 @@ namespace test { namespace jtx { Json::Value -offer(Account const& account, STAmount const& takerPays, STAmount const& takerGets, std::uint32_t flags) +offer( + Account const& account, + STAmount const& takerPays, + STAmount const& takerGets, + std::uint32_t flags) { Json::Value jv; jv[jss::Account] = account.human(); diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index ee6efc171c..855c5b04ff 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -1,5 +1,7 @@ #include +#include + namespace xrpl { namespace detail { @@ -15,7 +17,11 @@ owned_count_of(ReadView const& view, AccountID const& id, LedgerEntryType type) } void -owned_count_helper(test::jtx::Env& env, AccountID const& id, LedgerEntryType type, std::uint32_t value) +owned_count_helper( + test::jtx::Env& env, + AccountID const& id, + LedgerEntryType type, + std::uint32_t value) { env.test.expect(owned_count_of(*env.current(), id, type) == value); } diff --git a/src/test/jtx/impl/permissioned_domains.cpp b/src/test/jtx/impl/permissioned_domains.cpp index fb802cf084..60d653e956 100644 --- a/src/test/jtx/impl/permissioned_domains.cpp +++ b/src/test/jtx/impl/permissioned_domains.cpp @@ -59,7 +59,8 @@ getObjects(Account const& account, Env& env, bool withType) if (withType) { // impossible to get there Throw( - "Invalid object type: " + object["LedgerEntryType"].asString()); // LCOV_EXCL_LINE + "Invalid object type: " + + object["LedgerEntryType"].asString()); // LCOV_EXCL_LINE } continue; } @@ -95,7 +96,9 @@ objectExists(uint256 const& objID, Env& env) // Extract credentials from account_object object Credentials -credentialsFromJson(Json::Value const& object, std::unordered_map const& human2Acc) +credentialsFromJson( + Json::Value const& object, + std::unordered_map const& human2Acc) { Credentials ret; Json::Value credentials(Json::arrayValue); @@ -106,6 +109,7 @@ credentialsFromJson(Json::Value const& object, std::unordered_map const& meta) for (auto const& node : a) { - if (!node.isMember("CreatedNode") || node["CreatedNode"]["LedgerEntryType"] != "PermissionedDomain") + if (!node.isMember("CreatedNode") || + node["CreatedNode"]["LedgerEntryType"] != "PermissionedDomain") { continue; } diff --git a/src/test/jtx/impl/quality2.cpp b/src/test/jtx/impl/quality2.cpp index bbd0cff319..c202592b9d 100644 --- a/src/test/jtx/impl/quality2.cpp +++ b/src/test/jtx/impl/quality2.cpp @@ -7,12 +7,14 @@ namespace xrpl { namespace test { namespace jtx { -qualityInPercent::qualityInPercent(double percent) : qIn_(static_cast((percent / 100) * QUALITY_ONE)) +qualityInPercent::qualityInPercent(double percent) + : qIn_(static_cast((percent / 100) * QUALITY_ONE)) { assert(percent <= 400 && percent >= 0); } -qualityOutPercent::qualityOutPercent(double percent) : qOut_(static_cast((percent / 100) * QUALITY_ONE)) +qualityOutPercent::qualityOutPercent(double percent) + : qOut_(static_cast((percent / 100) * QUALITY_ONE)) { assert(percent <= 400 && percent >= 0); } diff --git a/src/test/jtx/impl/sig.cpp b/src/test/jtx/impl/sig.cpp index 3ea7f669a7..850c5af781 100644 --- a/src/test/jtx/impl/sig.cpp +++ b/src/test/jtx/impl/sig.cpp @@ -23,9 +23,13 @@ sig::operator()(Env&, JTx& jt) const jtx::sign(jtx.jv, account, sigObject); }; if (!subField_) + { jt.mainSigners.emplace_back(callback); + } else + { jt.postSigners.emplace_back(callback); + } } } diff --git a/src/test/jtx/impl/testline.cpp b/src/test/jtx/impl/testline.cpp deleted file mode 100644 index fbcb1d2de7..0000000000 --- a/src/test/jtx/impl/testline.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -namespace xrpl { -namespace test { -namespace jtx { - -void -testline::operator()(Env&, JTx& jt) const -{ - jt.testLine = line_; -} - -} // namespace jtx -} // namespace test -} // namespace xrpl diff --git a/src/test/jtx/impl/token.cpp b/src/test/jtx/impl/token.cpp index 172bd75bc7..9db79361eb 100644 --- a/src/test/jtx/impl/token.cpp +++ b/src/test/jtx/impl/token.cpp @@ -1,10 +1,9 @@ #include #include -#include - #include #include +#include namespace xrpl { namespace test { @@ -70,7 +69,8 @@ getID( // We must add issuer's FirstNFTokenSequence to offset the starting NFT // sequence number. nftSeq += env.le(issuer)->at(~sfFirstNFTokenSequence).value_or(env.seq(issuer)); - return xrpl::NFTokenMint::createNFTokenID(flags, xferFee, issuer, nft::toTaxon(nfTokenTaxon), nftSeq); + return xrpl::NFTokenMint::createNFTokenID( + flags, xferFee, issuer, nft::toTaxon(nfTokenTaxon), nftSeq); } Json::Value @@ -167,7 +167,10 @@ acceptSellOffer(jtx::Account const& account, uint256 const& offerIndex) } Json::Value -brokerOffers(jtx::Account const& account, uint256 const& buyOfferIndex, uint256 const& sellOfferIndex) +brokerOffers( + jtx::Account const& account, + uint256 const& buyOfferIndex, + uint256 const& sellOfferIndex) { Json::Value jv; jv[sfAccount.jsonName] = account.human(); diff --git a/src/test/jtx/impl/vault.cpp b/src/test/jtx/impl/vault.cpp index 90250aece0..49c0dddaec 100644 --- a/src/test/jtx/impl/vault.cpp +++ b/src/test/jtx/impl/vault.cpp @@ -12,7 +12,7 @@ namespace test { namespace jtx { std::tuple -Vault::create(CreateArgs const& args) +Vault::create(CreateArgs const& args) const { auto keylet = keylet::vault(args.owner.id(), env.seq(args.owner)); Json::Value jv; diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index 0f78ed3029..c2617f5073 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -183,7 +183,15 @@ claim_attestation( auto const& pk = signer.account.pk(); auto const& sk = signer.account.sk(); auto const sig = sign_claim_attestation( - pk, sk, stBridge, sendingAccount, sendingAmount.value, rewardAccount, wasLockingChainSend, claimID, dst); + pk, + sk, + stBridge, + sendingAccount, + sendingAmount.value, + rewardAccount, + wasLockingChainSend, + claimID, + dst); Json::Value result; @@ -249,7 +257,8 @@ create_account_attestation( result[sfAttestationRewardAccount.getJsonName()] = toBase58(rewardAccount); result[sfWasLockingChainSend.getJsonName()] = wasLockingChainSend ? 1 : 0; - result[sfXChainAccountCreateCount.getJsonName()] = STUInt64{createCount}.getJson(JsonOptions::none); + result[sfXChainAccountCreateCount.getJsonName()] = + STUInt64{createCount}.getJson(JsonOptions::none); result[sfDestination.getJsonName()] = toBase58(dst); result[sfSignatureReward.getJsonName()] = rewardAmount.value.getJson(JsonOptions::none); @@ -277,6 +286,7 @@ claim_attestations( JValueVec vec; vec.reserve(numAtts); for (auto i = fromIdx; i < fromIdx + numAtts; ++i) + { vec.emplace_back(claim_attestation( submittingAccount, jvBridge, @@ -287,6 +297,7 @@ claim_attestations( claimID, dst, signers[i])); + } return vec; } @@ -310,6 +321,7 @@ create_account_attestations( JValueVec vec; vec.reserve(numAtts); for (auto i = fromIdx; i < fromIdx + numAtts; ++i) + { vec.emplace_back(create_account_attestation( submittingAccount, jvBridge, @@ -321,6 +333,7 @@ create_account_attestations( createCount, dst, signers[i])); + } return vec; } @@ -360,7 +373,8 @@ XChainBridgeObjects::XChainBridgeObjects() for (int i = 0; i < numSigners; ++i) { using namespace std::literals; - auto const a = Account("signer_"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + auto const a = Account( + "signer_"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); result.emplace_back(a); } return result; @@ -372,7 +386,9 @@ XChainBridgeObjects::XChainBridgeObjects() for (int i = 0; i < numSigners; ++i) { using namespace std::literals; - auto const a = Account("alt_signer_"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); + auto const a = Account( + "alt_signer_"s + std::to_string(i), + (i % 2) ? KeyType::ed25519 : KeyType::secp256k1); result.emplace_back(a); } return result; @@ -402,9 +418,11 @@ XChainBridgeObjects::XChainBridgeObjects() , split_reward_quorum(divide(reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), reward.issue())) , split_reward_everyone(divide(reward, STAmount(UT_XCHAIN_DEFAULT_NUM_SIGNERS), reward.issue())) , tiny_reward(drops(37)) - , tiny_reward_split((divide(tiny_reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue()))) + , tiny_reward_split( + (divide(tiny_reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue()))) , tiny_reward_remainder( - tiny_reward - multiply(tiny_reward_split, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue())) + tiny_reward - + multiply(tiny_reward_split, STAmount(UT_XCHAIN_DEFAULT_QUORUM), tiny_reward.issue())) , one_xrp(XRP(1)) , xrp_dust(divide(one_xrp, STAmount(10000), one_xrp.issue())) { diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 510248240d..3368d129c1 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -24,7 +24,10 @@ private: std::optional holder_; public: - mptflags(MPTTester& tester, std::uint32_t flags, std::optional const& holder = std::nullopt) + mptflags( + MPTTester& tester, + std::uint32_t flags, + std::optional const& holder = std::nullopt) : tester_(tester), flags_(flags), holder_(holder) { } @@ -210,7 +213,8 @@ public: checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const; [[nodiscard]] bool - checkFlags(uint32_t const expectedFlags, std::optional const& holder = std::nullopt) const; + checkFlags(uint32_t const expectedFlags, std::optional const& holder = std::nullopt) + const; [[nodiscard]] bool checkMetadata(std::string const& metadata) const; @@ -240,7 +244,11 @@ public: std::optional> credentials = std::nullopt); void - claw(Account const& issuer, Account const& holder, std::int64_t amount, std::optional err = std::nullopt); + claw( + Account const& issuer, + Account const& holder, + std::int64_t amount, + std::optional err = std::nullopt); PrettyAmount mpt(std::int64_t amount) const; @@ -267,8 +275,9 @@ public: private: using SLEP = SLE::const_pointer; bool - forObject(std::function const& cb, std::optional const& holder = std::nullopt) - const; + forObject( + std::function const& cb, + std::optional const& holder = std::nullopt) const; template TER diff --git a/src/test/jtx/multisign.h b/src/test/jtx/multisign.h index 3bbf43bb05..56d0b10906 100644 --- a/src/test/jtx/multisign.h +++ b/src/test/jtx/multisign.h @@ -50,7 +50,8 @@ public: /// a subfield. static constexpr SField* const topLevel = nullptr; - msig(SField const* subField_, std::vector signers_) : signers(std::move(signers_)), subField(subField_) + msig(SField const* subField_, std::vector signers_) + : signers(std::move(signers_)), subField(subField_) { sortSigners(signers); } @@ -66,21 +67,27 @@ public: template requires std::convertible_to explicit msig(SField const* subField_, AccountType&& a0, Accounts&&... aN) - : msig{subField_, std::vector{std::forward(a0), std::forward(aN)...}} + : msig{ + subField_, + std::vector{std::forward(a0), std::forward(aN)...}} { } template requires std::convertible_to explicit msig(SField const& subField_, AccountType&& a0, Accounts&&... aN) - : msig{&subField_, std::vector{std::forward(a0), std::forward(aN)...}} + : msig{ + &subField_, + std::vector{std::forward(a0), std::forward(aN)...}} { } template requires(std::convertible_to && !std::is_same_v) explicit msig(AccountType&& a0, Accounts&&... aN) - : msig{topLevel, std::vector{std::forward(a0), std::forward(aN)...}} + : msig{ + topLevel, + std::vector{std::forward(a0), std::forward(aN)...}} { } diff --git a/src/test/jtx/offer.h b/src/test/jtx/offer.h index dab15a4c8c..0fad9ec6dc 100644 --- a/src/test/jtx/offer.h +++ b/src/test/jtx/offer.h @@ -11,7 +11,11 @@ namespace jtx { /** Create an offer. */ Json::Value -offer(Account const& account, STAmount const& takerPays, STAmount const& takerGets, std::uint32_t flags = 0); +offer( + Account const& account, + STAmount const& takerPays, + STAmount const& takerGets, + std::uint32_t flags = 0); /** Cancel an offer. */ Json::Value diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 87fa26417a..3f54e0c55f 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -2,7 +2,7 @@ #include -#include +#include #include #include @@ -16,7 +16,11 @@ std::uint32_t owned_count_of(ReadView const& view, AccountID const& id, LedgerEntryType type); void -owned_count_helper(test::jtx::Env& env, AccountID const& id, LedgerEntryType type, std::uint32_t value); +owned_count_helper( + test::jtx::Env& env, + AccountID const& id, + LedgerEntryType type, + std::uint32_t value); } // namespace detail diff --git a/src/test/jtx/paths.h b/src/test/jtx/paths.h index c4e69fe3f3..6522364e14 100644 --- a/src/test/jtx/paths.h +++ b/src/test/jtx/paths.h @@ -19,7 +19,8 @@ private: unsigned int limit_; public: - paths(Issue const& in, int depth = 7, unsigned int limit = 4) : in_(in), depth_(depth), limit_(limit) + paths(Issue const& in, int depth = 7, unsigned int limit = 4) + : in_(in), depth_(depth), limit_(limit) { } diff --git a/src/test/jtx/permissioned_domains.h b/src/test/jtx/permissioned_domains.h index ff8ab129ae..bf67722c9c 100644 --- a/src/test/jtx/permissioned_domains.h +++ b/src/test/jtx/permissioned_domains.h @@ -16,7 +16,10 @@ using Credentials = std::vector; // helpers // Make json for PermissionedDomainSet transaction Json::Value -setTx(AccountID const& account, Credentials const& credentials, std::optional domain = std::nullopt); +setTx( + AccountID const& account, + Credentials const& credentials, + std::optional domain = std::nullopt); // Make json for PermissionedDomainDelete transaction Json::Value @@ -32,7 +35,9 @@ objectExists(uint256 const& objID, Env& env); // Extract credentials from account_object object Credentials -credentialsFromJson(Json::Value const& object, std::unordered_map const& human2Acc); +credentialsFromJson( + Json::Value const& object, + std::unordered_map const& human2Acc); // Sort credentials the same way as PermissionedDomainSet Credentials diff --git a/src/test/jtx/rpc.h b/src/test/jtx/rpc.h index 6946cbc4e1..bbdecf1519 100644 --- a/src/test/jtx/rpc.h +++ b/src/test/jtx/rpc.h @@ -21,7 +21,8 @@ private: public: /// If there's an error code, we expect an error message - explicit rpc(error_code_i code, std::optional m = {}) : code_(code), errorMessage_(m) + explicit rpc(error_code_i code, std::optional m = {}) + : code_(code), errorMessage_(m) { } diff --git a/src/test/jtx/sig.h b/src/test/jtx/sig.h index ab22a46c69..0ed59de50e 100644 --- a/src/test/jtx/sig.h +++ b/src/test/jtx/sig.h @@ -39,7 +39,8 @@ public: { } - explicit sig(SField const* subField, Account const& account) : subField_(subField), account_(account) + explicit sig(SField const* subField, Account const& account) + : subField_(subField), account_(account) { } diff --git a/src/test/jtx/testline.h b/src/test/jtx/testline.h deleted file mode 100644 index e2be1f0276..0000000000 --- a/src/test/jtx/testline.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace xrpl { -namespace test { -namespace jtx { - -/** Store the line number of the current test in a JTx. - - Intended to help debug failing transaction submission tests. -*/ -class testline -{ -private: - int line_; - -public: - explicit testline(int line) : line_(line) - { - } - - void - operator()(Env&, JTx& jt) const; -}; - -#define THISLINE testline(__LINE__) - -} // namespace jtx -} // namespace test -} // namespace xrpl diff --git a/src/test/jtx/token.h b/src/test/jtx/token.h index f6d562587a..3f4a3f75a3 100644 --- a/src/test/jtx/token.h +++ b/src/test/jtx/token.h @@ -182,7 +182,10 @@ acceptSellOffer(jtx::Account const& account, uint256 const& offerIndex); /** Broker two NFToken offers. */ Json::Value -brokerOffers(jtx::Account const& account, uint256 const& buyOfferIndex, uint256 const& sellOfferIndex); +brokerOffers( + jtx::Account const& account, + uint256 const& buyOfferIndex, + uint256 const& sellOfferIndex); /** Sets the optional NFTokenBrokerFee field in a brokerOffer transaction. */ class brokerFee diff --git a/src/test/jtx/trust.h b/src/test/jtx/trust.h index 462538854b..afea256382 100644 --- a/src/test/jtx/trust.h +++ b/src/test/jtx/trust.h @@ -18,7 +18,10 @@ Json::Value trust(Account const& account, STAmount const& amount, Account const& peer, std::uint32_t flags); Json::Value -claw(Account const& account, STAmount const& amount, std::optional const& mptHolder = std::nullopt); +claw( + Account const& account, + STAmount const& amount, + std::optional const& mptHolder = std::nullopt); } // namespace jtx } // namespace test diff --git a/src/test/jtx/vault.h b/src/test/jtx/vault.h index 65a5706354..43f975323a 100644 --- a/src/test/jtx/vault.h +++ b/src/test/jtx/vault.h @@ -30,7 +30,7 @@ struct Vault /** Return a VaultCreate transaction and the Vault's expected keylet. */ std::tuple - create(CreateArgs const& args); + create(CreateArgs const& args) const; struct SetArgs { diff --git a/src/test/jtx/xchain_bridge.h b/src/test/jtx/xchain_bridge.h index cdacede234..698426c59c 100644 --- a/src/test/jtx/xchain_bridge.h +++ b/src/test/jtx/xchain_bridge.h @@ -70,7 +70,11 @@ sidechain_xchain_account_create( AnyAmount const& xChainFee); Json::Value -sidechain_xchain_account_claim(Account const& acc, Json::Value const& bridge, Account const& dst, AnyAmount const& amt); +sidechain_xchain_account_claim( + Account const& acc, + Json::Value const& bridge, + Account const& dst, + AnyAmount const& amt); Json::Value claim_attestation( @@ -201,7 +205,18 @@ struct XChainBridgeObjects std::size_t const fromIdx = 0) { return create_account_attestations( - scAttester, jvb, mcCarol, amt, reward, payees, true, createCount, dst, signers, numAtts, fromIdx); + scAttester, + jvb, + mcCarol, + amt, + reward, + payees, + true, + createCount, + dst, + signers, + numAtts, + fromIdx); } Json::Value @@ -211,7 +226,8 @@ struct XChainBridgeObjects STAmount const& _reward = XRP(1), std::optional const& minAccountCreate = std::nullopt) { - return bridge_create(acc, bridge == Json::nullValue ? jvb : bridge, _reward, minAccountCreate); + return bridge_create( + acc, bridge == Json::nullValue ? jvb : bridge, _reward, minAccountCreate); } }; diff --git a/src/test/ledger/BookDirs_test.cpp b/src/test/ledger/BookDirs_test.cpp index 45e585d68d..d7841fe502 100644 --- a/src/test/ledger/BookDirs_test.cpp +++ b/src/test/ledger/BookDirs_test.cpp @@ -33,7 +33,8 @@ struct BookDirs_test : public beast::unit_test::suite { env(offer("alice", Account("alice")["USD"](50), XRP(10))); - auto d = BookDirs(*env.current(), Book(Account("alice")["USD"].issue(), xrpIssue(), std::nullopt)); + auto d = BookDirs( + *env.current(), Book(Account("alice")["USD"].issue(), xrpIssue(), std::nullopt)); BEAST_EXPECT(std::distance(d.begin(), d.end()) == 1); } @@ -47,15 +48,18 @@ struct BookDirs_test : public beast::unit_test::suite env.trust(Account("bob")["CNY"](10), "alice"); env(pay("bob", "alice", Account("bob")["CNY"](10))); env(offer("alice", USD(50), Account("bob")["CNY"](10))); - auto d = BookDirs(*env.current(), Book(USD.issue(), Account("bob")["CNY"].issue(), std::nullopt)); + auto d = BookDirs( + *env.current(), Book(USD.issue(), Account("bob")["CNY"].issue(), std::nullopt)); BEAST_EXPECT(std::distance(d.begin(), d.end()) == 1); } { auto AUD = gw["AUD"]; for (auto i = 1, j = 3; i <= 3; ++i, --j) + { for (auto k = 0; k < 80; ++k) env(offer("alice", AUD(i), XRP(j))); + } auto d = BookDirs(*env.current(), Book(AUD.issue(), xrpIssue(), std::nullopt)); BEAST_EXPECT(std::distance(d.begin(), d.end()) == 240); diff --git a/src/test/ledger/Directory_test.cpp b/src/test/ledger/Directory_test.cpp index 0774390490..18649a20a8 100644 --- a/src/test/ledger/Directory_test.cpp +++ b/src/test/ledger/Directory_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -46,14 +47,22 @@ struct Directory_test : public beast::unit_test::suite p->setFieldV256(sfIndexes, STVector256{}); if (i + 1 == n) + { p->setFieldU64(sfIndexNext, 0); + } else + { p->setFieldU64(sfIndexNext, i + 1); + } if (i == 0) + { p->setFieldU64(sfIndexPrevious, n - 1); + } else + { p->setFieldU64(sfIndexPrevious, i - 1); + } sb.insert(p); } @@ -254,8 +263,10 @@ struct Directory_test : public beast::unit_test::suite // Fill up three pages of offers for (int i = 0; i < 3; ++i) + { for (int j = 0; j < dirNodeMaxEntries; ++j) env(offer(alice, XRP(1), USD(1))); + } env.close(); // remove all the offers. Remove the middle page last @@ -449,7 +460,9 @@ struct Directory_test : public beast::unit_test::suite // All of the other directories, including the order // book, did get touched, so they should have those // fields - BEAST_EXPECT(directory.isMember("PreviousTxnID") && directory["PreviousTxnID"].asString() == txID); + BEAST_EXPECT( + directory.isMember("PreviousTxnID") && + directory["PreviousTxnID"].asString() == txID); BEAST_EXPECT( directory.isMember("PreviousTxnLgrSeq") && directory["PreviousTxnLgrSeq"].asUInt() == ledgerSeq); @@ -521,10 +534,12 @@ struct Directory_test : public beast::unit_test::suite env.close(); }; - testCase(testable_amendments() - fixDirectoryLimit, [this](Env&) -> std::tuple { - testcase("directory full without fixDirectoryLimit"); - return {dirNodeMaxPages - 1, true}; - }); + testCase( + testable_amendments() - fixDirectoryLimit, + [this](Env&) -> std::tuple { + testcase("directory full without fixDirectoryLimit"); + return {dirNodeMaxPages - 1, true}; + }); testCase( testable_amendments(), // [this](Env&) -> std::tuple { diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index cd5b1d1c19..a5023aa328 100644 --- a/src/test/ledger/PaymentSandbox_test.cpp +++ b/src/test/ledger/PaymentSandbox_test.cpp @@ -2,7 +2,8 @@ #include #include -#include +#include +#include #include #include @@ -66,7 +67,9 @@ class PaymentSandbox_test : public beast::unit_test::suite PathSet paths(Path(gw1, USD_gw2, gw2), Path(gw2, USD_gw1, gw1)); - env(pay(snd, rcv, any(USD_gw1(4))), json(paths.json()), txflags(tfNoRippleDirect | tfPartialPayment)); + env(pay(snd, rcv, any(USD_gw1(4))), + json(paths.json()), + txflags(tfNoRippleDirect | tfPartialPayment)); env.require(balance("rcv", USD_gw1(0))); env.require(balance("rcv", USD_gw2(2))); @@ -103,16 +106,18 @@ class PaymentSandbox_test : public beast::unit_test::suite ApplyViewImpl av(&*env.current(), tapNONE); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); { auto r = accountSend(av, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( - accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit); + accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount + toCredit); { auto r = accountSend(av, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == @@ -124,11 +129,13 @@ class PaymentSandbox_test : public beast::unit_test::suite ApplyViewImpl av(&*env.current(), tapNONE); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); rippleCredit(av, gw1, alice, toCredit, true, j); BEAST_EXPECT( - accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit); + accountHolds(av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount + toCredit); rippleCredit(av, alice, gw1, toDebit, true, j); BEAST_EXPECT( @@ -142,20 +149,24 @@ class PaymentSandbox_test : public beast::unit_test::suite PaymentSandbox pv(&av); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); { auto r = accountSend(pv, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } - BEAST_EXPECT(accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); { auto r = accountSend(pv, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( - accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount - toDebit); + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount - toDebit); } { @@ -164,10 +175,13 @@ class PaymentSandbox_test : public beast::unit_test::suite PaymentSandbox pv(&av); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); rippleCredit(pv, gw1, alice, toCredit, true, j); - BEAST_EXPECT(accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); } { @@ -176,11 +190,13 @@ class PaymentSandbox_test : public beast::unit_test::suite PaymentSandbox pv(&av); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); BEAST_EXPECT(redeemIOU(pv, alice, toDebit, iss, j) == tesSUCCESS); BEAST_EXPECT( - accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount - toDebit); + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount - toDebit); } { @@ -189,10 +205,13 @@ class PaymentSandbox_test : public beast::unit_test::suite PaymentSandbox pv(&av); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); BEAST_EXPECT(issueIOU(pv, alice, toCredit, iss, j) == tesSUCCESS); - BEAST_EXPECT(accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); } { @@ -201,30 +220,38 @@ class PaymentSandbox_test : public beast::unit_test::suite PaymentSandbox pv(&av); auto const iss = USD_gw1.issue(); - auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); + auto const startingAmount = + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); { auto r = accountSend(pv, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } - BEAST_EXPECT(accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); { PaymentSandbox pv2(&pv); - BEAST_EXPECT(accountHolds(pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); { auto r = accountSend(pv2, gw1, alice, toCredit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } - BEAST_EXPECT(accountHolds(pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); + BEAST_EXPECT( + accountHolds(pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount); } { auto r = accountSend(pv, alice, gw1, toDebit, j); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT( - accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount - toDebit); + accountHolds(pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == + startingAmount - toDebit); } } @@ -246,8 +273,10 @@ class PaymentSandbox_test : public beast::unit_test::suite auto const USD = gw["USD"]; auto const issue = USD.issue(); - STAmount tinyAmt(issue, STAmount::cMinValue, STAmount::cMinOffset + 1, false, STAmount::unchecked{}); - STAmount hugeAmt(issue, STAmount::cMaxValue, STAmount::cMaxOffset - 1, false, STAmount::unchecked{}); + STAmount tinyAmt( + issue, STAmount::cMinValue, STAmount::cMinOffset + 1, false, STAmount::unchecked{}); + STAmount hugeAmt( + issue, STAmount::cMaxValue, STAmount::cMaxOffset - 1, false, STAmount::unchecked{}); ApplyViewImpl av(&*env.current(), tapNONE); PaymentSandbox pv(&av); @@ -261,8 +290,10 @@ class PaymentSandbox_test : public beast::unit_test::suite testcase("Reserve"); using namespace jtx; - auto accountFundsXRP = [](ReadView const& view, AccountID const& id, beast::Journal j) -> XRPAmount { - return toAmount(accountHolds(view, id, xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, j)); + auto accountFundsXRP = + [](ReadView const& view, AccountID const& id, beast::Journal j) -> XRPAmount { + return toAmount( + accountHolds(view, id, xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, j)); }; auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount { @@ -284,11 +315,11 @@ class PaymentSandbox_test : public beast::unit_test::suite { auto r = accountSend(sb, xrpAccount(), alice, XRP(100), env.journal); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } { auto r = accountSend(sb, alice, xrpAccount(), XRP(100), env.journal); - BEAST_EXPECT(r == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(r)); } BEAST_EXPECT(accountFundsXRP(sb, alice, env.journal) == beast::zero); } diff --git a/src/test/ledger/SkipList_test.cpp b/src/test/ledger/SkipList_test.cpp index 2f4a231c14..8430db3904 100644 --- a/src/test/ledger/SkipList_test.cpp +++ b/src/test/ledger/SkipList_test.cpp @@ -17,8 +17,8 @@ class SkipList_test : public beast::unit_test::suite std::vector> history; { Config config; - auto prev = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + auto prev = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); history.push_back(prev); for (auto i = 0; i < 1023; ++i) { @@ -43,7 +43,8 @@ class SkipList_test : public beast::unit_test::suite { for (auto n = i; n != std::next(i, (*i)->header().seq - 256 > 1 ? 257 : 256); ++n) { - BEAST_EXPECT(hashOfSeq(**i, (*n)->header().seq, env.journal) == (*n)->header().hash); + BEAST_EXPECT( + hashOfSeq(**i, (*n)->header().seq, env.journal) == (*n)->header().hash); } // edge case accessing beyond 256 @@ -55,7 +56,8 @@ class SkipList_test : public beast::unit_test::suite { for (auto n = std::next(i, 512); n != history.crend(); n += 256) { - BEAST_EXPECT(hashOfSeq(**i, (*n)->header().seq, env.journal) == (*n)->header().hash); + BEAST_EXPECT( + hashOfSeq(**i, (*n)->header().seq, env.journal) == (*n)->header().hash); } } } diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index 33813e135e..f57cb609cc 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -88,7 +90,7 @@ class View_test : public beast::unit_test::suite auto const next = v.succ(k(id).key); if (answer) { - if (BEAST_EXPECT(next)) + if (BEAST_EXPECT(next); next.has_value()) BEAST_EXPECT(*next == k(*answer).key); } else @@ -113,8 +115,8 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); Config config; - std::shared_ptr const genesis = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + std::shared_ptr const genesis = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); wipe(*ledger); ReadView& v = *ledger; @@ -376,8 +378,8 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); Config config; - std::shared_ptr const genesis = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + std::shared_ptr const genesis = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); auto setup = [&ledger](std::vector const& vec) { @@ -580,8 +582,8 @@ class View_test : public beast::unit_test::suite using namespace jtx; Env env(*this); Config config; - std::shared_ptr const genesis = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); + std::shared_ptr const genesis = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); auto setup123 = [&ledger, this]() { // erase middle element @@ -732,7 +734,10 @@ class View_test : public beast::unit_test::suite env.close(); // Alice's USD balance should be zero if frozen. - BEAST_EXPECT(USD(0) == accountHolds(*env.closed(), alice, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); + BEAST_EXPECT( + USD(0) == + accountHolds( + *env.closed(), alice, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); // Thaw gw and try again. env(fclear(gw, asfGlobalFreeze)); @@ -749,12 +754,16 @@ class View_test : public beast::unit_test::suite env.close(); // Bob's balance should be zero if frozen. - BEAST_EXPECT(USD(0) == accountHolds(*env.closed(), bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); + BEAST_EXPECT( + USD(0) == + accountHolds(*env.closed(), bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); // gw thaws bob's trust line. bob gets his money back. env(trust(gw, USD(100), bob, tfClearFreeze)); env.close(); - BEAST_EXPECT(USD(50) == accountHolds(*env.closed(), bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); + BEAST_EXPECT( + USD(50) == + accountHolds(*env.closed(), bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); } { // accountHolds(). @@ -762,15 +771,20 @@ class View_test : public beast::unit_test::suite env.close(); // carol has no EUR. - BEAST_EXPECT(EUR(0) == accountHolds(*env.closed(), carol, EUR.currency, gw, fhZERO_IF_FROZEN, env.journal)); + BEAST_EXPECT( + EUR(0) == + accountHolds( + *env.closed(), carol, EUR.currency, gw, fhZERO_IF_FROZEN, env.journal)); // But carol does have USD. BEAST_EXPECT( - USD(50) == accountHolds(*env.closed(), carol, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); + USD(50) == + accountHolds( + *env.closed(), carol, USD.currency, gw, fhZERO_IF_FROZEN, env.journal)); // carol's XRP balance should be her holdings minus her reserve. - auto const carolsXRP = - accountHolds(*env.closed(), carol, xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, env.journal); + auto const carolsXRP = accountHolds( + *env.closed(), carol, xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, env.journal); // carol's XRP balance: 10000 // base reserve: -200 // 1 trust line times its reserve: 1 * -50 @@ -785,16 +799,20 @@ class View_test : public beast::unit_test::suite // carol's XRP balance should now show as zero. BEAST_EXPECT( - XRP(0) == accountHolds(*env.closed(), carol, xrpCurrency(), gw, fhZERO_IF_FROZEN, env.journal)); + XRP(0) == + accountHolds( + *env.closed(), carol, xrpCurrency(), gw, fhZERO_IF_FROZEN, env.journal)); } { // accountFunds(). // Gateways have whatever funds they claim to have. - auto const gwUSD = accountFunds(*env.closed(), gw, USD(314159), fhZERO_IF_FROZEN, env.journal); + auto const gwUSD = + accountFunds(*env.closed(), gw, USD(314159), fhZERO_IF_FROZEN, env.journal); BEAST_EXPECT(gwUSD == USD(314159)); // carol has funds from the gateway. - auto carolsUSD = accountFunds(*env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal); + auto carolsUSD = + accountFunds(*env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal); BEAST_EXPECT(carolsUSD == USD(50)); // If carol's funds are frozen she has no funds... @@ -910,9 +928,10 @@ class View_test : public beast::unit_test::suite { Env env(*this); Config config; - std::shared_ptr const genesis = - std::make_shared(create_genesis, config, std::vector{}, env.app().getNodeFamily()); - auto const ledger = std::make_shared(*genesis, env.app().timeKeeper().closeTime()); + std::shared_ptr const genesis = std::make_shared( + create_genesis, config, std::vector{}, env.app().getNodeFamily()); + auto const ledger = + std::make_shared(*genesis, env.app().timeKeeper().closeTime()); wipe(*ledger); ledger->rawInsert(sle(1)); ReadView& v0 = *ledger; diff --git a/src/test/nodestore/Database_test.cpp b/src/test/nodestore/Database_test.cpp index 03f0c11990..6943f0733e 100644 --- a/src/test/nodestore/Database_test.cpp +++ b/src/test/nodestore/Database_test.cpp @@ -4,11 +4,10 @@ #include #include -#include - #include #include #include +#include namespace xrpl { @@ -509,7 +508,10 @@ public: //-------------------------------------------------------------------------- void - testImport(std::string const& destBackendType, std::string const& srcBackendType, std::int64_t seedValue) + testImport( + std::string const& destBackendType, + std::string const& srcBackendType, + std::int64_t seedValue) { DummyScheduler scheduler; @@ -628,8 +630,8 @@ public: { // Verify default earliest ledger sequence { - std::unique_ptr db = - Manager::instance().make_Database(megabytes(4), scheduler, 2, nodeParams, journal_); + std::unique_ptr db = Manager::instance().make_Database( + megabytes(4), scheduler, 2, nodeParams, journal_); BEAST_EXPECT(db->earliestLedgerSeq() == XRP_LEDGER_EARLIEST_SEQ); } @@ -637,8 +639,8 @@ public: try { nodeParams.set("earliest_seq", "0"); - std::unique_ptr db = - Manager::instance().make_Database(megabytes(4), scheduler, 2, nodeParams, journal_); + std::unique_ptr db = Manager::instance().make_Database( + megabytes(4), scheduler, 2, nodeParams, journal_); } catch (std::runtime_error const& e) { @@ -648,8 +650,8 @@ public: { // Set a valid earliest ledger sequence nodeParams.set("earliest_seq", "1"); - std::unique_ptr db = - Manager::instance().make_Database(megabytes(4), scheduler, 2, nodeParams, journal_); + std::unique_ptr db = Manager::instance().make_Database( + megabytes(4), scheduler, 2, nodeParams, journal_); // Verify database uses the earliest ledger sequence setting BEAST_EXPECT(db->earliestLedgerSeq() == 1); @@ -660,8 +662,8 @@ public: { // Set to default earliest ledger sequence nodeParams.set("earliest_seq", std::to_string(XRP_LEDGER_EARLIEST_SEQ)); - std::unique_ptr db2 = - Manager::instance().make_Database(megabytes(4), scheduler, 2, nodeParams, journal_); + std::unique_ptr db2 = Manager::instance().make_Database( + megabytes(4), scheduler, 2, nodeParams, journal_); } catch (std::runtime_error const& e) { diff --git a/src/test/nodestore/NuDBFactory_test.cpp b/src/test/nodestore/NuDBFactory_test.cpp index 7d8f1df45d..9a9b7d83bd 100644 --- a/src/test/nodestore/NuDBFactory_test.cpp +++ b/src/test/nodestore/NuDBFactory_test.cpp @@ -37,7 +37,8 @@ private: DummyScheduler scheduler; test::SuiteJournal journal("NuDBFactory_test", *this); - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); if (!BEAST_EXPECT(backend)) return false; @@ -69,7 +70,10 @@ private: // Helper function to test log messages void - testLogMessage(Section const& params, beast::severities::Severity level, std::string const& expectedMessage) + testLogMessage( + Section const& params, + beast::severities::Severity level, + std::string const& expectedMessage) { test::StreamSink sink(level); beast::Journal journal(sink); @@ -204,14 +208,17 @@ public: DummyScheduler scheduler; try { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); fail(); } catch (std::exception const& e) { std::string logOutput{e.what()}; BEAST_EXPECT(logOutput.find("Invalid nudb_block_size: 5000") != std::string::npos); - BEAST_EXPECT(logOutput.find("Must be power of 2 between 4096 and 32768") != std::string::npos); + BEAST_EXPECT( + logOutput.find("Must be power of 2 between 4096 and 32768") != + std::string::npos); } } @@ -226,14 +233,16 @@ public: DummyScheduler scheduler; try { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); fail(); } catch (std::exception const& e) { std::string logOutput{e.what()}; - BEAST_EXPECT(logOutput.find("Invalid nudb_block_size value: invalid") != std::string::npos); + BEAST_EXPECT( + logOutput.find("Invalid nudb_block_size value: invalid") != std::string::npos); } } } @@ -269,7 +278,8 @@ public: DummyScheduler scheduler; try { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); BEAST_EXPECT(shouldWork); } catch (std::exception const& e) @@ -293,7 +303,8 @@ public: // Test first constructor (without nudb::context) { - auto backend1 = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend1 = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); BEAST_EXPECT(backend1 != nullptr); BEAST_EXPECT(testBackendFunctionality(params, 16384)); } @@ -333,11 +344,13 @@ public: beast::Journal journal(sink); DummyScheduler scheduler; - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); // Should log success message for valid values std::string logOutput = sink.messages().str(); - bool hasSuccessMessage = logOutput.find("Using custom NuDB block size") != std::string::npos; + bool hasSuccessMessage = + logOutput.find("Using custom NuDB block size") != std::string::npos; BEAST_EXPECT(hasSuccessMessage); } @@ -355,7 +368,8 @@ public: DummyScheduler scheduler; try { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); fail(); } catch (...) @@ -386,7 +400,8 @@ public: // Store data { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); backend->open(); storeBatch(*backend, batch); backend->close(); @@ -394,7 +409,8 @@ public: // Retrieve data in new backend instance { - auto backend = Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); + auto backend = + Manager::instance().make_Backend(params, megabytes(4), scheduler, journal); backend->open(); Batch copy; diff --git a/src/test/nodestore/TestBase.h b/src/test/nodestore/TestBase.h index dfa2b05642..cb2a8e3bd5 100644 --- a/src/test/nodestore/TestBase.h +++ b/src/test/nodestore/TestBase.h @@ -26,7 +26,8 @@ namespace NodeStore { struct LessThan { bool - operator()(std::shared_ptr const& lhs, std::shared_ptr const& rhs) const noexcept + operator()(std::shared_ptr const& lhs, std::shared_ptr const& rhs) + const noexcept { return lhs->getHash() < rhs->getHash(); } @@ -137,7 +138,7 @@ public: { std::shared_ptr object; - Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object); + Status const status = backend.fetch(batch[i]->getHash(), &object); BEAST_EXPECT(status == ok); @@ -157,7 +158,7 @@ public: { std::shared_ptr object; - Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object); + Status const status = backend.fetch(batch[i]->getHash(), &object); BEAST_EXPECT(status == notFound); } diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp index 09e39028e8..d5c89dbcc9 100644 --- a/src/test/nodestore/Timing_test.cpp +++ b/src/test/nodestore/Timing_test.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -100,7 +101,8 @@ public: rngcpy(data + 1, key.size() - 1, gen_); Blob value(d_size_(gen_)); rngcpy(&value[0], value.size(), gen_); - return NodeObject::createObject(safe_cast(d_type_(gen_)), std::move(value), key); + return NodeObject::createObject( + safe_cast(d_type_(gen_)), std::move(value), key); } // returns a batch of NodeObjects starting at n @@ -313,7 +315,7 @@ public: std::shared_ptr obj; std::shared_ptr result; obj = seq1_.obj(dist_(gen_)); - backend_.fetch(obj->getHash().data(), &result); + backend_.fetch(obj->getHash(), &result); suite_.expect(result && isSame(result, obj)); } catch (std::exception const& e) @@ -324,7 +326,12 @@ public: }; try { - parallel_for_id(params.items, params.threads, std::ref(*this), std::ref(params), std::ref(*backend)); + parallel_for_id( + params.items, + params.threads, + std::ref(*this), + std::ref(params), + std::ref(*backend)); } catch (std::exception const&) { @@ -371,9 +378,9 @@ public: { try { - auto const key = seq2_.key(i); + auto const hash = seq2_.key(i); std::shared_ptr result; - backend_.fetch(key.data(), &result); + backend_.fetch(hash, &result); suite_.expect(!result); } catch (std::exception const& e) @@ -385,7 +392,12 @@ public: try { - parallel_for_id(params.items, params.threads, std::ref(*this), std::ref(params), std::ref(*backend)); + parallel_for_id( + params.items, + params.threads, + std::ref(*this), + std::ref(params), + std::ref(*backend)); } catch (std::exception const&) { @@ -438,9 +450,9 @@ public: { if (rand_(gen_) < missingNodePercent) { - auto const key = seq2_.key(dist_(gen_)); + auto const hash = seq2_.key(dist_(gen_)); std::shared_ptr result; - backend_.fetch(key.data(), &result); + backend_.fetch(hash, &result); suite_.expect(!result); } else @@ -448,7 +460,7 @@ public: std::shared_ptr obj; std::shared_ptr result; obj = seq1_.obj(dist_(gen_)); - backend_.fetch(obj->getHash().data(), &result); + backend_.fetch(obj->getHash(), &result); suite_.expect(result && isSame(result, obj)); } } @@ -461,7 +473,12 @@ public: try { - parallel_for_id(params.items, params.threads, std::ref(*this), std::ref(params), std::ref(*backend)); + parallel_for_id( + params.items, + params.threads, + std::ref(*this), + std::ref(params), + std::ref(*backend)); } catch (std::exception const&) { @@ -524,8 +541,7 @@ public: std::shared_ptr result; auto const j = older_(gen_); obj = seq1_.obj(j); - std::shared_ptr result1; - backend_.fetch(obj->getHash().data(), &result); + backend_.fetch(obj->getHash(), &result); suite_.expect(result != nullptr); suite_.expect(isSame(result, obj)); } @@ -535,6 +551,7 @@ public: p[1] = 1 - p[0]; for (int q = 0; q < 2; ++q) { + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (p[q]) { case 0: { @@ -543,7 +560,7 @@ public: std::shared_ptr result; auto const j = recent_(gen_); obj = seq1_.obj(j); - backend_.fetch(obj->getHash().data(), &result); + backend_.fetch(obj->getHash(), &result); suite_.expect(!result || isSame(result, obj)); break; } @@ -566,7 +583,12 @@ public: try { - parallel_for_id(params.items, params.threads, std::ref(*this), std::ref(params), std::ref(*backend)); + parallel_for_id( + params.items, + params.threads, + std::ref(*this), + std::ref(params), + std::ref(*backend)); } catch (std::exception const&) { @@ -592,14 +614,19 @@ public: } void - do_tests(std::size_t threads, test_list const& tests, std::vector const& config_strings) + do_tests( + std::size_t threads, + test_list const& tests, + std::vector const& config_strings) { using std::setw; int w = 8; for (auto const& test : tests) - if (w < test.first.size()) - w = test.first.size(); - log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << default_items << " Objects" << std::endl; + { + w = std::max::size_type>(w, test.first.size()); + } + log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << default_items + << " Objects" << std::endl; { std::stringstream ss; ss << std::left << setw(10) << "Backend" << std::right; @@ -613,7 +640,7 @@ public: for (auto const& config_string : config_strings) { - Params params; + Params params{}; params.items = default_items; params.threads = threads; for (auto i = default_repeat; i--;) @@ -624,7 +651,10 @@ public: std::stringstream ss; ss << std::left << setw(10) << get(config, "type", std::string()) << std::right; for (auto const& test : tests) - ss << " " << setw(w) << to_string(do_test(test.second, config, params, journal)); + { + ss << " " << setw(w) + << to_string(do_test(test.second, config, params, journal)); + } ss << " " << to_string(config); log << ss.str() << std::endl; } @@ -664,10 +694,16 @@ public: std::vector config_strings; boost::split(config_strings, args, boost::algorithm::is_any_of(";")); for (auto iter = config_strings.begin(); iter != config_strings.end();) + { if (iter->empty()) + { iter = config_strings.erase(iter); + } else + { ++iter; + } + } do_tests(1, tests, config_strings); do_tests(4, tests, config_strings); diff --git a/src/test/nodestore/import_test.cpp b/src/test/nodestore/import_test.cpp index 896f9558da..e5f281486b 100644 --- a/src/test/nodestore/import_test.cpp +++ b/src/test/nodestore/import_test.cpp @@ -209,9 +209,11 @@ public: return; } auto const rate = elapsed.count() / double(work); - clock_type::duration const remain(static_cast((work_ - work) * rate)); + clock_type::duration const remain( + static_cast((work_ - work) * rate)); log << "Remaining: " << detail::fmtdur(remain) << " (" << work << " of " << work_ << " in " - << detail::fmtdur(elapsed) << ", " << (work - prev_) << " in " << detail::fmtdur(now - report_) << ")"; + << detail::fmtdur(elapsed) << ", " << (work - prev_) << " in " + << detail::fmtdur(now - report_) << ")"; report_ = now; prev_ = work; } @@ -337,7 +339,7 @@ public: } // Create data file with values std::size_t nitems = 0; - dat_file_header dh; + dat_file_header dh{}; dh.version = currentVersion; dh.uid = make_uid(); dh.appnum = 1; @@ -365,7 +367,10 @@ public: for (it->SeekToFirst(); it->Valid(); it->Next()) { if (it->key().size() != 32) - Throw("Unexpected key size " + std::to_string(it->key().size())); + { + Throw( + "Unexpected key size " + std::to_string(it->key().size())); + } void const* const key = it->key().data(); void const* const data = it->value().data(); auto const size = it->value().size(); @@ -403,7 +408,7 @@ public: if (ec) Throw(ec); // Create key file - key_file_header kh; + key_file_header kh{}; kh.version = currentVersion; kh.uid = dh.uid; kh.appnum = dh.appnum; @@ -462,7 +467,7 @@ public: { auto const offset = r.offset(); // Data Record or Spill Record - std::size_t size; + std::size_t size = 0; auto is = r.prepare(field::size, ec); // Size if (ec) Throw(ec); diff --git a/src/test/nodestore/varint_test.cpp b/src/test/nodestore/varint_test.cpp index 7ef624e777..f6145a9f52 100644 --- a/src/test/nodestore/varint_test.cpp +++ b/src/test/nodestore/varint_test.cpp @@ -17,11 +17,11 @@ public: testcase("encode, decode"); for (auto const v : vv) { - std::array::max> vi; + std::array::max> vi{}; auto const n0 = write_varint(vi.data(), v); expect(n0 > 0, "write error"); expect(n0 == size_varint(v), "size error"); - std::size_t v1; + std::size_t v1 = 0; auto const n1 = read_varint(vi.data(), n0, v1); expect(n1 == n0, "read error"); expect(v == v1, "wrong value"); diff --git a/src/test/overlay/ProtocolVersion_test.cpp b/src/test/overlay/ProtocolVersion_test.cpp index 02cbaba909..f0d02edbca 100644 --- a/src/test/overlay/ProtocolVersion_test.cpp +++ b/src/test/overlay/ProtocolVersion_test.cpp @@ -62,9 +62,12 @@ public: testcase("Protocol version negotiation"); BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2") == std::nullopt); - BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2, XRPL/2.0, XRPL/2.1") == make_protocol(2, 1)); + BEAST_EXPECT( + negotiateProtocolVersion("RTXP/1.2, XRPL/2.0, XRPL/2.1") == make_protocol(2, 1)); BEAST_EXPECT(negotiateProtocolVersion("XRPL/2.2") == make_protocol(2, 2)); - BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2, XRPL/2.2, XRPL/2.3, XRPL/999.999") == make_protocol(2, 2)); + BEAST_EXPECT( + negotiateProtocolVersion("RTXP/1.2, XRPL/2.2, XRPL/2.3, XRPL/999.999") == + make_protocol(2, 2)); BEAST_EXPECT(negotiateProtocolVersion("XRPL/999.999, WebSocket/1.0") == std::nullopt); BEAST_EXPECT(negotiateProtocolVersion("") == std::nullopt); } diff --git a/src/test/overlay/TMGetObjectByHash_test.cpp b/src/test/overlay/TMGetObjectByHash_test.cpp index c4e70ba305..0624c7b088 100644 --- a/src/test/overlay/TMGetObjectByHash_test.cpp +++ b/src/test/overlay/TMGetObjectByHash_test.cpp @@ -97,7 +97,8 @@ class TMGetObjectByHash_test : public beast::unit_test::suite { auto& overlay = dynamic_cast(env.app().overlay()); boost::beast::http::request request; - auto stream_ptr = std::make_unique(socket_type(env.app().getIOContext()), *context_); + auto stream_ptr = + std::make_unique(socket_type(env.app().getIOContext()), *context_); beast::IP::Endpoint local(boost::asio::ip::make_address("172.1.1.1"), 51235); beast::IP::Endpoint remote(boost::asio::ip::make_address("172.1.1.2"), 51235); @@ -107,7 +108,14 @@ class TMGetObjectByHash_test : public beast::unit_test::suite auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote); auto peer = std::make_shared( - env.app(), slot, std::move(request), key, protocolVersion_, consumer, std::move(stream_ptr), overlay); + env.app(), + slot, + std::move(request), + key, + protocolVersion_, + consumer, + std::move(stream_ptr), + overlay); overlay.add_active(peer); return peer; diff --git a/src/test/overlay/cluster_test.cpp b/src/test/overlay/cluster_test.cpp index 60fd5d62b3..6fa4604f78 100644 --- a/src/test/overlay/cluster_test.cpp +++ b/src/test/overlay/cluster_test.cpp @@ -121,7 +121,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->empty()); + BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access) } // Updating too quickly: should fail @@ -129,7 +129,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->empty()); + BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access) } using namespace std::chrono_literals; @@ -140,7 +140,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare(name) == 0); + BEAST_EXPECT(member->compare(name) == 0); // NOLINT(bugprone-unchecked-optional-access) } // Updating the name (non-empty doesn't go to empty) @@ -149,7 +149,7 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare(name) == 0); + BEAST_EXPECT(member->compare(name) == 0); // NOLINT(bugprone-unchecked-optional-access) } // Updating the name (non-empty updates to new non-empty) @@ -158,7 +158,8 @@ public: { auto member = c->member(node); BEAST_EXPECT(static_cast(member)); - BEAST_EXPECT(member->compare("test") == 0); + BEAST_EXPECT( + member->compare("test") == 0); // NOLINT(bugprone-unchecked-optional-access) } } diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index 19876809d2..f784a91350 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -82,7 +82,9 @@ public: auto start = buffer.begin() + sz * i; auto end = i < nbuffers - 1 ? (buffer.begin() + sz * (i + 1)) : buffer.end(); std::vector slice(start, end); - buffers.commit(boost::asio::buffer_copy(buffers.prepare(slice.size()), boost::asio::buffer(slice))); + buffers.commit( + boost::asio::buffer_copy( + buffers.prepare(slice.size()), boost::asio::buffer(slice))); } boost::system::error_code ec; @@ -108,8 +110,11 @@ public: BEAST_EXPECT(proto1->ParseFromArray(decompressed.data(), decompressedSize)); auto uncompressed = m.getBuffer(Compressed::Off); - BEAST_EXPECT(std::equal( - uncompressed.begin() + xrpl::compression::headerBytes, uncompressed.end(), decompressed.begin())); + BEAST_EXPECT( + std::equal( + uncompressed.begin() + xrpl::compression::headerBytes, + uncompressed.end(), + decompressed.begin())); } std::shared_ptr @@ -125,8 +130,10 @@ public: st[sfSequence] = i; st[sfPublicKey] = std::get<0>(master); st[sfSigningPubKey] = std::get<0>(signing); - st[sfDomain] = makeSlice(std::string("example") + std::to_string(i) + std::string(".com")); - sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(master), sfMasterSignature); + st[sfDomain] = + makeSlice(std::string("example") + std::to_string(i) + std::string(".com")); + sign( + st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(master), sfMasterSignature); sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing)); Serializer s; st.add(s); @@ -169,7 +176,7 @@ public: return std::string{reinterpret_cast(blob->data()), blob->size()}; }; - std::string usdTxBlob = ""; + std::string usdTxBlob; auto wsc = makeWSClient(env.app().config()); { Json::Value requestUSD; @@ -249,7 +256,8 @@ public: { auto getObject = std::make_shared(); - getObject->set_type(protocol::TMGetObjectByHash_ObjectType::TMGetObjectByHash_ObjectType_otTRANSACTION); + getObject->set_type( + protocol::TMGetObjectByHash_ObjectType::TMGetObjectByHash_ObjectType_otTRANSACTION); getObject->set_query(true); getObject->set_seq(123456789); uint256 hash(xrpl::sha512Half(123456789)); @@ -365,7 +373,11 @@ public: doTest(buildGetObjectByHash(), protocol::mtGET_OBJECTS, 4, "TMGetObjectByHash"); // 895B doTest(buildValidatorList(), protocol::mtVALIDATOR_LIST, 4, "TMValidatorList"); - doTest(buildValidatorListCollection(), protocol::mtVALIDATOR_LIST_COLLECTION, 4, "TMValidatorListCollection"); + doTest( + buildValidatorListCollection(), + protocol::mtVALIDATOR_LIST_COLLECTION, + 4, + "TMValidatorListCollection"); } void @@ -382,7 +394,8 @@ public: c.loadFromString(str.str()); auto env = std::make_shared(*this); env->app().config().COMPRESSION = c.COMPRESSION; - env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; + env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = + c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; return env; }; auto handshake = [&](int outboundEnable, int inboundEnable) { @@ -403,15 +416,18 @@ public: auto const peerEnabled = inboundEnable && outboundEnable; // inbound is enabled if the request's header has the feature // enabled and the peer's configuration is enabled - auto const inboundEnabled = peerFeatureEnabled(http_request, FEATURE_COMPR, "lz4", inboundEnable); + auto const inboundEnabled = + peerFeatureEnabled(http_request, FEATURE_COMPR, "lz4", inboundEnable); BEAST_EXPECT(!(peerEnabled ^ inboundEnabled)); env.reset(); env = getEnv(inboundEnable); - auto http_resp = xrpl::makeResponse(true, http_request, addr, addr, uint256{1}, 1, {1, 0}, env->app()); + auto http_resp = xrpl::makeResponse( + true, http_request, addr, addr, uint256{1}, 1, {1, 0}, env->app()); // outbound is enabled if the response's header has the feature // enabled and the peer's configuration is enabled - auto const outboundEnabled = peerFeatureEnabled(http_resp, FEATURE_COMPR, "lz4", outboundEnable); + auto const outboundEnabled = + peerFeatureEnabled(http_resp, FEATURE_COMPR, "lz4", outboundEnable); BEAST_EXPECT(!(peerEnabled ^ outboundEnabled)); }; handshake(1, 1); diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index 9d9aef7a36..8e50415aa9 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -245,8 +246,11 @@ class Link using Latency = std::pair; public: - Link(Validator& validator, PeerSPtr peer, Latency const& latency = {milliseconds(5), milliseconds(15)}) - : validator_(validator), peer_(peer), latency_(latency), up_(true) + Link( + Validator& validator, + PeerSPtr peer, + Latency const& latency = {milliseconds(5), milliseconds(15)}) + : validator_(validator), peer_(peer), latency_(latency) { auto sp = peer_.lock(); assert(sp); @@ -291,7 +295,7 @@ private: Validator& validator_; PeerWPtr peer_; Latency latency_; - bool up_; + bool up_{true}; }; /** Simulate Validator */ @@ -367,7 +371,9 @@ public: for_links(LinkIterCB f, bool simulateSlow = false) { std::vector v; - std::transform(links_.begin(), links_.end(), std::back_inserter(v), [](auto& kv) { return kv.second; }); + std::transform(links_.begin(), links_.end(), std::back_inserter(v), [](auto& kv) { + return kv.second; + }); std::random_device d; std::mt19937 g(d()); std::shuffle(v.begin(), v.end(), g); @@ -399,7 +405,7 @@ public: } std::uint16_t - id() + id() const { return id_; } @@ -463,10 +469,11 @@ public: { auto validator = m->getValidatorKey(); assert(validator); - if (!squelch_.expireSquelch(*validator)) + if (!squelch_.expireSquelch(*validator)) // NOLINT(bugprone-unchecked-optional-access) return; - overlay_.updateSlotAndSquelch({}, *validator, id(), f); + overlay_.updateSlotAndSquelch( + {}, *validator, id(), f); // NOLINT(bugprone-unchecked-optional-access) } /** Remote Peer (Directly connected Peer) */ @@ -476,9 +483,13 @@ public: auto validator = squelch.validatorpubkey(); PublicKey key(Slice(validator.data(), validator.size())); if (squelch.squelch()) + { squelch_.addSquelch(key, std::chrono::seconds{squelch.squelchduration()}); + } else + { squelch_.removeSquelch(key); + } } private: @@ -547,7 +558,7 @@ public: addPeer(bool useCache = true) { PeerSPtr peer{}; - Peer::id_t id; + Peer::id_t id = 0; if (peersCache_.empty() || !useCache) { peer = std::make_shared(*this, logs_.journal("Squelch")); @@ -595,8 +606,7 @@ public: for (auto& [id, _] : peers_) { (void)_; - if (id > maxId) - maxId = id; + maxId = std::max(id, maxId); } deletePeer(maxId, false); @@ -620,7 +630,7 @@ public: isSelected(PublicKey const& validator, Peer::id_t peer) { auto selected = slots_.getSelected(validator); - return selected.find(peer) != selected.end(); + return selected.contains(peer); } id_t @@ -631,7 +641,9 @@ public: return *selected.begin(); } - std::unordered_map> + std::unordered_map< + id_t, + std::tuple> getPeers(PublicKey const& validator) { return slots_.getPeers(validator); @@ -740,12 +752,17 @@ public: void enableLink(std::uint16_t validatorId, Peer::id_t peer, bool enable) { - auto it = std::find_if(validators_.begin(), validators_.end(), [&](auto& v) { return v.id() == validatorId; }); + auto it = std::find_if( + validators_.begin(), validators_.end(), [&](auto& v) { return v.id() == validatorId; }); assert(it != validators_.end()); if (enable) + { it->linkUp(peer); + } else + { it->linkDown(peer); + } } void @@ -762,8 +779,9 @@ public: PublicKey key = v; squelch.clear_validatorpubkey(); squelch.set_validatorpubkey(key.data(), key.size()); - v.for_links( - {peer}, [&](Link& l, MessageSPtr) { std::dynamic_pointer_cast(l.getPeer())->send(squelch); }); + v.for_links({peer}, [&](Link& l, MessageSPtr) { + std::dynamic_pointer_cast(l.getPeer())->send(squelch); + }); } } @@ -862,7 +880,10 @@ protected: /** Send squelch (if duration is set) or unsquelch (if duration not set) */ Peer::id_t - sendSquelch(PublicKey const& validator, PeerWPtr const& peerPtr, std::optional duration) + sendSquelch( + PublicKey const& validator, + PeerWPtr const& peerPtr, + std::optional duration) { protocol::TMSquelch squelch; bool res = duration ? true : false; @@ -888,8 +909,8 @@ protected: std::uint32_t cnt_ = 0; std::uint32_t handledCnt_ = 0; bool isSelected_ = false; - Peer::id_t peer_; - std::uint16_t validator_; + Peer::id_t peer_{}; + std::uint16_t validator_{}; std::optional key_; time_point time_; bool handled_ = false; @@ -912,12 +933,13 @@ protected: bool squelched = false; std::stringstream str; - link.send(m, [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { - assert(key == validator); - auto p = sendSquelch(key, peerPtr, duration); - squelched = true; - str << p << " "; - }); + link.send( + m, [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { + assert(key == validator); + auto p = sendSquelch(key, peerPtr, duration); + squelched = true; + str << p << " "; + }); if (squelched) { @@ -926,13 +948,16 @@ protected: for (auto s : selected) str << s << " "; if (log) + { std::cout << (double)reduce_relay::epoch(now).count() / 1000. - << " random, squelched, validator: " << validator.id() << " peers: " << str.str() - << std::endl; + << " random, squelched, validator: " << validator.id() + << " peers: " << str.str() << std::endl; + } auto countingState = network_.overlay().isCountingState(validator); BEAST_EXPECT( countingState == false && - selected.size() == env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + selected.size() == + env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); } // Trigger Link Down or Peer Disconnect event @@ -949,10 +974,13 @@ protected: if (event == EventType::LinkDown) { network_.enableLink(validator.id(), link.peerId(), false); - events[event].isSelected_ = network_.overlay().isSelected(validator, link.peerId()); + events[event].isSelected_ = + network_.overlay().isSelected(validator, link.peerId()); } else + { events[event].isSelected_ = network_.isSelected(link.peerId()); + } }; auto r = rand_int(0, 1000); if (r == (int)EventType::LinkDown || r == (int)EventType::PeerDisconnected) @@ -965,11 +993,12 @@ protected: { auto& event = events[EventType::PeerDisconnected]; bool allCounting = network_.allCounting(event.peer_); - network_.overlay().deletePeer(event.peer_, [&](PublicKey const& v, PeerWPtr const& peerPtr) { - if (event.isSelected_) - sendSquelch(v, peerPtr, {}); - event.handled_ = true; - }); + network_.overlay().deletePeer( + event.peer_, [&](PublicKey const& v, PeerWPtr const& peerPtr) { + if (event.isSelected_) + sendSquelch(v, peerPtr, {}); + event.handled_ = true; + }); // Should only be unsquelched if the peer is in Selected state // If in Selected state it's possible unsquelching didn't // take place because there is no peers in Squelched state in @@ -1002,10 +1031,13 @@ protected: { event.isSelected_ = network_.overlay().isSelected(*event.key_, event.peer_); auto peers = network_.overlay().getPeers(*event.key_); - auto d = reduce_relay::epoch(now).count() - std::get<3>(peers[event.peer_]); - mustHandle = event.isSelected_ && d > milliseconds(reduce_relay::IDLED).count() && - network_.overlay().inState(*event.key_, reduce_relay::PeerState::Squelched) > 0 && - peers.find(event.peer_) != peers.end(); + auto d = reduce_relay::epoch(now).count() - + std::get<3>(peers[event.peer_]); + mustHandle = event.isSelected_ && + d > milliseconds(reduce_relay::IDLED).count() && + network_.overlay().inState( + *event.key_, reduce_relay::PeerState::Squelched) > 0 && + peers.contains(event.peer_); } network_.overlay().deleteIdlePeers([&](PublicKey const& v, PeerWPtr const& ptr) { event.handled_ = true; @@ -1015,11 +1047,13 @@ protected: sendSquelch(validator, ptr, {}); } }); - bool handled = (event.handled_ && event.state_ == State::WaitReset) || (!event.handled_ && !mustHandle); + bool handled = (event.handled_ && event.state_ == State::WaitReset) || + (!event.handled_ && !mustHandle); BEAST_EXPECT(handled); } if (event.state_ == State::WaitReset || - (event.state_ == State::On && (now - event.time_ > (reduce_relay::IDLED + seconds(2))))) + (event.state_ == State::On && + (now - event.time_ > (reduce_relay::IDLED + seconds(2))))) { bool handled = event.state_ == State::WaitReset || !event.handled_; BEAST_EXPECT(handled); @@ -1038,8 +1072,11 @@ protected: // All Peer Disconnect events must be handled BEAST_EXPECT(disconnected.cnt_ == disconnected.handledCnt_); if (log) + { std::cout << "link down count: " << down.cnt_ << "/" << down.handledCnt_ - << " peer disconnect count: " << disconnected.cnt_ << "/" << disconnected.handledCnt_; + << " peer disconnect count: " << disconnected.cnt_ << "/" + << disconnected.handledCnt_; + } } bool @@ -1086,8 +1123,9 @@ protected: testPeerUnsquelched(bool log) { ManualClock::advance(seconds(601)); - doTest( - "Peer Unsquelched", log, [this](bool log) { BEAST_EXPECT(propagateNoSquelch(log, 2, true, true, false)); }); + doTest("Peer Unsquelched", log, [this](bool log) { + BEAST_EXPECT(propagateNoSquelch(log, 2, true, true, false)); + }); } /** Propagate enough messages to generate one squelch event */ @@ -1098,14 +1136,17 @@ protected: network_.propagate( [&](Link& link, MessageSPtr message) { std::uint16_t squelched = 0; - link.send(message, [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { - squelched++; - sendSquelch(key, peerPtr, duration); - }); + link.send( + message, + [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { + squelched++; + sendSquelch(key, peerPtr, duration); + }); if (squelched) { BEAST_EXPECT( - squelched == MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + squelched == + MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); n++; } }, @@ -1114,7 +1155,8 @@ protected: purge, resetClock); auto selected = network_.overlay().getSelected(network_.validator(0)); - BEAST_EXPECT(selected.size() == env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + BEAST_EXPECT( + selected.size() == env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); BEAST_EXPECT(n == 1); // only one selection round auto res = checkCounting(network_.validator(0), false); BEAST_EXPECT(res); @@ -1123,15 +1165,22 @@ protected: /** Send fewer message so that squelch event is not generated */ bool - propagateNoSquelch(bool log, std::uint16_t nMessages, bool countingState, bool purge = true, bool resetClock = true) + propagateNoSquelch( + bool log, + std::uint16_t nMessages, + bool countingState, + bool purge = true, + bool resetClock = true) { bool squelched = false; network_.propagate( [&](Link& link, MessageSPtr message) { - link.send(message, [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { - squelched = true; - BEAST_EXPECT(false); - }); + link.send( + message, + [&](PublicKey const& key, PeerWPtr const& peerPtr, std::uint32_t duration) { + squelched = true; + BEAST_EXPECT(false); + }); }, 1, nMessages, @@ -1164,8 +1213,11 @@ protected: BEAST_EXPECT(propagateAndSquelch(log, true, false)); auto id = network_.overlay().getSelectedPeer(network_.validator(0)); std::uint16_t unsquelched = 0; - network_.overlay().deletePeer(id, [&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; }); - BEAST_EXPECT(unsquelched == MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + network_.overlay().deletePeer( + id, [&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; }); + BEAST_EXPECT( + unsquelched == + MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); BEAST_EXPECT(checkCounting(network_.validator(0), true)); }); } @@ -1180,9 +1232,12 @@ protected: BEAST_EXPECT(propagateAndSquelch(log, true, false)); ManualClock::advance(reduce_relay::IDLED + seconds(1)); std::uint16_t unsquelched = 0; - network_.overlay().deleteIdlePeers([&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; }); + network_.overlay().deleteIdlePeers( + [&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; }); auto peers = network_.overlay().getPeers(network_.validator(0)); - BEAST_EXPECT(unsquelched == MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + BEAST_EXPECT( + unsquelched == + MAX_PEERS - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); BEAST_EXPECT(checkCounting(network_.validator(0), true)); }); } @@ -1197,7 +1252,8 @@ protected: BEAST_EXPECT(propagateAndSquelch(log, true, false)); auto peers = network_.overlay().getPeers(network_.validator(0)); auto it = std::find_if(peers.begin(), peers.end(), [&](auto it) { - return std::get(it.second) == reduce_relay::PeerState::Squelched; + return std::get(it.second) == + reduce_relay::PeerState::Squelched; }); assert(it != peers.end()); std::uint16_t unsquelched = 0; @@ -1349,7 +1405,8 @@ vp_base_squelch_max_selected_peers=2 ManualClock::reset(); auto createSlots = [&](bool baseSquelchEnabled) -> reduce_relay::Slots { env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = baseSquelchEnabled; - return reduce_relay::Slots(env_.app().logs(), network_.overlay(), env_.app().config()); + return reduce_relay::Slots( + env_.app().logs(), network_.overlay(), env_.app().config()); }; // base squelching must not be ready if squelching is disabled BEAST_EXPECT(!createSlots(false).baseSquelchReady()); @@ -1380,7 +1437,8 @@ vp_base_squelch_max_selected_peers=2 { uint256 key(i); network_.overlay().updateSlotAndSquelch( - key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {}); + key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) { + }); } auto peers = network_.overlay().getPeers(network_.validator(0)); // first message changes Slot state to Counting and is not counted, @@ -1405,20 +1463,19 @@ vp_base_squelch_max_selected_peers=2 struct Handler : public reduce_relay::SquelchHandler { - Handler() : maxDuration_(0) + Handler() { } void squelch(PublicKey const&, Peer::id_t, std::uint32_t duration) const override { - if (duration > maxDuration_) - maxDuration_ = duration; + maxDuration_ = std::max(duration, maxDuration_); } void unsquelch(PublicKey const&, Peer::id_t) const override { } - mutable int maxDuration_; + mutable int maxDuration_{0}; }; void @@ -1430,7 +1487,8 @@ vp_base_squelch_max_selected_peers=2 auto run = [&](int npeers) { handler.maxDuration_ = 0; - reduce_relay::Slots slots(env_.app().logs(), handler, env_.app().config()); + reduce_relay::Slots slots( + env_.app().logs(), handler, env_.app().config()); // 1st message from a new peer switches the slot // to counting state and resets the counts of all peers + // MAX_MESSAGE_THRESHOLD + 1 messages to reach the threshold @@ -1443,7 +1501,8 @@ vp_base_squelch_max_selected_peers=2 // slot's internal hash router accept the message std::uint64_t mid = m * 1000 + peer; uint256 const message{mid}; - slots.updateSlotAndSquelch(message, validator, peer, protocol::MessageType::mtVALIDATION); + slots.updateSlotAndSquelch( + message, validator, peer, protocol::MessageType::mtVALIDATION); } } // make Slot's internal hash router expire all messages @@ -1473,14 +1532,22 @@ vp_base_squelch_max_selected_peers=2 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count()); using namespace beast::unit_test::detail; if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count()) - log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) << std::endl << std::flush; + { + log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) + << std::endl + << std::flush; + } // more than 400 is still less than MAX_UNSQUELCH_EXPIRE_PEERS run(400); BEAST_EXPECT( handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() && handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count()); if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count()) - log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) << std::endl << std::flush; + { + log << make_reason("warning: squelch duration is low", __FILE__, __LINE__) + << std::endl + << std::flush; + } }); } @@ -1496,7 +1563,8 @@ vp_base_squelch_max_selected_peers=2 << "[compression]\n" << "1\n"; c.loadFromString(str.str()); - env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; + env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = + c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; env_.app().config().COMPRESSION = c.COMPRESSION; }; @@ -1518,14 +1586,17 @@ vp_base_squelch_max_selected_peers=2 auto const peerEnabled = inboundEnable && outboundEnable; // inbound is enabled if the request's header has the feature // enabled and the peer's configuration is enabled - auto const inboundEnabled = peerFeatureEnabled(http_request, FEATURE_VPRR, inboundEnable); + auto const inboundEnabled = + peerFeatureEnabled(http_request, FEATURE_VPRR, inboundEnable); BEAST_EXPECT(!(peerEnabled ^ inboundEnabled)); setEnv(inboundEnable); - auto http_resp = xrpl::makeResponse(true, http_request, addr, addr, uint256{1}, 1, {1, 0}, env_.app()); + auto http_resp = xrpl::makeResponse( + true, http_request, addr, addr, uint256{1}, 1, {1, 0}, env_.app()); // outbound is enabled if the response's header has the feature // enabled and the peer's configuration is enabled - auto const outboundEnabled = peerFeatureEnabled(http_resp, FEATURE_VPRR, outboundEnable); + auto const outboundEnabled = + peerFeatureEnabled(http_resp, FEATURE_VPRR, outboundEnable); BEAST_EXPECT(!(peerEnabled ^ outboundEnabled)); }; handshake(1, 1); diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index 498ecb6a27..d96f67c8a1 100644 --- a/src/test/overlay/short_read_test.cpp +++ b/src/test/overlay/short_read_test.cpp @@ -180,7 +180,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Acceptor::close, shared_from_this())); + { + post(strand_, std::bind(&Acceptor::close, shared_from_this())); + return; + } acceptor_.close(); } @@ -189,7 +192,10 @@ private: { acceptor_.async_accept( socket_, - bind_executor(strand_, std::bind(&Acceptor::on_accept, shared_from_this(), std::placeholders::_1))); + bind_executor( + strand_, + std::bind( + &Acceptor::on_accept, shared_from_this(), std::placeholders::_1))); } void @@ -207,13 +213,19 @@ private: on_accept(error_code ec) { if (ec) - return fail("accept", ec); + { + fail("accept", ec); + return; + } auto const p = std::make_shared(server_, std::move(socket_)); server_.add(p); p->run(); acceptor_.async_accept( socket_, - bind_executor(strand_, std::bind(&Acceptor::on_accept, shared_from_this(), std::placeholders::_1))); + bind_executor( + strand_, + std::bind( + &Acceptor::on_accept, shared_from_this(), std::placeholders::_1))); } }; @@ -242,7 +254,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Connection::close, shared_from_this())); + { + post(strand_, std::bind(&Connection::close, shared_from_this())); + return; + } if (socket_.is_open()) { socket_.close(); @@ -255,11 +270,14 @@ private: { timer_.expires_after(std::chrono::seconds(3)); timer_.async_wait(bind_executor( - strand_, std::bind(&Connection::on_timer, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind(&Connection::on_timer, shared_from_this(), std::placeholders::_1))); stream_.async_handshake( stream_type::server, bind_executor( - strand_, std::bind(&Connection::on_handshake, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_handshake, shared_from_this(), std::placeholders::_1))); } void @@ -280,7 +298,10 @@ private: if (ec == boost::asio::error::operation_aborted) return; if (ec) - return fail("timer", ec); + { + fail("timer", ec); + return; + } test_.log << "[server] timeout" << std::endl; socket_.close(); } @@ -289,7 +310,10 @@ private: on_handshake(error_code ec) { if (ec) - return fail("handshake", ec); + { + fail("handshake", ec); + return; + } #if 1 boost::asio::async_read_until( stream_, @@ -298,7 +322,10 @@ private: bind_executor( strand_, std::bind( - &Connection::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + &Connection::on_read, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); #else close(); #endif @@ -310,11 +337,17 @@ private: if (ec == boost::asio::error::eof) { server_.test_.log << "[server] read: EOF" << std::endl; - return stream_.async_shutdown(bind_executor( - strand_, std::bind(&Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + stream_.async_shutdown(bind_executor( + strand_, + std::bind( + &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + return; } if (ec) - return fail("read", ec); + { + fail("read", ec); + return; + } buf_.commit(bytes_transferred); buf_.consume(bytes_transferred); @@ -325,7 +358,10 @@ private: bind_executor( strand_, std::bind( - &Connection::on_write, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + &Connection::on_write, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } void @@ -333,16 +369,24 @@ private: { buf_.consume(bytes_transferred); if (ec) - return fail("write", ec); + { + fail("write", ec); + return; + } stream_.async_shutdown(bind_executor( - strand_, std::bind(&Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); } void on_shutdown(error_code ec) { if (ec) - return fail("shutdown", ec); + { + fail("shutdown", ec); + return; + } socket_.close(); timer_.cancel(); } @@ -403,7 +447,10 @@ private: close() override { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&Connection::close, shared_from_this())); + { + post(strand_, std::bind(&Connection::close, shared_from_this())); + return; + } if (socket_.is_open()) { socket_.close(); @@ -416,11 +463,14 @@ private: { timer_.expires_after(std::chrono::seconds(3)); timer_.async_wait(bind_executor( - strand_, std::bind(&Connection::on_timer, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind(&Connection::on_timer, shared_from_this(), std::placeholders::_1))); socket_.async_connect( ep, bind_executor( - strand_, std::bind(&Connection::on_connect, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_connect, shared_from_this(), std::placeholders::_1))); } void @@ -441,7 +491,10 @@ private: if (ec == boost::asio::error::operation_aborted) return; if (ec) - return fail("timer", ec); + { + fail("timer", ec); + return; + } test_.log << "[client] timeout"; socket_.close(); } @@ -450,18 +503,26 @@ private: on_connect(error_code ec) { if (ec) - return fail("connect", ec); + { + fail("connect", ec); + return; + } stream_.async_handshake( stream_type::client, bind_executor( - strand_, std::bind(&Connection::on_handshake, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_handshake, shared_from_this(), std::placeholders::_1))); } void on_handshake(error_code ec) { if (ec) - return fail("handshake", ec); + { + fail("handshake", ec); + return; + } write(buf_, "HELLO\n"); #if 1 @@ -471,10 +532,15 @@ private: bind_executor( strand_, std::bind( - &Connection::on_write, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + &Connection::on_write, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); #else stream_.async_shutdown(bind_executor( - strand_, std::bind(&Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); #endif } @@ -483,7 +549,10 @@ private: { buf_.consume(bytes_transferred); if (ec) - return fail("write", ec); + { + fail("write", ec); + return; + } #if 1 boost::asio::async_read_until( stream_, @@ -492,10 +561,15 @@ private: bind_executor( strand_, std::bind( - &Connection::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + &Connection::on_read, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); #else stream_.async_shutdown(bind_executor( - strand_, std::bind(&Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); #endif } @@ -503,17 +577,25 @@ private: on_read(error_code ec, std::size_t bytes_transferred) { if (ec) - return fail("read", ec); + { + fail("read", ec); + return; + } buf_.commit(bytes_transferred); stream_.async_shutdown(bind_executor( - strand_, std::bind(&Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); + strand_, + std::bind( + &Connection::on_shutdown, shared_from_this(), std::placeholders::_1))); } void on_shutdown(error_code ec) { if (ec) - return fail("shutdown", ec); + { + fail("shutdown", ec); + return; + } socket_.close(); timer_.cancel(); } diff --git a/src/test/overlay/traffic_count_test.cpp b/src/test/overlay/traffic_count_test.cpp index 6516fe3843..5e6e4e685c 100644 --- a/src/test/overlay/traffic_count_test.cpp +++ b/src/test/overlay/traffic_count_test.cpp @@ -25,7 +25,8 @@ public: BEAST_EXPECT(known == TrafficCount::category::base); // an unknown message type is categorized as unknown - auto const unknown = TrafficCount::categorize(message, static_cast(99), false); + auto const unknown = + TrafficCount::categorize(message, static_cast(99), false); BEAST_EXPECT(unknown == TrafficCount::category::unknown); } @@ -109,7 +110,8 @@ public: BEAST_EXPECT(TrafficCount::to_string(TrafficCount::category::total) == "total"); // return "unknown" for unknown categories - BEAST_EXPECT(TrafficCount::to_string(static_cast(1000)) == "unknown"); + BEAST_EXPECT( + TrafficCount::to_string(static_cast(1000)) == "unknown"); } void diff --git a/src/test/overlay/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index b8e3300c03..11b5c95a4e 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -32,7 +32,11 @@ private: testConfig(bool log) { doTest("Config Test", log, [&](bool log) { - auto test = [&](bool enable, bool metrics, std::uint16_t min, std::uint16_t pct, bool success = true) { + auto test = [&](bool enable, + bool metrics, + std::uint16_t min, + std::uint16_t pct, + bool success = true) { std::stringstream str("[reduce_relay]"); str << "[reduce_relay]\n" << "tx_enable=" << static_cast(enable) << "\n" @@ -49,16 +53,24 @@ private: BEAST_EXPECT(c.TX_REDUCE_RELAY_MIN_PEERS == min); BEAST_EXPECT(c.TX_RELAY_PERCENTAGE == pct); if (success) + { pass(); + } else + { fail(); + } } catch (...) { if (success) + { fail(); + } else + { pass(); + } } }; @@ -141,17 +153,27 @@ private: { auto& overlay = dynamic_cast(env.app().overlay()); boost::beast::http::request request; - (nDisabled == 0) ? (void)request.insert("X-Protocol-Ctl", makeFeaturesRequestHeader(false, false, true, false)) - : (void)nDisabled--; + (nDisabled == 0) + ? request.insert("X-Protocol-Ctl", makeFeaturesRequestHeader(false, false, true, false)) + : (void)nDisabled--; auto stream_ptr = std::make_unique( - socket_type(std::forward(env.app().getIOContext())), *context_); + socket_type(std::forward(env.app().getIOContext())), + *context_); beast::IP::Endpoint local(boost::asio::ip::make_address("172.1.1." + std::to_string(lid_))); - beast::IP::Endpoint remote(boost::asio::ip::make_address("172.1.1." + std::to_string(rid_))); + beast::IP::Endpoint remote( + boost::asio::ip::make_address("172.1.1." + std::to_string(rid_))); PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519))); auto consumer = overlay.resourceManager().newInboundEndpoint(remote); auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote); auto const peer = std::make_shared( - env.app(), slot, std::move(request), key, protocolVersion_, consumer, std::move(stream_ptr), overlay); + env.app(), + slot, + std::move(request), + key, + protocolVersion_, + consumer, + std::move(stream_ptr), + overlay); BEAST_EXPECT(overlay.findPeerByPublicKey(key) == std::shared_ptr{}); overlay.add_active(peer); BEAST_EXPECT(overlay.findPeerByPublicKey(key) == peer); diff --git a/src/test/peerfinder/Livecache_test.cpp b/src/test/peerfinder/Livecache_test.cpp index 2c2ac01111..b2d0eeeaf1 100644 --- a/src/test/peerfinder/Livecache_test.cpp +++ b/src/test/peerfinder/Livecache_test.cpp @@ -30,7 +30,7 @@ public: // Add the address as an endpoint template - inline void + void add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) { Endpoint cep{ep, hops}; @@ -152,10 +152,12 @@ public: }; all_hops before; all_hops before_sorted; - for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); ++i.first, ++i.second) + for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); + ++i.first, ++i.second) { std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(before[i.first])); - std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(before_sorted[i.first])); + std::copy( + (*i.second).begin(), (*i.second).end(), std::back_inserter(before_sorted[i.first])); std::sort(before_sorted[i.first].begin(), before_sorted[i.first].end(), cmp_EP); } @@ -163,10 +165,12 @@ public: all_hops after; all_hops after_sorted; - for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); ++i.first, ++i.second) + for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); + ++i.first, ++i.second) { std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(after[i.first])); - std::copy((*i.second).begin(), (*i.second).end(), std::back_inserter(after_sorted[i.first])); + std::copy( + (*i.second).begin(), (*i.second).end(), std::back_inserter(after_sorted[i.first])); std::sort(after_sorted[i.first].begin(), after_sorted[i.first].end(), cmp_EP); } diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index 49ddc52c9d..c39564c54c 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -80,7 +80,8 @@ public: { BEAST_EXPECT(list.size() == 1); auto const [slot, _] = logic.new_outbound_slot(list.front()); - BEAST_EXPECT(logic.onConnected(slot, beast::IP::Endpoint::from_string("65.0.0.2:5"))); + BEAST_EXPECT( + logic.onConnected(slot, beast::IP::Endpoint::from_string("65.0.0.2:5"))); logic.on_closed(slot); ++n; } @@ -119,9 +120,9 @@ public: { BEAST_EXPECT(list.size() == 1); auto const [slot, _] = logic.new_outbound_slot(list.front()); - if (!BEAST_EXPECT(logic.onConnected(slot, beast::IP::Endpoint::from_string("65.0.0.2:5")))) + if (!BEAST_EXPECT( + logic.onConnected(slot, beast::IP::Endpoint::from_string("65.0.0.2:5")))) return; - std::string s = "."; if (!BEAST_EXPECT(logic.activate(slot, pk, false) == PeerFinder::Result::success)) return; logic.on_closed(slot); @@ -218,15 +219,18 @@ public: } auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); - auto const [slot, r] = logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); + auto const [slot, r] = + logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); BEAST_EXPECT(slot != nullptr); BEAST_EXPECT(r == Result::success); - auto const [slot1, r1] = logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); + auto const [slot1, r1] = + logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); BEAST_EXPECT(slot1 != nullptr); BEAST_EXPECT(r1 == Result::success); - auto const [slot2, r2] = logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1027")); + auto const [slot2, r2] = + logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1027")); BEAST_EXPECT(r2 == Result::ipLimitExceeded); if (!BEAST_EXPECT(slot2 == nullptr)) @@ -255,11 +259,13 @@ public: PublicKey const pk1(randomKeyPair(KeyType::secp256k1).first); - auto const [slot, rSlot] = logic.new_outbound_slot(beast::IP::Endpoint::from_string("55.104.0.2:1025")); + auto const [slot, rSlot] = + logic.new_outbound_slot(beast::IP::Endpoint::from_string("55.104.0.2:1025")); BEAST_EXPECT(slot != nullptr); BEAST_EXPECT(rSlot == Result::success); - auto const [slot2, r2Slot] = logic.new_outbound_slot(beast::IP::Endpoint::from_string("55.104.0.2:1026")); + auto const [slot2, r2Slot] = + logic.new_outbound_slot(beast::IP::Endpoint::from_string("55.104.0.2:1026")); BEAST_EXPECT(slot2 != nullptr); BEAST_EXPECT(r2Slot == Result::success); @@ -297,7 +303,8 @@ public: PublicKey const pk1(randomKeyPair(KeyType::secp256k1).first); auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); - auto const [slot, rSlot] = logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); + auto const [slot, rSlot] = + logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); BEAST_EXPECT(slot != nullptr); BEAST_EXPECT(rSlot == Result::success); @@ -315,7 +322,8 @@ public: BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::success); // creating a new inbound slot must succeed as IP Limit is not exceeded - auto const [slot2, r2Slot] = logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); + auto const [slot2, r2Slot] = + logic.new_inbound_slot(local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); BEAST_EXPECT(slot2 != nullptr); BEAST_EXPECT(r2Slot == Result::success); @@ -383,7 +391,7 @@ public: testcase(test); - std::string toLoad = ""; + std::string toLoad; int max = 0; if (maxPeers) { @@ -408,7 +416,8 @@ public: Counts counts; counts.onConfig(config); BEAST_EXPECT( - counts.out_max() == expectOut && counts.in_max() == expectIn && config.ipLimit == expectIpLimit); + counts.out_max() == expectOut && counts.in_max() == expectIn && + config.ipLimit == expectIpLimit); TestStore store; TestChecker checker; diff --git a/src/test/protocol/Hooks_test.cpp b/src/test/protocol/Hooks_test.cpp index f4b33f7e4d..a280fd6bd4 100644 --- a/src/test/protocol/Hooks_test.cpp +++ b/src/test/protocol/Hooks_test.cpp @@ -134,6 +134,7 @@ class Hooks_test : public beast::unit_test::suite } case STI_ACCOUNT: { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) AccountID id = *parseBase58("rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT"); dummy.setAccountID(f, id); BEAST_EXPECT(dummy.getAccountID(f) == id); diff --git a/src/test/protocol/InnerObjectFormats_test.cpp b/src/test/protocol/InnerObjectFormats_test.cpp index 7d69bb7afb..c06524b90e 100644 --- a/src/test/protocol/InnerObjectFormats_test.cpp +++ b/src/test/protocol/InnerObjectFormats_test.cpp @@ -163,7 +163,8 @@ public: Json::Reader().parse(test.txt, req); if (RPC::contains_error(req)) { - Throw("Internal InnerObjectFormatsParsedJSON error. Bad JSON."); + Throw( + "Internal InnerObjectFormatsParsedJSON error. Bad JSON."); } STParsedJSONObject parsed("request", req); bool const noObj = !parsed.object.has_value(); diff --git a/src/test/protocol/Memo_test.cpp b/src/test/protocol/Memo_test.cpp index 3c5d3f5a38..9a0fd7d94c 100644 --- a/src/test/protocol/Memo_test.cpp +++ b/src/test/protocol/Memo_test.cpp @@ -35,14 +35,16 @@ public: { // Make sure that too big a memo is flagged as invalid. JTx memoSize = makeJtxWithMemo(); - memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] = std::string(2020, '0'); + memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] = + std::string(2020, '0'); env(memoSize, rpc("invalidTransaction", "fails local checks: The memo exceeds the maximum allowed " "size.")); // This memo is just barely small enough. - memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] = std::string(2018, '1'); + memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] = + std::string(2018, '1'); env(memoSize); } { diff --git a/src/test/protocol/MultiApiJson_test.cpp b/src/test/protocol/MultiApiJson_test.cpp index 66eea9888f..0da511b976 100644 --- a/src/test/protocol/MultiApiJson_test.cpp +++ b/src/test/protocol/MultiApiJson_test.cpp @@ -63,10 +63,12 @@ struct MultiApiJson_test : beast::unit_test::suite static_assert(std::size(primes) > RPC::apiMaximumValidVersion); MultiApiJson<1, 3> s1{}; - static_assert(s1.size == RPC::apiMaximumValidVersion + 1 - RPC::apiMinimumSupportedVersion); + static_assert( + s1.size == RPC::apiMaximumValidVersion + 1 - RPC::apiMinimumSupportedVersion); int productAllVersions = 1; - for (unsigned i = RPC::apiMinimumSupportedVersion; i <= RPC::apiMaximumValidVersion; ++i) + for (unsigned i = RPC::apiMinimumSupportedVersion; i <= RPC::apiMaximumValidVersion; + ++i) { auto const index = i - RPC::apiMinimumSupportedVersion; BEAST_EXPECT(index == s1.index(i)); @@ -76,7 +78,8 @@ struct MultiApiJson_test : beast::unit_test::suite } BEAST_EXPECT(!s1.valid(0)); BEAST_EXPECT(!s1.valid(RPC::apiMaximumValidVersion + 1)); - BEAST_EXPECT(!s1.valid(std::numeric_limits::max())); + BEAST_EXPECT( + !s1.valid(std::numeric_limits::max())); int result = 1; static_assert(RPC::apiMinimumSupportedVersion + 1 <= RPC::apiMaximumValidVersion); @@ -84,7 +87,8 @@ struct MultiApiJson_test : beast::unit_test::suite std::as_const(s1).visit(), [this](Json::Value const& json, unsigned int version, int* result) { BEAST_EXPECT( - version >= RPC::apiMinimumSupportedVersion && version <= RPC::apiMinimumSupportedVersion + 1); + version >= RPC::apiMinimumSupportedVersion && + version <= RPC::apiMinimumSupportedVersion + 1); if (BEAST_EXPECT(json.isMember("value"))) { *result *= json["value"].asInt(); @@ -92,7 +96,9 @@ struct MultiApiJson_test : beast::unit_test::suite }, &result); BEAST_EXPECT( - result == primes[RPC::apiMinimumSupportedVersion] * primes[RPC::apiMinimumSupportedVersion + 1]); + result == + primes[RPC::apiMinimumSupportedVersion] * + primes[RPC::apiMinimumSupportedVersion + 1]); // Check all the values with mutable data forAllApiVersions(s1.visit(), [&s1, this](Json::Value& json, auto version) { @@ -107,7 +113,9 @@ struct MultiApiJson_test : beast::unit_test::suite forAllApiVersions( std::as_const(s1).visit(), [this](Json::Value const& json, unsigned int version, int* result) { - BEAST_EXPECT(version >= RPC::apiMinimumSupportedVersion && version <= RPC::apiMaximumValidVersion); + BEAST_EXPECT( + version >= RPC::apiMinimumSupportedVersion && + version <= RPC::apiMaximumValidVersion); if (BEAST_EXPECT(json.isMember("value"))) { *result *= json["value"].asInt(); @@ -207,7 +215,10 @@ struct MultiApiJson_test : beast::unit_test::suite forAllApiVersions( std::forward(v).visit(), // []( - Json::Value const&, std::integral_constant, int, char const*) {}, + Json::Value const&, + std::integral_constant, + int, + char const*) {}, 0, ""); }; @@ -355,12 +366,16 @@ struct MultiApiJson_test : beast::unit_test::suite s1, std::integral_constant{}, Overload{ - [](Json::Value& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value const&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 2); static_assert([](auto&& v) { - return requires { v.visitor(v, std::integral_constant{}, [](Json::Value&) {}); }; + return requires { + v.visitor(v, std::integral_constant{}, [](Json::Value&) {}); + }; }(s1)); BEAST_EXPECT( s1.visitor( @@ -384,12 +399,16 @@ struct MultiApiJson_test : beast::unit_test::suite std::as_const(s1), std::integral_constant{}, Overload{ - [](Json::Value const& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value const& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 3); static_assert([](auto&& v) { - return requires { v.visitor(v, std::integral_constant{}, [](Json::Value const&) {}); }; + return requires { + v.visitor(v, std::integral_constant{}, [](Json::Value const&) {}); + }; }(std::as_const(s1))); BEAST_EXPECT( s1.visitor( @@ -400,7 +419,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value&) { return 0; }, [](auto...) { return 0; }}) == 3); - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&, unsigned) {}); }; }(s1)); + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](Json::Value&, unsigned) {}); }; + }(s1)); BEAST_EXPECT( s1.visitor( s1, // @@ -410,7 +431,8 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value const&, unsigned) { return 0; }, [](auto, auto) { return 0; }}) == 5); - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&) {}); }; }(s1)); + static_assert( + [](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&) {}); }; }(s1)); BEAST_EXPECT( s1.visitor( s1, // @@ -432,8 +454,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value const&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 3); - static_assert( - [](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value const&) {}); }; }(std::as_const(s1))); + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](Json::Value const&) {}); }; + }(std::as_const(s1))); BEAST_EXPECT( s1.visitor( std::as_const(s1), // @@ -505,14 +528,18 @@ struct MultiApiJson_test : beast::unit_test::suite s1.visitor( s1, std::integral_constant{}, - [](Json::Value& v, auto ver, auto a1, auto a2) { return ver * a1 * a2 * v["value"].asInt(); }, + [](Json::Value& v, auto ver, auto a1, auto a2) { + return ver * a1 * a2 * v["value"].asInt(); + }, 5, 7) == 2 * 5 * 7 * 3); BEAST_EXPECT( s1.visitor( s1, std::integral_constant{}, - [](Json::Value& v, auto ver, auto... args) { return ver * (1 * ... * args) * v["value"].asInt(); }, + [](Json::Value& v, auto ver, auto... args) { + return ver * (1 * ... * args) * v["value"].asInt(); + }, 5, 7) == 2 * 5 * 7 * 3); @@ -529,7 +556,7 @@ struct MultiApiJson_test : beast::unit_test::suite static_assert([](auto&& v) { return !requires { v.visitor( - std::move(v), // cannot bind rvalue + decltype(v){}, // cannot bind rvalue 1, [](Json::Value&, auto) {}); }; @@ -556,25 +583,38 @@ struct MultiApiJson_test : beast::unit_test::suite // Want these to be unambiguous static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](auto) {}); }; }(s1)); - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&) {}); }; }(s1)); + static_assert( + [](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&) {}); }; }(s1)); - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value&, auto...) {}); }; }(s1)); + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](Json::Value&, auto...) {}); }; + }(s1)); - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value const&) {}); }; }(s1)); + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](Json::Value const&) {}); }; + }(s1)); + + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](Json::Value const&, auto...) {}); }; + }(s1)); static_assert( - [](auto&& v) { return requires { v.visitor(v, 1, [](Json::Value const&, auto...) {}); }; }(s1)); - - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](auto...) {}); }; }(s1)); - - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](auto, auto...) {}); }; }(s1)); - - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](auto, auto, auto...) {}); }; }(s1)); - - static_assert([](auto&& v) { return requires { v.visitor(v, 1, [](auto, auto, auto...) {}, ""); }; }(s1)); + [](auto&& v) { return requires { v.visitor(v, 1, [](auto...) {}); }; }(s1)); static_assert( - [](auto&& v) { return requires { v.visitor(v, 1, [](auto, auto, auto, auto...) {}, ""); }; }(s1)); + [](auto&& v) { return requires { v.visitor(v, 1, [](auto, auto...) {}); }; }(s1)); + + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](auto, auto, auto...) {}); }; + }(s1)); + + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](auto, auto, auto...) {}, ""); }; + }(s1)); + + static_assert([](auto&& v) { + return requires { v.visitor(v, 1, [](auto, auto, auto, auto...) {}, ""); }; + }(s1)); } { @@ -597,7 +637,9 @@ struct MultiApiJson_test : beast::unit_test::suite s1.visit( std::integral_constant{}, Overload{ - [](Json::Value& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value const&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 2); static_assert([](auto&& v) { @@ -611,12 +653,16 @@ struct MultiApiJson_test : beast::unit_test::suite s1.visit()( std::integral_constant{}, Overload{ - [](Json::Value& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value const&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 2); static_assert([](auto&& v) { - return requires { v.visit(std::integral_constant{}, [](Json::Value&) {}); }; + return requires { + v.visit(std::integral_constant{}, [](Json::Value&) {}); + }; }(s1)); BEAST_EXPECT( s1.visit( @@ -626,7 +672,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value const&) { return 0; }, [](auto...) { return 0; }}) == 2); static_assert([](auto&& v) { - return requires { v.visit()(std::integral_constant{}, [](Json::Value&) {}); }; + return requires { + v.visit()(std::integral_constant{}, [](Json::Value&) {}); + }; }(s1)); BEAST_EXPECT( s1.visit()( @@ -647,7 +695,9 @@ struct MultiApiJson_test : beast::unit_test::suite std::as_const(s1).visit( std::integral_constant{}, Overload{ - [](Json::Value const& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value const& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 3); static_assert([](auto&& v) { @@ -661,12 +711,16 @@ struct MultiApiJson_test : beast::unit_test::suite std::as_const(s1).visit()( std::integral_constant{}, Overload{ - [](Json::Value const& v, std::integral_constant) { return v["value"].asInt(); }, + [](Json::Value const& v, std::integral_constant) { + return v["value"].asInt(); + }, [](Json::Value&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 3); static_assert([](auto&& v) { - return requires { v.visit(std::integral_constant{}, [](Json::Value const&) {}); }; + return requires { + v.visit(std::integral_constant{}, [](Json::Value const&) {}); + }; }(std::as_const(s1))); BEAST_EXPECT( std::as_const(s1).visit( @@ -676,7 +730,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value&) { return 0; }, [](auto...) { return 0; }}) == 3); static_assert([](auto&& v) { - return requires { v.visit()(std::integral_constant{}, [](Json::Value const&) {}); }; + return requires { + v.visit()(std::integral_constant{}, [](Json::Value const&) {}); + }; }(std::as_const(s1))); BEAST_EXPECT( std::as_const(s1).visit()( @@ -686,7 +742,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value&) { return 0; }, [](auto...) { return 0; }}) == 3); - static_assert([](auto&& v) { return requires { v.visit(1, [](Json::Value&, unsigned) {}); }; }(s1)); + static_assert([](auto&& v) { + return requires { v.visit(1, [](Json::Value&, unsigned) {}); }; + }(s1)); BEAST_EXPECT( s1.visit( 3u, @@ -695,7 +753,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value const&, unsigned) { return 0; }, [](Json::Value&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 5); - static_assert([](auto&& v) { return requires { v.visit()(1, [](Json::Value&, unsigned) {}); }; }(s1)); + static_assert([](auto&& v) { + return requires { v.visit()(1, [](Json::Value&, unsigned) {}); }; + }(s1)); BEAST_EXPECT( s1.visit()( 3u, @@ -705,7 +765,8 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value&, auto) { return 0; }, [](auto, auto) { return 0; }}) == 5); - static_assert([](auto&& v) { return requires { v.visit(1, [](Json::Value&) {}); }; }(s1)); + static_assert( + [](auto&& v) { return requires { v.visit(1, [](Json::Value&) {}); }; }(s1)); BEAST_EXPECT( s1.visit( 3, @@ -713,7 +774,8 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value& v) { return v["value"].asInt(); }, [](Json::Value const&) { return 0; }, [](auto...) { return 0; }}) == 5); - static_assert([](auto&& v) { return requires { v.visit()(1, [](Json::Value&) {}); }; }(s1)); + static_assert( + [](auto&& v) { return requires { v.visit()(1, [](Json::Value&) {}); }; }(s1)); BEAST_EXPECT( s1.visit()( 3, @@ -745,8 +807,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value&, unsigned) { return 0; }, [](auto, auto) { return 0; }}) == 3); - static_assert( - [](auto&& v) { return requires { v.visit(1, [](Json::Value const&) {}); }; }(std::as_const(s1))); + static_assert([](auto&& v) { + return requires { v.visit(1, [](Json::Value const&) {}); }; + }(std::as_const(s1))); BEAST_EXPECT( std::as_const(s1).visit( 2, @@ -754,8 +817,9 @@ struct MultiApiJson_test : beast::unit_test::suite [](Json::Value const& v) { return v["value"].asInt(); }, [](Json::Value&) { return 0; }, [](auto...) { return 0; }}) == 3); - static_assert( - [](auto&& v) { return requires { v.visit()(1, [](Json::Value const&) {}); }; }(std::as_const(s1))); + static_assert([](auto&& v) { + return requires { v.visit()(1, [](Json::Value const&) {}); }; + }(std::as_const(s1))); BEAST_EXPECT( std::as_const(s1).visit()( 2, @@ -766,53 +830,79 @@ struct MultiApiJson_test : beast::unit_test::suite // Rvalue MultivarJson visitor only binds to regular reference static_assert([](auto&& v) { + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) return !requires { std::forward(v).visit(1, [](Json::Value&&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { - return !requires { std::forward(v).visit(1, [](Json::Value const&&) {}); }; + return !requires { + std::forward(v).visit(1, [](Json::Value const&&) {}); + }; }(std::move(s1))); static_assert([](auto&& v) { return requires { std::forward(v).visit(1, [](Json::Value&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { - return requires { std::forward(v).visit(1, [](Json::Value const&) {}); }; + return requires { + std::forward(v).visit(1, [](Json::Value const&) {}); + }; }(std::move(s1))); static_assert([](auto&& v) { + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) return !requires { std::forward(v).visit()(1, [](Json::Value&&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { - return !requires { std::forward(v).visit()(1, [](Json::Value const&&) {}); }; + return !requires { + std::forward(v).visit()(1, [](Json::Value const&&) {}); + }; }(std::move(s1))); static_assert([](auto&& v) { return requires { std::forward(v).visit()(1, [](Json::Value&) {}); }; }(std::move(s1))); static_assert([](auto&& v) { - return requires { std::forward(v).visit()(1, [](Json::Value const&) {}); }; + return requires { + std::forward(v).visit()(1, [](Json::Value const&) {}); + }; }(std::move(s1))); static_assert([](auto&& v) { - return !requires { std::forward(v).visit(1, [](Json::Value const&&) {}); }; + return !requires { + std::forward(v).visit(1, [](Json::Value const&&) {}); + }; }(std::move(std::as_const(s1)))); static_assert([](auto&& v) { - return requires { std::forward(v).visit(1, [](Json::Value const&) {}); }; + return requires { + std::forward(v).visit(1, [](Json::Value const&) {}); + }; }(std::move(std::as_const(s1)))); static_assert([](auto&& v) { - return !requires { std::forward(v).visit()(1, [](Json::Value const&&) {}); }; + return !requires { + std::forward(v).visit()(1, [](Json::Value const&&) {}); + }; }(std::move(std::as_const(s1)))); static_assert([](auto&& v) { - return requires { std::forward(v).visit()(1, [](Json::Value const&) {}); }; + return requires { + std::forward(v).visit()(1, [](Json::Value const&) {}); + }; }(std::move(std::as_const(s1)))); // Missing const static_assert([](auto&& v) { - return !requires { std::forward(v).visit(1, [](Json::Value&, auto) {}); }; + return !requires { + std::forward(v).visit(1, [](Json::Value&, auto) {}); + }; }(std::as_const(s1))); static_assert([](auto&& v) { - return !requires { std::forward(v).visit()(1, [](Json::Value&, auto) {}); }; + return !requires { + std::forward(v).visit()(1, [](Json::Value&, auto) {}); + }; }(std::as_const(s1))); // Missing parameter - static_assert([](auto&& v) { return !requires { std::forward(v).visit(1, []() {}); }; }(s1)); - static_assert([](auto&& v) { return !requires { std::forward(v).visit()(1, []() {}); }; }(s1)); + static_assert([](auto&& v) { + return !requires { std::forward(v).visit(1, []() {}); }; + }(s1)); + static_assert([](auto&& v) { + return !requires { std::forward(v).visit()(1, []() {}); }; + }(s1)); // Sanity checks static_assert([](auto&& v) { diff --git a/src/test/protocol/PublicKey_test.cpp b/src/test/protocol/PublicKey_test.cpp index cad1e87198..053e945a7b 100644 --- a/src/test/protocol/PublicKey_test.cpp +++ b/src/test/protocol/PublicKey_test.cpp @@ -17,7 +17,7 @@ public: { struct Table { - int val[256]; + int val[256]{}; Table() { std::fill(val, val + 256, 0); @@ -296,7 +296,8 @@ public: BEAST_EXPECT(!parseBase58(TokenType::NodePublic, " ")); BEAST_EXPECT(!parseBase58(TokenType::NodePublic, "!ty89234gh45")); - auto const good = toBase58(TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey())); + auto const good = + toBase58(TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey())); // Short (non-empty) strings { @@ -369,7 +370,8 @@ public: auto const skj = parseBase58(TokenType::NodePublic, sj); BEAST_EXPECT(skj && (keys[j] == *skj)); - BEAST_EXPECT((*ski == *skj) == (i == j)); + BEAST_EXPECT( + (*ski == *skj) == (i == j)); // NOLINT(bugprone-unchecked-optional-access) } } } @@ -381,13 +383,14 @@ public: { auto const pk1 = derivePublicKey( - KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase"))); + KeyType::secp256k1, + generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase"))); - auto const pk2 = - parseBase58(TokenType::NodePublic, "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); + auto const pk2 = parseBase58( + TokenType::NodePublic, "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::secp256k1); @@ -396,13 +399,14 @@ public: { auto const pk1 = derivePublicKey( - KeyType::ed25519, generateSecretKey(KeyType::ed25519, generateSeed("masterpassphrase"))); + KeyType::ed25519, + generateSecretKey(KeyType::ed25519, generateSeed("masterpassphrase"))); - auto const pk2 = - parseBase58(TokenType::NodePublic, "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); + auto const pk2 = parseBase58( + TokenType::NodePublic, "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::ed25519); @@ -414,14 +418,16 @@ public: testcase("Miscellaneous operations"); auto const pk1 = derivePublicKey( - KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase"))); + KeyType::secp256k1, + generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase"))); PublicKey pk2(pk1); BEAST_EXPECT(pk1 == pk2); BEAST_EXPECT(pk2 == pk1); PublicKey pk3 = derivePublicKey( - KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, generateSeed("arbitraryPassPhrase"))); + KeyType::secp256k1, + generateSecretKey(KeyType::secp256k1, generateSeed("arbitraryPassPhrase"))); // Testing the copy assignment operation of PublicKey class pk3 = pk2; BEAST_EXPECT(pk3 == pk2); diff --git a/src/test/protocol/STAccount_test.cpp b/src/test/protocol/STAccount_test.cpp index 76b633df80..3b12605a92 100644 --- a/src/test/protocol/STAccount_test.cpp +++ b/src/test/protocol/STAccount_test.cpp @@ -12,7 +12,7 @@ struct STAccount_test : public beast::unit_test::suite // Test default constructor. STAccount const defaultAcct; BEAST_EXPECT(defaultAcct.getSType() == STI_ACCOUNT); - BEAST_EXPECT(defaultAcct.getText() == ""); + BEAST_EXPECT(defaultAcct.getText().empty()); BEAST_EXPECT(defaultAcct.isDefault() == true); BEAST_EXPECT(defaultAcct.value() == AccountID{}); { @@ -38,7 +38,7 @@ struct STAccount_test : public beast::unit_test::suite // Test constructor from SField. STAccount const sfAcct{sfAccount}; BEAST_EXPECT(sfAcct.getSType() == STI_ACCOUNT); - BEAST_EXPECT(sfAcct.getText() == ""); + BEAST_EXPECT(sfAcct.getText().empty()); BEAST_EXPECT(sfAcct.isDefault()); BEAST_EXPECT(sfAcct.value() == AccountID{}); BEAST_EXPECT(sfAcct.isEquivalent(defaultAcct)); @@ -106,7 +106,7 @@ struct STAccount_test : public beast::unit_test::suite auto const s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; if (auto const parsed = parseBase58(s); BEAST_EXPECT(parsed)) { - BEAST_EXPECT(toBase58(*parsed) == s); + BEAST_EXPECT(toBase58(*parsed) == s); // NOLINT(bugprone-unchecked-optional-access) } { diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index 98bcc7b1bc..c9382d508a 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -37,7 +37,12 @@ public: if (mantissa < STAmount::cMinValue) return {amount.issue(), mantissa, amount.exponent(), amount.negative()}; - return {amount.issue(), mantissa, amount.exponent(), amount.negative(), STAmount::unchecked{}}; + return { + amount.issue(), + mantissa, + amount.exponent(), + amount.negative(), + STAmount::unchecked{}}; } if (valueDigits == 999999999) @@ -47,7 +52,12 @@ public: if (mantissa > STAmount::cMaxValue) return {amount.issue(), mantissa, amount.exponent(), amount.negative()}; - return {amount.issue(), mantissa, amount.exponent(), amount.negative(), STAmount::unchecked{}}; + return { + amount.issue(), + mantissa, + amount.exponent(), + amount.negative(), + STAmount::unchecked{}}; } return amount; @@ -73,8 +83,8 @@ public: if (res != cmp) { - log << "(" << num.getText() << "/" << den.getText() << ") X " << mul.getText() << " = " << res.getText() - << " not " << cmp.getText(); + log << "(" << num.getText() << "/" << den.getText() << ") X " << mul.getText() << " = " + << res.getText() << " not " << cmp.getText(); fail("Rounding"); return; } @@ -93,8 +103,8 @@ public: if (prod1 != prod2) { - log << "nn(" << aa.getFullText() << " * " << bb.getFullText() << ") = " << prod1.getFullText() << " not " - << prod2.getFullText(); + log << "nn(" << aa.getFullText() << " * " << bb.getFullText() + << ") = " << prod1.getFullText() << " not " << prod2.getFullText(); fail("Multiplication result is not exact"); } } @@ -338,11 +348,17 @@ public: unexpected(STAmount(noIssue(), 31, -1).getText() != "3.1", "STAmount fail"); unexpected(STAmount(noIssue(), 31, -2).getText() != "0.31", "STAmount fail"); unexpected( - multiply(STAmount(noIssue(), 20), STAmount(3), noIssue()).getText() != "60", "STAmount multiply fail 1"); + multiply(STAmount(noIssue(), 20), STAmount(3), noIssue()).getText() != "60", + "STAmount multiply fail 1"); unexpected( - multiply(STAmount(noIssue(), 20), STAmount(3), xrpIssue()).getText() != "60", "STAmount multiply fail 2"); - unexpected(multiply(STAmount(20), STAmount(3), noIssue()).getText() != "60", "STAmount multiply fail 3"); - unexpected(multiply(STAmount(20), STAmount(3), xrpIssue()).getText() != "60", "STAmount multiply fail 4"); + multiply(STAmount(noIssue(), 20), STAmount(3), xrpIssue()).getText() != "60", + "STAmount multiply fail 2"); + unexpected( + multiply(STAmount(20), STAmount(3), noIssue()).getText() != "60", + "STAmount multiply fail 3"); + unexpected( + multiply(STAmount(20), STAmount(3), xrpIssue()).getText() != "60", + "STAmount multiply fail 4"); if (divide(STAmount(noIssue(), 60), STAmount(3), noIssue()).getText() != "20") { @@ -354,7 +370,9 @@ public: pass(); } - unexpected(divide(STAmount(noIssue(), 60), STAmount(3), xrpIssue()).getText() != "20", "STAmount divide fail"); + unexpected( + divide(STAmount(noIssue(), 60), STAmount(3), xrpIssue()).getText() != "20", + "STAmount divide fail"); unexpected( divide(STAmount(noIssue(), 60), STAmount(noIssue(), 3), noIssue()).getText() != "20", @@ -366,9 +384,13 @@ public: STAmount a1(noIssue(), 60), a2(noIssue(), 10, -1); - unexpected(divide(a2, a1, noIssue()) != amountFromQuality(getRate(a1, a2)), "STAmount setRate(getRate) fail"); + unexpected( + divide(a2, a1, noIssue()) != amountFromQuality(getRate(a1, a2)), + "STAmount setRate(getRate) fail"); - unexpected(divide(a1, a2, noIssue()) != amountFromQuality(getRate(a2, a1)), "STAmount setRate(getRate) fail"); + unexpected( + divide(a1, a2, noIssue()) != amountFromQuality(getRate(a2, a1)), + "STAmount setRate(getRate) fail"); } //-------------------------------------------------------------------------- @@ -383,11 +405,13 @@ public: // and getNeeded unexpected( - getRate(STAmount(1), STAmount(10)) != (((100ull - 14) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(1), STAmount(10)) != + (((100ull - 14) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 1"); unexpected( - getRate(STAmount(10), STAmount(1)) != (((100ull - 16) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(10), STAmount(1)) != + (((100ull - 16) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 2"); unexpected( @@ -401,19 +425,23 @@ public: "STAmount getRate fail 4"); unexpected( - getRate(STAmount(noIssue(), 1), STAmount(10)) != (((100ull - 14) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(noIssue(), 1), STAmount(10)) != + (((100ull - 14) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 5"); unexpected( - getRate(STAmount(noIssue(), 10), STAmount(1)) != (((100ull - 16) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(noIssue(), 10), STAmount(1)) != + (((100ull - 16) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 6"); unexpected( - getRate(STAmount(1), STAmount(noIssue(), 10)) != (((100ull - 14) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(1), STAmount(noIssue(), 10)) != + (((100ull - 14) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 7"); unexpected( - getRate(STAmount(10), STAmount(noIssue(), 1)) != (((100ull - 16) << (64 - 8)) | 1000000000000000ull), + getRate(STAmount(10), STAmount(noIssue(), 1)) != + (((100ull - 16) << (64 - 8)) | 1000000000000000ull), "STAmount getRate fail 8"); roundTest(1, 3, 3); @@ -437,8 +465,10 @@ public: testcase("underflow"); STAmount bigNative(STAmount::cMaxNative / 2); - STAmount bigValue(noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMaxOffset - 1); - STAmount smallValue(noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMinOffset + 1); + STAmount bigValue( + noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMaxOffset - 1); + STAmount smallValue( + noIssue(), (STAmount::cMinValue + STAmount::cMaxValue) / 2, STAmount::cMinOffset + 1); STAmount zeroSt(noIssue(), 0); STAmount smallXSmall = multiply(smallValue, smallValue, noIssue()); diff --git a/src/test/protocol/STInteger_test.cpp b/src/test/protocol/STInteger_test.cpp index 74340d53ea..937dbd1a7f 100644 --- a/src/test/protocol/STInteger_test.cpp +++ b/src/test/protocol/STInteger_test.cpp @@ -21,7 +21,8 @@ struct STInteger_test : public beast::unit_test::suite // there is some special handling for sfTransactionResult STUInt8 tr(sfTransactionResult, 0); BEAST_EXPECT(tr.value() == 0); - BEAST_EXPECT(tr.getText() == "The transaction was applied. Only final in a validated ledger."); + BEAST_EXPECT( + tr.getText() == "The transaction was applied. Only final in a validated ledger."); BEAST_EXPECT(tr.getSType() == STI_UINT8); BEAST_EXPECT(tr.getJson(JsonOptions::none) == "tesSUCCESS"); diff --git a/src/test/protocol/STIssue_test.cpp b/src/test/protocol/STIssue_test.cpp index cfd047e752..7e6fe5487c 100644 --- a/src/test/protocol/STIssue_test.cpp +++ b/src/test/protocol/STIssue_test.cpp @@ -123,8 +123,11 @@ public: BEAST_EXPECT(STIssue(sfAsset, asset1) != asset3); BEAST_EXPECT(STIssue(sfAsset, asset1) == asset1); BEAST_EXPECT(STIssue(sfAsset, asset1).getText() == "XRP"); - BEAST_EXPECT(STIssue(sfAsset, asset2).getText() == "USD/rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn"); - BEAST_EXPECT(STIssue(sfAsset, asset3).getText() == "000000000000000000000000000000000000000000000002"); + BEAST_EXPECT( + STIssue(sfAsset, asset2).getText() == "USD/rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn"); + BEAST_EXPECT( + STIssue(sfAsset, asset3).getText() == + "000000000000000000000000000000000000000000000002"); } void diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index a1a94accaa..9d43ace638 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -40,11 +40,16 @@ struct STNumber_test : public beast::unit_test::suite } std::initializer_list const mantissas = { - std::numeric_limits::min(), -1, 0, 1, std::numeric_limits::max()}; + std::numeric_limits::min(), + -1, + 0, + 1, + std::numeric_limits::max()}; for (std::int64_t mantissa : mantissas) testCombo(Number{mantissa}); - std::initializer_list const exponents = {Number::minExponent, -1, 0, 1, Number::maxExponent - 1}; + std::initializer_list const exponents = { + Number::minExponent, -1, 0, 1, Number::maxExponent - 1}; for (std::int32_t exponent : exponents) testCombo(Number{123, exponent}); @@ -98,31 +103,42 @@ struct STNumber_test : public beast::unit_test::suite if (Number::getMantissaScale() == MantissaRange::small) { BEAST_EXPECT( - numberFromJson(sfNumber, maxInt) == STNumber(sfNumber, Number{9'223'372'036'854'775, 3})); + numberFromJson(sfNumber, maxInt) == + STNumber(sfNumber, Number{9'223'372'036'854'775, 3})); BEAST_EXPECT( - numberFromJson(sfNumber, minInt) == STNumber(sfNumber, Number{-9'223'372'036'854'775, 3})); + numberFromJson(sfNumber, minInt) == + STNumber(sfNumber, Number{-9'223'372'036'854'775, 3})); } else { BEAST_EXPECT( - numberFromJson(sfNumber, maxInt) == STNumber(sfNumber, Number{9'223'372'036'854'775'807, 0})); + numberFromJson(sfNumber, maxInt) == + STNumber(sfNumber, Number{9'223'372'036'854'775'807, 0})); BEAST_EXPECT( numberFromJson(sfNumber, minInt) == - STNumber(sfNumber, Number{true, 9'223'372'036'854'775'808ULL, 0, Number::normalized{}})); + STNumber( + sfNumber, + Number{true, 9'223'372'036'854'775'808ULL, 0, Number::normalized{}})); } } constexpr auto imin = std::numeric_limits::min(); BEAST_EXPECT(numberFromJson(sfNumber, imin) == STNumber(sfNumber, Number(imin, 0))); - BEAST_EXPECT(numberFromJson(sfNumber, std::to_string(imin)) == STNumber(sfNumber, Number(imin, 0))); + BEAST_EXPECT( + numberFromJson(sfNumber, std::to_string(imin)) == + STNumber(sfNumber, Number(imin, 0))); constexpr auto imax = std::numeric_limits::max(); BEAST_EXPECT(numberFromJson(sfNumber, imax) == STNumber(sfNumber, Number(imax, 0))); - BEAST_EXPECT(numberFromJson(sfNumber, std::to_string(imax)) == STNumber(sfNumber, Number(imax, 0))); + BEAST_EXPECT( + numberFromJson(sfNumber, std::to_string(imax)) == + STNumber(sfNumber, Number(imax, 0))); constexpr auto umax = std::numeric_limits::max(); BEAST_EXPECT(numberFromJson(sfNumber, umax) == STNumber(sfNumber, Number(umax, 0))); - BEAST_EXPECT(numberFromJson(sfNumber, std::to_string(umax)) == STNumber(sfNumber, Number(umax, 0))); + BEAST_EXPECT( + numberFromJson(sfNumber, std::to_string(umax)) == + STNumber(sfNumber, Number(umax, 0))); // Obvious non-numbers tested here try diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 861a1ab967..0faa1946a9 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -63,7 +63,9 @@ public: unexpected(object1.getSerializer() != object2.getSerializer(), "STObject error 1"); - unexpected(object1.isFieldPresent(sfTestH256) || !object1.isFieldPresent(sfTestVL), "STObject error"); + unexpected( + object1.isFieldPresent(sfTestH256) || !object1.isFieldPresent(sfTestVL), + "STObject error"); object1.makeFieldPresent(sfTestH256); @@ -167,8 +169,8 @@ public: BEAST_EXPECT(st[sf1Outer] == 1); BEAST_EXPECT(st[sf2Outer] == 2); except([&]() { st[sf3Outer]; }); - BEAST_EXPECT(*st[~sf1Outer] == 1); - BEAST_EXPECT(*st[~sf2Outer] == 2); + BEAST_EXPECT(*st[~sf1Outer] == 1); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf2Outer] == 2); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(st[~sf3Outer] == std::nullopt); BEAST_EXPECT(!!st[~sf1Outer]); BEAST_EXPECT(!!st[~sf2Outer]); @@ -198,9 +200,9 @@ public: BEAST_EXPECT(st[sf1Outer] == 1); BEAST_EXPECT(st[sf2Outer] == 2); BEAST_EXPECT(st[sf3Outer] == 0); - BEAST_EXPECT(*st[~sf1Outer] == 1); - BEAST_EXPECT(*st[~sf2Outer] == 2); - BEAST_EXPECT(*st[~sf3Outer] == 0); + BEAST_EXPECT(*st[~sf1Outer] == 1); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf2Outer] == 2); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(*st[~sf3Outer] == 0); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!!st[~sf1Outer]); BEAST_EXPECT(!!st[~sf2Outer]); BEAST_EXPECT(!!st[~sf3Outer]); @@ -325,7 +327,8 @@ public: { STObject st(sfGeneric); auto const v = ~st[~sf1Outer]; - static_assert(std::is_same, std::optional>::value, ""); + static_assert( + std::is_same, std::optional>::value, ""); } // UDT scalar fields @@ -348,7 +351,7 @@ public: Buffer b(1); BEAST_EXPECT(!b.empty()); st[sf4] = std::move(b); - BEAST_EXPECT(b.empty()); + BEAST_EXPECT(b.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(Slice(st[sf4]).size() == 1); st[~sf4] = std::nullopt; BEAST_EXPECT(!~st[~sf4]); @@ -367,7 +370,7 @@ public: BEAST_EXPECT(!!~st[~sf5]); Buffer b(1); st[sf5] = std::move(b); - BEAST_EXPECT(b.empty()); + BEAST_EXPECT(b.empty()); // NOLINT(bugprone-use-after-move) BEAST_EXPECT(Slice(st[sf5]).size() == 1); st[~sf4] = std::nullopt; BEAST_EXPECT(!~st[~sf4]); @@ -396,10 +399,11 @@ public: st[sf] = std::move(v); auto const& cst = st; BEAST_EXPECT(cst[sf].size() == 2); - BEAST_EXPECT(cst[~sf]->size() == 2); + BEAST_EXPECT(cst[~sf]->size() == 2); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(cst[sf][0] == 1); BEAST_EXPECT(cst[sf][1] == 2); - static_assert(std::is_same const&>::value, ""); + static_assert( + std::is_same const&>::value, ""); } // Default by reference field @@ -416,9 +420,9 @@ public: STObject st(sot, sfGeneric); auto const& cst(st); - BEAST_EXPECT(cst[sf1].size() == 0); + BEAST_EXPECT(cst[sf1].empty()); BEAST_EXPECT(!cst[~sf2]); - BEAST_EXPECT(cst[sf3].size() == 0); + BEAST_EXPECT(cst[sf3].empty()); std::vector v; v.emplace_back(1); st[sf1] = v; @@ -433,7 +437,7 @@ public: BEAST_EXPECT(cst[sf3].size() == 1); BEAST_EXPECT(cst[sf3][0] == uint256{1}); st[sf3] = std::vector{}; - BEAST_EXPECT(cst[sf3].size() == 0); + BEAST_EXPECT(cst[sf3].empty()); } } // namespace xrpl diff --git a/src/test/protocol/STParsedJSON_test.cpp b/src/test/protocol/STParsedJSON_test.cpp index 0ef7878cdc..18e695d25d 100644 --- a/src/test/protocol/STParsedJSON_test.cpp +++ b/src/test/protocol/STParsedJSON_test.cpp @@ -27,7 +27,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 255; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -37,7 +39,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 255u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -47,7 +51,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = "255"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255); } @@ -57,6 +63,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfCloseResolution] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 0); } @@ -103,7 +110,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 65535; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -113,7 +122,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 65535u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -123,7 +134,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = "65535"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); } @@ -133,6 +146,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerEntryType] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 0); } @@ -194,7 +208,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = 4294967295u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u); } @@ -204,7 +220,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = "4294967295"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u); } @@ -214,6 +232,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNetworkID] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 0); } @@ -259,7 +278,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfIndexNext] = "ffffffffffffffff"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfIndexNext)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 18446744073709551615ull); } @@ -269,6 +290,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfIndexNext] = 0; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 0ull); } @@ -333,10 +355,28 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); std::array expected = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + 0x01, + 0x23, + 0x45, + 0x67, + 0x89, + 0xAB, + 0xCD, + 0xEF, + 0x01, + 0x23, + 0x45, + 0x67, + 0x89, + 0xAB, + 0xCD, + 0xEF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128{expected}); } @@ -346,7 +386,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = "0123456789abcdef0123456789abcdef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); } @@ -356,7 +398,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfEmailHash] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h128 = obj.object->getFieldH128(sfEmailHash); BEAST_EXPECT(h128.size() == 16); bool allZero = std::all_of(h128.begin(), h128.end(), [](auto b) { return b == 0; }); @@ -421,10 +465,14 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); - std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, - 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; + std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, + 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, + 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency) == uint160{expected}); } // Valid lowercase hex string for UInt160 @@ -433,7 +481,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = "0123456789abcdef0123456789abcdef01234567"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); } @@ -443,7 +493,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTakerPaysCurrency] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h160 = obj.object->getFieldH160(sfTakerPaysCurrency); BEAST_EXPECT(h160.size() == 20); bool allZero = std::all_of(h160.begin(), h160.end(), [](auto b) { return b == 0; }); @@ -500,10 +552,14 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); - std::array expected = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + std::array expected = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192{expected}); } @@ -513,7 +569,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = "ffffffffffffffffffffffffffffffffffffffffffffffff"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); } @@ -523,7 +581,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfMPTokenIssuanceID] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h192 = obj.object->getFieldH192(sfMPTokenIssuanceID); BEAST_EXPECT(h192.size() == 24); bool allZero = std::all_of(h192.begin(), h192.end(), [](auto b) { return b == 0; }); @@ -591,11 +651,15 @@ class STParsedJSON_test : public beast::unit_test::suite "EF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); - std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, - 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, - 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + std::array expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256{expected}); } // Valid lowercase hex string for UInt256 @@ -606,7 +670,9 @@ class STParsedJSON_test : public beast::unit_test::suite "ef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); } @@ -616,7 +682,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLedgerHash] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& h256 = obj.object->getFieldH256(sfLedgerHash); BEAST_EXPECT(h256.size() == 32); bool allZero = std::all_of(h256.begin(), h256.end(), [](auto b) { return b == 0; }); @@ -686,8 +754,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = minInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == minInt32); + } } // max value @@ -697,8 +769,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = maxInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == maxInt32); + } } // max uint value @@ -708,8 +784,13 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = maxUInt32; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) - BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == static_cast(maxUInt32)); + { + BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + obj.object->getFieldI32(sfLoanScale) == static_cast(maxUInt32)); + } } // Test with string value @@ -718,8 +799,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = "2147483647"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == 2147483647u); + } } // Test with string negative value @@ -729,8 +814,12 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfLoanScale] = std::to_string(value); STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale))) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == value); + } } // Test out of range value for int32 (negative) @@ -784,8 +873,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = "DEADBEEF"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(blob.size() == 4); BEAST_EXPECT(blob[0] == 0xDE); BEAST_EXPECT(blob[1] == 0xAD); @@ -799,9 +890,11 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); - BEAST_EXPECT(blob.size() == 0); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(blob.empty()); } // Test lowercase hex string for blob @@ -810,8 +903,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPublicKey] = "deadbeef"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); - auto const& blob = obj.object->getFieldVL(sfPublicKey); + auto const& blob = + obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(blob.size() == 4); BEAST_EXPECT(blob[0] == 0xDE); BEAST_EXPECT(blob[1] == 0xAD); @@ -861,8 +956,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfHashes] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfHashes)); - auto const& vec = obj.object->getFieldV256(sfHashes); + auto const& vec = + obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(vec.size() == 2); BEAST_EXPECT(to_string(vec[0]) == arr[0u].asString()); BEAST_EXPECT(to_string(vec[1]) == arr[1u].asString()); @@ -874,9 +971,11 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfHashes] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfHashes)); - auto const& vec = obj.object->getFieldV256(sfHashes); - BEAST_EXPECT(vec.size() == 0); + auto const& vec = + obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access) + BEAST_EXPECT(vec.empty()); } // Test array with invalid hex string (should fail) @@ -940,8 +1039,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAccount] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAccount)); - auto const& acct = obj.object->getAccountID(sfAccount); + auto const& acct = + obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(acct.size() == 20); BEAST_EXPECT(toBase58(acct) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); } @@ -952,8 +1053,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAccount] = "000102030405060708090A0B0C0D0E0F10111213"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAccount)); - auto const& acct = obj.object->getAccountID(sfAccount); + auto const& acct = + obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(acct.size() == 20); } @@ -1024,7 +1127,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "USD"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1035,7 +1140,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "EUR"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1045,7 +1152,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value j; j[sfBaseAsset] = "0123456789ABCDEF01230123456789ABCDEF0123"; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); @@ -1067,7 +1174,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = "usd"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1094,7 +1203,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfBaseAsset] = ""; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); BEAST_EXPECT(curr.currency().size() == 20); } @@ -1126,7 +1237,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAmount] = "100000000000000000"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAmount)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(100000000000000000ull)); } @@ -1136,7 +1249,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfAmount] = 4294967295u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAmount)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(4294967295u)); } @@ -1192,14 +1307,20 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject obj("Test", j); if (BEAST_EXPECT(obj.object.has_value())) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfPaths)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& ps = obj.object->getFieldPathSet(sfPaths); BEAST_EXPECT(!ps.empty()); BEAST_EXPECT(ps.size() == 1); BEAST_EXPECT(ps[0].size() == 1); - BEAST_EXPECT(ps[0][0].getAccountID() == parseBase58("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")); + BEAST_EXPECT( + ps[0][0].getAccountID() == + parseBase58("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")); BEAST_EXPECT(to_string(ps[0][0].getCurrency()) == "USD"); - BEAST_EXPECT(ps[0][0].getIssuerID() == parseBase58("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe")); + BEAST_EXPECT( + ps[0][0].getIssuerID() == + parseBase58("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe")); } } @@ -1217,8 +1338,10 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfPaths] = pathset; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); - BEAST_EXPECT(obj.object->isFieldPresent(sfPaths)); - auto const& ps = obj.object->getFieldPathSet(sfPaths); + BEAST_EXPECT( + obj.object->isFieldPresent(sfPaths)); // NOLINT(bugprone-unchecked-optional-access) + auto const& ps = + obj.object->getFieldPathSet(sfPaths); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT(!ps.empty()); } @@ -1368,13 +1491,16 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject obj("Test", j); if (BEAST_EXPECT(obj.object.has_value())) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); - auto const& issueField = (*obj.object)[sfAsset]; + auto const& issueField = + (*obj.object)[sfAsset]; // NOLINT(bugprone-unchecked-optional-access) auto const issue = issueField.value().get(); BEAST_EXPECT(issue.currency.size() == 20); BEAST_EXPECT(to_string(issue.currency) == "USD"); BEAST_EXPECT(issue.account.size() == 20); - BEAST_EXPECT(issue.account == parseBase58("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")); + BEAST_EXPECT( + issue.account == parseBase58("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")); } } @@ -1386,7 +1512,7 @@ class STParsedJSON_test : public beast::unit_test::suite issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfAsset] = issueJson; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); auto const& issueField = (*obj.object)[sfAsset]; @@ -1403,7 +1529,7 @@ class STParsedJSON_test : public beast::unit_test::suite issueJson["mpt_issuance_id"] = "0000000000000000000000004D5054494431323334234234"; j[sfAsset] = issueJson; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfAsset)); auto const& issueField = (*obj.object)[sfAsset]; @@ -1505,7 +1631,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge)); auto const& bridgeField = (*obj.object)[sfXChainBridge]; @@ -1530,7 +1656,7 @@ class STParsedJSON_test : public beast::unit_test::suite bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; j[sfXChainBridge] = bridge; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge)); auto const& bridgeField = (*obj.object)[sfXChainBridge]; @@ -1684,7 +1810,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = 12345; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0)); } @@ -1694,7 +1822,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = 12345u; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0)); } @@ -1704,7 +1834,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = "67890"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(67890, 0)); } @@ -1714,7 +1846,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = -42; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-42, 0)); } @@ -1724,7 +1858,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfNumber] = "-123"; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-123, 0)); } @@ -1733,7 +1869,7 @@ class STParsedJSON_test : public beast::unit_test::suite Json::Value j; j[sfNumber] = "3.14159"; STParsedJSONObject obj("Test", j); - if (BEAST_EXPECT(obj.object.has_value())) + if (BEAST_EXPECT(obj.object); obj.object.has_value()) { BEAST_EXPECT(obj.object->isFieldPresent(sfNumber)); BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(314159, -5)); @@ -1785,7 +1921,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTransactionMetaData] = objVal; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfTransactionMetaData)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& result = obj.object->peekFieldObject(sfTransactionMetaData); BEAST_EXPECT(result.getFieldU8(sfTransactionResult) == 1); } @@ -1832,6 +1970,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfTransactionMetaData] = obj; STParsedJSONObject parsed("Test", j); BEAST_EXPECT(parsed.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(parsed.object->isFieldPresent(sfTransactionMetaData)); } @@ -1869,7 +2008,9 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfSignerEntries] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const& result = obj.object->getFieldArray(sfSignerEntries); if (BEAST_EXPECT(result.size() == 1)) { @@ -1941,6 +2082,7 @@ class STParsedJSON_test : public beast::unit_test::suite j[sfSignerEntries] = arr; STParsedJSONObject obj("Test", j); BEAST_EXPECT(obj.object.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries)); } @@ -2003,8 +2145,9 @@ class STParsedJSON_test : public beast::unit_test::suite } { - std::string const goodJson(R"({"CloseResolution":19,"Method":250,)" - R"("TransactionResult":"tecFROZEN"})"); + std::string const goodJson( + R"({"CloseResolution":19,"Method":250,)" + R"("TransactionResult":"tecFROZEN"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(goodJson, jv))) @@ -2012,17 +2155,21 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jv); if (BEAST_EXPECT(parsed.object)) { - std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none))); + std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == goodJson); } } } { - std::string const goodJson(R"({"CloseResolution":19,"Method":"250",)" - R"("TransactionResult":"tecFROZEN"})"); - std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)" - R"("TransactionResult":"tecFROZEN"})"); + std::string const goodJson( + R"({"CloseResolution":19,"Method":"250",)" + R"("TransactionResult":"tecFROZEN"})"); + std::string const expectedJson( + R"({"CloseResolution":19,"Method":250,)" + R"("TransactionResult":"tecFROZEN"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(goodJson, jv))) @@ -2033,17 +2180,21 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jv); if (BEAST_EXPECT(parsed.object)) { - std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none))); + std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } } } { - std::string const goodJson(R"({"CloseResolution":"19","Method":"250",)" - R"("TransactionResult":"tecFROZEN"})"); - std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)" - R"("TransactionResult":"tecFROZEN"})"); + std::string const goodJson( + R"({"CloseResolution":"19","Method":"250",)" + R"("TransactionResult":"tecFROZEN"})"); + std::string const expectedJson( + R"({"CloseResolution":19,"Method":250,)" + R"("TransactionResult":"tecFROZEN"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(goodJson, jv))) @@ -2054,15 +2205,18 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jv); if (BEAST_EXPECT(parsed.object)) { - std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none))); + std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } } } { - std::string const json(R"({"CloseResolution":19,"Method":250,)" - R"("TransactionResult":"terQUEUED"})"); + std::string const json( + R"({"CloseResolution":19,"Method":250,)" + R"("TransactionResult":"terQUEUED"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2071,13 +2225,16 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransactionResult' is out of range."); + BEAST_EXPECT( + parsed.error[jss::error_message] == + "Field 'test.TransactionResult' is out of range."); } } { - std::string const json(R"({"CloseResolution":19,"Method":"pony",)" - R"("TransactionResult":"tesSUCCESS"})"); + std::string const json( + R"({"CloseResolution":19,"Method":"pony",)" + R"("TransactionResult":"tesSUCCESS"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2086,13 +2243,15 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type."); + BEAST_EXPECT( + parsed.error[jss::error_message] == "Field 'test.Method' has bad type."); } } { - std::string const json(R"({"CloseResolution":19,"Method":3294967296,)" - R"("TransactionResult":"tesSUCCESS"})"); + std::string const json( + R"({"CloseResolution":19,"Method":3294967296,)" + R"("TransactionResult":"tesSUCCESS"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2101,13 +2260,15 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' is out of range."); + BEAST_EXPECT( + parsed.error[jss::error_message] == "Field 'test.Method' is out of range."); } } { - std::string const json(R"({"CloseResolution":-10,"Method":42,)" - R"("TransactionResult":"tesSUCCESS"})"); + std::string const json( + R"({"CloseResolution":-10,"Method":42,)" + R"("TransactionResult":"tesSUCCESS"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2116,13 +2277,16 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.CloseResolution' is out of range."); + BEAST_EXPECT( + parsed.error[jss::error_message] == + "Field 'test.CloseResolution' is out of range."); } } { - std::string const json(R"({"CloseResolution":19,"Method":3.141592653,)" - R"("TransactionResult":"tesSUCCESS"})"); + std::string const json( + R"({"CloseResolution":19,"Method":3.141592653,)" + R"("TransactionResult":"tesSUCCESS"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2131,15 +2295,18 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type."); + BEAST_EXPECT( + parsed.error[jss::error_message] == "Field 'test.Method' has bad type."); } } { - std::string const goodJson(R"({"CloseResolution":19,"Method":250,)" - R"("TransferFee":"65535"})"); - std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)" - R"("TransferFee":65535})"); + std::string const goodJson( + R"({"CloseResolution":19,"Method":250,)" + R"("TransferFee":"65535"})"); + std::string const expectedJson( + R"({"CloseResolution":19,"Method":250,)" + R"("TransferFee":65535})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(goodJson, jv))) @@ -2147,15 +2314,18 @@ class STParsedJSON_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jv); if (BEAST_EXPECT(parsed.object)) { - std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none))); + std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } } } { - std::string const json(R"({"CloseResolution":19,"Method":250,)" - R"("TransferFee":"65536"})"); + std::string const json( + R"({"CloseResolution":19,"Method":250,)" + R"("TransferFee":"65536"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2164,13 +2334,16 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data."); + BEAST_EXPECT( + parsed.error[jss::error_message] == + "Field 'test.TransferFee' has invalid data."); } } { - std::string const json(R"({"CloseResolution":19,"Method":250,)" - R"("TransferFee":"Payment"})"); + std::string const json( + R"({"CloseResolution":19,"Method":250,)" + R"("TransferFee":"Payment"})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2179,13 +2352,16 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data."); + BEAST_EXPECT( + parsed.error[jss::error_message] == + "Field 'test.TransferFee' has invalid data."); } } { - std::string const json(R"({"CloseResolution":19,"Method":250,)" - R"("TransferFee":true})"); + std::string const json( + R"({"CloseResolution":19,"Method":250,)" + R"("TransferFee":true})"); Json::Value jv; if (BEAST_EXPECT(parseJSONString(json, jv))) @@ -2194,7 +2370,8 @@ class STParsedJSON_test : public beast::unit_test::suite BEAST_EXPECT(!parsed.object); BEAST_EXPECT(parsed.error); BEAST_EXPECT(parsed.error[jss::error] == "invalidParams"); - BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type."); + BEAST_EXPECT( + parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type."); } } } diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index 853ff35635..93cfd16d3d 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -47,814 +47,1042 @@ public: { testcase("Malformed serialized form"); - constexpr unsigned char payload1[] = {0x0a, 0xff, 0xff, 0xff, 0xff, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x29, 0x1b, 0x1b, 0x1b, 0x1b, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef}; + constexpr unsigned char payload1[] = { + 0x0a, 0xff, 0xff, 0xff, 0xff, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x29, 0x1b, 0x1b, 0x1b, + 0x1b, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef}; constexpr unsigned char payload2[] = { - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, - 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, - 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, - 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, - 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, - 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, - 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, - 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, - 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, - 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, - 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, - 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, - 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, - 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, - 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, - 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, - 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, - 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, - 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, - 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, - 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, - 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, - 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, - 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, - 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, - 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, - 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, - 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, - 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, - 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, - 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, - 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, - 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, - 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x14, 0x14, 0x14, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, - 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, - 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, - 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, - 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xda, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, - 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, - 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, - 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, - 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, - 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, - 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, - 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0x29, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xef, - 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, - 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, - 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, - 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, - 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, - 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, - 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, - 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, - 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, - 0xff, 0xef, 0xff, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, - 0xe7, 0x3b, 0x3b, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, - 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x7f, 0x3b, 0x3b, 0xef, 0xfd, 0xf1, 0xff, 0xf1, 0xff, 0xf1, 0xfb, 0xef, 0xfd, 0xf1, 0xff, 0xf1, 0xfd, - 0xf1, 0xfb, 0xf1, 0xfd, 0xf1, 0xef, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0x01, 0x00, 0xfd, 0xf1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0x3b, 0x3b, 0x43, - 0x3b, 0x3b, 0xff, 0x3b, 0x12, 0xf1, 0x12, 0x12, 0x12, 0xff}; + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, + 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, + 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, + 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, + 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, + 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, + 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, + 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, + 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, + 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, + 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, + 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, + 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, + 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, + 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, + 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, + 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, + 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, + 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, + 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, + 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, + 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, + 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, + 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, + 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, + 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, + 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, + 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, + 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, + 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, + 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, + 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, + 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, + 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, + 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, + 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xe7, 0xe7, 0x29, 0xe7, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, + 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, + 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, + 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x14, 0x14, 0x14, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, + 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, + 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, + 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, + 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, + 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, + 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, + 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xda, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, + 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, + 0xff, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0x29, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, + 0x3b, 0x3b, 0x3b, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, + 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0x29, 0xe7, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, + 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, 0x3b, 0x3b, + 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x12, 0x12, + 0x12, 0x12, 0x12, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0x29, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xef, + 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, + 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, + 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xef, 0xff, 0xef, 0xf4, + 0xef, 0x3b, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, + 0x3b, 0x18, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xf4, 0xef, 0x3b, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x3b, 0x3b, 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xed, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0x3b, 0x3b, + 0x3b, 0x3b, 0x29, 0x2b, 0x3b, 0x18, 0x3b, 0x3b, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xed, 0xff, 0xef, + 0xff, 0xef, 0xff, 0xef, 0xef, 0x3b, 0x3b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xe7, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3b, 0x3b, 0x3b, 0x26, 0xdf, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xec, 0xff, 0xef, 0xff, 0xef, + 0xff, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x3b, 0x3b, 0x3b, 0x27, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, + 0xe7, 0xe7, 0xe7, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0xe7, 0x3b, 0x3b, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0xef, 0xff, 0xef, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x7f, 0x3b, 0x3b, 0xef, 0xfd, + 0xf1, 0xff, 0xf1, 0xff, 0xf1, 0xfb, 0xef, 0xfd, 0xf1, 0xff, 0xf1, 0xfd, 0xf1, 0xfb, + 0xf1, 0xfd, 0xf1, 0xef, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, + 0xef, 0xff, 0x01, 0x00, 0xfd, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0x3b, 0x3b, 0x43, 0x3b, 0x3b, 0xff, 0x3b, + 0x12, 0xf1, 0x12, 0x12, 0x12, 0xff}; constexpr unsigned char payload3[] = { - 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, - 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, - 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x24, 0x59, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, - 0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, 0xf6, - 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x73, - 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; + 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, + 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, + 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, + 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, + 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, + 0x24, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, + 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, + 0x20, 0xf6, 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; constexpr unsigned char payload4[] = { - 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, - 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, - 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, - 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x24, 0x59, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20, - 0xf6, 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, - 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; + 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, + 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, + 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f, + 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x24, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, + 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, + 0x00, 0x20, 0xf6, 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, + 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00, 0x73, 0x00, 0x81, 0x14, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe}; // Construct an STObject with 11 levels of object nesting so the // maximum nesting level exception is thrown. @@ -910,7 +1138,8 @@ public: } catch (std::runtime_error const& ex) { - BEAST_EXPECT(std::strcmp(ex.what(), "Maximum nesting depth of STVar exceeded") == 0); + BEAST_EXPECT( + std::strcmp(ex.what(), "Maximum nesting depth of STVar exceeded") == 0); } } { @@ -973,7 +1202,8 @@ public: } catch (std::runtime_error const& ex) { - BEAST_EXPECT(std::strcmp(ex.what(), "Maximum nesting depth of STVar exceeded") == 0); + BEAST_EXPECT( + std::strcmp(ex.what(), "Maximum nesting depth of STVar exceeded") == 0); } } @@ -1113,10 +1343,11 @@ public: } STParsedJSONObject parsed("test", j.getJson(JsonOptions::none)); - if (!parsed.object) + if (!parsed.object.has_value()) + { fail("Unable to build object from json"); - - if (STObject(j) != parsed.object) + } + else if (STObject(j) != parsed.object) { log << "ORIG: " << j.getJson(JsonOptions::none) << '\n' << "BUILT " << parsed.object->getJson(JsonOptions::none) << std::endl; @@ -1244,7 +1475,7 @@ public: // Signer { // Account: "...", // TxnSignature: "...", - // PublicKey: "..."" + // PublicKey: "..." // } // Make one well formed Signer and several mal-formed ones. See // whether the serializer lets the good one through and catches diff --git a/src/test/protocol/STValidation_test.cpp b/src/test/protocol/STValidation_test.cpp index 0c0951fde2..e426af3e44 100644 --- a/src/test/protocol/STValidation_test.cpp +++ b/src/test/protocol/STValidation_test.cpp @@ -14,109 +14,123 @@ class STValidation_test : public beast::unit_test::suite { // No public key: static constexpr std::uint8_t payload1[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x3A, 0x4E, - 0x69, 0x6B, 0x2B, 0x54, 0x69, 0x66, 0x66, 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, - 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, - 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, - 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, - 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xAF, 0x1D, 0x17, 0xA2, - 0x12, 0x7B, 0xA4, 0x6B, 0x40, 0xBD, 0x58, 0x76, 0x39, 0x3F, 0xF4, 0x49, 0x6B, 0x25, 0xA1, 0xAD, 0xB7, - 0x36, 0xFB, 0x64, 0x4C, 0x05, 0x21, 0x0C, 0x43, 0x02, 0xE5, 0xEE, 0x02, 0x20, 0x26, 0x01, 0x7C, 0x5F, - 0x69, 0xDA, 0xD1, 0xC3, 0x28, 0xED, 0x80, 0x05, 0x36, 0x86, 0x8B, 0x1B, 0x22, 0xE4, 0x8E, 0x09, 0x11, - 0x52, 0x28, 0x5A, 0x48, 0x8F, 0x98, 0x7A, 0x5A, 0x10, 0x74, 0xCC}; + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x3A, 0x4E, 0x69, 0x6B, 0x2B, 0x54, 0x69, 0x66, 0x66, 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, + 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, + 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, + 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, + 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, + 0xE0, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xAF, 0x1D, 0x17, 0xA2, 0x12, 0x7B, 0xA4, + 0x6B, 0x40, 0xBD, 0x58, 0x76, 0x39, 0x3F, 0xF4, 0x49, 0x6B, 0x25, 0xA1, 0xAD, 0xB7, 0x36, + 0xFB, 0x64, 0x4C, 0x05, 0x21, 0x0C, 0x43, 0x02, 0xE5, 0xEE, 0x02, 0x20, 0x26, 0x01, 0x7C, + 0x5F, 0x69, 0xDA, 0xD1, 0xC3, 0x28, 0xED, 0x80, 0x05, 0x36, 0x86, 0x8B, 0x1B, 0x22, 0xE4, + 0x8E, 0x09, 0x11, 0x52, 0x28, 0x5A, 0x48, 0x8F, 0x98, 0x7A, 0x5A, 0x10, 0x74, 0xCC}; // Short public key: static constexpr std::uint8_t payload2[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, 0x1F, - 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, - 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, - 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, - 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x20, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, - 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, - 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x76, 0x46, 0x30, 0x44, 0x02, 0x20, 0x34, 0x89, 0xA3, 0xBF, - 0xA9, 0x97, 0x13, 0xBC, 0x87, 0x61, 0xC5, 0x2B, 0x7F, 0xAA, 0xE9, 0x31, 0x4C, 0xCD, 0x6F, 0x57, 0x68, 0x70, - 0xC8, 0xDC, 0x58, 0x76, 0x91, 0x2F, 0x70, 0x2F, 0xD0, 0x78, 0x02, 0x20, 0x7E, 0x57, 0x9D, 0xCA, 0x11, 0xF1, - 0x3B, 0xA0, 0x39, 0x38, 0x37, 0x40, 0xC5, 0xC8, 0xFE, 0xC1, 0xFC, 0xE9, 0xE7, 0x84, 0x6C, 0x2D, 0x47, 0x6E, + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, + 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, + 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, + 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, + 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x20, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, + 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, + 0xA5, 0xC3, 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x76, 0x46, 0x30, 0x44, + 0x02, 0x20, 0x34, 0x89, 0xA3, 0xBF, 0xA9, 0x97, 0x13, 0xBC, 0x87, 0x61, 0xC5, 0x2B, 0x7F, + 0xAA, 0xE9, 0x31, 0x4C, 0xCD, 0x6F, 0x57, 0x68, 0x70, 0xC8, 0xDC, 0x58, 0x76, 0x91, 0x2F, + 0x70, 0x2F, 0xD0, 0x78, 0x02, 0x20, 0x7E, 0x57, 0x9D, 0xCA, 0x11, 0xF1, 0x3B, 0xA0, 0x39, + 0x38, 0x37, 0x40, 0xC5, 0xC8, 0xFE, 0xC1, 0xFC, 0xE9, 0xE7, 0x84, 0x6C, 0x2D, 0x47, 0x6E, 0xD7, 0xFF, 0x83, 0x9D, 0xEF, 0x7D, 0xF7, 0x6A}; // Long public key: static constexpr std::uint8_t payload3[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, 0x1F, - 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, - 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, - 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, - 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x22, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, - 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, - 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x78, 0x76, 0x46, 0x30, 0x44, 0x02, 0x20, 0x3C, 0xAB, - 0xEE, 0x36, 0xD8, 0xF3, 0x74, 0x5F, 0x50, 0x28, 0x66, 0x17, 0x57, 0x26, 0x6A, 0xBD, 0x9A, 0x19, 0x08, 0xAA, - 0x65, 0x94, 0x0B, 0xDF, 0x24, 0x20, 0x44, 0x99, 0x05, 0x8C, 0xB7, 0x3D, 0x02, 0x20, 0x79, 0x66, 0xE6, 0xCC, - 0xA2, 0x5E, 0x15, 0xFE, 0x18, 0x4B, 0xB2, 0xA8, 0x01, 0x3A, 0xD6, 0x63, 0x54, 0x08, 0x1B, 0xDA, 0xD0, 0x04, + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, + 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, + 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, + 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, + 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x22, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, + 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, + 0xA5, 0xC3, 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x78, 0x76, 0x46, + 0x30, 0x44, 0x02, 0x20, 0x3C, 0xAB, 0xEE, 0x36, 0xD8, 0xF3, 0x74, 0x5F, 0x50, 0x28, 0x66, + 0x17, 0x57, 0x26, 0x6A, 0xBD, 0x9A, 0x19, 0x08, 0xAA, 0x65, 0x94, 0x0B, 0xDF, 0x24, 0x20, + 0x44, 0x99, 0x05, 0x8C, 0xB7, 0x3D, 0x02, 0x20, 0x79, 0x66, 0xE6, 0xCC, 0xA2, 0x5E, 0x15, + 0xFE, 0x18, 0x4B, 0xB2, 0xA8, 0x01, 0x3A, 0xD6, 0x63, 0x54, 0x08, 0x1B, 0xDA, 0xD0, 0x04, 0xEF, 0x4C, 0x73, 0xB3, 0xFF, 0xFE, 0xA9, 0x8E, 0x92, 0xE8}; // Ed25519 public key: static constexpr std::uint8_t payload4[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, - 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, - 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, - 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, - 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0xED, - 0x04, 0x8B, 0x9A, 0x31, 0x5E, 0xC7, 0x33, 0xC0, 0x15, 0x3B, 0x67, 0x04, 0x73, 0x7A, 0x91, 0x3D, 0xEF, - 0x57, 0x1D, 0xAD, 0xEC, 0x57, 0xE5, 0x91, 0x5D, 0x55, 0xD9, 0x32, 0x9D, 0x45, 0x12, 0x85, 0x76, 0x40, - 0x52, 0x07, 0xF9, 0x0D, 0x18, 0x2B, 0xB7, 0xAF, 0x5D, 0x43, 0xF8, 0xF9, 0xC5, 0xAD, 0xF9, 0xBA, 0x33, - 0x23, 0xC0, 0x2F, 0x95, 0xFF, 0x36, 0x94, 0xD8, 0x99, 0x99, 0xE0, 0x66, 0xF8, 0xB6, 0x27, 0x22, 0xFD, - 0x29, 0x39, 0x30, 0x39, 0xAB, 0x93, 0xDB, 0x9D, 0x2C, 0xE5, 0xF0, 0x4C, 0xB7, 0x30, 0xFD, 0xC7, 0xD3, - 0x21, 0xC9, 0x4E, 0x0D, 0x8A, 0x1B, 0xB2, 0x89, 0x97, 0x10, 0x7E, 0x84, 0x09}; + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, + 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, + 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, + 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, + 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0xED, 0x04, 0x8B, 0x9A, 0x31, 0x5E, + 0xC7, 0x33, 0xC0, 0x15, 0x3B, 0x67, 0x04, 0x73, 0x7A, 0x91, 0x3D, 0xEF, 0x57, 0x1D, 0xAD, + 0xEC, 0x57, 0xE5, 0x91, 0x5D, 0x55, 0xD9, 0x32, 0x9D, 0x45, 0x12, 0x85, 0x76, 0x40, 0x52, + 0x07, 0xF9, 0x0D, 0x18, 0x2B, 0xB7, 0xAF, 0x5D, 0x43, 0xF8, 0xF9, 0xC5, 0xAD, 0xF9, 0xBA, + 0x33, 0x23, 0xC0, 0x2F, 0x95, 0xFF, 0x36, 0x94, 0xD8, 0x99, 0x99, 0xE0, 0x66, 0xF8, 0xB6, + 0x27, 0x22, 0xFD, 0x29, 0x39, 0x30, 0x39, 0xAB, 0x93, 0xDB, 0x9D, 0x2C, 0xE5, 0xF0, 0x4C, + 0xB7, 0x30, 0xFD, 0xC7, 0xD3, 0x21, 0xC9, 0x4E, 0x0D, 0x8A, 0x1B, 0xB2, 0x89, 0x97, 0x10, + 0x7E, 0x84, 0x09}; // No ledger sequence: static constexpr std::uint8_t payload5[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, - 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, - 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, - 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, - 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, - 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, - 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, - 0x83, 0xB3, 0x1B, 0xE9, 0x03, 0x8F, 0x4A, 0x92, 0x8B, 0x9B, 0x51, 0xEF, 0x79, 0xED, 0xA1, 0x4A, 0x58, - 0x9B, 0x20, 0xCF, 0x89, 0xC4, 0x75, 0x99, 0x5F, 0x6D, 0x79, 0x51, 0x79, 0x07, 0xF9, 0x93, 0x02, 0x20, - 0x39, 0xA6, 0x0C, 0x77, 0x68, 0x84, 0x50, 0xDB, 0xDA, 0x64, 0x32, 0x74, 0xEC, 0x63, 0x48, 0x48, 0x96, - 0xB5, 0x94, 0x57, 0x55, 0x8D, 0x7D, 0xD8, 0x25, 0x78, 0xD1, 0xEA, 0x5F, 0xD9, 0xC7, 0xAA}; + 0x22, 0x80, 0x00, 0x00, 0x01, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, 0x1F, 0x1A, 0x4E, + 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, + 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, + 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, + 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, + 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, 0xE5, 0xC0, 0xD8, 0x58, 0x73, + 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, 0x92, 0x8C, + 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0x83, + 0xB3, 0x1B, 0xE9, 0x03, 0x8F, 0x4A, 0x92, 0x8B, 0x9B, 0x51, 0xEF, 0x79, 0xED, 0xA1, 0x4A, + 0x58, 0x9B, 0x20, 0xCF, 0x89, 0xC4, 0x75, 0x99, 0x5F, 0x6D, 0x79, 0x51, 0x79, 0x07, 0xF9, + 0x93, 0x02, 0x20, 0x39, 0xA6, 0x0C, 0x77, 0x68, 0x84, 0x50, 0xDB, 0xDA, 0x64, 0x32, 0x74, + 0xEC, 0x63, 0x48, 0x48, 0x96, 0xB5, 0x94, 0x57, 0x55, 0x8D, 0x7D, 0xD8, 0x25, 0x78, 0xD1, + 0xEA, 0x5F, 0xD9, 0xC7, 0xAA}; // No sign time: static constexpr std::uint8_t payload6[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, - 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, - 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, - 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, - 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, - 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, - 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, - 0xDD, 0xB0, 0x59, 0x9A, 0x02, 0x3E, 0xF2, 0x44, 0xCE, 0x1D, 0xA8, 0x99, 0x06, 0xF3, 0x8A, 0x4B, 0xEB, - 0x95, 0x42, 0x63, 0x6A, 0x6C, 0x04, 0x30, 0x7F, 0x62, 0x78, 0x3A, 0x89, 0xB0, 0x3F, 0x22, 0x02, 0x20, - 0x4E, 0x6A, 0x55, 0x63, 0x8A, 0x19, 0xED, 0xFE, 0x70, 0x34, 0xD1, 0x30, 0xED, 0x7C, 0xAF, 0xB2, 0x78, - 0xBB, 0x15, 0x6C, 0x42, 0x3E, 0x19, 0x5D, 0xEA, 0xC5, 0x5E, 0x23, 0xE2, 0x14, 0x80, 0x54}; + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x51, 0x53, 0x1F, 0x1A, 0x4E, + 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, + 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, + 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, + 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, + 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, 0xE5, 0xC0, 0xD8, 0x58, 0x73, + 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, 0x92, 0x8C, + 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xDD, + 0xB0, 0x59, 0x9A, 0x02, 0x3E, 0xF2, 0x44, 0xCE, 0x1D, 0xA8, 0x99, 0x06, 0xF3, 0x8A, 0x4B, + 0xEB, 0x95, 0x42, 0x63, 0x6A, 0x6C, 0x04, 0x30, 0x7F, 0x62, 0x78, 0x3A, 0x89, 0xB0, 0x3F, + 0x22, 0x02, 0x20, 0x4E, 0x6A, 0x55, 0x63, 0x8A, 0x19, 0xED, 0xFE, 0x70, 0x34, 0xD1, 0x30, + 0xED, 0x7C, 0xAF, 0xB2, 0x78, 0xBB, 0x15, 0x6C, 0x42, 0x3E, 0x19, 0x5D, 0xEA, 0xC5, 0x5E, + 0x23, 0xE2, 0x14, 0x80, 0x54}; // No signature field: static constexpr std::uint8_t payload7[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, - 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, - 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, - 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, - 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, - 0x9D, 0x19, 0xFB, 0x09, 0x40, 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, - 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32}; + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, + 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, + 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, + 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, + 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, + 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, + 0xA5, 0xC3, 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32}; // Good: static constexpr std::uint8_t payload8[] = { - 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, 0x51, 0x53, 0x1F, - 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, - 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, - 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, - 0x35, 0x28, 0xEB, 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, - 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, 0xA5, 0xC3, 0x3E, - 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xDD, 0x29, - 0xDC, 0xAC, 0x82, 0x5E, 0xF9, 0xE2, 0x2D, 0x26, 0x03, 0x95, 0xC2, 0x11, 0x3A, 0x2A, 0x83, 0xEE, 0xA0, 0x2B, - 0x9F, 0x2A, 0x51, 0xBD, 0x6B, 0xF7, 0x83, 0xCE, 0x4A, 0x7C, 0x52, 0x29, 0x02, 0x20, 0x52, 0x45, 0xB9, 0x07, - 0x57, 0xEF, 0xB2, 0x6C, 0x69, 0xC5, 0x47, 0xCA, 0xE2, 0x76, 0x00, 0xFC, 0x35, 0x46, 0x5D, 0x19, 0x64, 0xCE, + 0x22, 0x80, 0x00, 0x00, 0x01, 0x26, 0x03, 0x4B, 0xEA, 0x97, 0x29, 0x26, 0x47, 0x31, 0x1A, + 0x51, 0x53, 0x1F, 0x1A, 0x4E, 0xBB, 0x43, 0x19, 0x69, 0x16, 0xF8, 0x3E, 0xEA, 0x5C, 0x77, + 0x94, 0x08, 0x19, 0x0B, 0x4B, 0x40, 0x8C, 0xDE, 0xB8, 0x79, 0x39, 0xF3, 0x9D, 0x66, 0x7B, + 0x12, 0xCA, 0x97, 0x50, 0x17, 0x21, 0x0B, 0xAB, 0xBC, 0x8C, 0xB7, 0xFB, 0x45, 0x49, 0xED, + 0x1E, 0x07, 0xB4, 0xFB, 0xC5, 0xF2, 0xFB, 0x67, 0x2D, 0x18, 0xA6, 0x43, 0x35, 0x28, 0xEB, + 0xD9, 0x06, 0x3E, 0xB3, 0x8B, 0xC2, 0xE0, 0x73, 0x21, 0x02, 0x9D, 0x19, 0xFB, 0x09, 0x40, + 0xE5, 0xC0, 0xD8, 0x58, 0x73, 0xFA, 0x71, 0x19, 0x99, 0x94, 0x4A, 0x68, 0x7D, 0x12, 0x9D, + 0xA5, 0xC3, 0x3E, 0x92, 0x8C, 0x27, 0x51, 0xFC, 0x1B, 0x31, 0xEB, 0x32, 0x76, 0x47, 0x30, + 0x45, 0x02, 0x21, 0x00, 0xDD, 0x29, 0xDC, 0xAC, 0x82, 0x5E, 0xF9, 0xE2, 0x2D, 0x26, 0x03, + 0x95, 0xC2, 0x11, 0x3A, 0x2A, 0x83, 0xEE, 0xA0, 0x2B, 0x9F, 0x2A, 0x51, 0xBD, 0x6B, 0xF7, + 0x83, 0xCE, 0x4A, 0x7C, 0x52, 0x29, 0x02, 0x20, 0x52, 0x45, 0xB9, 0x07, 0x57, 0xEF, 0xB2, + 0x6C, 0x69, 0xC5, 0x47, 0xCA, 0xE2, 0x76, 0x00, 0xFC, 0x35, 0x46, 0x5D, 0x19, 0x64, 0xCE, 0xCA, 0x88, 0xA1, 0x2A, 0x20, 0xCF, 0x3C, 0xF9, 0xCE, 0xCF}; public: @@ -129,7 +143,8 @@ public: { SerialIter sit{payload8}; - auto val = std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, true); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, true); BEAST_EXPECT(val); BEAST_EXPECT(val->isFieldPresent(sfLedgerSequence)); @@ -149,8 +164,8 @@ public: try { SerialIter sit{payload1}; - auto val = - std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("An exception should have been thrown"); } catch (std::exception const& ex) @@ -161,8 +176,8 @@ public: try { SerialIter sit{payload2}; - auto val = - std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("An exception should have been thrown"); } catch (std::exception const& ex) @@ -173,8 +188,8 @@ public: try { SerialIter sit{payload3}; - auto val = - std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("An exception should have been thrown"); } catch (std::exception const& ex) @@ -185,8 +200,8 @@ public: try { SerialIter sit{payload4}; - auto val = - std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("An exception should have been thrown"); } catch (std::exception const& ex) @@ -199,7 +214,8 @@ public: try { SerialIter sit{payload5}; - auto val = std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("Expected exception not thrown from validation"); } catch (std::exception const& ex) @@ -210,7 +226,8 @@ public: try { SerialIter sit{payload6}; - auto val = std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("Expected exception not thrown from validation"); } catch (std::exception const& ex) @@ -222,7 +239,8 @@ public: { SerialIter sit{payload7}; - auto val = std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, false); fail("Expected exception not thrown from validation"); } @@ -251,8 +269,8 @@ public: { SerialIter sit{makeSlice(v2)}; - auto val = - std::make_shared(sit, [](PublicKey const& pk) { return calcNodeID(pk); }, true); + auto val = std::make_shared( + sit, [](PublicKey const& pk) { return calcNodeID(pk); }, true); fail("Mutated validation signature checked out: offset=" + std::to_string(i)); } diff --git a/src/test/protocol/SecretKey_test.cpp b/src/test/protocol/SecretKey_test.cpp index 60081c8251..e42bec3363 100644 --- a/src/test/protocol/SecretKey_test.cpp +++ b/src/test/protocol/SecretKey_test.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -31,29 +33,36 @@ public: { testcase("secp256k1: canonicality"); - std::array const digestData{0x34, 0xC1, 0x90, 0x28, 0xC8, 0x0D, 0x21, 0xF3, 0xF4, 0x8C, 0x93, - 0x54, 0x89, 0x5F, 0x8D, 0x5B, 0xF0, 0xD5, 0xEE, 0x7F, 0xF4, 0x57, - 0x64, 0x7C, 0xF6, 0x55, 0xF5, 0x53, 0x0A, 0x30, 0x22, 0xA7}; + std::array const digestData{ + 0x34, 0xC1, 0x90, 0x28, 0xC8, 0x0D, 0x21, 0xF3, 0xF4, 0x8C, 0x93, + 0x54, 0x89, 0x5F, 0x8D, 0x5B, 0xF0, 0xD5, 0xEE, 0x7F, 0xF4, 0x57, + 0x64, 0x7C, 0xF6, 0x55, 0xF5, 0x53, 0x0A, 0x30, 0x22, 0xA7}; - std::array const pkData{0x02, 0x50, 0x96, 0xEB, 0x12, 0xD3, 0xE9, 0x24, 0x23, 0x4E, 0x71, - 0x62, 0x36, 0x9C, 0x11, 0xD8, 0xBF, 0x87, 0x7E, 0xDA, 0x23, 0x87, - 0x78, 0xE7, 0xA3, 0x1F, 0xF0, 0xAA, 0xC5, 0xD0, 0xDB, 0xCF, 0x37}; + std::array const pkData{ + 0x02, 0x50, 0x96, 0xEB, 0x12, 0xD3, 0xE9, 0x24, 0x23, 0x4E, 0x71, + 0x62, 0x36, 0x9C, 0x11, 0xD8, 0xBF, 0x87, 0x7E, 0xDA, 0x23, 0x87, + 0x78, 0xE7, 0xA3, 0x1F, 0xF0, 0xAA, 0xC5, 0xD0, 0xDB, 0xCF, 0x37}; - std::array const skData{0xAA, 0x92, 0x14, 0x17, 0xE7, 0xE5, 0xC2, 0x99, 0xDA, 0x4E, 0xEC, - 0x16, 0xD1, 0xCA, 0xA9, 0x2F, 0x19, 0xB1, 0x9F, 0x2A, 0x68, 0x51, - 0x1F, 0x68, 0xEC, 0x73, 0xBB, 0xB2, 0xF5, 0x23, 0x6F, 0x3D}; + std::array const skData{0xAA, 0x92, 0x14, 0x17, 0xE7, 0xE5, 0xC2, 0x99, + 0xDA, 0x4E, 0xEC, 0x16, 0xD1, 0xCA, 0xA9, 0x2F, + 0x19, 0xB1, 0x9F, 0x2A, 0x68, 0x51, 0x1F, 0x68, + 0xEC, 0x73, 0xBB, 0xB2, 0xF5, 0x23, 0x6F, 0x3D}; std::array const sig{ - 0x30, 0x45, 0x02, 0x21, 0x00, 0xB4, 0x9D, 0x07, 0xF0, 0xE9, 0x34, 0xBA, 0x46, 0x8C, 0x0E, 0xFC, 0x78, 0x11, - 0x77, 0x91, 0x40, 0x8D, 0x1F, 0xB8, 0xB6, 0x3A, 0x64, 0x92, 0xAD, 0x39, 0x5A, 0xC2, 0xF3, 0x60, 0xF2, 0x46, - 0x60, 0x02, 0x20, 0x50, 0x87, 0x39, 0xDB, 0x0A, 0x2E, 0xF8, 0x16, 0x76, 0xE3, 0x9F, 0x45, 0x9C, 0x8B, 0xBB, - 0x07, 0xA0, 0x9C, 0x3E, 0x9F, 0x9B, 0xEB, 0x69, 0x62, 0x94, 0xD5, 0x24, 0xD4, 0x79, 0xD6, 0x27, 0x40}; + 0x30, 0x45, 0x02, 0x21, 0x00, 0xB4, 0x9D, 0x07, 0xF0, 0xE9, 0x34, 0xBA, + 0x46, 0x8C, 0x0E, 0xFC, 0x78, 0x11, 0x77, 0x91, 0x40, 0x8D, 0x1F, 0xB8, + 0xB6, 0x3A, 0x64, 0x92, 0xAD, 0x39, 0x5A, 0xC2, 0xF3, 0x60, 0xF2, 0x46, + 0x60, 0x02, 0x20, 0x50, 0x87, 0x39, 0xDB, 0x0A, 0x2E, 0xF8, 0x16, 0x76, + 0xE3, 0x9F, 0x45, 0x9C, 0x8B, 0xBB, 0x07, 0xA0, 0x9C, 0x3E, 0x9F, 0x9B, + 0xEB, 0x69, 0x62, 0x94, 0xD5, 0x24, 0xD4, 0x79, 0xD6, 0x27, 0x40}; std::array const non{ - 0x30, 0x46, 0x02, 0x21, 0x00, 0xB4, 0x9D, 0x07, 0xF0, 0xE9, 0x34, 0xBA, 0x46, 0x8C, 0x0E, 0xFC, 0x78, 0x11, - 0x77, 0x91, 0x40, 0x8D, 0x1F, 0xB8, 0xB6, 0x3A, 0x64, 0x92, 0xAD, 0x39, 0x5A, 0xC2, 0xF3, 0x60, 0xF2, 0x46, - 0x60, 0x02, 0x21, 0x00, 0xAF, 0x78, 0xC6, 0x24, 0xF5, 0xD1, 0x07, 0xE9, 0x89, 0x1C, 0x60, 0xBA, 0x63, 0x74, - 0x44, 0xF7, 0x1A, 0x12, 0x9E, 0x47, 0x13, 0x5D, 0x36, 0xD9, 0x2A, 0xFD, 0x39, 0xB8, 0x56, 0x60, 0x1A, 0x01}; + 0x30, 0x46, 0x02, 0x21, 0x00, 0xB4, 0x9D, 0x07, 0xF0, 0xE9, 0x34, 0xBA, + 0x46, 0x8C, 0x0E, 0xFC, 0x78, 0x11, 0x77, 0x91, 0x40, 0x8D, 0x1F, 0xB8, + 0xB6, 0x3A, 0x64, 0x92, 0xAD, 0x39, 0x5A, 0xC2, 0xF3, 0x60, 0xF2, 0x46, + 0x60, 0x02, 0x21, 0x00, 0xAF, 0x78, 0xC6, 0x24, 0xF5, 0xD1, 0x07, 0xE9, + 0x89, 0x1C, 0x60, 0xBA, 0x63, 0x74, 0x44, 0xF7, 0x1A, 0x12, 0x9E, 0x47, + 0x13, 0x5D, 0x36, 0xD9, 0x2A, 0xFD, 0x39, 0xB8, 0x56, 0x60, 0x1A, 0x01}; auto const digest = uint256::fromVoid(digestData.data()); @@ -63,12 +72,16 @@ public: { auto const canonicality = ecdsaCanonicality(makeSlice(sig)); BEAST_EXPECT(canonicality); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*canonicality == ECDSACanonicality::fullyCanonical); } { auto const canonicality = ecdsaCanonicality(makeSlice(non)); BEAST_EXPECT(canonicality); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*canonicality != ECDSACanonicality::fullyCanonical); } @@ -88,6 +101,8 @@ public: auto const [pk, sk] = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT(pk == derivePublicKey(KeyType::secp256k1, sk)); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(*publicKeyType(pk) == KeyType::secp256k1); for (std::size_t j = 0; j < 32; j++) @@ -97,7 +112,7 @@ public: auto sig = signDigest(pk, sk, digest); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verifyDigest(pk, digest, sig, true)); // Wrong digest: @@ -126,7 +141,7 @@ public: auto const [pk, sk] = randomKeyPair(type); BEAST_EXPECT(pk == derivePublicKey(type, sk)); - BEAST_EXPECT(*publicKeyType(pk) == type); + BEAST_EXPECT(*publicKeyType(pk) == type); // NOLINT(bugprone-unchecked-optional-access) for (std::size_t j = 0; j < 32; j++) { @@ -135,7 +150,7 @@ public: auto sig = sign(pk, sk, makeSlice(data)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(data), sig)); // Construct wrong data: @@ -143,7 +158,8 @@ public: // swaps the smallest and largest elements in buffer std::iter_swap( - std::min_element(badData.begin(), badData.end()), std::max_element(badData.begin(), badData.end())); + std::min_element(badData.begin(), badData.end()), + std::max_element(badData.begin(), badData.end())); // Wrong data: should fail BEAST_EXPECT(!verify(pk, makeSlice(badData), sig)); @@ -168,23 +184,24 @@ public: // Ensure that parsing some well-known secret keys works { - auto const sk1 = generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase")); + auto const sk1 = + generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase")); - auto const sk2 = - parseBase58(TokenType::NodePrivate, "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); + auto const sk2 = parseBase58( + TokenType::NodePrivate, "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); BEAST_EXPECT(sk2); - BEAST_EXPECT(sk1 == *sk2); + BEAST_EXPECT(test::equal(sk1, *sk2)); // NOLINT(bugprone-unchecked-optional-access) } { auto const sk1 = generateSecretKey(KeyType::ed25519, generateSeed("masterpassphrase")); - auto const sk2 = - parseBase58(TokenType::NodePrivate, "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); + auto const sk2 = parseBase58( + TokenType::NodePrivate, "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); BEAST_EXPECT(sk2); - BEAST_EXPECT(sk1 == *sk2); + BEAST_EXPECT(test::equal(sk1, *sk2)); // NOLINT(bugprone-unchecked-optional-access) } // Try converting short, long and malformed data @@ -252,20 +269,21 @@ public: BEAST_EXPECT(!si.empty()); auto const ski = parseBase58(TokenType::NodePrivate, si); - BEAST_EXPECT(ski && keys[i] == *ski); + BEAST_EXPECT(ski && test::equal(keys[i], *ski)); for (std::size_t j = i; j != keys.size(); ++j) { - BEAST_EXPECT((keys[i] == keys[j]) == (i == j)); + BEAST_EXPECT(test::equal(keys[i], keys[j]) == (i == j)); auto const sj = toBase58(TokenType::NodePrivate, keys[j]); BEAST_EXPECT((si == sj) == (i == j)); auto const skj = parseBase58(TokenType::NodePrivate, sj); - BEAST_EXPECT(skj && keys[j] == *skj); + BEAST_EXPECT(skj && test::equal(keys[j], *skj)); - BEAST_EXPECT((*ski == *skj) == (i == j)); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + BEAST_EXPECT(test::equal(*ski, *skj) == (i == j)); } } } @@ -283,8 +301,9 @@ public: auto kp = generateKeyPair(KeyType::secp256k1, Seed{makeSlice(test.seed)}); BEAST_EXPECT(kp.first == PublicKey{makeSlice(test.pubkey)}); - BEAST_EXPECT(kp.second == SecretKey{makeSlice(test.seckey)}); - BEAST_EXPECT(calcAccountID(kp.first) == *id); + BEAST_EXPECT(test::equal(kp.second, SecretKey{makeSlice(test.seckey)})); + BEAST_EXPECT( + calcAccountID(kp.first) == *id); // NOLINT(bugprone-unchecked-optional-access) } } @@ -301,8 +320,9 @@ public: auto kp = generateKeyPair(KeyType::ed25519, Seed{makeSlice(test.seed)}); BEAST_EXPECT(kp.first == PublicKey{makeSlice(test.pubkey)}); - BEAST_EXPECT(kp.second == SecretKey{makeSlice(test.seckey)}); - BEAST_EXPECT(calcAccountID(kp.first) == *id); + BEAST_EXPECT(test::equal(kp.second, SecretKey{makeSlice(test.seckey)})); + BEAST_EXPECT( + calcAccountID(kp.first) == *id); // NOLINT(bugprone-unchecked-optional-access) } } diff --git a/src/test/protocol/Seed_test.cpp b/src/test/protocol/Seed_test.cpp index d6b200cca8..d7ad1f4afa 100644 --- a/src/test/protocol/Seed_test.cpp +++ b/src/test/protocol/Seed_test.cpp @@ -50,7 +50,7 @@ public: auto const seed2 = parseBase58(toBase58(seed1)); BEAST_EXPECT(static_cast(seed2)); - BEAST_EXPECT(equal(seed1, *seed2)); + BEAST_EXPECT(equal(seed1, *seed2)); // NOLINT(bugprone-unchecked-optional-access) return toBase58(seed1); } @@ -60,7 +60,8 @@ public: testcase("generation from passphrase"); BEAST_EXPECT(testPassphrase("masterpassphrase") == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb"); BEAST_EXPECT(testPassphrase("Non-Random Passphrase") == "snMKnVku798EnBwUfxeSD8953sLYA"); - BEAST_EXPECT(testPassphrase("cookies excitement hand public") == "sspUXGrmjQhq6mgc24jiRuevZiwKT"); + BEAST_EXPECT( + testPassphrase("cookies excitement hand public") == "sspUXGrmjQhq6mgc24jiRuevZiwKT"); } void @@ -92,7 +93,7 @@ public: auto const seed2 = parseBase58(toBase58(seed1)); BEAST_EXPECT(static_cast(seed2)); - BEAST_EXPECT(equal(seed1, *seed2)); + BEAST_EXPECT(equal(seed1, *seed2)); // NOLINT(bugprone-unchecked-optional-access) } } @@ -105,17 +106,21 @@ public: { testcase("Node keypair generation & signing (secp256k1)"); - auto const secretKey = generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase")); + auto const secretKey = + generateSecretKey(KeyType::secp256k1, generateSeed("masterpassphrase")); auto const publicKey = derivePublicKey(KeyType::secp256k1, secretKey); BEAST_EXPECT( - toBase58(TokenType::NodePublic, publicKey) == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); + toBase58(TokenType::NodePublic, publicKey) == + "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT( - toBase58(TokenType::NodePrivate, secretKey) == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); - BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "7E59C17D50F5959C7B158FEC95C8F815BF653DC8"); + toBase58(TokenType::NodePrivate, secretKey) == + "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); + BEAST_EXPECT( + to_string(calcNodeID(publicKey)) == "7E59C17D50F5959C7B158FEC95C8F815BF653DC8"); auto sig = sign(publicKey, secretKey, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(publicKey, makeSlice(message1), sig)); // Correct public key but wrong message @@ -124,7 +129,8 @@ public: // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey( - KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, generateSeed("otherpassphrase"))); + KeyType::secp256k1, + generateSecretKey(KeyType::secp256k1, generateSeed("otherpassphrase"))); BEAST_EXPECT(!verify(otherPublicKey, makeSlice(message1), sig)); } @@ -142,17 +148,21 @@ public: { testcase("Node keypair generation & signing (ed25519)"); - auto const secretKey = generateSecretKey(KeyType::ed25519, generateSeed("masterpassphrase")); + auto const secretKey = + generateSecretKey(KeyType::ed25519, generateSeed("masterpassphrase")); auto const publicKey = derivePublicKey(KeyType::ed25519, secretKey); BEAST_EXPECT( - toBase58(TokenType::NodePublic, publicKey) == "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); + toBase58(TokenType::NodePublic, publicKey) == + "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT( - toBase58(TokenType::NodePrivate, secretKey) == "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); - BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "AA066C988C712815CC37AF71472B7CBBBD4E2A0A"); + toBase58(TokenType::NodePrivate, secretKey) == + "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); + BEAST_EXPECT( + to_string(calcNodeID(publicKey)) == "AA066C988C712815CC37AF71472B7CBBBD4E2A0A"); auto sig = sign(publicKey, secretKey, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(publicKey, makeSlice(message1), sig)); // Correct public key but wrong message @@ -161,7 +171,8 @@ public: // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey( - KeyType::ed25519, generateSecretKey(KeyType::ed25519, generateSeed("otherpassphrase"))); + KeyType::ed25519, + generateSecretKey(KeyType::ed25519, generateSeed("otherpassphrase"))); BEAST_EXPECT(!verify(otherPublicKey, makeSlice(message1), sig)); } @@ -179,16 +190,19 @@ public: { testcase("Account keypair generation & signing (secp256k1)"); - auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")); + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")); BEAST_EXPECT(toBase58(calcAccountID(pk)) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); BEAST_EXPECT( - toBase58(TokenType::AccountPublic, pk) == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw"); + toBase58(TokenType::AccountPublic, pk) == + "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw"); BEAST_EXPECT( - toBase58(TokenType::AccountSecret, sk) == "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh"); + toBase58(TokenType::AccountSecret, sk) == + "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh"); auto sig = sign(pk, sk, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(message1), sig)); // Correct public key but wrong message @@ -196,7 +210,8 @@ public: // Verify with incorrect public key { - auto const otherKeyPair = generateKeyPair(KeyType::secp256k1, generateSeed("otherpassphrase")); + auto const otherKeyPair = + generateKeyPair(KeyType::secp256k1, generateSeed("otherpassphrase")); BEAST_EXPECT(!verify(otherKeyPair.first, makeSlice(message1), sig)); } @@ -214,16 +229,19 @@ public: { testcase("Account keypair generation & signing (ed25519)"); - auto const [pk, sk] = generateKeyPair(KeyType::ed25519, generateSeed("masterpassphrase")); + auto const [pk, sk] = + generateKeyPair(KeyType::ed25519, generateSeed("masterpassphrase")); BEAST_EXPECT(to_string(calcAccountID(pk)) == "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf"); BEAST_EXPECT( - toBase58(TokenType::AccountPublic, pk) == "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); + toBase58(TokenType::AccountPublic, pk) == + "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); BEAST_EXPECT( - toBase58(TokenType::AccountSecret, sk) == "pwDQjwEhbUBmPuEjFpEG75bFhv2obkCB7NxQsfFxM7xGHBMVPu9"); + toBase58(TokenType::AccountSecret, sk) == + "pwDQjwEhbUBmPuEjFpEG75bFhv2obkCB7NxQsfFxM7xGHBMVPu9"); auto sig = sign(pk, sk, makeSlice(message1)); - BEAST_EXPECT(sig.size() != 0); + BEAST_EXPECT(!sig.empty()); BEAST_EXPECT(verify(pk, makeSlice(message1), sig)); // Correct public key but wrong message @@ -231,7 +249,8 @@ public: // Verify with incorrect public key { - auto const otherKeyPair = generateKeyPair(KeyType::ed25519, generateSeed("otherpassphrase")); + auto const otherKeyPair = + generateKeyPair(KeyType::ed25519, generateSeed("otherpassphrase")); BEAST_EXPECT(!verify(otherKeyPair.first, makeSlice(message1), sig)); } diff --git a/src/test/protocol/SeqProxy_test.cpp b/src/test/protocol/SeqProxy_test.cpp index 0e91680c8c..9096fe7daf 100644 --- a/src/test/protocol/SeqProxy_test.cpp +++ b/src/test/protocol/SeqProxy_test.cpp @@ -13,28 +13,32 @@ struct SeqProxy_test : public beast::unit_test::suite expectValues(SeqProxy seqProx, std::uint32_t value, SeqProxy::Type type) { bool const expectSeq{type == SeqProxy::seq}; - return (seqProx.value() == value) && (seqProx.isSeq() == expectSeq) && (seqProx.isTicket() == !expectSeq); + return (seqProx.value() == value) && (seqProx.isSeq() == expectSeq) && + (seqProx.isTicket() == !expectSeq); } // Exercise all SeqProxy comparison operators expecting lhs < rhs. static constexpr bool expectLt(SeqProxy lhs, SeqProxy rhs) { - return (lhs < rhs) && (lhs <= rhs) && (!(lhs == rhs)) && (lhs != rhs) && (!(lhs >= rhs)) && (!(lhs > rhs)); + return (lhs < rhs) && (lhs <= rhs) && (!(lhs == rhs)) && (lhs != rhs) && (!(lhs >= rhs)) && + (!(lhs > rhs)); } // Exercise all SeqProxy comparison operators expecting lhs == rhs. static constexpr bool expectEq(SeqProxy lhs, SeqProxy rhs) { - return (!(lhs < rhs)) && (lhs <= rhs) && (lhs == rhs) && (!(lhs != rhs)) && (lhs >= rhs) && (!(lhs > rhs)); + return (!(lhs < rhs)) && (lhs <= rhs) && (lhs == rhs) && (!(lhs != rhs)) && (lhs >= rhs) && + (!(lhs > rhs)); } // Exercise all SeqProxy comparison operators expecting lhs > rhs. static constexpr bool expectGt(SeqProxy lhs, SeqProxy rhs) { - return (!(lhs < rhs)) && (!(lhs <= rhs)) && (!(lhs == rhs)) && (lhs != rhs) && (lhs >= rhs) && (lhs > rhs); + return (!(lhs < rhs)) && (!(lhs <= rhs)) && (!(lhs == rhs)) && (lhs != rhs) && + (lhs >= rhs) && (lhs > rhs); } // Verify streaming. @@ -48,7 +52,8 @@ struct SeqProxy_test : public beast::unit_test::suite ss << seqProx; std::string str{ss.str()}; - return str.find(type) == 0 && str[type.size()] == ' ' && str.find(value) == (type.size() + 1); + return str.find(type) == 0 && str[type.size()] == ' ' && + str.find(value) == (type.size() + 1); } void diff --git a/src/test/protocol/Serializer_test.cpp b/src/test/protocol/Serializer_test.cpp index cd34446fa8..c5b56c3029 100644 --- a/src/test/protocol/Serializer_test.cpp +++ b/src/test/protocol/Serializer_test.cpp @@ -12,7 +12,11 @@ struct Serializer_test : public beast::unit_test::suite { { std::initializer_list const values = { - std::numeric_limits::min(), -1, 0, 1, std::numeric_limits::max()}; + std::numeric_limits::min(), + -1, + 0, + 1, + std::numeric_limits::max()}; for (std::int32_t value : values) { Serializer s; @@ -24,7 +28,11 @@ struct Serializer_test : public beast::unit_test::suite } { std::initializer_list const values = { - std::numeric_limits::min(), -1, 0, 1, std::numeric_limits::max()}; + std::numeric_limits::min(), + -1, + 0, + 1, + std::numeric_limits::max()}; for (std::int64_t value : values) { Serializer s; diff --git a/src/test/protocol/TER_test.cpp b/src/test/protocol/TER_test.cpp index 6dce46ed6b..cf88a570c5 100644 --- a/src/test/protocol/TER_test.cpp +++ b/src/test/protocol/TER_test.cpp @@ -14,8 +14,8 @@ struct TER_test : public beast::unit_test::suite for (auto i = -400; i < 400; ++i) { TER t = TER::fromInt(i); - auto inRange = isTelLocal(t) || isTemMalformed(t) || isTefFailure(t) || isTerRetry(t) || isTesSuccess(t) || - isTecClaim(t); + auto inRange = isTelLocal(t) || isTemMalformed(t) || isTefFailure(t) || isTerRetry(t) || + isTesSuccess(t) || isTecClaim(t); std::string token, text; auto good = transResultInfo(t, token, text); @@ -48,11 +48,16 @@ struct TER_test : public beast::unit_test::suite // unless they are the same types. using To_t = std::decay_t(tup))>; using From_t = std::decay_t(tup))>; - static_assert(std::is_same::value == std::is_convertible::value, "Convert err"); static_assert( - std::is_same::value == std::is_constructible::value, "Construct err"); + std::is_same::value == std::is_convertible::value, + "Convert err"); static_assert( - std::is_same::value == std::is_assignable::value, "Assign err"); + std::is_same::value == std::is_constructible::value, + "Construct err"); + static_assert( + std::is_same::value == + std::is_assignable::value, + "Assign err"); // Assignment or conversion from integer to type should never work. static_assert(!std::is_convertible::value, "Convert err"); @@ -62,7 +67,11 @@ struct TER_test : public beast::unit_test::suite }; // Fast iteration over the tuple. - template class Func, typename Tup> + template < + std::size_t I1, + std::size_t I2, + template class Func, + typename Tup> std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { @@ -72,7 +81,11 @@ struct TER_test : public beast::unit_test::suite } // Slow iteration over the tuple. - template class Func, typename Tup> + template < + std::size_t I1, + std::size_t I2, + template class Func, + typename Tup> std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { @@ -82,7 +95,11 @@ struct TER_test : public beast::unit_test::suite } // Finish iteration over the tuple. - template class Func, typename Tup> + template < + std::size_t I1, + std::size_t I2, + template class Func, + typename Tup> std::enable_if_t testIterate(Tup const& tup, beast::unit_test::suite& s) { @@ -97,8 +114,8 @@ struct TER_test : public beast::unit_test::suite // are not valid. // Examples of each kind of enum. - static auto const terEnums = - std::make_tuple(telLOCAL_ERROR, temMALFORMED, tefFAILURE, terRETRY, tesSUCCESS, tecCLAIM); + static auto const terEnums = std::make_tuple( + telLOCAL_ERROR, temMALFORMED, tefFAILURE, terRETRY, tesSUCCESS, tecCLAIM); static int const hiIndex{std::tuple_size::value - 1}; // Verify that enums cannot be converted to other enum types. diff --git a/src/test/resource/Logic_test.cpp b/src/test/resource/Logic_test.cpp index f3e44e8b32..c555b92860 100644 --- a/src/test/resource/Logic_test.cpp +++ b/src/test/resource/Logic_test.cpp @@ -24,7 +24,8 @@ public: using clock_type = boost::base_from_member; public: - explicit TestLogic(beast::Journal journal) : Logic(beast::insight::NullCollector::New(), member, journal) + explicit TestLogic(beast::Journal journal) + : Logic(beast::insight::NullCollector::New(), member, journal) { } @@ -65,9 +66,13 @@ public: testDrop(beast::Journal j, bool limited) { if (limited) + { testcase("Limited warn/drop"); + } else + { testcase("Unlimited warn/drop"); + } TestLogic logic(j); @@ -89,18 +94,26 @@ public: if (n == 0) { if (limited) + { fail("Loop count exceeded without warning"); + } else + { pass(); + } return; } if (c.charge(fee) == warn) { if (limited) + { pass(); + } else + { fail("Should loop forever with no warning"); + } break; } ++logic.clock(); @@ -112,9 +125,13 @@ public: if (n == 0) { if (limited) + { fail("Loop count exceeded without dropping"); + } else + { pass(); + } return; } @@ -135,9 +152,13 @@ public: if (c.disposition() != drop) { if (limited) + { fail("Dropped consumer not put on blacklist"); + } else + { pass(); + } return; } } diff --git a/src/test/rpc/AMMInfo_test.cpp b/src/test/rpc/AMMInfo_test.cpp index cf515d7c84..3f20e0378d 100644 --- a/src/test/rpc/AMMInfo_test.cpp +++ b/src/test/rpc/AMMInfo_test.cpp @@ -23,11 +23,15 @@ public: enum TestAccount { None, Alice, Bogie }; auto accountId = [&](AMM const& ammAlice, TestAccount v) -> std::optional { if (v == Alice) + { return ammAlice.ammAccount(); - else if (v == Bogie) + } + if (v == Bogie) + { return bogie; - else - return std::nullopt; + } + + return std::nullopt; }; // Invalid tokens pair @@ -44,20 +48,26 @@ public: BEAST_EXPECT(jv[jss::error_message] == "Account malformed."); }); - std::vector, std::optional, TestAccount, bool>> const invalidParams = { - {xrpIssue(), std::nullopt, None, false}, - {std::nullopt, USD.issue(), None, false}, - {xrpIssue(), std::nullopt, Alice, false}, - {std::nullopt, USD.issue(), Alice, false}, - {xrpIssue(), USD.issue(), Alice, false}, - {std::nullopt, std::nullopt, None, true}}; + std::vector, std::optional, TestAccount, bool>> const + invalidParams = { + {xrpIssue(), std::nullopt, None, false}, + {std::nullopt, USD.issue(), None, false}, + {xrpIssue(), std::nullopt, Alice, false}, + {std::nullopt, USD.issue(), Alice, false}, + {xrpIssue(), USD.issue(), Alice, false}, + {std::nullopt, std::nullopt, None, true}}; // Invalid parameters testAMM([&](AMM& ammAlice, Env&) { for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams) { auto const jv = ammAlice.ammRpcInfo( - std::nullopt, std::nullopt, iss1, iss2, accountId(ammAlice, acct), ignoreParams); + std::nullopt, + std::nullopt, + iss1, + iss2, + accountId(ammAlice, acct), + ignoreParams); BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters."); } }); @@ -95,7 +105,8 @@ public: // Invalid AMM account id testAMM([&](AMM& ammAlice, Env&) { - auto const jv = ammAlice.ammRpcInfo(std::nullopt, std::nullopt, std::nullopt, std::nullopt, bogie.id()); + auto const jv = ammAlice.ammRpcInfo( + std::nullopt, std::nullopt, std::nullopt, std::nullopt, bogie.id()); BEAST_EXPECT(jv[jss::error_message] == "Account malformed."); }); @@ -113,7 +124,12 @@ public: for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParamsBadAccount) { auto const jv = ammAlice.ammRpcInfo( - std::nullopt, std::nullopt, iss1, iss2, accountId(ammAlice, acct), ignoreParams); + std::nullopt, + std::nullopt, + iss1, + iss2, + accountId(ammAlice, acct), + ignoreParams); BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters."); } }); @@ -123,10 +139,17 @@ public: for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParamsBadAccount) { auto const jv = ammAlice.ammRpcInfo( - std::nullopt, std::nullopt, iss1, iss2, accountId(ammAlice, acct), ignoreParams, 3); + std::nullopt, + std::nullopt, + iss1, + iss2, + accountId(ammAlice, acct), + ignoreParams, + 3); BEAST_EXPECT( jv[jss::error_message] == - (acct == Bogie ? std::string("Account malformed.") : std::string("Invalid parameters."))); + (acct == Bogie ? std::string("Account malformed.") + : std::string("Invalid parameters."))); } }); } @@ -140,7 +163,12 @@ public: testAMM([&](AMM& ammAlice, Env&) { BEAST_EXPECT(ammAlice.expectAmmRpcInfo(XRP(10000), USD(10000), IOUAmount{10000000, 0})); BEAST_EXPECT(ammAlice.expectAmmRpcInfo( - XRP(10000), USD(10000), IOUAmount{10000000, 0}, std::nullopt, std::nullopt, ammAlice.ammAccount())); + XRP(10000), + USD(10000), + IOUAmount{10000000, 0}, + std::nullopt, + std::nullopt, + ammAlice.ammAccount())); }); } @@ -152,7 +180,8 @@ public: using namespace jtx; testAMM( [&](AMM& ammAlice, Env& env) { - BEAST_EXPECT(ammAlice.expectAmmRpcInfo(XRP(10000), USD(10000), IOUAmount{10000000, 0})); + BEAST_EXPECT( + ammAlice.expectAmmRpcInfo(XRP(10000), USD(10000), IOUAmount{10000000, 0})); std::unordered_map votes; votes.insert({alice.human(), 0}); for (int i = 0; i < 7; ++i) @@ -160,9 +189,13 @@ public: Account a(std::to_string(i)); votes.insert({a.human(), 50 * (i + 1)}); if (!features[fixAMMv1_3]) + { fund(env, gw, {a}, {USD(10000)}, Fund::Acct); + } else + { fund(env, gw, {a}, {USD(10001)}, Fund::Acct); + } ammAlice.deposit(a, 10000000); ammAlice.vote(a, 50 * (i + 1)); } @@ -172,6 +205,7 @@ public: env.fund(XRP(1000), bob, ed, bill); env(ammAlice.bid({.bidMin = 100, .authAccounts = {carol, bob, ed, bill}})); if (!features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectAmmRpcInfo( XRP(80000), USD(80000), @@ -179,7 +213,9 @@ public: std::nullopt, std::nullopt, ammAlice.ammAccount())); + } else + { BEAST_EXPECT(ammAlice.expectAmmRpcInfo( XRPAmount(80000000005), STAmount{USD, UINT64_C(80'000'00000000005), -11}, @@ -187,14 +223,18 @@ public: std::nullopt, std::nullopt, ammAlice.ammAccount())); + } for (auto i = 0; i < 2; ++i) { std::unordered_set authAccounts = { carol.human(), bob.human(), ed.human(), bill.human()}; - auto const ammInfo = i - ? ammAlice.ammRpcInfo() - : ammAlice.ammRpcInfo( - std::nullopt, std::nullopt, std::nullopt, std::nullopt, ammAlice.ammAccount()); + auto const ammInfo = i ? ammAlice.ammRpcInfo() + : ammAlice.ammRpcInfo( + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + ammAlice.ammAccount()); auto const& amm = ammInfo[jss::amm]; try { @@ -218,10 +258,11 @@ public: auto const auctionSlot = amm[jss::auction_slot]; for (std::uint8_t i = 0; i < 4; ++i) { - if (!BEAST_EXPECT( - authAccounts.contains(auctionSlot[jss::auth_accounts][i][jss::account].asString()))) + if (!BEAST_EXPECT(authAccounts.contains( + auctionSlot[jss::auth_accounts][i][jss::account].asString()))) return; - authAccounts.erase(auctionSlot[jss::auth_accounts][i][jss::account].asString()); + authAccounts.erase( + auctionSlot[jss::auth_accounts][i][jss::account].asString()); } if (!BEAST_EXPECT(authAccounts.empty())) return; @@ -231,7 +272,8 @@ public: auctionSlot[jss::price][jss::value].asString() == "5600" && auctionSlot[jss::price][jss::currency].asString() == to_string(ammAlice.lptIssue().currency) && - auctionSlot[jss::price][jss::issuer].asString() == to_string(ammAlice.lptIssue().account)); + auctionSlot[jss::price][jss::issuer].asString() == + to_string(ammAlice.lptIssue().account)); } catch (std::exception const& e) { @@ -270,7 +312,8 @@ public: testcase("Invalid amm field"); testAMM([&](AMM& amm, Env&) { - auto const resp = amm.ammRpcInfo(std::nullopt, jss::validated.c_str(), std::nullopt, std::nullopt, gw); + auto const resp = amm.ammRpcInfo( + std::nullopt, jss::validated.c_str(), std::nullopt, std::nullopt, gw); BEAST_EXPECT(resp.isMember("error") && resp["error"] == "actNotFound"); }); } diff --git a/src/test/rpc/AccountCurrencies_test.cpp b/src/test/rpc/AccountCurrencies_test.cpp index f467b50adc..bb8ccf0a85 100644 --- a/src/test/rpc/AccountCurrencies_test.cpp +++ b/src/test/rpc/AccountCurrencies_test.cpp @@ -23,9 +23,11 @@ class AccountCurrencies_test : public beast::unit_test::suite Json::Value params; params[jss::account] = Account{"bob"}.human(); params[jss::ledger_hash] = 1; - auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; + auto const result = + env.rpc("json", "account_currencies", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); - BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); + BEAST_EXPECT( + result[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); } { // missing account field @@ -73,7 +75,8 @@ class AccountCurrencies_test : public beast::unit_test::suite { Json::Value params; params[jss::account] = "llIIOO"; // these are invalid in bitcoin alphabet - auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; + auto const result = + env.rpc("json", "account_currencies", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "actMalformed"); BEAST_EXPECT(result[jss::error_message] == "Account malformed."); } @@ -82,7 +85,8 @@ class AccountCurrencies_test : public beast::unit_test::suite // Cannot use a seed as account Json::Value params; params[jss::account] = "Bob"; - auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; + auto const result = + env.rpc("json", "account_currencies", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "actMalformed"); BEAST_EXPECT(result[jss::error_message] == "Account malformed."); } @@ -90,7 +94,8 @@ class AccountCurrencies_test : public beast::unit_test::suite { // ask for nonexistent account Json::Value params; params[jss::account] = Account{"bob"}.human(); - auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; + auto const result = + env.rpc("json", "account_currencies", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "actNotFound"); BEAST_EXPECT(result[jss::error_message] == "Account not found."); } @@ -121,8 +126,10 @@ class AccountCurrencies_test : public beast::unit_test::suite auto result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; auto arrayCheck = [&result]( - Json::StaticString const& fld, std::vector> const& expected) -> bool { - bool stat = result.isMember(fld) && result[fld].isArray() && result[fld].size() == expected.size(); + Json::StaticString const& fld, + std::vector> const& expected) -> bool { + bool stat = result.isMember(fld) && result[fld].isArray() && + result[fld].size() == expected.size(); for (size_t i = 0; stat && i < expected.size(); ++i) { stat &= (to_string(expected[i].value().currency) == result[fld][i].asString()); @@ -135,7 +142,7 @@ class AccountCurrencies_test : public beast::unit_test::suite // now form a payment for each currency for (auto const& c : gwCurrencies) - env(pay(gw, alice, c.value()(50))); + env(pay(gw, alice, c.value()(50))); // NOLINT(bugprone-unchecked-optional-access) // send_currencies should be populated now result = env.rpc("json", "account_currencies", to_string(params))[jss::result]; diff --git a/src/test/rpc/AccountInfo_test.cpp b/src/test/rpc/AccountInfo_test.cpp index bdc8e63e3d..518398f9ea 100644 --- a/src/test/rpc/AccountInfo_test.cpp +++ b/src/test/rpc/AccountInfo_test.cpp @@ -106,13 +106,15 @@ public: { // account_info without the "signer_lists" argument. auto const info = env.rpc("json", "account_info", to_string(withoutSigners)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(jss::signer_lists)); } { // account_info with the "signer_lists" argument. auto const info = env.rpc("json", "account_info", to_string(withSigners)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -128,13 +130,15 @@ public: { // account_info without the "signer_lists" argument. auto const info = env.rpc("json", "account_info", to_string(withoutSigners)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(jss::signer_lists)); } { // account_info with the "signer_lists" argument. auto const info = env.rpc("json", "account_info", to_string(withSigners)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -175,7 +179,8 @@ public: { // account_info with the "signer_lists" argument. auto const info = env.rpc("json", "account_info", to_string(withSigners)); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -215,8 +220,8 @@ public: withSigners[jss::account] = alice.human(); withSigners[jss::signer_lists] = true; - auto const withSignersAsString = std::string("{ ") + "\"api_version\": 2, \"account\": \"" + alice.human() + - "\", " + "\"signer_lists\": asdfggh }"; + auto const withSignersAsString = std::string("{ ") + "\"api_version\": 2, \"account\": \"" + + alice.human() + "\", " + "\"signer_lists\": asdfggh }"; // Alice has no SignerList yet. { @@ -349,7 +354,8 @@ public: { // account_info without the "signer_lists" argument. auto const info = env.rpc("json2", withoutSigners); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(jss::signer_lists)); BEAST_EXPECT(info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0"); BEAST_EXPECT(info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0"); @@ -358,7 +364,8 @@ public: { // account_info with the "signer_lists" argument. auto const info = env.rpc("json2", withSigners); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -371,13 +378,17 @@ public: { // Do both of the above as a batch job auto const info = env.rpc("json2", '[' + withoutSigners + ", " + withSigners + ']'); - BEAST_EXPECT(info[0u].isMember(jss::result) && info[0u][jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info[0u].isMember(jss::result) && + info[0u][jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[0u][jss::result][jss::account_data].isMember(jss::signer_lists)); BEAST_EXPECT(info[0u].isMember(jss::jsonrpc) && info[0u][jss::jsonrpc] == "2.0"); BEAST_EXPECT(info[0u].isMember(jss::ripplerpc) && info[0u][jss::ripplerpc] == "2.0"); BEAST_EXPECT(info[0u].isMember(jss::id) && info[0u][jss::id] == 5); - BEAST_EXPECT(info[1u].isMember(jss::result) && info[1u][jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info[1u].isMember(jss::result) && + info[1u][jss::result].isMember(jss::account_data)); auto const& data = info[1u][jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -396,7 +407,8 @@ public: { // account_info without the "signer_lists" argument. auto const info = env.rpc("json2", withoutSigners); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(jss::signer_lists)); BEAST_EXPECT(info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0"); BEAST_EXPECT(info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0"); @@ -405,7 +417,8 @@ public: { // account_info with the "signer_lists" argument. auto const info = env.rpc("json2", withSigners); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -449,7 +462,8 @@ public: { // account_info with the "signer_lists" argument. auto const info = env.rpc("json2", withSigners); - BEAST_EXPECT(info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); + BEAST_EXPECT( + info.isMember(jss::result) && info[jss::result].isMember(jss::account_data)); auto const& data = info[jss::result][jss::account_data]; BEAST_EXPECT(data.isMember(jss::signer_lists)); auto const& signerLists = data[jss::signer_lists]; @@ -488,11 +502,12 @@ public: Json::Value params; params[jss::account] = account.human(); auto const info = env.rpc("json", "account_info", to_string(params)); + auto const name = std::string(fName); std::optional res; if (info[jss::result][jss::status] == "success" && - info[jss::result][jss::account_flags].isMember(fName.data())) - res.emplace(info[jss::result][jss::account_flags][fName.data()].asBool()); + info[jss::result][jss::account_flags].isMember(name)) + res.emplace(info[jss::result][jss::account_flags][name].asBool()); return res; }; @@ -514,7 +529,7 @@ public: env.close(); auto const f1 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set a flag and check that account_info returns results // as expected @@ -522,14 +537,15 @@ public: env.close(); auto const f2 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } - static constexpr std::array, 4> disallowIncomingFlags{ - {{"disallowIncomingCheck", asfDisallowIncomingCheck}, - {"disallowIncomingNFTokenOffer", asfDisallowIncomingNFTokenOffer}, - {"disallowIncomingPayChan", asfDisallowIncomingPayChan}, - {"disallowIncomingTrustline", asfDisallowIncomingTrustline}}}; + static constexpr std::array, 4> + disallowIncomingFlags{ + {{"disallowIncomingCheck", asfDisallowIncomingCheck}, + {"disallowIncomingNFTokenOffer", asfDisallowIncomingNFTokenOffer}, + {"disallowIncomingPayChan", asfDisallowIncomingPayChan}, + {"disallowIncomingTrustline", asfDisallowIncomingTrustline}}}; for (auto& asf : disallowIncomingFlags) { @@ -539,7 +555,7 @@ public: env.close(); auto const f1 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set a flag and check that account_info returns results // as expected @@ -547,7 +563,7 @@ public: env.close(); auto const f2 = getAccountFlag(asf.first, alice); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } static constexpr std::pair allowTrustLineClawbackFlag{ @@ -558,14 +574,14 @@ public: // must use bob's account because alice has noFreeze set auto const f1 = getAccountFlag(allowTrustLineClawbackFlag.first, bob); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set allowTrustLineClawback env(fset(bob, allowTrustLineClawbackFlag.second)); env.close(); auto const f2 = getAccountFlag(allowTrustLineClawbackFlag.first, bob); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } else { @@ -579,14 +595,14 @@ public: { auto const f1 = getAccountFlag(allowTrustLineLockingFlag.first, bob); BEAST_EXPECT(f1.has_value()); - BEAST_EXPECT(!f1.value()); + BEAST_EXPECT(!f1.value()); // NOLINT(bugprone-unchecked-optional-access) // Set allowTrustLineLocking env(fset(bob, allowTrustLineLockingFlag.second)); env.close(); auto const f2 = getAccountFlag(allowTrustLineLockingFlag.first, bob); BEAST_EXPECT(f2.has_value()); - BEAST_EXPECT(f2.value()); + BEAST_EXPECT(f2.value()); // NOLINT(bugprone-unchecked-optional-access) } else { diff --git a/src/test/rpc/AccountLines_test.cpp b/src/test/rpc/AccountLines_test.cpp index 1f5f190cfa..f91b2aed1c 100644 --- a/src/test/rpc/AccountLines_test.cpp +++ b/src/test/rpc/AccountLines_test.cpp @@ -22,7 +22,8 @@ public: // account_lines with no account. auto const lines = env.rpc("json", "account_lines", "{ }"); BEAST_EXPECT( - lines[jss::result][jss::error_message] == RPC::missing_field_error(jss::account)[jss::error_message]); + lines[jss::result][jss::error_message] == + RPC::missing_field_error(jss::account)[jss::error_message]); } { // account_lines with a malformed account. @@ -30,7 +31,8 @@ public: params[jss::account] = "n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"; auto const lines = env.rpc("json", "account_lines", to_string(params)); BEAST_EXPECT( - lines[jss::result][jss::error_message] == RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); + lines[jss::result][jss::error_message] == + RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); } { // test account non-string @@ -56,7 +58,8 @@ public: params[jss::account] = alice.human(); auto const lines = env.rpc("json", "account_lines", to_string(params)); BEAST_EXPECT( - lines[jss::result][jss::error_message] == RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]); + lines[jss::result][jss::error_message] == + RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]); } env.fund(XRP(10000), alice); env.close(); @@ -78,7 +81,8 @@ public: params[jss::ledger_index] = "nonsense"; auto const lines = env.rpc("json", "account_lines", to_string(params)); BEAST_EXPECT( - lines[jss::result][jss::error_message] == "Invalid field 'ledger_index', not string or number."); + lines[jss::result][jss::error_message] == + "Invalid field 'ledger_index', not string or number."); } { // Specify a different ledger that doesn't exist. @@ -138,23 +142,24 @@ public: BEAST_EXPECT(ledger58Info.seq == 58); // A re-usable test for historic ledgers. - auto testAccountLinesHistory = [this, &env](Account const& account, LedgerHeader const& info, int count) { - // Get account_lines by ledger index. - Json::Value paramsSeq; - paramsSeq[jss::account] = account.human(); - paramsSeq[jss::ledger_index] = info.seq; - auto const linesSeq = env.rpc("json", "account_lines", to_string(paramsSeq)); - BEAST_EXPECT(linesSeq[jss::result][jss::lines].isArray()); - BEAST_EXPECT(linesSeq[jss::result][jss::lines].size() == count); + auto testAccountLinesHistory = + [this, &env](Account const& account, LedgerHeader const& info, int count) { + // Get account_lines by ledger index. + Json::Value paramsSeq; + paramsSeq[jss::account] = account.human(); + paramsSeq[jss::ledger_index] = info.seq; + auto const linesSeq = env.rpc("json", "account_lines", to_string(paramsSeq)); + BEAST_EXPECT(linesSeq[jss::result][jss::lines].isArray()); + BEAST_EXPECT(linesSeq[jss::result][jss::lines].size() == count); - // Get account_lines by ledger hash. - Json::Value paramsHash; - paramsHash[jss::account] = account.human(); - paramsHash[jss::ledger_hash] = to_string(info.hash); - auto const linesHash = env.rpc("json", "account_lines", to_string(paramsHash)); - BEAST_EXPECT(linesHash[jss::result][jss::lines].isArray()); - BEAST_EXPECT(linesHash[jss::result][jss::lines].size() == count); - }; + // Get account_lines by ledger hash. + Json::Value paramsHash; + paramsHash[jss::account] = account.human(); + paramsHash[jss::ledger_hash] = to_string(info.hash); + auto const linesHash = env.rpc("json", "account_lines", to_string(paramsHash)); + BEAST_EXPECT(linesHash[jss::result][jss::lines].isArray()); + BEAST_EXPECT(linesHash[jss::result][jss::lines].size() == count); + }; // Alice should have no trust lines in ledger 3. testAccountLinesHistory(alice, ledger3Info, 0); @@ -185,7 +190,8 @@ public: params[jss::ledger_index] = Json::objectValue; auto const lines = env.rpc("json", "account_lines", to_string(params))[jss::result]; BEAST_EXPECT(lines[jss::error] == "invalidParams"); - BEAST_EXPECT(lines[jss::error_message] == "Invalid field 'ledger_index', not string or number."); + BEAST_EXPECT( + lines[jss::error_message] == "Invalid field 'ledger_index', not string or number."); } { // alice should have 52 trust lines in the current ledger. @@ -215,7 +221,8 @@ public: params[jss::peer] = "n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"; auto const lines = env.rpc("json", "account_lines", to_string(params)); BEAST_EXPECT( - lines[jss::result][jss::error_message] == RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); + lines[jss::result][jss::error_message] == + RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); } { // A negative limit should fail. @@ -224,7 +231,8 @@ public: params[jss::limit] = -1; auto const lines = env.rpc("json", "account_lines", to_string(params)); BEAST_EXPECT( - lines[jss::result][jss::error_message] == RPC::expected_field_message(jss::limit, "unsigned integer")); + lines[jss::result][jss::error_message] == + RPC::expected_field_message(jss::limit, "unsigned integer")); } { // Limit the response to 1 trust line. @@ -260,7 +268,8 @@ public: paramsD[jss::marker] = marker; auto const linesD = env.rpc("json", "account_lines", to_string(paramsD)); BEAST_EXPECT( - linesD[jss::result][jss::error_message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); + linesD[jss::result][jss::error_message] == + RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); } { // A non-string marker should also fail. @@ -268,7 +277,9 @@ public: params[jss::account] = alice.human(); params[jss::marker] = true; auto const lines = env.rpc("json", "account_lines", to_string(params)); - BEAST_EXPECT(lines[jss::result][jss::error_message] == RPC::expected_field_message(jss::marker, "string")); + BEAST_EXPECT( + lines[jss::result][jss::error_message] == + RPC::expected_field_message(jss::marker, "string")); } { // Check that the flags we expect from alice to gw2 are present. @@ -347,7 +358,8 @@ public: Json::Value aliceObjectsParams; aliceObjectsParams[jss::account] = alice.human(); aliceObjectsParams[jss::limit] = 10; - Json::Value const aliceObjects = env.rpc("json", "account_objects", to_string(aliceObjectsParams)); + Json::Value const aliceObjects = + env.rpc("json", "account_objects", to_string(aliceObjectsParams)); Json::Value const& aliceSignerList = aliceObjects[jss::result][jss::account_objects][0u]; if (!(aliceSignerList[sfLedgerEntryType.jsonName] == jss::SignerList)) { @@ -455,7 +467,8 @@ public: linesEndParams[jss::marker] = linesBeg[jss::result][jss::marker]; auto const linesEnd = env.rpc("json", "account_lines", to_string(linesEndParams)); BEAST_EXPECT( - linesEnd[jss::result][jss::error_message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); + linesEnd[jss::result][jss::error_message] == + RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); } void @@ -531,11 +544,19 @@ public: env(token::createOffer(alice, beckyNFtokenID, drops(1)), token::owner(becky)); env(token::createOffer(becky, aliceNFtokenID, drops(1)), token::owner(alice)); - env(token::createOffer(becky, beckyNFtokenID, drops(1)), txflags(tfSellNFToken), token::destination(alice)); - env(token::createOffer(alice, aliceNFtokenID, drops(1)), txflags(tfSellNFToken), token::destination(becky)); + env(token::createOffer(becky, beckyNFtokenID, drops(1)), + txflags(tfSellNFToken), + token::destination(alice)); + env(token::createOffer(alice, aliceNFtokenID, drops(1)), + txflags(tfSellNFToken), + token::destination(becky)); - env(token::createOffer(gw1, beckyNFtokenID, drops(1)), token::owner(becky), token::destination(alice)); - env(token::createOffer(gw1, aliceNFtokenID, drops(1)), token::owner(alice), token::destination(becky)); + env(token::createOffer(gw1, beckyNFtokenID, drops(1)), + token::owner(becky), + token::destination(alice)); + env(token::createOffer(gw1, aliceNFtokenID, drops(1)), + token::owner(alice), + token::destination(becky)); env(token::createOffer(becky, beckyNFtokenID, drops(1)), txflags(tfSellNFToken)); env(token::createOffer(alice, aliceNFtokenID, drops(1)), txflags(tfSellNFToken)); @@ -567,15 +588,16 @@ public: // Now make repeated calls to `account_lines` with a limit of 1. // That should iterate all of alice's relevant objects, even though // the list will be empty for most calls. - auto getNextLine = [](Env& env, Account const& alice, std::optional const marker) { - Json::Value params(Json::objectValue); - params[jss::account] = alice.human(); - params[jss::limit] = 1; - if (marker) - params[jss::marker] = *marker; + auto getNextLine = + [](Env& env, Account const& alice, std::optional const marker) { + Json::Value params(Json::objectValue); + params[jss::account] = alice.human(); + params[jss::limit] = 1; + if (marker) + params[jss::marker] = *marker; - return env.rpc("json", "account_lines", to_string(params)); - }; + return env.rpc("json", "account_lines", to_string(params)); + }; auto aliceLines = getNextLine(env, alice, std::nullopt); constexpr std::size_t expectedIterations = 16; @@ -583,11 +605,17 @@ public: constexpr std::size_t expectedNFTs = 1; std::size_t foundLines = 0; - auto hasMarker = [](auto const& aliceLines) { return aliceLines[jss::result].isMember(jss::marker); }; - auto marker = [](auto const& aliceLines) { return aliceLines[jss::result][jss::marker].asString(); }; + auto hasMarker = [](auto const& aliceLines) { + return aliceLines[jss::result].isMember(jss::marker); + }; + auto marker = [](auto const& aliceLines) { + return aliceLines[jss::result][jss::marker].asString(); + }; auto checkLines = [](auto const& aliceLines) { - return aliceLines.isMember(jss::result) && !aliceLines[jss::result].isMember(jss::error_message) && - aliceLines[jss::result].isMember(jss::lines) && aliceLines[jss::result][jss::lines].isArray() && + return aliceLines.isMember(jss::result) && + !aliceLines[jss::result].isMember(jss::error_message) && + aliceLines[jss::result].isMember(jss::lines) && + aliceLines[jss::result][jss::lines].isArray() && aliceLines[jss::result][jss::lines].size() <= 1; }; @@ -610,7 +638,8 @@ public: Json::Value aliceObjectsParams2; aliceObjectsParams2[jss::account] = alice.human(); aliceObjectsParams2[jss::limit] = 200; - Json::Value const aliceObjects = env.rpc("json", "account_objects", to_string(aliceObjectsParams2)); + Json::Value const aliceObjects = + env.rpc("json", "account_objects", to_string(aliceObjectsParams2)); BEAST_EXPECT(aliceObjects.isMember(jss::result)); BEAST_EXPECT(!aliceObjects[jss::result].isMember(jss::error_message)); BEAST_EXPECT(aliceObjects[jss::result].isMember(jss::account_objects)); @@ -618,7 +647,9 @@ public: // account_objects does not currently return NFTPages. If // that ever changes, without also changing account_lines, // this test will need to be updated. - BEAST_EXPECT(aliceObjects[jss::result][jss::account_objects].size() == iterations + expectedNFTs); + BEAST_EXPECT( + aliceObjects[jss::result][jss::account_objects].size() == + iterations + expectedNFTs); // If ledger object association ever changes, for whatever // reason, this test will need to be updated. BEAST_EXPECTS(iterations == expectedIterations, std::to_string(iterations)); @@ -627,7 +658,8 @@ public: Json::Value beckyObjectsParams; beckyObjectsParams[jss::account] = becky.human(); beckyObjectsParams[jss::limit] = 200; - Json::Value const beckyObjects = env.rpc("json", "account_objects", to_string(beckyObjectsParams)); + Json::Value const beckyObjects = + env.rpc("json", "account_objects", to_string(beckyObjectsParams)); BEAST_EXPECT(beckyObjects.isMember(jss::result)); BEAST_EXPECT(!beckyObjects[jss::result].isMember(jss::error_message)); BEAST_EXPECT(beckyObjects[jss::result].isMember(jss::account_objects)); @@ -666,7 +698,9 @@ public: request[jss::ripplerpc] = "2.0"; request[jss::id] = 5; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == RPC::missing_field_error(jss::account)[jss::error_message]); + BEAST_EXPECT( + lines[jss::error][jss::message] == + RPC::missing_field_error(jss::account)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -682,7 +716,9 @@ public: request[jss::id] = 5; request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); + BEAST_EXPECT( + lines[jss::error][jss::message] == + RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -699,7 +735,9 @@ public: request[jss::id] = 5; request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]); + BEAST_EXPECT( + lines[jss::error][jss::message] == + RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -738,7 +776,9 @@ public: request[jss::id] = 5; request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == "Invalid field 'ledger_index', not string or number."); + BEAST_EXPECT( + lines[jss::error][jss::message] == + "Invalid field 'ledger_index', not string or number."); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -810,7 +850,10 @@ public: BEAST_EXPECT(ledger58Info.seq == 58); // A re-usable test for historic ledgers. - auto testAccountLinesHistory = [this, &env](Account const& account, LedgerHeader const& info, int count) { + auto testAccountLinesHistory = [this, &env]( + Account const& account, + LedgerHeader const& info, + int count) { // Get account_lines by ledger index. Json::Value paramsSeq; paramsSeq[jss::account] = account.human(); @@ -925,7 +968,9 @@ public: request[jss::id] = 5; request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); + BEAST_EXPECT( + lines[jss::error][jss::message] == + RPC::make_error(rpcACT_MALFORMED)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -943,7 +988,8 @@ public: request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); BEAST_EXPECT( - lines[jss::error][jss::message] == RPC::expected_field_message(jss::limit, "unsigned integer")); + lines[jss::error][jss::message] == + RPC::expected_field_message(jss::limit, "unsigned integer")); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -1014,7 +1060,9 @@ public: requestD[jss::id] = 5; requestD[jss::params] = paramsD; auto const linesD = env.rpc("json2", to_string(requestD)); - BEAST_EXPECT(linesD[jss::error][jss::message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); + BEAST_EXPECT( + linesD[jss::error][jss::message] == + RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); BEAST_EXPECT(linesD.isMember(jss::jsonrpc) && linesD[jss::jsonrpc] == "2.0"); BEAST_EXPECT(linesD.isMember(jss::ripplerpc) && linesD[jss::ripplerpc] == "2.0"); BEAST_EXPECT(linesD.isMember(jss::id) && linesD[jss::id] == 5); @@ -1031,7 +1079,9 @@ public: request[jss::id] = 5; request[jss::params] = params; auto const lines = env.rpc("json2", to_string(request)); - BEAST_EXPECT(lines[jss::error][jss::message] == RPC::expected_field_message(jss::marker, "string")); + BEAST_EXPECT( + lines[jss::error][jss::message] == + RPC::expected_field_message(jss::marker, "string")); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5); @@ -1186,7 +1236,9 @@ public: linesEndRequest[jss::id] = 5; linesEndRequest[jss::params] = linesEndParams; auto const linesEnd = env.rpc("json2", to_string(linesEndRequest)); - BEAST_EXPECT(linesEnd[jss::error][jss::message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); + BEAST_EXPECT( + linesEnd[jss::error][jss::message] == + RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); BEAST_EXPECT(linesEnd.isMember(jss::jsonrpc) && linesEnd[jss::jsonrpc] == "2.0"); BEAST_EXPECT(linesEnd.isMember(jss::ripplerpc) && linesEnd[jss::ripplerpc] == "2.0"); BEAST_EXPECT(linesEnd.isMember(jss::id) && linesEnd[jss::id] == 5); diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 0dec2bde7f..435bbd37d5 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -2,13 +2,12 @@ #include #include -#include - #include #include #include #include #include +#include #include @@ -160,7 +159,8 @@ public: params[jss::account] = bob.human(); params[jss::type] = 10; auto resp = env.rpc("json", "account_objects", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'type', not string."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Invalid field 'type', not string."); } // test error on type param not a valid type { @@ -176,7 +176,9 @@ public: params[jss::account] = bob.human(); params[jss::limit] = -1; auto resp = env.rpc("json", "account_objects", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'limit', not unsigned integer."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == + "Invalid field 'limit', not unsigned integer."); } // test errors on marker { @@ -196,7 +198,8 @@ public: std::string mark = to_string(resume_marker); params[jss::marker] = 10; resp = env.rpc("json", "account_objects", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker', not string."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Invalid field 'marker', not string."); params[jss::marker] = "This is a string with no comma"; resp = env.rpc("json", "account_objects", to_string(params)); @@ -302,9 +305,13 @@ public: BEAST_EXPECT(aobjs.size() == 1); auto& aobj = aobjs[0U]; if (i < 3) + { BEAST_EXPECT(resp[jss::result][jss::limit] == 1); + } else + { BEAST_EXPECT(!resp[jss::result].isMember(jss::limit)); + } aobj.removeMember("PreviousTxnID"); aobj.removeMember("PreviousTxnLgrSeq"); @@ -526,7 +533,8 @@ public: Account const gw{"gateway"}; auto const USD = gw["USD"]; - auto const features = testable_amendments() | featureXChainBridge | featurePermissionedDomains; + auto const features = + testable_amendments() | featureXChainBridge | featurePermissionedDomains; Env env(*this, features); // Make a lambda we can use to get "account_objects" easily. @@ -597,7 +605,8 @@ public: auto const& nftPage = resp[jss::result][jss::account_objects][0u]; BEAST_EXPECT(nftPage[sfNFTokens.jsonName].size() == 1); BEAST_EXPECT( - nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName][sfNFTokenID.jsonName] == to_string(nftID)); + nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName][sfNFTokenID.jsonName] == + to_string(nftID)); } // Set up a trust line so we can find it. @@ -675,7 +684,9 @@ public: BEAST_EXPECT(acctObjsIsSize(resp, 1)); auto const& permissionedDomain = resp[jss::result][jss::account_objects][0u]; - BEAST_EXPECT(permissionedDomain.isMember(jss::Owner) && (permissionedDomain[jss::Owner] == gw.human())); + BEAST_EXPECT( + permissionedDomain.isMember(jss::Owner) && + (permissionedDomain[jss::Owner] == gw.human())); bool const check1 = BEAST_EXPECT( permissionedDomain.isMember(jss::AcceptedCredentials) && permissionedDomain[jss::AcceptedCredentials].isArray() && @@ -684,9 +695,11 @@ public: if (check1) { - auto const& credential = permissionedDomain[jss::AcceptedCredentials][0u][jss::Credential]; + auto const& credential = + permissionedDomain[jss::AcceptedCredentials][0u][jss::Credential]; BEAST_EXPECT( - credential.isMember(sfIssuer.jsonName) && (credential[sfIssuer.jsonName] == issuer.human())); + credential.isMember(sfIssuer.jsonName) && + (credential[sfIssuer.jsonName] == issuer.human())); BEAST_EXPECT( credential.isMember(sfCredentialType.jsonName) && (credential[sfCredentialType.jsonName] == strHex(credentialType1))); @@ -769,8 +782,18 @@ public: // xchain_create_account_claim_id should be present on the door // account (Account::master) to collect the signatures until a // quorum is reached - scEnv(test::jtx::create_account_attestation( - x.scAttester, x.jvb, x.mcCarol, amt, x.reward, x.payees[0], true, 1, x.scuAlice, x.signers[0])); + scEnv( + test::jtx::create_account_attestation( + x.scAttester, + x.jvb, + x.mcCarol, + amt, + x.reward, + x.payees[0], + true, + 1, + x.scuAlice, + x.signers[0])); scEnv.close(); auto scEnvAcctObjs = [&](Account const& acct, char const* type) { @@ -783,12 +806,17 @@ public: { // Find the xchain_create_account_claim_id - Json::Value const resp = scEnvAcctObjs(Account::master, jss::xchain_owned_create_account_claim_id); + Json::Value const resp = + scEnvAcctObjs(Account::master, jss::xchain_owned_create_account_claim_id); BEAST_EXPECT(acctObjsIsSize(resp, 1)); - auto const& xchain_create_account_claim_id = resp[jss::result][jss::account_objects][0u]; - BEAST_EXPECT(xchain_create_account_claim_id[sfAccount.jsonName] == Account::master.human()); - BEAST_EXPECT(xchain_create_account_claim_id[sfXChainAccountCreateCount.getJsonName()].asUInt() == 1); + auto const& xchain_create_account_claim_id = + resp[jss::result][jss::account_objects][0u]; + BEAST_EXPECT( + xchain_create_account_claim_id[sfAccount.jsonName] == Account::master.human()); + BEAST_EXPECT( + xchain_create_account_claim_id[sfXChainAccountCreateCount.getJsonName()] + .asUInt() == 1); } } @@ -897,7 +925,8 @@ public: return v; }(); - std::uint32_t const expectedAccountObjects{static_cast(std::size(expectedLedgerTypes))}; + std::uint32_t const expectedAccountObjects{ + static_cast(std::size(expectedLedgerTypes))}; if (BEAST_EXPECT(acctObjsIsSize(resp, expectedAccountObjects))) { @@ -937,7 +966,8 @@ public: }; // Make a lambda we can use to check the number of fetched // account objects and their ledger type - auto expectObjects = [&](Json::Value const& resp, std::vector const& types) -> bool { + auto expectObjects = [&](Json::Value const& resp, + std::vector const& types) -> bool { if (!acctObjsIsSize(resp, types.size())) return false; std::vector typesOut; @@ -957,16 +987,21 @@ public: std::vector typesOut; getTypes(resp, typesOut); // request next two objects - resp = acctObjs(amm.ammAccount(), std::nullopt, 10, resp[jss::result][jss::marker].asString()); + resp = acctObjs( + amm.ammAccount(), std::nullopt, 10, resp[jss::result][jss::marker].asString()); getTypes(resp, typesOut); BEAST_EXPECT( (typesOut == std::vector{ - jss::AMM.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()})); + jss::AMM.c_str(), + jss::RippleState.c_str(), + jss::RippleState.c_str(), + jss::RippleState.c_str()})); // filter by state: there are three trustlines resp = acctObjs(amm.ammAccount(), jss::state, 10); - BEAST_EXPECT( - expectObjects(resp, {jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()})); + BEAST_EXPECT(expectObjects( + resp, + {jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()})); // AMM account doesn't own offers BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::offer), 0)); // gw account doesn't own AMM object @@ -1079,7 +1114,8 @@ public: // test an invalid marker that has a non-hex character BEAST_EXPECT(testInvalidMarker( - "00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G", "Invalid field \'marker\'.")); + "00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G", + "Invalid field \'marker\'.")); // this lambda function is used to create some fake marker using given // taxon and sequence because we want to test some unassociated markers @@ -1090,17 +1126,18 @@ public: std::uint16_t flags = 0, std::uint16_t fee = 0) { // the marker has the exact same format as an NFTokenID - return to_string(NFTokenMint::createNFTokenID(flags, fee, issuer, nft::toTaxon(taxon), tokenSeq)); + return to_string( + NFTokenMint::createNFTokenID(flags, fee, issuer, nft::toTaxon(taxon), tokenSeq)); }; // test an unassociated marker which does not exist in the NFTokenIDs - BEAST_EXPECT( - testInvalidMarker(createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000), "Invalid field \'marker\'.")); + BEAST_EXPECT(testInvalidMarker( + createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000), "Invalid field \'marker\'.")); // test an unassociated marker which exceeds the maximum value of the // existing NFTokenID - BEAST_EXPECT( - testInvalidMarker(createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF), "Invalid field \'marker\'.")); + BEAST_EXPECT(testInvalidMarker( + createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF), "Invalid field \'marker\'.")); } void diff --git a/src/test/rpc/AccountOffers_test.cpp b/src/test/rpc/AccountOffers_test.cpp index ffa1d70992..6b93e0570f 100644 --- a/src/test/rpc/AccountOffers_test.cpp +++ b/src/test/rpc/AccountOffers_test.cpp @@ -12,7 +12,8 @@ public: static bool checkMarker(Json::Value const& val) { - return val.isMember(jss::marker) && val[jss::marker].isString() && val[jss::marker].asString().size() > 0; + return val.isMember(jss::marker) && val[jss::marker].isString() && + !val[jss::marker].asString().empty(); } void @@ -52,7 +53,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::limit] = 1u; - auto const jrr_l = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr_l = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; auto const& jro_l = jrr_l[jss::offers]; BEAST_EXPECT(checkMarker(jrr_l)); // 9u is the expected size, since one account object is a trustline @@ -115,7 +117,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::limit] = 1u; - auto const jrr_l_1 = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr_l_1 = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; auto const& jro_l_1 = jrr_l_1[jss::offers]; // there is a difference in the validation of the limit param // between admin and non-admin requests. with admin requests, the @@ -130,7 +133,8 @@ public: // second item...with previous marker passed jvParams[jss::marker] = jrr_l_1[jss::marker]; - auto const jrr_l_2 = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr_l_2 = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; auto const& jro_l_2 = jrr_l_2[jss::offers]; BEAST_EXPECT(checkMarker(jrr_l_2)); BEAST_EXPECT(checkArraySize(jro_l_2, 1u)); @@ -139,7 +143,8 @@ public: // last item...with previous marker passed jvParams[jss::marker] = jrr_l_2[jss::marker]; jvParams[jss::limit] = 10u; - auto const jrr_l_3 = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr_l_3 = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; auto const& jro_l_3 = jrr_l_3[jss::offers]; BEAST_EXPECT(!jrr_l_3.isMember(jss::marker)); BEAST_EXPECT(checkArraySize(jro_l_3, 1u)); @@ -155,7 +160,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::limit] = 0u; - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr.isMember(jss::error_message)); } } @@ -205,7 +211,8 @@ public: // empty string account Json::Value jvParams; jvParams[jss::account] = ""; - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr[jss::error] == "actMalformed"); BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error_message] == "Account malformed."); @@ -224,7 +231,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::limit] = "0"; // NOT an integer - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not unsigned integer."); @@ -235,10 +243,12 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::marker] = "NOT_A_MARKER"; - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT(jrr[jss::status] == "error"); - BEAST_EXPECTS(jrr[jss::error_message] == "Invalid field 'marker'.", jrr.toStyledString()); + BEAST_EXPECTS( + jrr[jss::error_message] == "Invalid field 'marker'.", jrr.toStyledString()); } { @@ -246,7 +256,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::marker] = 1; - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not string."); @@ -257,7 +268,8 @@ public: Json::Value jvParams; jvParams[jss::account] = bob.human(); jvParams[jss::ledger_index] = 10u; - auto const jrr = env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const jrr = + env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(jrr[jss::error] == "lgrNotFound"); BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound"); diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 3cc9dbc77c..a11f957628 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -62,16 +62,24 @@ class AccountTx_test : public beast::unit_test::suite for (Json::Value const& metaNode : txNode[jss::meta][sfAffectedNodes.jsonName]) { if (metaNode.isMember(sfCreatedNode.jsonName)) - createdNodes.insert(metaNode[sfCreatedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + { + createdNodes.insert( + metaNode[sfCreatedNode.jsonName][sfLedgerEntryType.jsonName].asString()); + } else if (metaNode.isMember(sfDeletedNode.jsonName)) - deletedNodes.insert(metaNode[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + { + deletedNodes.insert( + metaNode[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString()); + } else if (metaNode.isMember(sfModifiedNode.jsonName)) - modifiedNodes.insert(metaNode[sfModifiedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + { + modifiedNodes.insert( + metaNode[sfModifiedNode.jsonName][sfLedgerEntryType.jsonName].asString()); + } else + { fail("Unexpected or unlabeled node type in metadata.", __FILE__, __LINE__); + } } BEAST_EXPECT(createdNodes == sane.created); @@ -102,16 +110,20 @@ class AccountTx_test : public beast::unit_test::suite case 1: return j.isMember(jss::result) && (j[jss::result][jss::status] == "success") && (j[jss::result][jss::transactions].size() == 2) && - (j[jss::result][jss::transactions][0u][jss::tx][jss::TransactionType] == jss::AccountSet) && - (j[jss::result][jss::transactions][1u][jss::tx][jss::TransactionType] == jss::Payment) && - (j[jss::result][jss::transactions][1u][jss::tx][jss::DeliverMax] == "10000000010") && + (j[jss::result][jss::transactions][0u][jss::tx][jss::TransactionType] == + jss::AccountSet) && + (j[jss::result][jss::transactions][1u][jss::tx][jss::TransactionType] == + jss::Payment) && + (j[jss::result][jss::transactions][1u][jss::tx][jss::DeliverMax] == + "10000000010") && (j[jss::result][jss::transactions][1u][jss::tx][jss::Amount] == j[jss::result][jss::transactions][1u][jss::tx][jss::DeliverMax]); case 2: case 3: if (j.isMember(jss::result) && (j[jss::result][jss::status] == "success") && (j[jss::result][jss::transactions].size() == 2) && - (j[jss::result][jss::transactions][0u][jss::tx_json][jss::TransactionType] == jss::AccountSet)) + (j[jss::result][jss::transactions][0u][jss::tx_json] + [jss::TransactionType] == jss::AccountSet)) { auto const& payment = j[jss::result][jss::transactions][1u]; @@ -123,14 +135,17 @@ class AccountTx_test : public beast::unit_test::suite (payment[jss::hash] == "9F3085D85F472D1CC29627F260DF68EDE59D42D1D0C33E345" "ECF0D4CE981D0A8") && - (payment[jss::validated] == true) && (payment[jss::ledger_index] == 3) && + (payment[jss::validated] == true) && + (payment[jss::ledger_index] == 3) && (payment[jss::ledger_hash] == "5476DCD816EA04CBBA57D47BBF1FC58A5217CC93A5ADD79CB" "580A5AFDD727E33") && (payment[jss::close_time_iso] == "2000-01-01T00:00:10Z"); } else + { return false; + } default: return false; @@ -169,16 +184,26 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_min] = 0; p[jss::ledger_index_max] = 100; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else - BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + { + BEAST_EXPECT( + isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = 1; p[jss::ledger_index_max] = 2; if (apiVersion < 2u) + { BEAST_EXPECT(noTxs(env.rpc("json", "account_tx", to_string(p)))); + } else - BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + { + BEAST_EXPECT( + isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = 2; p[jss::ledger_index_max] = 1; @@ -194,9 +219,14 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_min] = 1; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else - BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + { + BEAST_EXPECT( + isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_min] = env.current()->header().seq; BEAST_EXPECT(isErr( @@ -212,9 +242,14 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index_max] = env.current()->header().seq; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else - BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + { + BEAST_EXPECT( + isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } p[jss::ledger_index_max] = 3; BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); @@ -266,9 +301,13 @@ class AccountTx_test : public beast::unit_test::suite p[jss::ledger_index] = -1; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } } // Ledger index max only @@ -276,9 +315,14 @@ class AccountTx_test : public beast::unit_test::suite Json::Value p{jParams}; p[jss::ledger_index_max] = env.current()->header().seq; if (apiVersion < 2u) + { BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); + } else - BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + { + BEAST_EXPECT( + isErr(env.rpc("json", "account_tx", to_string(p)), rpcLGR_IDX_MALFORMED)); + } } // test account non-string { @@ -307,7 +351,9 @@ class AccountTx_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::result][jss::status] == "success"); } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } p[jss::binary] = true; Json::Value result{env.rpc("json", "account_tx", to_string(p))}; @@ -315,9 +361,13 @@ class AccountTx_test : public beast::unit_test::suite p[jss::forward] = "true"; if (apiVersion < 2u) + { BEAST_EXPECT(result[jss::result][jss::status] == "success"); + } else + { BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), rpcINVALID_PARAMS)); + } p[jss::forward] = false; result = env.rpc("json", "account_tx", to_string(p)); @@ -396,7 +446,8 @@ class AccountTx_test : public beast::unit_test::suite // Test case: limit = 10 should succeed (valid integer) p[jss::limit] = 10; - BEAST_EXPECT(env.rpc("json", "account_tx", to_string(p))[jss::result][jss::status] == "success"); + BEAST_EXPECT( + env.rpc("json", "account_tx", to_string(p))[jss::result][jss::status] == "success"); } } @@ -672,7 +723,9 @@ class AccountTx_test : public beast::unit_test::suite // The first two transactions listed in sanity haven't happened yet. constexpr unsigned int beckyDeletedOffset = 2; - BEAST_EXPECT(std::size(sanity) == result[jss::result][jss::transactions].size() + beckyDeletedOffset); + BEAST_EXPECT( + std::size(sanity) == + result[jss::result][jss::transactions].size() + beckyDeletedOffset); Json::Value const& txs{result[jss::result][jss::transactions]}; @@ -684,7 +737,8 @@ class AccountTx_test : public beast::unit_test::suite // All it takes is a large enough XRP payment to resurrect // becky's account. Try too small a payment. - env(pay(alice, becky, drops(env.current()->fees().accountReserve(0)) - XRP(1)), ter(tecNO_DST_INSUF_XRP)); + env(pay(alice, becky, drops(env.current()->fees().accountReserve(0)) - XRP(1)), + ter(tecNO_DST_INSUF_XRP)); env.close(); // Actually resurrect becky's account. @@ -756,7 +810,9 @@ class AccountTx_test : public beast::unit_test::suite // alice creates issuance mptAlice.create( - {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback | tfMPTRequireAuth | tfMPTCanTransfer}); + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanClawback | tfMPTRequireAuth | tfMPTCanTransfer}); checkAliceAcctTx(3, jss::MPTokenIssuanceCreate); diff --git a/src/test/rpc/AmendmentBlocked_test.cpp b/src/test/rpc/AmendmentBlocked_test.cpp index 73a8c6d299..e61f22fa0e 100644 --- a/src/test/rpc/AmendmentBlocked_test.cpp +++ b/src/test/rpc/AmendmentBlocked_test.cpp @@ -1,10 +1,10 @@ #include #include -#include #include #include +#include namespace xrpl { @@ -63,7 +63,8 @@ class AmendmentBlocked_test : public beast::unit_test::suite pf_req[jss::destination_amount] = bob["USD"](20).value().getJson(JsonOptions::none); jr = wsc->invoke("path_find", pf_req)[jss::result]; BEAST_EXPECT( - jr.isMember(jss::alternatives) && jr[jss::alternatives].isArray() && jr[jss::alternatives].size() == 1); + jr.isMember(jss::alternatives) && jr[jss::alternatives].isArray() && + jr[jss::alternatives].size() == 1); BEAST_EXPECT(!jr.isMember(jss::warnings)); // submit @@ -127,7 +128,8 @@ class AmendmentBlocked_test : public beast::unit_test::suite pf_req[jss::destination_amount] = bob["USD"](20).value().getJson(JsonOptions::none); jr = wsc->invoke("path_find", pf_req)[jss::result]; BEAST_EXPECT( - jr.isMember(jss::alternatives) && jr[jss::alternatives].isArray() && jr[jss::alternatives].size() == 1); + jr.isMember(jss::alternatives) && jr[jss::alternatives].isArray() && + jr[jss::alternatives].size() == 1); BEAST_EXPECT(!jr.isMember(jss::warnings)); // submit diff --git a/src/test/rpc/BookChanges_test.cpp b/src/test/rpc/BookChanges_test.cpp index 4ea4b8233b..6bd71b141b 100644 --- a/src/test/rpc/BookChanges_test.cpp +++ b/src/test/rpc/BookChanges_test.cpp @@ -74,7 +74,8 @@ public: using namespace jtx; FeatureBitset const all{ - jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | featurePermissionedDEX}; + jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | + featurePermissionedDEX}; Env env(*this, all); PermissionedDEX permDex(env); diff --git a/src/test/rpc/Book_test.cpp b/src/test/rpc/Book_test.cpp index 543e163432..8136af310a 100644 --- a/src/test/rpc/Book_test.cpp +++ b/src/test/rpc/Book_test.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -15,7 +16,11 @@ namespace test { class Book_test : public beast::unit_test::suite { std::string - getBookDir(jtx::Env& env, Issue const& in, Issue const& out, std::optional const& domain = std::nullopt) + getBookDir( + jtx::Env& env, + Issue const& in, + Issue const& out, + std::optional const& domain = std::nullopt) { std::string dir; auto uBookBase = getBookBase({in, out, domain}); @@ -26,7 +31,7 @@ class Book_test : public beast::unit_test::suite { auto sleOfferDir = view->read(keylet::page(key.value())); uint256 offerIndex; - unsigned int bookEntry; + unsigned int bookEntry = 0; cdirFirst(*view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex); auto sleOffer = view->read(keylet::offer(offerIndex)); dir = to_string(sleOffer->getFieldH256(sfBookDirectory)); @@ -67,7 +72,8 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); BEAST_EXPECT(!jv[jss::result].isMember(jss::asks)); BEAST_EXPECT(!jv[jss::result].isMember(jss::bids)); } @@ -143,11 +149,14 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerGets] == XRP(200).value().getJson(JsonOptions::none)); + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerPays] == USD(100).value().getJson(JsonOptions::none)); + jv[jss::result][jss::offers][0u][jss::TakerGets] == + XRP(200).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][0u][jss::TakerPays] == + USD(100).value().getJson(JsonOptions::none)); BEAST_EXPECT(!jv[jss::result].isMember(jss::asks)); BEAST_EXPECT(!jv[jss::result].isMember(jss::bids)); } @@ -217,8 +226,10 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 0); - BEAST_EXPECT(jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 0); BEAST_EXPECT(!jv[jss::result].isMember(jss::offers)); } @@ -301,12 +312,22 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 1); - BEAST_EXPECT(jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 1); - BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerGets] == USD(100).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerPays] == XRP(500).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerGets] == XRP(200).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerPays] == USD(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 1); + BEAST_EXPECT( + jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 1); + BEAST_EXPECT( + jv[jss::result][jss::asks][0u][jss::TakerGets] == + USD(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::asks][0u][jss::TakerPays] == + XRP(500).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][0u][jss::TakerGets] == + XRP(200).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][0u][jss::TakerPays] == + USD(100).value().getJson(JsonOptions::none)); BEAST_EXPECT(!jv[jss::result].isMember(jss::offers)); } @@ -391,7 +412,8 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); BEAST_EXPECT(!jv[jss::result].isMember(jss::asks)); BEAST_EXPECT(!jv[jss::result].isMember(jss::bids)); } @@ -504,15 +526,20 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 2); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerGets] == XRP(200).value().getJson(JsonOptions::none)); + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 2); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerPays] == USD(100).value().getJson(JsonOptions::none)); + jv[jss::result][jss::offers][0u][jss::TakerGets] == + XRP(200).value().getJson(JsonOptions::none)); BEAST_EXPECT( - jv[jss::result][jss::offers][1u][jss::TakerGets] == CNY(200).value().getJson(JsonOptions::none)); + jv[jss::result][jss::offers][0u][jss::TakerPays] == + USD(100).value().getJson(JsonOptions::none)); BEAST_EXPECT( - jv[jss::result][jss::offers][1u][jss::TakerPays] == JPY(100).value().getJson(JsonOptions::none)); + jv[jss::result][jss::offers][1u][jss::TakerGets] == + CNY(200).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][1u][jss::TakerPays] == + JPY(100).value().getJson(JsonOptions::none)); BEAST_EXPECT(!jv[jss::result].isMember(jss::asks)); BEAST_EXPECT(!jv[jss::result].isMember(jss::bids)); } @@ -614,8 +641,10 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 0); - BEAST_EXPECT(jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 0); BEAST_EXPECT(!jv[jss::result].isMember(jss::offers)); } @@ -744,16 +773,34 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 2); - BEAST_EXPECT(jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 2); - BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerGets] == USD(100).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerPays] == XRP(500).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::asks][1u][jss::TakerGets] == JPY(100).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::asks][1u][jss::TakerPays] == CNY(500).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerGets] == XRP(200).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerPays] == USD(100).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][1u][jss::TakerGets] == CNY(200).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::bids][1u][jss::TakerPays] == JPY(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result].isMember(jss::asks) && jv[jss::result][jss::asks].size() == 2); + BEAST_EXPECT( + jv[jss::result].isMember(jss::bids) && jv[jss::result][jss::bids].size() == 2); + BEAST_EXPECT( + jv[jss::result][jss::asks][0u][jss::TakerGets] == + USD(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::asks][0u][jss::TakerPays] == + XRP(500).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::asks][1u][jss::TakerGets] == + JPY(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::asks][1u][jss::TakerPays] == + CNY(500).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][0u][jss::TakerGets] == + XRP(200).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][0u][jss::TakerPays] == + USD(100).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][1u][jss::TakerGets] == + CNY(200).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::bids][1u][jss::TakerPays] == + JPY(100).value().getJson(JsonOptions::none)); BEAST_EXPECT(!jv[jss::result].isMember(jss::offers)); } @@ -858,7 +905,8 @@ public: } if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); BEAST_EXPECT(!jv[jss::result].isMember(jss::asks)); BEAST_EXPECT(!jv[jss::result].isMember(jss::bids)); } @@ -907,7 +955,8 @@ public: BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jval) { auto const& t = jval[jss::transaction]; return t[jss::TransactionType] == jss::OfferCreate && - t[jss::TakerGets] == USD(10).value().getJson(JsonOptions::none) && t[jss::owner_funds] == "100" && + t[jss::TakerGets] == USD(10).value().getJson(JsonOptions::none) && + t[jss::owner_funds] == "100" && t[jss::TakerPays] == XRP(4000).value().getJson(JsonOptions::none); })); @@ -917,7 +966,8 @@ public: BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jval) { auto const& t = jval[jss::transaction]; return t[jss::TransactionType] == jss::OfferCreate && - t[jss::TakerGets] == USD(5).value().getJson(JsonOptions::none) && t[jss::owner_funds] == "50" && + t[jss::TakerGets] == USD(5).value().getJson(JsonOptions::none) && + t[jss::owner_funds] == "50" && t[jss::TakerPays] == XRP(2000).value().getJson(JsonOptions::none); })); @@ -1204,7 +1254,8 @@ public: jvParams[jss::taker_gets] = Json::objectValue; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.currency', not string."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_pays.currency', not string."); } { @@ -1224,7 +1275,8 @@ public: jvParams[jss::taker_gets][jss::currency] = 1; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets.currency', not string."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_gets.currency', not string."); } { @@ -1234,7 +1286,8 @@ public: jvParams[jss::taker_gets][jss::currency] = "XRP"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "srcCurMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.currency', bad currency."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_pays.currency', bad currency."); } { @@ -1244,7 +1297,8 @@ public: jvParams[jss::taker_gets][jss::currency] = "NOT_VALID"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "dstAmtMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets.currency', bad currency."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_gets.currency', bad currency."); } { @@ -1255,7 +1309,8 @@ public: jvParams[jss::taker_gets][jss::issuer] = 1; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', not string."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', not string."); } { @@ -1266,7 +1321,8 @@ public: jvParams[jss::taker_gets][jss::currency] = "USD"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', not string."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', not string."); } { @@ -1277,7 +1333,8 @@ public: jvParams[jss::taker_gets][jss::currency] = "USD"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', bad issuer."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', bad issuer."); } { @@ -1288,7 +1345,9 @@ public: jvParams[jss::taker_gets][jss::currency] = "USD"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', bad issuer account one."); + BEAST_EXPECT( + jrr[jss::error_message] == + "Invalid field 'taker_pays.issuer', bad issuer account one."); } { @@ -1299,7 +1358,8 @@ public: jvParams[jss::taker_gets][jss::issuer] = gw.human() + "DEAD"; auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', bad issuer."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', bad issuer."); } { @@ -1310,7 +1370,9 @@ public: jvParams[jss::taker_gets][jss::issuer] = toBase58(noAccount()); auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', bad issuer account one."); + BEAST_EXPECT( + jrr[jss::error_message] == + "Invalid field 'taker_gets.issuer', bad issuer account one."); } { @@ -1337,7 +1399,9 @@ public: jvParams[jss::taker_gets][jss::issuer] = gw.human(); auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', expected non-XRP issuer."); + BEAST_EXPECT( + jrr[jss::error_message] == + "Invalid field 'taker_pays.issuer', expected non-XRP issuer."); } { @@ -1494,7 +1558,8 @@ public: using namespace jtx; FeatureBitset const all{ - jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | featurePermissionedDEX}; + jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | + featurePermissionedDEX}; Env env(*this, all); PermissionedDEX permDex(env); @@ -1515,7 +1580,8 @@ public: BEAST_EXPECT(jrr[jss::offers].size() == 1); auto const jrOffer = jrr[jss::offers][0u]; BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human()); - BEAST_EXPECT(jrOffer[sfBookDirectory.fieldName] == getBookDir(env, XRP, USD.issue(), domainID)); + BEAST_EXPECT( + jrOffer[sfBookDirectory.fieldName] == getBookDir(env, XRP, USD.issue(), domainID)); BEAST_EXPECT(jrOffer[sfBookNode.fieldName] == "0"); BEAST_EXPECT(jrOffer[jss::Flags] == 0); BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == jss::Offer); @@ -1541,12 +1607,17 @@ public: } auto checkSubBooks = [&](Json::Value const& jv) { - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerGets] == USD(10).value().getJson(JsonOptions::none)); + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerPays] == XRP(10).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::offers][0u][sfDomainID.jsonName].asString() == to_string(domainID)); + jv[jss::result][jss::offers][0u][jss::TakerGets] == + USD(10).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][0u][jss::TakerPays] == + XRP(10).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][0u][sfDomainID.jsonName].asString() == + to_string(domainID)); }; // book_offers: requesting domain book returns hybrid offer @@ -1598,7 +1669,8 @@ public: auto jv = wsc->invoke("subscribe", books); if (!BEAST_EXPECT(jv[jss::status] == "success")) return; - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); + BEAST_EXPECT( + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 0); } } @@ -1609,7 +1681,8 @@ public: using namespace jtx; FeatureBitset const all{ - jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | featurePermissionedDEX}; + jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | + featurePermissionedDEX}; Env env(*this, all); PermissionedDEX permDex(env); @@ -1630,7 +1703,8 @@ public: BEAST_EXPECT(jrr[jss::offers].size() == 1); auto const jrOffer = jrr[jss::offers][0u]; BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human()); - BEAST_EXPECT(jrOffer[sfBookDirectory.fieldName] == getBookDir(env, XRP, USD.issue(), domainID)); + BEAST_EXPECT( + jrOffer[sfBookDirectory.fieldName] == getBookDir(env, XRP, USD.issue(), domainID)); BEAST_EXPECT(jrOffer[sfBookNode.fieldName] == "0"); BEAST_EXPECT(jrOffer[jss::Flags] == lsfHybrid); BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == jss::Offer); @@ -1656,12 +1730,17 @@ public: } auto checkSubBooks = [&](Json::Value const& jv) { - BEAST_EXPECT(jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerGets] == USD(10).value().getJson(JsonOptions::none)); + jv[jss::result].isMember(jss::offers) && jv[jss::result][jss::offers].size() == 1); BEAST_EXPECT( - jv[jss::result][jss::offers][0u][jss::TakerPays] == XRP(10).value().getJson(JsonOptions::none)); - BEAST_EXPECT(jv[jss::result][jss::offers][0u][sfDomainID.jsonName].asString() == to_string(domainID)); + jv[jss::result][jss::offers][0u][jss::TakerGets] == + USD(10).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][0u][jss::TakerPays] == + XRP(10).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + jv[jss::result][jss::offers][0u][sfDomainID.jsonName].asString() == + to_string(domainID)); }; // book_offers: requesting domain book returns hybrid offer diff --git a/src/test/rpc/DeliveredAmount_test.cpp b/src/test/rpc/DeliveredAmount_test.cpp index 8d376893ec..00f8c54ee5 100644 --- a/src/test/rpc/DeliveredAmount_test.cpp +++ b/src/test/rpc/DeliveredAmount_test.cpp @@ -36,9 +36,13 @@ class CheckDeliveredAmount if (!afterSwitchTime_) { if (partial) + { ++numExpectedAvailable_; + } else + { ++numExpectedSetUnavailable_; + } return; } // normal case: after switch time & successful transaction @@ -91,16 +95,26 @@ public: if (isSet) { if (metaData[jss::delivered_amount] != "unavailable") + { isSetAvailable = true; + } else + { isSetUnavailable = true; + } } if (isSetAvailable) + { --numExpectedAvailable_; + } else if (isSetUnavailable) + { --numExpectedSetUnavailable_; + } else if (!isSet) + { --numExpectedNotSet_; + } if (isSet) { @@ -178,9 +192,13 @@ class DeliveredAmount_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, carol, gw); env.trust(USD(1000), alice, bob, carol); if (afterSwitchTime) + { env.close(NetClock::time_point{446000000s}); + } else + { env.close(); + } CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime}; { @@ -228,14 +246,16 @@ class DeliveredAmount_test : public beast::unit_test::suite // Check stream update while (true) { - auto const r = wsc->findMsg(1s, [&](auto const& jv) { return jv[jss::ledger_index] == 4; }); + auto const r = wsc->findMsg( + 1s, [&](auto const& jv) { return jv[jss::ledger_index] == 4; }); if (!r) break; if (!r->isMember(jss::transaction)) continue; - BEAST_EXPECT(checkDeliveredAmount.checkTxn((*r)[jss::transaction], (*r)[jss::meta])); + BEAST_EXPECT( + checkDeliveredAmount.checkTxn((*r)[jss::transaction], (*r)[jss::meta])); } } BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters()); @@ -263,9 +283,13 @@ class DeliveredAmount_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, carol, gw); env.trust(USD(1000), alice, bob, carol); if (afterSwitchTime) + { env.close(NetClock::time_point{446000000s}); + } else + { env.close(); + } CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime}; // normal payments @@ -285,13 +309,12 @@ class DeliveredAmount_test : public beast::unit_test::suite env.require(balance(carol, USD(0))); env.close(); - std::string index; Json::Value jvParams; jvParams[jss::ledger_index] = 4u; jvParams[jss::transactions] = true; jvParams[jss::expand] = true; - auto const jtxn = - env.rpc("json", "ledger", to_string(jvParams))[jss::result][jss::ledger][jss::transactions]; + auto const jtxn = env.rpc( + "json", "ledger", to_string(jvParams))[jss::result][jss::ledger][jss::transactions]; for (auto const& t : jtxn) BEAST_EXPECT(checkDeliveredAmount.checkTxn(t, t[jss::metaData])); BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters()); @@ -311,7 +334,8 @@ class DeliveredAmount_test : public beast::unit_test::suite MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .close = false}); - mptAlice.create({.transferFee = 25000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + mptAlice.create( + {.transferFee = 25000, .ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); auto const MPT = mptAlice["MPT"]; mptAlice.authorize({.account = bob}); @@ -330,8 +354,10 @@ class DeliveredAmount_test : public beast::unit_test::suite if (features[fixMPTDeliveredAmount]) { - BEAST_EXPECT(meta[sfDeliveredAmount.jsonName] == STAmount{MPT(800)}.getJson(JsonOptions::none)); - BEAST_EXPECT(meta[jss::delivered_amount] == STAmount{MPT(800)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[sfDeliveredAmount.jsonName] == STAmount{MPT(800)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[jss::delivered_amount] == STAmount{MPT(800)}.getJson(JsonOptions::none)); } else { @@ -347,8 +373,10 @@ class DeliveredAmount_test : public beast::unit_test::suite if (features[fixMPTDeliveredAmount]) { - BEAST_EXPECT(meta[sfDeliveredAmount.jsonName] == STAmount{MPT(960)}.getJson(JsonOptions::none)); - BEAST_EXPECT(meta[jss::delivered_amount] == STAmount{MPT(960)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[sfDeliveredAmount.jsonName] == STAmount{MPT(960)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[jss::delivered_amount] == STAmount{MPT(960)}.getJson(JsonOptions::none)); } else { diff --git a/src/test/rpc/DepositAuthorized_test.cpp b/src/test/rpc/DepositAuthorized_test.cpp index e8abfd58b8..87951e397c 100644 --- a/src/test/rpc/DepositAuthorized_test.cpp +++ b/src/test/rpc/DepositAuthorized_test.cpp @@ -58,27 +58,44 @@ public: // becky is authorized to deposit to herself. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(becky, becky, "validated").toStyledString()), true); + env.rpc( + "json", + "deposit_authorized", + depositAuthArgs(becky, becky, "validated").toStyledString()), + true); // alice should currently be authorized to deposit to becky. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky, "validated").toStyledString()), true); + env.rpc( + "json", + "deposit_authorized", + depositAuthArgs(alice, becky, "validated").toStyledString()), + true); // becky sets the DepositAuth flag in the current ledger. env(fset(becky, asfDepositAuth)); // alice is no longer authorized to deposit to becky in current ledger. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky).toStyledString()), false); + env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky).toStyledString()), + false); env.close(); // becky is still authorized to deposit to herself. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(becky, becky, "validated").toStyledString()), true); + env.rpc( + "json", + "deposit_authorized", + depositAuthArgs(becky, becky, "validated").toStyledString()), + true); // It's not a reciprocal arrangement. becky can deposit to alice. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(becky, alice, "current").toStyledString()), true); + env.rpc( + "json", + "deposit_authorized", + depositAuthArgs(becky, alice, "current").toStyledString()), + true); // becky creates a deposit authorization for alice. env(deposit::auth(becky, alice)); @@ -86,22 +103,29 @@ public: // alice is now authorized to deposit to becky. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky, "closed").toStyledString()), true); + env.rpc( + "json", + "deposit_authorized", + depositAuthArgs(alice, becky, "closed").toStyledString()), + true); // carol is still not authorized to deposit to becky. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(carol, becky).toStyledString()), false); + env.rpc("json", "deposit_authorized", depositAuthArgs(carol, becky).toStyledString()), + false); // becky clears the DepositAuth flag so carol becomes authorized. env(fclear(becky, asfDepositAuth)); env.close(); validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(carol, becky).toStyledString()), true); + env.rpc("json", "deposit_authorized", depositAuthArgs(carol, becky).toStyledString()), + true); // alice is still authorized to deposit to becky. validateDepositAuthResult( - env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky).toStyledString()), true); + env.rpc("json", "deposit_authorized", depositAuthArgs(alice, becky).toStyledString()), + true); } // Test malformed cases. @@ -114,7 +138,8 @@ public: Account const becky{"becky"}; // Lambda that checks the (error) result of deposit_authorized. - auto verifyErr = [this](Json::Value const& result, char const* error, char const* errorMsg) { + auto verifyErr = [this]( + Json::Value const& result, char const* error, char const* errorMsg) { BEAST_EXPECT(result[jss::result][jss::status] == jss::error); BEAST_EXPECT(result[jss::result][jss::error] == error); BEAST_EXPECT(result[jss::result][jss::error_message] == errorMsg); @@ -154,7 +179,8 @@ public: Json::Value args{depositAuthArgs(alice, becky)}; args[jss::destination_account] = 7.3; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; - verifyErr(result, "invalidParams", "Invalid field 'destination_account', not a string."); + verifyErr( + result, "invalidParams", "Invalid field 'destination_account', not a string."); } { // Corrupt destination_account field. @@ -167,7 +193,8 @@ public: // Request an invalid ledger. Json::Value args{depositAuthArgs(alice, becky, "-1")}; Json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())}; - verifyErr(result, "invalidParams", "Invalid field 'ledger_index', not string or number."); + verifyErr( + result, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { // Request a ledger that doesn't exist yet as a string. @@ -219,7 +246,11 @@ public: if (result.isMember(jss::deposit_authorized)) BEAST_EXPECT(result[jss::deposit_authorized] == authorized); if (authorized) - BEAST_EXPECT(result.isMember(jss::deposit_authorized) && (result[jss::deposit_authorized] == true)); + { + BEAST_EXPECT( + result.isMember(jss::deposit_authorized) && + (result[jss::deposit_authorized] == true)); + } BEAST_EXPECT(result.isMember(jss::error) == !error.empty()); if (!error.empty()) @@ -314,7 +345,8 @@ public: args[jss::credentials].append("hello world"); auto const jv = env.rpc("json", "deposit_authorized", args.toStyledString()); - checkCredentialsResponse(jv[jss::result], alice, becky, false, {"hello world"}, "invalidParams"); + checkCredentialsResponse( + jv[jss::result], alice, becky, false, {"hello world"}, "invalidParams"); } { @@ -345,8 +377,11 @@ public: "deposit_authorized with credentials not authorized: " "credential not accepted"); auto const jv = env.rpc( - "json", "deposit_authorized", depositAuthArgs(alice, becky, "validated", {credIdx}).toStyledString()); - checkCredentialsResponse(jv[jss::result], alice, becky, false, {credIdx}, "badCredentials"); + "json", + "deposit_authorized", + depositAuthArgs(alice, becky, "validated", {credIdx}).toStyledString()); + checkCredentialsResponse( + jv[jss::result], alice, becky, false, {credIdx}, "badCredentials"); } // alice accept credentials @@ -359,41 +394,39 @@ public: "json", "deposit_authorized", depositAuthArgs(alice, becky, "validated", {credIdx, credIdx}).toStyledString()); - checkCredentialsResponse(jv[jss::result], alice, becky, false, {credIdx, credIdx}, "badCredentials"); + checkCredentialsResponse( + jv[jss::result], alice, becky, false, {credIdx, credIdx}, "badCredentials"); } { static std::vector const credIds = { - "18004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "28004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "38004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "58004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "68004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "78004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "88004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4", - "98004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" - "E4"}; + "18004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "28004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "38004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "58004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "68004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "78004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "88004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + "98004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4", + }; assert(credIds.size() > maxCredentialsArraySize); testcase("deposit_authorized too long credentials"); auto const jv = env.rpc( - "json", "deposit_authorized", depositAuthArgs(alice, becky, "validated", credIds).toStyledString()); - checkCredentialsResponse(jv[jss::result], alice, becky, false, credIds, "invalidParams"); + "json", + "deposit_authorized", + depositAuthArgs(alice, becky, "validated", credIds).toStyledString()); + checkCredentialsResponse( + jv[jss::result], alice, becky, false, credIds, "invalidParams"); } { testcase("deposit_authorized with credentials"); auto const jv = env.rpc( - "json", "deposit_authorized", depositAuthArgs(alice, becky, "validated", {credIdx}).toStyledString()); + "json", + "deposit_authorized", + depositAuthArgs(alice, becky, "validated", {credIdx}).toStyledString()); checkCredentialsResponse(jv[jss::result], alice, becky, true, {credIdx}); } @@ -410,7 +443,9 @@ public: testcase("deposit_authorized account without preauth"); jv = env.rpc( - "json", "deposit_authorized", depositAuthArgs(becky, alice, "validated", {credBecky}).toStyledString()); + "json", + "deposit_authorized", + depositAuthArgs(becky, alice, "validated", {credBecky}).toStyledString()); checkCredentialsResponse(jv[jss::result], becky, alice, true, {credBecky}); } @@ -426,8 +461,11 @@ public: // alice try to use credential for different account jv = env.rpc( - "json", "deposit_authorized", depositAuthArgs(becky, alice, "validated", {credDiana}).toStyledString()); - checkCredentialsResponse(jv[jss::result], becky, alice, false, {credDiana}, "badCredentials"); + "json", + "deposit_authorized", + depositAuthArgs(becky, alice, "validated", {credDiana}).toStyledString()); + checkCredentialsResponse( + jv[jss::result], becky, alice, false, {credDiana}, "badCredentials"); } { @@ -435,7 +473,8 @@ public: // check expired credentials char const credType2[] = "random"; - std::uint32_t const x = env.current()->header().parentCloseTime.time_since_epoch().count() + 40; + std::uint32_t const x = + env.current()->header().parentCloseTime.time_since_epoch().count() + 40; // create credentials with expire time 40s auto jv = credentials::create(alice, carol, credType2); @@ -474,7 +513,8 @@ public: "deposit_authorized", depositAuthArgs(alice, becky, "validated", {credIdx2}).toStyledString()); - checkCredentialsResponse(jv[jss::result], alice, becky, false, {credIdx2}, "badCredentials"); + checkCredentialsResponse( + jv[jss::result], alice, becky, false, {credIdx2}, "badCredentials"); } } } diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index 21596634e3..0050429c99 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -1,8 +1,8 @@ #include -#include - +#include #include +#include #include namespace xrpl { @@ -41,11 +41,15 @@ class Feature_test : public beast::unit_test::suite if (vote == VoteBehavior::Obsolete) { - BEAST_EXPECT(allAmendments.contains(name) && allAmendments.at(name) == AmendmentSupport::Retired); + BEAST_EXPECT( + allAmendments.contains(name) && + allAmendments.at(name) == AmendmentSupport::Retired); } else { - BEAST_EXPECT(allAmendments.contains(name) && allAmendments.at(name) == AmendmentSupport::Supported); + BEAST_EXPECT( + allAmendments.contains(name) && + allAmendments.at(name) == AmendmentSupport::Supported); } } BEAST_EXPECT(down + obsolete == xrpl::detail::numDownVotedAmendments()); @@ -91,24 +95,29 @@ class Feature_test : public beast::unit_test::suite { (void)vote; auto const registered = getRegisteredFeature(feature); - if (BEAST_EXPECT(registered)) + + if (BEAST_EXPECT(registered); registered.has_value()) { BEAST_EXPECT(featureToName(*registered) == feature); - BEAST_EXPECT(bitsetIndexToFeature(featureToBitsetIndex(*registered)) == *registered); + BEAST_EXPECT( + bitsetIndexToFeature(featureToBitsetIndex(*registered)) == *registered); } } // Test an arbitrary unknown feature uint256 zero{0}; BEAST_EXPECT(featureToName(zero) == to_string(zero)); - BEAST_EXPECT(featureToName(zero) == "0000000000000000000000000000000000000000000000000000000000000000"); + BEAST_EXPECT( + featureToName(zero) == + "0000000000000000000000000000000000000000000000000000000000000000"); // Test looking up an unknown feature BEAST_EXPECT(!getRegisteredFeature("unknown")); // Test a random sampling of the variables. If any of these get retired // or removed, swap out for any other feature. - BEAST_EXPECT(featureToName(fixRemoveNFTokenAutoTrustLine) == "fixRemoveNFTokenAutoTrustLine"); + BEAST_EXPECT( + featureToName(fixRemoveNFTokenAutoTrustLine) == "fixRemoveNFTokenAutoTrustLine"); BEAST_EXPECT(featureToName(featureBatch) == "Batch"); BEAST_EXPECT(featureToName(featureDID) == "DID"); BEAST_EXPECT(featureToName(fixIncludeKeyletFields) == "fixIncludeKeyletFields"); @@ -135,14 +144,17 @@ class Feature_test : public beast::unit_test::suite // default config - so all should be disabled, and // supported. Some may be vetoed. bool expectVeto = (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); - bool expectObsolete = (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); + bool expectObsolete = + (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); BEAST_EXPECTS( feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(), feature[jss::name].asString() + " enabled"); BEAST_EXPECTS( feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete && - (!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) && - (feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"), + (!feature[jss::vetoed].isBool() || + feature[jss::vetoed].asBool() == expectVeto) && + (feature[jss::vetoed].isBool() || + feature[jss::vetoed].asString() == "Obsolete"), feature[jss::name].asString() + " vetoed"); BEAST_EXPECTS( feature.isMember(jss::supported) && feature[jss::supported].asBool(), @@ -158,16 +170,18 @@ class Feature_test : public beast::unit_test::suite using namespace test::jtx; Env env{*this}; - auto jrr = env.rpc("feature", "fixAMMOverflowOffer")[jss::result]; + std::string const name = "fixAMMOverflowOffer"; + auto jrr = env.rpc("feature", name)[jss::result]; BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"); jrr.removeMember(jss::status); BEAST_EXPECT(jrr.size() == 1); - BEAST_EXPECT( - jrr.isMember("12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E" - "2A87F1D8107")); + auto const expected = to_string(sha512Half(Slice(name.data(), name.size()))); + char const sha[] = "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E2A87F1D8107"; + BEAST_EXPECT(expected == sha); + BEAST_EXPECT(jrr.isMember(expected)); auto feature = *(jrr.begin()); - BEAST_EXPECTS(feature[jss::name] == "fixAMMOverflowOffer", "name"); + BEAST_EXPECTS(feature[jss::name] == name, "name"); BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled"); BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed"); BEAST_EXPECTS(feature[jss::supported].asBool(), "supported"); @@ -176,6 +190,37 @@ class Feature_test : public beast::unit_test::suite jrr = env.rpc("feature", "fMM")[jss::result]; BEAST_EXPECT(jrr[jss::error] == "badFeature"); BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid."); + + // Test feature name size checks + constexpr auto ok63Name = [] { + return "123456789012345678901234567890123456789012345678901234567890123"; + }; + static_assert(validFeatureNameSize(ok63Name)); + + constexpr auto bad64Name = [] { + return "1234567890123456789012345678901234567890123456789012345678901234"; + }; + static_assert(!validFeatureNameSize(bad64Name)); + + constexpr auto ok31Name = [] { return "1234567890123456789012345678901"; }; + static_assert(validFeatureNameSize(ok31Name)); + + constexpr auto bad32Name = [] { return "12345678901234567890123456789012"; }; + static_assert(!validFeatureNameSize(bad32Name)); + + constexpr auto ok33Name = [] { return "123456789012345678901234567890123"; }; + static_assert(validFeatureNameSize(ok33Name)); + + // Test feature character set checks + constexpr auto okName = [] { return "AMM_123"; }; + static_assert(validFeatureName(okName)); + + // First character is Greek Capital Alpha, visually confusable with ASCII 'A' + constexpr auto badName = [] { return "ΑMM_123"; }; + static_assert(!validFeatureName(badName)); + + constexpr auto badEmoji = [] { return "🔥"; }; + static_assert(!validFeatureName(badEmoji)); } void @@ -239,7 +284,8 @@ class Feature_test : public beast::unit_test::suite (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled, (*it)[jss::name].asString() + " enabled"); BEAST_EXPECTS( - (*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported, + (*it).isMember(jss::supported) && + (*it)[jss::supported].asBool() == expectSupported, (*it)[jss::name].asString() + " supported"); BEAST_EXPECT(!(*it).isMember(jss::vetoed)); BEAST_EXPECT(!(*it).isMember(jss::majority)); @@ -269,7 +315,8 @@ class Feature_test : public beast::unit_test::suite params[jss::vetoed] = true; auto const result = env.rpc("json", "feature", to_string(params))[jss::result]; BEAST_EXPECTS(result[jss::error] == "noPermission", result[jss::error].asString()); - BEAST_EXPECT(result[jss::error_message] == "You don't have permission for this command."); + BEAST_EXPECT( + result[jss::error_message] == "You don't have permission for this command."); } } @@ -300,13 +347,20 @@ class Feature_test : public beast::unit_test::suite (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled, (*it)[jss::name].asString() + " enabled"); if (expectEnabled) - BEAST_EXPECTS(!(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed"); + { + BEAST_EXPECTS( + !(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed"); + } else + { BEAST_EXPECTS( (*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete && - (!(*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asBool() == expectVeto) && - ((*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asString() == "Obsolete"), + (!(*it)[jss::vetoed].isBool() || + (*it)[jss::vetoed].asBool() == expectVeto) && + ((*it)[jss::vetoed].isBool() || + (*it)[jss::vetoed].asString() == "Obsolete"), (*it)[jss::name].asString() + " vetoed"); + } BEAST_EXPECTS( (*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported, (*it)[jss::name].asString() + " supported"); @@ -331,10 +385,14 @@ class Feature_test : public beast::unit_test::suite { if (!BEAST_EXPECT(feature.isMember(jss::name))) return; - BEAST_EXPECTS(!feature.isMember(jss::majority), feature[jss::name].asString() + " majority"); + BEAST_EXPECTS( + !feature.isMember(jss::majority), feature[jss::name].asString() + " majority"); BEAST_EXPECTS(!feature.isMember(jss::count), feature[jss::name].asString() + " count"); - BEAST_EXPECTS(!feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold"); - BEAST_EXPECTS(!feature.isMember(jss::validations), feature[jss::name].asString() + " validations"); + BEAST_EXPECTS( + !feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold"); + BEAST_EXPECTS( + !feature.isMember(jss::validations), + feature[jss::name].asString() + " validations"); BEAST_EXPECTS(!feature.isMember(jss::vote), feature[jss::name].asString() + " vote"); } @@ -364,18 +422,23 @@ class Feature_test : public beast::unit_test::suite if (!BEAST_EXPECT(feature.isMember(jss::name))) return; bool expectVeto = (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo); - bool expectObsolete = (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); + bool expectObsolete = + (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete); BEAST_EXPECTS( (expectVeto || expectObsolete) ^ feature.isMember(jss::majority), feature[jss::name].asString() + " majority"); BEAST_EXPECTS( feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete && - (!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) && - (feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"), + (!feature[jss::vetoed].isBool() || + feature[jss::vetoed].asBool() == expectVeto) && + (feature[jss::vetoed].isBool() || + feature[jss::vetoed].asString() == "Obsolete"), feature[jss::name].asString() + " vetoed"); BEAST_EXPECTS(feature.isMember(jss::count), feature[jss::name].asString() + " count"); - BEAST_EXPECTS(feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold"); - BEAST_EXPECTS(feature.isMember(jss::validations), feature[jss::name].asString() + " validations"); + BEAST_EXPECTS( + feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold"); + BEAST_EXPECTS( + feature.isMember(jss::validations), feature[jss::name].asString() + " validations"); BEAST_EXPECT(feature[jss::count] == ((expectVeto || expectObsolete) ? 0 : 1)); BEAST_EXPECT(feature[jss::threshold] == 1); BEAST_EXPECT(feature[jss::validations] == 1); @@ -439,8 +502,8 @@ class Feature_test : public beast::unit_test::suite Env env{*this}; auto const& supportedAmendments = detail::supportedAmendments(); - auto obsoleteFeature = - std::find_if(std::begin(supportedAmendments), std::end(supportedAmendments), [](auto const& pair) { + auto obsoleteFeature = std::find_if( + std::begin(supportedAmendments), std::end(supportedAmendments), [](auto const& pair) { return pair.second == VoteBehavior::Obsolete; }); @@ -460,7 +523,9 @@ class Feature_test : public beast::unit_test::suite return; auto feature = *(jrr.begin()); BEAST_EXPECTS(feature[jss::name] == featureName, "name"); - BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed"); + BEAST_EXPECTS( + feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", + "vetoed"); jrr = env.rpc("feature", featureName, "reject")[jss::result]; if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status")) @@ -470,7 +535,9 @@ class Feature_test : public beast::unit_test::suite return; feature = *(jrr.begin()); BEAST_EXPECTS(feature[jss::name] == featureName, "name"); - BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed"); + BEAST_EXPECTS( + feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", + "vetoed"); jrr = env.rpc("feature", featureName, "accept")[jss::result]; if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status")) @@ -480,7 +547,9 @@ class Feature_test : public beast::unit_test::suite return; feature = *(jrr.begin()); BEAST_EXPECTS(feature[jss::name] == featureName, "name"); - BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed"); + BEAST_EXPECTS( + feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", + "vetoed"); // anything other than accept or reject is an error jrr = env.rpc("feature", featureName, "maybe"); diff --git a/src/test/rpc/GRPCTestClientBase.h b/src/test/rpc/GRPCTestClientBase.h index 3c39cd1747..495dcc22bc 100644 --- a/src/test/rpc/GRPCTestClientBase.h +++ b/src/test/rpc/GRPCTestClientBase.h @@ -12,9 +12,14 @@ namespace test { struct GRPCTestClientBase { explicit GRPCTestClientBase(std::string const& port) - : stub_(org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(grpc::CreateChannel( - beast::IP::Endpoint(boost::asio::ip::make_address(getEnvLocalhostAddr()), std::stoi(port)).to_string(), - grpc::InsecureChannelCredentials()))) + : stub_( + org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub( + grpc::CreateChannel( + beast::IP::Endpoint( + boost::asio::ip::make_address(getEnvLocalhostAddr()), + std::stoi(port)) + .to_string(), + grpc::InsecureChannelCredentials()))) { } diff --git a/src/test/rpc/GatewayBalances_test.cpp b/src/test/rpc/GatewayBalances_test.cpp index 6415f6a51f..0deb1fc627 100644 --- a/src/test/rpc/GatewayBalances_test.cpp +++ b/src/test/rpc/GatewayBalances_test.cpp @@ -223,6 +223,45 @@ public: expect(jv[jss::result][jss::obligations]["USD"] == maxUSD.getText()); } + void + testGWBWithMPT() + { + testcase("Gateway Balances with MPT Escrow"); + using namespace std::chrono_literals; + using namespace jtx; + + // Ensure MPT is enabled + FeatureBitset features = testable_amendments() | featureMPTokensV1; + Env env(*this, features); + + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(10000), alice, bob); + env.close(); + + // Create MPT issuance (Alice) with Escrow capability + MPTTester mpt(env, alice, {.holders = {bob}, .fund = false}); + mpt.create({.flags = tfMPTCanEscrow}); + + // Authorize Bob and fund him + mpt.authorize({.account = bob, .holderCount = 1}); + mpt.pay(alice, bob, 1000); + + // Bob creates an escrow of MPT to Alice. + auto const MPT = mpt["MPT"]; + env(escrow::create(bob, alice, MPT(100)), escrow::finish_time(env.now() + 10s)); + env.close(); + + // Query gateway_balances for Bob. + auto wsc = makeWSClient(env.app().config()); + Json::Value qry; + qry[jss::account] = bob.human(); + + auto jv = wsc->invoke("gateway_balances", qry); + expect(jv[jss::status] == "success"); + } + void run() override { @@ -233,7 +272,7 @@ public: testGWB(feature); testGWBApiVersions(feature); } - + testGWBWithMPT(); testGWBOverflow(); } }; diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index e6cfb3a229..5facd683a5 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -98,7 +98,10 @@ public: // oracles have wrong asset pair env.fund(XRP(1'000), owner); Oracle oracle( - env, {.owner = owner, .series = {{"XRP", "EUR", 740, 1}}, .fee = static_cast(baseFee.drops())}); + env, + {.owner = owner, + .series = {{"XRP", "EUR", 740, 1}}, + .fee = static_cast(baseFee.drops())}); ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}}); BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); @@ -106,7 +109,8 @@ public: std::vector invalidTrim = {NoneTag, 0, 26, -1, 1.2, "", "none", "1.2"}; for (auto const& v : invalidTrim) { - ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}}, v); + ret = + Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}}, v); BEAST_EXPECT(ret[jss::error].asString() == "invalidParams"); } @@ -114,7 +118,8 @@ public: std::vector invalidTime = {NoneTag, -1, 1.2, "", "none", "1.2"}; for (auto const& v : invalidTime) { - ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}}, std::nullopt, v); + ret = Oracle::aggregatePrice( + env, "XRP", "USD", {{{owner, oracle.documentID()}}}, std::nullopt, v); BEAST_EXPECT(ret[jss::error].asString() == "invalidParams"); } } @@ -226,7 +231,12 @@ public: for (int i = 0; i < 3; ++i) { Oracle oracle( - env, {.owner = oracles[i].first, .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); + env, + {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + .documentID = asUInt(*oracles[i].second), + .fee = baseFee}, + false); // push XRP/USD by more than three ledgers, so this price // oracle is not included in the dataset oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}, .fee = baseFee}); @@ -236,7 +246,12 @@ public: for (int i = 3; i < 6; ++i) { Oracle oracle( - env, {.owner = oracles[i].first, .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); + env, + {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + .documentID = asUInt(*oracles[i].second), + .fee = baseFee}, + false); // push XRP/USD by two ledgers, so this price // is included in the dataset oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}, .fee = baseFee}); @@ -271,7 +286,12 @@ public: for (int i = 0; i < oracles.size(); ++i) { Oracle oracle( - env, {.owner = oracles[i].first, .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); + env, + {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + .documentID = asUInt(*oracles[i].second), + .fee = baseFee}, + false); // push XRP/USD by two ledgers, so this price // is included in the dataset oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}, .fee = baseFee}); diff --git a/src/test/rpc/JSONRPC_test.cpp b/src/test/rpc/JSONRPC_test.cpp index f0329748e1..87c7d3dcd0 100644 --- a/src/test/rpc/JSONRPC_test.cpp +++ b/src/test/rpc/JSONRPC_test.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -10,6 +9,7 @@ #include #include #include +#include namespace xrpl { @@ -850,7 +850,10 @@ static constexpr TxnTestData txnTestArray[] = { "TransactionType": "Payment" } })", - {{"Secret does not match account.", "Secret does not match account.", "", "Missing field 'tx_json.Signers'."}}}, + {{"Secret does not match account.", + "Secret does not match account.", + "", + "Missing field 'tx_json.Signers'."}}}, {"Minimal offline sign_for.", __LINE__, @@ -909,7 +912,10 @@ static constexpr TxnTestData txnTestArray[] = { "TransactionType": "Payment" } })", - {{"Disallowed seed.", "Disallowed seed.", "Disallowed seed.", "Missing field 'tx_json.Signers'."}}}, + {{"Disallowed seed.", + "Disallowed seed.", + "Disallowed seed.", + "Missing field 'tx_json.Signers'."}}}, {"Missing 'Account' in sign_for.", __LINE__, @@ -1155,7 +1161,10 @@ static constexpr TxnTestData txnTestArray[] = { "TransactionType" : "Payment" } })", - {{"Already multisigned.", "Already multisigned.", "Invalid signature.", "Invalid signature."}}}, + {{"Already multisigned.", + "Already multisigned.", + "Invalid signature.", + "Invalid signature."}}}, {"Non-empty 'SigningPubKey' in sign_for.", __LINE__, @@ -1336,7 +1345,10 @@ static constexpr TxnTestData txnTestArray[] = { "TransactionType": "Payment" } })", - {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Invalid signature."}}}, + {{"Missing field 'secret'.", + "Missing field 'secret'.", + "Missing field 'account'.", + "Invalid signature."}}}, {"Missing tx_json in submit_multisigned.", __LINE__, @@ -1352,7 +1364,10 @@ static constexpr TxnTestData txnTestArray[] = { } ] })", - {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Missing field 'tx_json'."}}}, + {{"Missing field 'secret'.", + "Missing field 'secret'.", + "Missing field 'account'.", + "Missing field 'tx_json'."}}}, {"Missing sequence in submit_multisigned.", __LINE__, @@ -1541,7 +1556,10 @@ static constexpr TxnTestData txnTestArray[] = { "TransactionType": "Payment" } })", - {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Source account not found."}}}, + {{"Missing field 'secret'.", + "Missing field 'secret'.", + "Missing field 'account'.", + "Source account not found."}}}, {"Missing Fee in submit_multisigned.", __LINE__, @@ -2101,8 +2119,14 @@ public: jt.jv.removeMember(jss::Fee); jt.jv.removeMember(jss::TxnSignature); req[jss::tx_json] = jt.jv; - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(result.size() == 0); BEAST_EXPECT( req[jss::tx_json].isMember(jss::Fee) && @@ -2165,8 +2189,14 @@ public: alice)); req[jss::tx_json] = jt.jv; - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(result.size() == 0); BEAST_EXPECT( req[jss::tx_json].isMember(jss::Fee) && @@ -2186,11 +2216,18 @@ public: { Json::Value req; Json::Reader().parse("{ \"fee_mult_max\" : 1, \"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee); + BEAST_EXPECT( + req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee); } { @@ -2199,18 +2236,31 @@ public: "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 2, " "\"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee); + BEAST_EXPECT( + req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee); } { Json::Value req; Json::Reader().parse("{ \"fee_mult_max\" : 0, \"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2224,8 +2274,14 @@ public: "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 6, " "\"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2237,8 +2293,14 @@ public: "{ \"fee_mult_max\" : 0, \"fee_div_max\" : 2, " "\"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2250,8 +2312,14 @@ public: "{ \"fee_mult_max\" : 10, \"fee_div_max\" : 0, " "\"tx_json\" : { } } ", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2262,8 +2330,14 @@ public: Json::Value req; test::jtx::Account const alice("alice"); req[jss::tx_json] = test::jtx::acctdelete(env.master.human(), alice.human()); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrack, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(result.size() == 0); BEAST_EXPECT( @@ -2293,8 +2367,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 10); @@ -2309,8 +2389,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 10); @@ -2331,11 +2417,18 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889); + BEAST_EXPECT( + req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889); } { @@ -2347,8 +2440,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2364,8 +2463,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee)); @@ -2381,11 +2486,18 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889); + BEAST_EXPECT( + req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889); } { @@ -2397,8 +2509,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); } @@ -2412,8 +2530,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); } @@ -2428,8 +2552,14 @@ public: "tx_json" : { } })", req); - Json::Value result = - checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app()); + Json::Value result = checkFee( + req, + Role::ADMIN, + true, + env.app().config(), + feeTrackOuter, + env.app().getTxQ(), + env.app()); BEAST_EXPECT(RPC::contains_error(result)); } @@ -2445,7 +2575,8 @@ public: auto result = rpcResult[jss::result]; BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "10"); + BEAST_EXPECT( + result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "10"); BEAST_EXPECT( result[jss::tx_json].isMember(jss::Sequence) && result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue)); @@ -2470,7 +2601,9 @@ public: auto result = rpcResult[jss::result]; BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "7813"); + BEAST_EXPECT( + result[jss::tx_json].isMember(jss::Fee) && + result[jss::tx_json][jss::Fee] == "7813"); BEAST_EXPECT( result[jss::tx_json].isMember(jss::Sequence) && result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue)); @@ -2495,7 +2628,8 @@ public: auto result = rpcResult[jss::result]; BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "47"); + BEAST_EXPECT( + result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "47"); BEAST_EXPECT( result[jss::tx_json].isMember(jss::Sequence) && result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue)); @@ -2525,7 +2659,9 @@ public: auto result = rpcResult[jss::result]; BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "6806"); + BEAST_EXPECT( + result[jss::tx_json].isMember(jss::Fee) && + result[jss::tx_json][jss::Fee] == "6806"); BEAST_EXPECT( result[jss::tx_json].isMember(jss::Sequence) && result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue)); @@ -2552,7 +2688,9 @@ public: auto result = rpcResult[jss::result]; BEAST_EXPECT(!RPC::contains_error(result)); - BEAST_EXPECT(result[jss::tx_json].isMember(jss::NetworkID) && result[jss::tx_json][jss::NetworkID] == 1025); + BEAST_EXPECT( + result[jss::tx_json].isMember(jss::NetworkID) && + result[jss::tx_json][jss::NetworkID] == 1025); } } @@ -2632,7 +2770,8 @@ public: if (RPC::contains_error(req)) Throw("Internal JSONRPC_test error. Bad test JSON."); - static Role const testedRoles[] = {Role::GUEST, Role::USER, Role::ADMIN, Role::FORBID}; + static Role const testedRoles[] = { + Role::GUEST, Role::USER, Role::ADMIN, Role::FORBID}; for (Role testRole : testedRoles) { @@ -2647,7 +2786,8 @@ public: { auto const submitFn = get<1>(testFunc); assert(submitFn != nullptr); - result = submitFn(req, 1, NetworkOPs::FailHard::yes, testRole, 1s, env.app(), processTxn); + result = submitFn( + req, 1, NetworkOPs::FailHard::yes, testRole, 1s, env.app(), processTxn); } std::string errStr; @@ -2661,8 +2801,8 @@ public: else { std::ostringstream description; - description << txnTest.description << " Called " << get<2>(testFunc) << "(). Got \'" << errStr - << "\'"; + description << txnTest.description << " Called " << get<2>(testFunc) + << "(). Got \'" << errStr << "\'"; fail(description.str(), __FILE__, txnTest.line); } } diff --git a/src/test/rpc/KeyGeneration_test.cpp b/src/test/rpc/KeyGeneration_test.cpp index 987985070d..e4af2608b7 100644 --- a/src/test/rpc/KeyGeneration_test.cpp +++ b/src/test/rpc/KeyGeneration_test.cpp @@ -93,7 +93,9 @@ public: BEAST_EXPECT(result.isMember(jss::public_key_hex)); BEAST_EXPECT(result.isMember(jss::key_type)); - expectEquals(result[jss::key_type], params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); + expectEquals( + result[jss::key_type], + params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); BEAST_EXPECT(!result.isMember(jss::warning)); std::string seed = result[jss::master_seed].asString(); @@ -115,7 +117,9 @@ public: expectEquals(result[jss::master_seed_hex], s.master_seed_hex); expectEquals(result[jss::public_key], s.public_key); expectEquals(result[jss::public_key_hex], s.public_key_hex); - expectEquals(result[jss::key_type], params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); + expectEquals( + result[jss::key_type], + params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); return result; } @@ -148,7 +152,10 @@ public: } void - testLegacyPassphrase(char const* value, std::optional const& keyType, key_strings const& strings) + testLegacyPassphrase( + char const* value, + std::optional const& keyType, + key_strings const& strings) { Json::Value params; if (keyType) @@ -157,9 +164,13 @@ public: auto const wallet = testSecretWallet(params, strings); if (value == strings.passphrase) + { BEAST_EXPECT(wallet[jss::warning] == strings.passphrase_warning); + } else + { BEAST_EXPECT(!wallet.isMember(jss::warning)); + } } void @@ -287,7 +298,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -301,7 +312,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -315,7 +326,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -334,7 +345,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -350,7 +361,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -366,7 +377,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(ret->first == publicKey); @@ -423,7 +434,9 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(contains_error(error)); BEAST_EXPECT(!ret); - BEAST_EXPECT(error[jss::error_message] == "The secret field is not allowed if key_type is used."); + BEAST_EXPECT( + error[jss::error_message] == + "The secret field is not allowed if key_type is used."); } // Specify unknown or bad "key_type" @@ -686,7 +699,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(toBase58(calcAccountID(ret->first)) == addr); @@ -703,7 +716,8 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(contains_error(error)); - BEAST_EXPECT(error[jss::error_message] == "Specified seed is for an Ed25519 wallet."); + BEAST_EXPECT( + error[jss::error_message] == "Specified seed is for an Ed25519 wallet."); } { @@ -716,7 +730,7 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(!contains_error(error)); - if (BEAST_EXPECT(ret)) + if (BEAST_EXPECT(ret); ret.has_value()) { BEAST_EXPECT(ret->first.size() != 0); BEAST_EXPECT(toBase58(calcAccountID(ret->first)) == addr); @@ -733,7 +747,8 @@ public: auto ret = keypairForSignature(params, error); BEAST_EXPECT(contains_error(error)); - BEAST_EXPECT(error[jss::error_message] == "Specified seed is for an Ed25519 wallet."); + BEAST_EXPECT( + error[jss::error_message] == "Specified seed is for an Ed25519 wallet."); } }; diff --git a/src/test/rpc/LedgerClosed_test.cpp b/src/test/rpc/LedgerClosed_test.cpp index d6b67ce406..2bc5c2b0c9 100644 --- a/src/test/rpc/LedgerClosed_test.cpp +++ b/src/test/rpc/LedgerClosed_test.cpp @@ -21,7 +21,9 @@ public: env.fund(XRP(10000), alice); auto lc_result = env.rpc("ledger_closed")[jss::result]; - BEAST_EXPECT(lc_result[jss::ledger_hash] == "CCC3B3E88CCAC17F1BE6B4A648A55999411F19E3FE55EB721960EB0DF28EDDA5"); + BEAST_EXPECT( + lc_result[jss::ledger_hash] == + "CCC3B3E88CCAC17F1BE6B4A648A55999411F19E3FE55EB721960EB0DF28EDDA5"); BEAST_EXPECT(lc_result[jss::ledger_index] == 2); env.close(); @@ -34,7 +36,9 @@ public: BEAST_EXPECT((*ar_alice)[sfBalance] == XRP(10000)); lc_result = env.rpc("ledger_closed")[jss::result]; - BEAST_EXPECT(lc_result[jss::ledger_hash] == "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB853185"); + BEAST_EXPECT( + lc_result[jss::ledger_hash] == + "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB853185"); BEAST_EXPECT(lc_result[jss::ledger_index] == 3); } diff --git a/src/test/rpc/LedgerData_test.cpp b/src/test/rpc/LedgerData_test.cpp index 0148185276..472f5b2aa2 100644 --- a/src/test/rpc/LedgerData_test.cpp +++ b/src/test/rpc/LedgerData_test.cpp @@ -12,7 +12,8 @@ public: static bool checkMarker(Json::Value const& val) { - return val.isMember(jss::marker) && val[jss::marker].isString() && val[jss::marker].asString().size() > 0; + return val.isMember(jss::marker) && val[jss::marker].isString() && + !val[jss::marker].asString().empty(); } void @@ -43,7 +44,9 @@ public: jvParams[jss::binary] = false; { auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result]; - BEAST_EXPECT(jrr[jss::ledger_current_index].isIntegral() && jrr[jss::ledger_current_index].asInt() > 0); + BEAST_EXPECT( + jrr[jss::ledger_current_index].isIntegral() && + jrr[jss::ledger_current_index].asInt() > 0); BEAST_EXPECT(checkMarker(jrr)); BEAST_EXPECT(checkArraySize(jrr[jss::state], max_limit)); } @@ -53,7 +56,8 @@ public: { jvParams[jss::limit] = max_limit + delta; auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result]; - BEAST_EXPECT(checkArraySize(jrr[jss::state], (delta > 0 && !asAdmin) ? max_limit : max_limit + delta)); + BEAST_EXPECT(checkArraySize( + jrr[jss::state], (delta > 0 && !asAdmin) ? max_limit : max_limit + delta)); } } @@ -80,7 +84,9 @@ public: jvParams[jss::ledger_index] = "current"; jvParams[jss::binary] = true; auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result]; - BEAST_EXPECT(jrr[jss::ledger_current_index].isIntegral() && jrr[jss::ledger_current_index].asInt() > 0); + BEAST_EXPECT( + jrr[jss::ledger_current_index].isIntegral() && + jrr[jss::ledger_current_index].asInt() > 0); BEAST_EXPECT(!jrr.isMember(jss::marker)); BEAST_EXPECT(checkArraySize(jrr[jss::state], num_accounts + 4)); } @@ -192,7 +198,10 @@ public: jvParams[jss::ledger_index] = "closed"; auto jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result]; if (BEAST_EXPECT(jrr.isMember(jss::ledger))) - BEAST_EXPECT(jrr[jss::ledger][jss::ledger_hash] == to_string(env.closed()->header().hash)); + { + BEAST_EXPECT( + jrr[jss::ledger][jss::ledger_hash] == to_string(env.closed()->header().hash)); + } } { // Closed ledger with binary form @@ -203,7 +212,7 @@ public: if (BEAST_EXPECT(jrr.isMember(jss::ledger))) { auto data = strUnHex(jrr[jss::ledger][jss::ledger_data].asString()); - if (BEAST_EXPECT(data)) + if (BEAST_EXPECT(data); data.has_value()) { Serializer s(data->data(), data->size()); std::uint32_t seq = 0; @@ -230,7 +239,8 @@ public: // Make sure fixInnerObjTemplate2 doesn't break amendments. for (FeatureBitset const& features : - {testable_amendments() - fixInnerObjTemplate2, testable_amendments() | fixInnerObjTemplate2}) + {testable_amendments() - fixInnerObjTemplate2, + testable_amendments() | fixInnerObjTemplate2}) { using namespace std::chrono; Env env{*this, envconfig(validator, ""), features}; @@ -291,7 +301,8 @@ public: jv[jss::Account] = Account{"bob5"}.human(); jv[jss::Destination] = Account{"bob6"}.human(); jv[jss::Amount] = XRP(50).value().getJson(JsonOptions::none); - jv[sfFinishAfter.fieldName] = NetClock::time_point{env.now() + 10s}.time_since_epoch().count(); + jv[sfFinishAfter.fieldName] = + NetClock::time_point{env.now() + 10s}.time_since_epoch().count(); env(jv); } @@ -303,7 +314,8 @@ public: jv[jss::Amount] = XRP(100).value().getJson(JsonOptions::none); jv[jss::SettleDelay] = NetClock::duration{10s}.count(); jv[sfPublicKey.fieldName] = strHex(Account{"bob6"}.pk().slice()); - jv[sfCancelAfter.fieldName] = NetClock::time_point{env.now() + 300s}.time_since_epoch().count(); + jv[sfCancelAfter.fieldName] = + NetClock::time_point{env.now() + 300s}.time_since_epoch().count(); env(jv); } diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index aa959629b1..eec114b71c 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include #include #include @@ -15,6 +13,7 @@ #include #include + namespace xrpl { namespace test { @@ -55,15 +54,14 @@ std::vector> mappings{ FieldType getFieldType(Json::StaticString fieldName) { - auto it = std::ranges::find_if(mappings, [&fieldName](auto const& pair) { return pair.first == fieldName; }); + auto it = std::ranges::find_if( + mappings, [&fieldName](auto const& pair) { return pair.first == fieldName; }); if (it != mappings.end()) { return it->second; } - else - { - Throw("`mappings` is missing field " + std::string(fieldName.c_str())); - } + + Throw("`mappings` is missing field " + std::string(fieldName.c_str())); } std::string @@ -93,7 +91,8 @@ getTypeName(FieldType typeID) case FieldType::UInt64Field: return "number"; default: - Throw("unknown type " + std::to_string(static_cast(typeID))); + Throw( + "unknown type " + std::to_string(static_cast(typeID))); } } @@ -109,22 +108,27 @@ class LedgerEntry_test : public beast::unit_test::suite if (BEAST_EXPECT(jv.isMember(jss::status))) BEAST_EXPECTS(jv[jss::status] == "error", std::to_string(location.line())); if (BEAST_EXPECT(jv.isMember(jss::error))) + { BEAST_EXPECTS( jv[jss::error] == err, "Expected error " + err + ", received " + jv[jss::error].asString() + ", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); + } if (msg.empty()) { BEAST_EXPECTS( jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == "", - "Expected no error message, received \"" + jv[jss::error_message].asString() + "\", at line " + - std::to_string(location.line()) + ", " + jv.toStyledString()); + "Expected no error message, received \"" + jv[jss::error_message].asString() + + "\", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECTS( jv[jss::error_message] == msg, - "Expected error message \"" + msg + "\", received \"" + jv[jss::error_message].asString() + - "\", at line " + std::to_string(location.line()) + ", " + jv.toStyledString()); + "Expected error message \"" + msg + "\", received \"" + + jv[jss::error_message].asString() + "\", at line " + + std::to_string(location.line()) + ", " + jv.toStyledString()); + } } std::vector @@ -173,7 +177,7 @@ class LedgerEntry_test : public beast::unit_test::suite values.reserve(allBadValues.size() - indexSet.size()); for (std::size_t i = 0; i < allBadValues.size(); ++i) { - if (indexSet.find(i) == indexSet.end()) + if (!indexSet.contains(i)) { values.push_back(allBadValues[i]); } @@ -216,7 +220,8 @@ class LedgerEntry_test : public beast::unit_test::suite case FieldType::UInt64Field: return badUInt64Values; default: - Throw("unknown type " + std::to_string(static_cast(fieldType))); + Throw( + "unknown type " + std::to_string(static_cast(fieldType))); } } @@ -261,7 +266,8 @@ class LedgerEntry_test : public beast::unit_test::suite case FieldType::UInt64Field: return 1; default: - Throw("unknown type " + std::to_string(static_cast(typeID))); + Throw( + "unknown type " + std::to_string(static_cast(typeID))); } } @@ -279,18 +285,24 @@ class LedgerEntry_test : public beast::unit_test::suite if (required) { correctRequest.removeMember(fieldName); - Json::Value const jrr = - env.rpc(apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; + Json::Value const jrr = env.rpc( + apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; if (apiVersion < 2u) + { checkErrorValue(jrr, "unknownOption", "", location); + } else - checkErrorValue(jrr, "invalidParams", "No ledger_entry params provided.", location); + { + checkErrorValue( + jrr, "invalidParams", "No ledger_entry params provided.", location); + } } auto tryField = [&](Json::Value fieldValue) -> void { correctRequest[fieldName] = fieldValue; - Json::Value const jrr = - env.rpc(apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; - auto const expectedErrMsg = RPC::expected_field_message(fieldName, getTypeName(typeID)); + Json::Value const jrr = env.rpc( + apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; + auto const expectedErrMsg = + RPC::expected_field_message(fieldName, getTypeName(typeID)); checkErrorValue(jrr, expectedError, expectedErrMsg, location); }; @@ -321,22 +333,33 @@ class LedgerEntry_test : public beast::unit_test::suite if (required) { correctRequest[parentFieldName].removeMember(fieldName); - Json::Value const jrr = - env.rpc(apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; - checkErrorValue(jrr, "malformedRequest", RPC::missing_field_message(fieldName.c_str()), location); + Json::Value const jrr = env.rpc( + apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; + checkErrorValue( + jrr, + "malformedRequest", + RPC::missing_field_message(fieldName.c_str()), + location); correctRequest[parentFieldName][fieldName] = Json::nullValue; - Json::Value const jrr2 = - env.rpc(apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; - checkErrorValue(jrr2, "malformedRequest", RPC::missing_field_message(fieldName.c_str()), location); + Json::Value const jrr2 = env.rpc( + apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; + checkErrorValue( + jrr2, + "malformedRequest", + RPC::missing_field_message(fieldName.c_str()), + location); } auto tryField = [&](Json::Value fieldValue) -> void { correctRequest[parentFieldName][fieldName] = fieldValue; - Json::Value const jrr = - env.rpc(apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; + Json::Value const jrr = env.rpc( + apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result]; checkErrorValue( - jrr, expectedError, RPC::expected_field_message(fieldName, getTypeName(typeID)), location); + jrr, + expectedError, + RPC::expected_field_message(fieldName, getTypeName(typeID)), + location); }; auto const& badValues = getBadValues(typeID); @@ -354,7 +377,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::StaticString const& parentField, std::source_location const location = std::source_location::current()) { - testMalformedField(env, Json::Value{}, parentField, FieldType::HashField, "malformedRequest", true, location); + testMalformedField( + env, + Json::Value{}, + parentField, + FieldType::HashField, + "malformedRequest", + true, + location); } struct Subfield @@ -372,7 +402,13 @@ class LedgerEntry_test : public beast::unit_test::suite std::source_location const location = std::source_location::current()) { testMalformedField( - env, Json::Value{}, parentField, FieldType::HashOrObjectField, "malformedRequest", true, location); + env, + Json::Value{}, + parentField, + FieldType::HashOrObjectField, + "malformedRequest", + true, + location); Json::Value correctOutput; correctOutput[parentField] = Json::objectValue; @@ -424,9 +460,10 @@ class LedgerEntry_test : public beast::unit_test::suite forAllApiVersions([&, this](unsigned apiVersion) { auto tryField = [&](Json::Value fieldValue) -> void { jvParams[jss::ledger_hash] = fieldValue; - Json::Value const jrr = - env.rpc(apiVersion, "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "invalidParams", "Invalid field 'ledger_hash', not hex string."); + Json::Value const jrr = env.rpc( + apiVersion, "json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue( + jrr, "invalidParams", "Invalid field 'ledger_hash', not hex string."); }; auto const& badValues = getBadValues(typeId); @@ -456,12 +493,17 @@ class LedgerEntry_test : public beast::unit_test::suite "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAA"; jvParams[jss::api_version] = apiVersion; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; if (apiVersion < 2u) + { checkErrorValue(jrr, "unknownOption", ""); + } else + { checkErrorValue(jrr, "invalidParams", "No ledger_entry params provided."); + } } }); } @@ -494,7 +536,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::account_root] = alice.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000"); @@ -511,7 +554,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::account_root] = alice.human(); jvParams[jss::binary] = 1; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node_binary)); BEAST_EXPECT(jrr[jss::node_binary] == aliceAcctRootBinary); } @@ -519,7 +563,8 @@ class LedgerEntry_test : public beast::unit_test::suite // Request alice's account root using the index. Json::Value jvParams; jvParams[jss::index] = accountRootIndex; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(!jrr.isMember(jss::node_binary)); BEAST_EXPECT(jrr.isMember(jss::node)); BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human()); @@ -530,7 +575,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::index] = accountRootIndex; jvParams[jss::binary] = 0; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000"); @@ -540,7 +586,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::account] = alice.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000"); @@ -549,14 +596,16 @@ class LedgerEntry_test : public beast::unit_test::suite { // Check malformed cases Json::Value jvParams; - testMalformedField(env, jvParams, jss::account_root, FieldType::AccountField, "malformedAddress"); + testMalformedField( + env, jvParams, jss::account_root, FieldType::AccountField, "malformedAddress"); } { // Request an account that is not in the ledger. Json::Value jvParams; jvParams[jss::account_root] = Account("bob").human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } } @@ -581,17 +630,21 @@ class LedgerEntry_test : public beast::unit_test::suite // Create Amendments vector (enabled amendments) std::vector enabledAmendments; enabledAmendments.push_back( - uint256::fromVoid("42426C4D4F1009EE67080A9B7965B44656D7" - "714D104A72F9B4369F97ABF044EE")); + uint256::fromVoid( + "42426C4D4F1009EE67080A9B7965B44656D7" + "714D104A72F9B4369F97ABF044EE")); enabledAmendments.push_back( - uint256::fromVoid("4C97EBA926031A7CF7D7B36FDE3ED66DDA54" - "21192D63DE53FFB46E43B9DC8373")); + uint256::fromVoid( + "4C97EBA926031A7CF7D7B36FDE3ED66DDA54" + "21192D63DE53FFB46E43B9DC8373")); enabledAmendments.push_back( - uint256::fromVoid("03BDC0099C4E14163ADA272C1B6F6FABB448" - "CC3E51F522F978041E4B57D9158C")); + uint256::fromVoid( + "03BDC0099C4E14163ADA272C1B6F6FABB448" + "CC3E51F522F978041E4B57D9158C")); enabledAmendments.push_back( - uint256::fromVoid("35291ADD2D79EB6991343BDA0912269C817D" - "0F094B02226C1C14AD2858962ED4")); + uint256::fromVoid( + "35291ADD2D79EB6991343BDA0912269C817D" + "0F094B02226C1C14AD2858962ED4")); sle->setFieldV256(sfAmendments, STVector256(enabledAmendments)); // Create Majorities array @@ -600,16 +653,18 @@ class LedgerEntry_test : public beast::unit_test::suite auto majority1 = STObject::makeInnerObject(sfMajority); majority1.setFieldH256( sfAmendment, - uint256::fromVoid("7BB62DC13EC72B775091E9C71BF8CF97E122" - "647693B50C5E87A80DFD6FCFAC50")); + uint256::fromVoid( + "7BB62DC13EC72B775091E9C71BF8CF97E122" + "647693B50C5E87A80DFD6FCFAC50")); majority1.setFieldU32(sfCloseTime, 779561310); majorities.push_back(std::move(majority1)); auto majority2 = STObject::makeInnerObject(sfMajority); majority2.setFieldH256( sfAmendment, - uint256::fromVoid("755C971C29971C9F20C6F080F2ED96F87884" - "E40AD19554A5EBECDCEC8A1F77FE")); + uint256::fromVoid( + "755C971C29971C9F20C6F080F2ED96F87884" + "E40AD19554A5EBECDCEC8A1F77FE")); majority2.setFieldU32(sfCloseTime, 779561310); majorities.push_back(std::move(majority2)); @@ -623,12 +678,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::amendments] = to_string(keylet.key); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Amendments); } // negative tests - testMalformedField(env, Json::Value{}, jss::amendments, FieldType::FixedHashField, "malformedRequest"); + testMalformedField( + env, Json::Value{}, jss::amendments, FieldType::FixedHashField, "malformedRequest"); } void @@ -650,7 +707,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::amm] = to_string(amm.ammID()); auto const result = env.rpc("json", "ledger_entry", to_string(jvParams)); BEAST_EXPECT( - result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) && + result.isObject() && result.isMember(jss::result) && + !result[jss::result].isMember(jss::error) && result[jss::result].isMember(jss::node) && result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM); @@ -673,7 +731,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::amm] = ammParams; auto const result = env.rpc("json", "ledger_entry", to_string(jvParams)); BEAST_EXPECT( - result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) && + result.isObject() && result.isMember(jss::result) && + !result[jss::result].isMember(jss::error) && result[jss::result].isMember(jss::node) && result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM); @@ -710,7 +769,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::check] = to_string(checkId.key); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check); BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000"); } @@ -721,13 +781,15 @@ class LedgerEntry_test : public beast::unit_test::suite { Json::Value jvParams; jvParams[jss::account_root] = alice.human(); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; accountRootIndex = jrr[jss::index].asString(); } Json::Value jvParams; jvParams[jss::check] = accountRootIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type."); } { @@ -760,8 +822,8 @@ class LedgerEntry_test : public beast::unit_test::suite // Succeed auto jv = credentials::ledgerEntry(env, alice, issuer, credType); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && - jv[jss::result].isMember(jss::node) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential); @@ -769,8 +831,8 @@ class LedgerEntry_test : public beast::unit_test::suite jv = credentials::ledgerEntry(env, credIdx); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && - jv[jss::result].isMember(jss::node) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential); } @@ -819,7 +881,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::delegate][jss::account] = alice.human(); jvParams[jss::delegate][jss::authorize] = bob.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate); BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human()); @@ -830,7 +893,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::delegate] = delegateIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate); BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human()); @@ -873,7 +937,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::deposit_preauth][jss::owner] = alice.human(); jvParams[jss::deposit_preauth][jss::authorized] = becky.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth); BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human()); @@ -885,7 +950,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::deposit_preauth] = depositPreauthIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth); BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human()); @@ -943,8 +1009,8 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); BEAST_EXPECT( - jrr.isObject() && jrr.isMember(jss::result) && !jrr[jss::result].isMember(jss::error) && - jrr[jss::result].isMember(jss::node) && + jrr.isObject() && jrr.isMember(jss::result) && + !jrr[jss::result].isMember(jss::error) && jrr[jss::result].isMember(jss::node) && jrr[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth); } @@ -963,9 +1029,11 @@ class LedgerEntry_test : public beast::unit_test::suite arr.append(jo); jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; - auto const expectedErrMsg = fieldValue.isNull() ? RPC::missing_field_message(jss::issuer.c_str()) - : RPC::expected_field_message(jss::issuer, "AccountID"); + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + auto const expectedErrMsg = fieldValue.isNull() + ? RPC::missing_field_message(jss::issuer.c_str()) + : RPC::expected_field_message(jss::issuer, "AccountID"); checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg); }; @@ -1012,7 +1080,8 @@ class LedgerEntry_test : public beast::unit_test::suite arr.append(jo); jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; auto const expectedErrMsg = fieldValue.isNull() ? RPC::missing_field_message(jss::credential_type.c_str()) : RPC::expected_field_message(jss::credential_type, "hex string"); @@ -1173,13 +1242,15 @@ class LedgerEntry_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::ledger_index] == 5); } - std::string const dirRootIndex = "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D"; + std::string const dirRootIndex = + "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D"; { // Locate directory by index. Json::Value jvParams; jvParams[jss::directory] = dirRootIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32); } { @@ -1187,7 +1258,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::dir_root] = dirRootIndex; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::index] == dirRootIndex); } { @@ -1196,7 +1268,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::owner] = alice.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::index] == dirRootIndex); } { @@ -1205,7 +1278,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::dir_root] = dirRootIndex; jvParams[jss::directory][jss::sub_index] = 1; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::index] != dirRootIndex); BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2); } @@ -1216,7 +1290,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory][jss::owner] = alice.human(); jvParams[jss::directory][jss::sub_index] = 1; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::index] != dirRootIndex); BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2); } @@ -1224,7 +1299,8 @@ class LedgerEntry_test : public beast::unit_test::suite // Bad directory argument. Json::Value jvParams; jvParams[jss::ledger_hash] = ledgerHash; - testMalformedField(env, jvParams, jss::directory, FieldType::HashOrObjectField, "malformedRequest"); + testMalformedField( + env, jvParams, jss::directory, FieldType::HashOrObjectField, "malformedRequest"); } { // Non-integer sub_index. @@ -1233,7 +1309,13 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory][jss::dir_root] = dirRootIndex; jvParams[jss::ledger_hash] = ledgerHash; testMalformedSubfield( - env, jvParams, jss::directory, jss::sub_index, FieldType::UInt64Field, "malformedRequest", false); + env, + jvParams, + jss::directory, + jss::sub_index, + FieldType::UInt64Field, + "malformedRequest", + false); } { // Malformed owner entry. @@ -1242,7 +1324,13 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; testMalformedSubfield( - env, jvParams, jss::directory, jss::owner, FieldType::AccountField, "malformedAddress", false); + env, + jvParams, + jss::directory, + jss::owner, + FieldType::AccountField, + "malformedAddress", + false); } { // Malformed directory object. Specifies both dir_root and owner. @@ -1251,8 +1339,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory][jss::owner] = alice.human(); jvParams[jss::directory][jss::dir_root] = dirRootIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields."); + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue( + jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields."); } { // Incomplete directory object. Missing both dir_root and owner. @@ -1260,8 +1350,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::sub_index] = 1; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields."); + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + checkErrorValue( + jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields."); } } @@ -1301,7 +1393,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::escrow] = Json::objectValue; jvParams[jss::escrow][jss::owner] = alice.human(); jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText()); escrowIndex = jrr[jss::index].asString(); } @@ -1310,12 +1403,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::escrow] = escrowIndex; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText()); } { // Malformed escrow fields - runLedgerEntryTest(env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}}); + runLedgerEntryTest( + env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}}); } } @@ -1331,12 +1426,14 @@ class LedgerEntry_test : public beast::unit_test::suite Keylet const keylet = keylet::fees(); Json::Value jvParams; jvParams[jss::fee] = to_string(keylet.key); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::FeeSettings); } // negative tests - testMalformedField(env, Json::Value{}, jss::fee, FieldType::FixedHashField, "malformedRequest"); + testMalformedField( + env, Json::Value{}, jss::fee, FieldType::FixedHashField, "malformedRequest"); } void @@ -1351,12 +1448,14 @@ class LedgerEntry_test : public beast::unit_test::suite Keylet const keylet = keylet::skip(); Json::Value jvParams; jvParams[jss::hashes] = to_string(keylet.key); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes); } // negative tests - testMalformedField(env, Json::Value{}, jss::hashes, FieldType::FixedHashField, "malformedRequest"); + testMalformedField( + env, Json::Value{}, jss::hashes, FieldType::FixedHashField, "malformedRequest"); } void @@ -1375,12 +1474,15 @@ class LedgerEntry_test : public beast::unit_test::suite env(token::mint(issuer, 0), txflags(tfTransferable)); env.close(); uint256 const offerID = keylet::nftoffer(issuer, env.seq(issuer)).key; - env(token::createOffer(issuer, nftokenID0, drops(1)), token::destination(buyer), txflags(tfSellNFToken)); + env(token::createOffer(issuer, nftokenID0, drops(1)), + token::destination(buyer), + txflags(tfSellNFToken)); { Json::Value jvParams; jvParams[jss::nft_offer] = to_string(offerID); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenOffer); BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == issuer.human()); BEAST_EXPECT(jrr[jss::node][sfNFTokenID.jsonName] == to_string(nftokenID0)); @@ -1411,7 +1513,8 @@ class LedgerEntry_test : public beast::unit_test::suite { Json::Value jvParams; jvParams[jss::nft_page] = to_string(nftpage.key); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenPage); } @@ -1449,9 +1552,10 @@ class LedgerEntry_test : public beast::unit_test::suite sle->setFieldArray(sfDisabledValidators, disabledValidators); sle->setFieldH256( sfPreviousTxnID, - uint256::fromVoid("8D47FFE664BE6C335108DF689537625855A6" - "A95160CC6D351341B9" - "2624D9C5E3")); + uint256::fromVoid( + "8D47FFE664BE6C335108DF689537625855A6" + "A95160CC6D351341B9" + "2624D9C5E3")); sle->setFieldU32(sfPreviousTxnLgrSeq, 91442944); view.rawInsert(sle); @@ -1462,12 +1566,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::nunl] = to_string(keylet.key); - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NegativeUNL); } // negative tests - testMalformedField(env, Json::Value{}, jss::nunl, FieldType::FixedHashField, "malformedRequest"); + testMalformedField( + env, Json::Value{}, jss::nunl, FieldType::FixedHashField, "malformedRequest"); } void @@ -1494,7 +1600,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::offer][jss::account] = alice.human(); jvParams[jss::offer][jss::seq] = env.seq(alice) - 1; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000"); offerIndex = jrr[jss::index].asString(); } @@ -1502,13 +1609,17 @@ class LedgerEntry_test : public beast::unit_test::suite // Request the offer using its index. Json::Value jvParams; jvParams[jss::offer] = offerIndex; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000"); } { // Malformed offer fields - runLedgerEntryTest(env, jss::offer, {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}}); + runLedgerEntryTest( + env, + jss::offer, + {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}}); } } @@ -1551,7 +1662,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::payment_channel] = to_string(payChanIndex); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000"); BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0"); BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18); @@ -1561,7 +1673,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::payment_channel] = ledgerHash; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } @@ -1602,7 +1715,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][1u] = gw.human(); jvParams[fieldName][jss::currency] = "USD"; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName][jss::value] == "-97"); BEAST_EXPECT(jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999"); } @@ -1624,7 +1738,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][0u] = alice.human(); jvParams[fieldName][jss::currency] = "USD"; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue( jrr, "malformedRequest", @@ -1641,7 +1756,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][2u] = alice.human(); jvParams[fieldName][jss::currency] = "USD"; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue( jrr, "malformedRequest", @@ -1660,9 +1776,12 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][1u] = gw.human(); jvParams[fieldName][jss::currency] = "USD"; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue( - jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts")); + jrr, + "malformedAddress", + RPC::expected_field_message(jss::accounts, "array of Accounts")); } { @@ -1672,9 +1791,12 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][1u] = badAccount; jvParams[fieldName][jss::currency] = "USD"; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue( - jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts")); + jrr, + "malformedAddress", + RPC::expected_field_message(jss::accounts, "array of Accounts")); } }; @@ -1694,7 +1816,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[fieldName][jss::accounts][1u] = alice.human(); jvParams[fieldName][jss::currency] = "USD"; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "malformedRequest", "Cannot have a trustline to self."); } } @@ -1731,7 +1854,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1 - 1)); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { @@ -1739,7 +1863,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1)); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket); BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1); } @@ -1750,8 +1875,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ticket][jss::account] = env.master.human(); jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; - BEAST_EXPECT(jrr[jss::node][jss::index] == to_string(getTicketIndex(env.master, tkt1 + 1))); + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT( + jrr[jss::node][jss::index] == to_string(getTicketIndex(env.master, tkt1 + 1))); } { // Not a valid ticket requested by account and sequence. @@ -1760,7 +1887,8 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ticket][jss::account] = env.master.human(); jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { @@ -1768,7 +1896,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ticket] = to_string(keylet::account(env.master).key); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type."); } @@ -1816,7 +1945,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::did] = alice.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfDIDDocument.jsonName] == strHex(std::string{"data"})); BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"})); } @@ -1825,13 +1955,15 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::did] = env.master.human(); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // Malformed DID index Json::Value jvParams; - testMalformedField(env, jvParams, jss::did, FieldType::AccountField, "malformedAddress"); + testMalformedField( + env, jvParams, jss::did, FieldType::AccountField, "malformedAddress"); } } @@ -1845,7 +1977,8 @@ class LedgerEntry_test : public beast::unit_test::suite Env env(*this); Account const owner("owner"); env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); + Oracle oracle( + env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); { // test basic malformed scenarios @@ -1918,8 +2051,8 @@ class LedgerEntry_test : public beast::unit_test::suite {.transferFee = 10, .metadata = "123", .ownerCount = 1, - .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | - tfMPTCanClawback}); + .flags = tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | + tfMPTCanTransfer | tfMPTCanClawback}); mptAlice.authorize({.account = bob, .holderCount = 1}); std::string const ledgerHash{to_string(env.closed()->header().hash)}; @@ -1930,7 +2063,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID()); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][sfMPTokenMetadata.jsonName] == strHex(std::string{"123"})); BEAST_EXPECT(jrr[jss::node][jss::mpt_issuance_id] == strHex(mptAlice.issuanceID())); } @@ -1939,7 +2073,8 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::mpt_issuance] = badMptID; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { @@ -1949,8 +2084,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::mptoken][jss::account] = bob.human(); jvParams[jss::mptoken][jss::mpt_issuance_id] = strHex(mptAlice.issuanceID()); jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; - BEAST_EXPECT(jrr[jss::node][sfMPTokenIssuanceID.jsonName] == strHex(mptAlice.issuanceID())); + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT( + jrr[jss::node][sfMPTokenIssuanceID.jsonName] == strHex(mptAlice.issuanceID())); } { // Request the MPToken using a bad mptIssuanceID. @@ -1959,13 +2096,15 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::mptoken][jss::account] = bob.human(); jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // Malformed MPTIssuance index Json::Value jvParams; - testMalformedField(env, jvParams, jss::mptoken, FieldType::HashOrObjectField, "malformedRequest"); + testMalformedField( + env, jvParams, jss::mptoken, FieldType::HashOrObjectField, "malformedRequest"); } } @@ -1999,8 +2138,8 @@ class LedgerEntry_test : public beast::unit_test::suite params[jss::permissioned_domain][jss::seq] = seq; auto jv = env.rpc("json", "ledger_entry", to_string(params)); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && - jv[jss::result].isMember(jss::node) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain); @@ -2012,8 +2151,8 @@ class LedgerEntry_test : public beast::unit_test::suite params[jss::permissioned_domain] = pdIdx; jv = env.rpc("json", "ledger_entry", to_string(params)); BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && - jv[jss::result].isMember(jss::node) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain); } @@ -2050,7 +2189,7 @@ class LedgerEntry_test : public beast::unit_test::suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = Config::FRESH; + cfg->START_UP = StartUpType::FRESH; return cfg; })}; @@ -2073,7 +2212,8 @@ class LedgerEntry_test : public beast::unit_test::suite if (good) { BEAST_EXPECTS( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == expectedType, @@ -2082,7 +2222,8 @@ class LedgerEntry_test : public beast::unit_test::suite else { BEAST_EXPECTS( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && !jv[jss::result].isMember(jss::node) && jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"), to_string(jv)); @@ -2241,7 +2382,7 @@ class LedgerEntry_test : public beast::unit_test::suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = Config::FRESH; + cfg->START_UP = StartUpType::FRESH; return cfg; })}; @@ -2264,20 +2405,23 @@ class LedgerEntry_test : public beast::unit_test::suite if (good) { BEAST_EXPECTS( - jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + !jv[jss::result].isMember(jss::error) && jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) && jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes, to_string(jv)); BEAST_EXPECTS( - jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember("Hashes") && + jv[jss::result].isMember(jss::node) && + jv[jss::result][jss::node].isMember("Hashes") && jv[jss::result][jss::node]["Hashes"].size() == expectedCount, to_string(jv[jss::result][jss::node]["Hashes"].size())); } else { BEAST_EXPECTS( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && !jv[jss::result].isMember(jss::node) && jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"), to_string(jv)); @@ -2294,111 +2438,114 @@ class LedgerEntry_test : public beast::unit_test::suite * @param expectedCount: The number of Hashes expected in the * object if "good". */ - auto test = [&](Json::Value ledger, Keylet const& expectedKey, bool good, int expectedCount = 0) { - testcase << "LedgerHashes: seq: " << env.current()->header().seq << " \"hashes\":" << to_string(ledger) - << (good ? "" : " not") << " found"; + auto test = + [&](Json::Value ledger, Keylet const& expectedKey, bool good, int expectedCount = 0) { + testcase << "LedgerHashes: seq: " << env.current()->header().seq + << " \"hashes\":" << to_string(ledger) << (good ? "" : " not") << " found"; - auto const hexKey = strHex(expectedKey.key); + auto const hexKey = strHex(expectedKey.key); - { - // Test bad values - // "hashes":null - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = Json::nullValue; - auto jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "malformedRequest"); - BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); - } + { + // Test bad values + // "hashes":null + Json::Value params; + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = Json::nullValue; + auto jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "malformedRequest"); + BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); + } - { - Json::Value params; - // "hashes":"non-uint string" - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = "arbitrary string"; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "malformedRequest"); - BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); - } + { + Json::Value params; + // "hashes":"non-uint string" + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = "arbitrary string"; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "malformedRequest"); + BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); + } - { - Json::Value params; - // "hashes":"uint string" is invalid, too - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = "10"; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "malformedRequest"); - BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); - } + { + Json::Value params; + // "hashes":"uint string" is invalid, too + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = "10"; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "malformedRequest"); + BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); + } - { - Json::Value params; - // "hashes":false - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = false; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "invalidParams"); - BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); - } + { + Json::Value params; + // "hashes":false + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = false; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "invalidParams"); + BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); + } - { - Json::Value params; - // "hashes":-1 - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = -1; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "internal"); - BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); - } + { + Json::Value params; + // "hashes":-1 + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = -1; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "internal"); + BEAST_EXPECT(!jv[jss::result].isMember(jss::index)); + } - // "hashes":[incorrect index hash] - { - Json::Value params; - auto const badKey = strHex(expectedKey.key + uint256{1}); - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = badKey; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(false, jv, 0, "entryNotFound"); - BEAST_EXPECT(jv[jss::result][jss::index] == badKey); - } + // "hashes":[incorrect index hash] + { + Json::Value params; + auto const badKey = strHex(expectedKey.key + uint256{1}); + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = badKey; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(false, jv, 0, "entryNotFound"); + BEAST_EXPECT(jv[jss::result][jss::index] == badKey); + } - { - Json::Value params; - // Test good values - // Use the "hashes":ledger notation - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = ledger; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(good, jv, expectedCount); - // Index will always be returned for valid parameters. - std::string const pdIdx = jv[jss::result][jss::index].asString(); - BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx)); - } + { + Json::Value params; + // Test good values + // Use the "hashes":ledger notation + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = ledger; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(good, jv, expectedCount); + // Index will always be returned for valid parameters. + std::string const pdIdx = jv[jss::result][jss::index].asString(); + BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx)); + } - { - Json::Value params; - // "hashes":"[index hash]" - params[jss::ledger_index] = jss::validated; - params[jss::hashes] = hexKey; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(good, jv, expectedCount); - // Index is correct either way - BEAST_EXPECTS( - hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString())); - } + { + Json::Value params; + // "hashes":"[index hash]" + params[jss::ledger_index] = jss::validated; + params[jss::hashes] = hexKey; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(good, jv, expectedCount); + // Index is correct either way + BEAST_EXPECTS( + hexKey == jv[jss::result][jss::index].asString(), + strHex(jv[jss::result][jss::index].asString())); + } - { - Json::Value params; - // Use the "index":"[index hash]" notation - params[jss::ledger_index] = jss::validated; - params[jss::index] = hexKey; - auto const jv = env.rpc("json", "ledger_entry", to_string(params)); - checkResult(good, jv, expectedCount); - // Index is correct either way - BEAST_EXPECTS( - hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString())); - } - }; + { + Json::Value params; + // Use the "index":"[index hash]" notation + params[jss::ledger_index] = jss::validated; + params[jss::index] = hexKey; + auto const jv = env.rpc("json", "ledger_entry", to_string(params)); + checkResult(good, jv, expectedCount); + // Index is correct either way + BEAST_EXPECTS( + hexKey == jv[jss::result][jss::index].asString(), + strHex(jv[jss::result][jss::index].asString())); + } + }; // short skip list test(true, keylet::skip(), true, 2); @@ -2446,13 +2593,10 @@ class LedgerEntry_test : public beast::unit_test::suite env(check::create(env.master, alice, XRP(100))); env.close(); - std::string const ledgerHash{to_string(env.closed()->header().hash)}; - { - // Request a check. - Json::Value const jrr = env.rpc("ledger_entry", to_string(checkId.key))[jss::result]; - BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check); - BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000"); - } + // Request a check. + Json::Value const jrr = env.rpc("ledger_entry", to_string(checkId.key))[jss::result]; + BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check); + BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000"); } public: @@ -2491,7 +2635,8 @@ public: } }; -class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx::XChainBridgeObjects +class LedgerEntry_XChain_test : public beast::unit_test::suite, + public test::jtx::XChainBridgeObjects { void checkErrorValue(Json::Value const& jv, std::string const& err, std::string const& msg) @@ -2505,7 +2650,9 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == ""); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECT(jv[jss::error_message] == msg); + } } void @@ -2527,7 +2674,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx Json::Value jvParams; jvParams[jss::bridge_account] = mcDoor.human(); jvParams[jss::bridge] = jvb; - Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2556,7 +2704,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx // request the bridge via RPC by index Json::Value jvParams; jvParams[jss::index] = bridge_index; - Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); BEAST_EXPECT(jrr[jss::node] == mcBridge); @@ -2568,7 +2717,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx jvParams[jss::bridge_account] = Account::master.human(); jvParams[jss::bridge] = jvb; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } @@ -2584,7 +2734,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx Json::Value jvParams; jvParams[jss::bridge_account] = mcDoor.human(); jvParams[jss::bridge] = jvb; - Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2611,13 +2762,13 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx scEnv(xchain_create_claim_id(scBob, jvb, reward, mcBob)); scEnv.close(); - std::string bridge_index; { // request the xchain_claim_id via RPC Json::Value jvParams; jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC; jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 1; - Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2634,7 +2785,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx Json::Value jvParams; jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC; jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 2; - Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + Json::Value const jrr = + scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2690,8 +2842,10 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx // request the create account claim_id via RPC Json::Value jvParams; jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC; - jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1; - Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + jvParams[jss::xchain_owned_create_account_claim_id] + [jss::xchain_owned_create_account_claim_id] = 1; + Json::Value const jrr = + scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2706,13 +2860,16 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx auto attest = r[sfXChainCreateAccountAttestations.jsonName]; BEAST_EXPECT(attest.isArray()); BEAST_EXPECT(attest.size() == 3); - BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(sfXChainCreateAccountProofSig.jsonName)); + BEAST_EXPECT( + attest[Json::Value::UInt(0)].isMember(sfXChainCreateAccountProofSig.jsonName)); Json::Value a[num_attest]; for (size_t i = 0; i < num_attest; ++i) { a[i] = attest[Json::Value::UInt(0)][sfXChainCreateAccountProofSig.jsonName]; - BEAST_EXPECT(a[i].isMember(jss::Amount) && a[i][jss::Amount].asInt() == 1000 * drop_per_xrp); - BEAST_EXPECT(a[i].isMember(jss::Destination) && a[i][jss::Destination] == scCarol.human()); + BEAST_EXPECT( + a[i].isMember(jss::Amount) && a[i][jss::Amount].asInt() == 1000 * drop_per_xrp); + BEAST_EXPECT( + a[i].isMember(jss::Destination) && a[i][jss::Destination] == scCarol.human()); BEAST_EXPECT( a[i].isMember(sfAttestationSignerAccount.jsonName) && std::any_of(signers.begin(), signers.end(), [&](signer const& s) { @@ -2724,7 +2881,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx return a[i][sfAttestationRewardAccount.jsonName] == account.human(); })); BEAST_EXPECT( - a[i].isMember(sfWasLockingChainSend.jsonName) && a[i][sfWasLockingChainSend.jsonName] == 1); + a[i].isMember(sfWasLockingChainSend.jsonName) && + a[i][sfWasLockingChainSend.jsonName] == 1); BEAST_EXPECT( a[i].isMember(sfSignatureReward.jsonName) && a[i][sfSignatureReward.jsonName].asInt() == 1 * drop_per_xrp); @@ -2742,8 +2900,10 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, public test::jtx // request the create account claim_id via RPC Json::Value jvParams; jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC; - jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1; - Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + jvParams[jss::xchain_owned_create_account_claim_id] + [jss::xchain_owned_create_account_claim_id] = 1; + Json::Value const jrr = + scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "entryNotFound", "Entry not found."); } } diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index a656db558c..2f4a63a090 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -30,9 +30,12 @@ class LedgerRPC_test : public beast::unit_test::suite BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == ""); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) + { BEAST_EXPECTS( jv[jss::error_message] == msg, - "Expected error message \"" + msg + "\", received \"" + jv[jss::error_message].asString() + "\""); + "Expected error message \"" + msg + "\", received \"" + + jv[jss::error_message].asString() + "\""); + } } // Corrupt a valid address by replacing the 10th character with '!'. @@ -77,7 +80,8 @@ class LedgerRPC_test : public beast::unit_test::suite // using current identifier auto const jrr = env.rpc("ledger", "current")[jss::result]; BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false); - BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == std::to_string(env.current()->header().seq)); + BEAST_EXPECT( + jrr[jss::ledger][jss::ledger_index] == std::to_string(env.current()->header().seq)); BEAST_EXPECT(jrr[jss::ledger_current_index] == env.current()->header().seq); } } @@ -102,7 +106,8 @@ class LedgerRPC_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ledger_index] = "potato"; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); + checkErrorValue( + jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { @@ -110,7 +115,8 @@ class LedgerRPC_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ledger_index] = -1; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); + checkErrorValue( + jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { @@ -268,7 +274,8 @@ class LedgerRPC_test : public beast::unit_test::suite jvParams[jss::ledger] = "invalid"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger', not string or number."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'ledger', not string or number."); // numeric index jvParams[jss::ledger] = 4; @@ -351,7 +358,8 @@ class LedgerRPC_test : public beast::unit_test::suite jvParams[jss::ledger_index] = "invalid"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); - BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger_index', not string or number."); + BEAST_EXPECT( + jrr[jss::error_message] == "Invalid field 'ledger_index', not string or number."); // numeric index for (auto i : {1, 2, 3, 4, 5, 6}) @@ -628,19 +636,24 @@ class LedgerRPC_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); for (auto i = 0; i < jrr[jss::ledger][jss::accountState].size(); i++) + { if (jrr[jss::ledger][jss::accountState][i]["LedgerEntryType"] == jss::LedgerHashes) { index = jrr[jss::ledger][jss::accountState][i]["index"].asString(); hashesLedgerEntryIndex = i; } + } for (auto const& object : jrr[jss::ledger][jss::accountState]) + { if (object["LedgerEntryType"] == jss::LedgerHashes) index = object["index"].asString(); + } // jss::type is a deprecated field BEAST_EXPECT( - jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && jrr[jss::warnings].size() == 1 && + jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && + jrr[jss::warnings].size() == 1 && jrr[jss::warnings][0u][jss::id].asInt() == warnRPC_FIELDS_DEPRECATED); } { @@ -653,11 +666,13 @@ class LedgerRPC_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState)); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); BEAST_EXPECT( - hashesLedgerEntryIndex > 0 && jrr[jss::ledger][jss::accountState][hashesLedgerEntryIndex] == index); + hashesLedgerEntryIndex > 0 && + jrr[jss::ledger][jss::accountState][hashesLedgerEntryIndex] == index); // jss::type is a deprecated field BEAST_EXPECT( - jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && jrr[jss::warnings].size() == 1 && + jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && + jrr[jss::warnings].size() == 1 && jrr[jss::warnings][0u][jss::id].asInt() == warnRPC_FIELDS_DEPRECATED); } } diff --git a/src/test/rpc/LedgerRequest_test.cpp b/src/test/rpc/LedgerRequest_test.cpp index 6a1bb2fb89..18c717ec19 100644 --- a/src/test/rpc/LedgerRequest_test.cpp +++ b/src/test/rpc/LedgerRequest_test.cpp @@ -14,10 +14,13 @@ namespace RPC { class LedgerRequest_test : public beast::unit_test::suite { - static constexpr char const* hash1 = "3020EB9E7BE24EF7D7A060CB051583EC117384636D1781AFB5B87F3E348DA489"; - static constexpr char const* accounthash1 = "BD8A3D72CA73DDE887AD63666EC2BAD07875CBA997A102579B5B95ECDFFEAED8"; + static constexpr char const* hash1 = + "3020EB9E7BE24EF7D7A060CB051583EC117384636D1781AFB5B87F3E348DA489"; + static constexpr char const* accounthash1 = + "BD8A3D72CA73DDE887AD63666EC2BAD07875CBA997A102579B5B95ECDFFEAED8"; - static constexpr char const* zerohash = "0000000000000000000000000000000000000000000000000000000000000000"; + static constexpr char const* zerohash = + "0000000000000000000000000000000000000000000000000000000000000000"; public: void @@ -56,7 +59,8 @@ public: { auto const result = env.rpc("ledger_request", "1"); BEAST_EXPECT( - !RPC::contains_error(result[jss::result]) && result[jss::result][jss::ledger_index] == 1 && + !RPC::contains_error(result[jss::result]) && + result[jss::result][jss::ledger_index] == 1 && result[jss::result].isMember(jss::ledger)); BEAST_EXPECT( result[jss::result][jss::ledger].isMember(jss::ledger_hash) && @@ -66,7 +70,8 @@ public: { auto const result = env.rpc("ledger_request", "2"); BEAST_EXPECT( - !RPC::contains_error(result[jss::result]) && result[jss::result][jss::ledger_index] == 2 && + !RPC::contains_error(result[jss::result]) && + result[jss::result][jss::ledger_index] == 2 && result[jss::result].isMember(jss::ledger)); BEAST_EXPECT( result[jss::result][jss::ledger].isMember(jss::ledger_hash) && @@ -76,7 +81,8 @@ public: { auto const result = env.rpc("ledger_request", "3"); BEAST_EXPECT( - !RPC::contains_error(result[jss::result]) && result[jss::result][jss::ledger_index] == 3 && + !RPC::contains_error(result[jss::result]) && + result[jss::result][jss::ledger_index] == 3 && result[jss::result].isMember(jss::ledger)); BEAST_EXPECT( result[jss::result][jss::ledger].isMember(jss::ledger_hash) && @@ -87,8 +93,8 @@ public: { auto const r = env.rpc("ledger_request", ledgerHash); BEAST_EXPECT( - !RPC::contains_error(r[jss::result]) && r[jss::result][jss::ledger_index] == 3 && - r[jss::result].isMember(jss::ledger)); + !RPC::contains_error(r[jss::result]) && + r[jss::result][jss::ledger_index] == 3 && r[jss::result].isMember(jss::ledger)); BEAST_EXPECT( r[jss::result][jss::ledger].isMember(jss::ledger_hash) && r[jss::result][jss::ledger][jss::ledger_hash] == ledgerHash); @@ -102,7 +108,8 @@ public: BEAST_EXPECT( RPC::contains_error(result[jss::result]) && - result[jss::result][jss::error_message] == "Invalid field 'ledger_hash', not hex string."); + result[jss::result][jss::error_message] == + "Invalid field 'ledger_hash', not hex string."); } { @@ -110,7 +117,9 @@ public: auto const result = env.rpc("ledger_request", ledgerHash); - BEAST_EXPECT(!RPC::contains_error(result[jss::result]) && result[jss::result][jss::have_header] == false); + BEAST_EXPECT( + !RPC::contains_error(result[jss::result]) && + result[jss::result][jss::have_header] == false); } { @@ -164,7 +173,8 @@ public: BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == zerohash); result = env.rpc("ledger_request", "2")[jss::result]; - constexpr char const* hash2 = "CCC3B3E88CCAC17F1BE6B4A648A55999411F19E3FE55EB721960EB0DF28EDDA5"; + constexpr char const* hash2 = + "CCC3B3E88CCAC17F1BE6B4A648A55999411F19E3FE55EB721960EB0DF28EDDA5"; BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "2"); BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "100000000000000000"); BEAST_EXPECT(result[jss::ledger][jss::closed] == true); @@ -176,7 +186,8 @@ public: BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == zerohash); result = env.rpc("ledger_request", "3")[jss::result]; - constexpr char const* hash3 = "9FFD8AE09190D5002FE4252A1B29EABCF40DABBCE3B42619C6BD0BE381D51103"; + constexpr char const* hash3 = + "9FFD8AE09190D5002FE4252A1B29EABCF40DABBCE3B42619C6BD0BE381D51103"; BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "3"); BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999980"); BEAST_EXPECT(result[jss::ledger][jss::closed] == true); @@ -190,7 +201,8 @@ public: "CBD7F0948EBFA2241DE4EA627939A0FFEE6B80A90FE09C42C825DA546E9B73FF"); result = env.rpc("ledger_request", "4")[jss::result]; - constexpr char const* hash4 = "7C9B614445517B8C6477E0AB10A35FFC1A23A34FEA41A91ECBDE884CC097C6E1"; + constexpr char const* hash4 = + "7C9B614445517B8C6477E0AB10A35FFC1A23A34FEA41A91ECBDE884CC097C6E1"; BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "4"); BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999960"); BEAST_EXPECT(result[jss::ledger][jss::closed] == true); @@ -204,7 +216,8 @@ public: "9BBDDBF926100DFFF364E16268F544B19F5B9BC6ECCBBC104F98D13FA9F3BC35"); result = env.rpc("ledger_request", "5")[jss::result]; - constexpr char const* hash5 = "98885D02145CCE4AD2605F1809F17188DB2053B14ED399CAC985DD8E03DCA8C0"; + constexpr char const* hash5 = + "98885D02145CCE4AD2605F1809F17188DB2053B14ED399CAC985DD8E03DCA8C0"; BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "5"); BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999940"); BEAST_EXPECT(result[jss::ledger][jss::closed] == true); @@ -239,7 +252,8 @@ public: "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936" "C7"; jvParams[jss::ledger_index] = "1"; - auto const result = env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result]; + auto const result = + env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); BEAST_EXPECT(result[jss::status] == "error"); BEAST_EXPECT( @@ -251,7 +265,8 @@ public: { Json::Value jvParams; jvParams[jss::ledger_index] = "index"; - auto const result = env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result]; + auto const result = + env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); BEAST_EXPECT(result[jss::status] == "error"); BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger_index', not number."); diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index 0857561fda..54a2931ba5 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -77,7 +77,8 @@ class NoRippleCheck_test : public beast::unit_test::suite params[jss::limit] = -1; auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); - BEAST_EXPECT(result[jss::error_message] == "Invalid field 'limit', not unsigned integer."); + BEAST_EXPECT( + result[jss::error_message] == "Invalid field 'limit', not unsigned integer."); } { // invalid ledger (hash) @@ -87,7 +88,8 @@ class NoRippleCheck_test : public beast::unit_test::suite params[jss::ledger_hash] = 1; auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); - BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); + BEAST_EXPECT( + result[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); } { // account not found @@ -134,7 +136,8 @@ class NoRippleCheck_test : public beast::unit_test::suite params[jss::ledger] = Json::objectValue; auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result]; BEAST_EXPECT(result[jss::error] == "invalidParams"); - BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger', not string or number."); + BEAST_EXPECT( + result[jss::error_message] == "Invalid field 'ledger', not string or number."); } } @@ -214,7 +217,8 @@ class NoRippleCheck_test : public beast::unit_test::suite } BEAST_EXPECT(result[jss::transactions][txs.size() - 1][jss::Account] == alice.human()); - BEAST_EXPECT(result[jss::transactions][txs.size() - 1][jss::TransactionType] == jss::TrustSet); + BEAST_EXPECT( + result[jss::transactions][txs.size() - 1][jss::TransactionType] == jss::TrustSet); BEAST_EXPECT( result[jss::transactions][txs.size() - 1][jss::LimitAmount] == gw["USD"](100).value().getJson(JsonOptions::none)); @@ -231,8 +235,10 @@ public: { testBadInput(); for (auto user : {true, false}) + { for (auto problem : {true, false}) testBasic(user, problem); + } } }; @@ -263,14 +269,15 @@ class NoRippleCheckLimits_test : public beast::unit_test::suite using namespace xrpl::Resource; using namespace std::chrono; using namespace beast::IP; - auto c = - env.app().getResourceManager().newInboundEndpoint(Endpoint::from_string(test::getEnvLocalhostAddr())); + auto c = env.app().getResourceManager().newInboundEndpoint( + Endpoint::from_string(test::getEnvLocalhostAddr())); // if we go above the warning threshold, reset if (c.balance() > warningThreshold) { using ct = beast::abstract_clock; - c.entry().local_balance = DecayingSample{steady_clock::now()}; + c.entry().local_balance = + DecayingSample{steady_clock::now()}; } }; diff --git a/src/test/rpc/NoRipple_test.cpp b/src/test/rpc/NoRipple_test.cpp index 9cfc69ccd5..fcf412bbd4 100644 --- a/src/test/rpc/NoRipple_test.cpp +++ b/src/test/rpc/NoRipple_test.cpp @@ -234,9 +234,13 @@ public: auto lines = env.rpc("json", "noripple_check", to_string(params)); if (apiVersion < 2u) + { BEAST_EXPECT(lines[jss::result][jss::status] == "success"); + } else + { BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams"); + } } } } @@ -247,7 +251,8 @@ public: testSetAndClear(); auto withFeatsTests = [this](FeatureBitset features) { - forAllApiVersions([&, this](unsigned testVersion) { testDefaultRipple(features, testVersion); }); + forAllApiVersions( + [&, this](unsigned testVersion) { testDefaultRipple(features, testVersion); }); testNegativeBalance(features); testPairwise(features); }; diff --git a/src/test/rpc/OwnerInfo_test.cpp b/src/test/rpc/OwnerInfo_test.cpp index 9e3fee04c2..8a09fed467 100644 --- a/src/test/rpc/OwnerInfo_test.cpp +++ b/src/test/rpc/OwnerInfo_test.cpp @@ -92,14 +92,21 @@ class OwnerInfo_test : public beast::unit_test::suite BEAST_EXPECT( lines[0u][sfBalance.fieldName] == - (STAmount{Issue{to_currency("CNY"), noAccount()}, 0}.value().getJson(JsonOptions::none))); - BEAST_EXPECT(lines[0u][sfHighLimit.fieldName] == alice["CNY"](1000).value().getJson(JsonOptions::none)); - BEAST_EXPECT(lines[0u][sfLowLimit.fieldName] == gw["CNY"](0).value().getJson(JsonOptions::none)); + (STAmount{Issue{to_currency("CNY"), noAccount()}, 0}.value().getJson( + JsonOptions::none))); + BEAST_EXPECT( + lines[0u][sfHighLimit.fieldName] == + alice["CNY"](1000).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + lines[0u][sfLowLimit.fieldName] == gw["CNY"](0).value().getJson(JsonOptions::none)); BEAST_EXPECT( lines[1u][sfBalance.fieldName] == - (STAmount{Issue{to_currency("USD"), noAccount()}, 0}.value().getJson(JsonOptions::none))); - BEAST_EXPECT(lines[1u][sfHighLimit.fieldName] == alice["USD"](1000).value().getJson(JsonOptions::none)); + (STAmount{Issue{to_currency("USD"), noAccount()}, 0}.value().getJson( + JsonOptions::none))); + BEAST_EXPECT( + lines[1u][sfHighLimit.fieldName] == + alice["USD"](1000).value().getJson(JsonOptions::none)); BEAST_EXPECT(lines[1u][sfLowLimit.fieldName] == USD(0).value().getJson(JsonOptions::none)); if (!BEAST_EXPECT(result[jss::accepted].isMember(jss::offers))) @@ -109,8 +116,10 @@ class OwnerInfo_test : public beast::unit_test::suite return; BEAST_EXPECT(offers[0u][jss::Account] == alice.human()); - BEAST_EXPECT(offers[0u][sfTakerGets.fieldName] == XRP(1000).value().getJson(JsonOptions::none)); - BEAST_EXPECT(offers[0u][sfTakerPays.fieldName] == USD(1).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + offers[0u][sfTakerGets.fieldName] == XRP(1000).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + offers[0u][sfTakerPays.fieldName] == USD(1).value().getJson(JsonOptions::none)); // current ledger entry if (!BEAST_EXPECT(result[jss::current].isMember(jss::ripple_lines))) @@ -121,15 +130,23 @@ class OwnerInfo_test : public beast::unit_test::suite BEAST_EXPECT( lines[0u][sfBalance.fieldName] == - (STAmount{Issue{to_currency("CNY"), noAccount()}, -50}.value().getJson(JsonOptions::none))); - BEAST_EXPECT(lines[0u][sfHighLimit.fieldName] == alice["CNY"](1000).value().getJson(JsonOptions::none)); - BEAST_EXPECT(lines[0u][sfLowLimit.fieldName] == gw["CNY"](0).value().getJson(JsonOptions::none)); + (STAmount{Issue{to_currency("CNY"), noAccount()}, -50}.value().getJson( + JsonOptions::none))); + BEAST_EXPECT( + lines[0u][sfHighLimit.fieldName] == + alice["CNY"](1000).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + lines[0u][sfLowLimit.fieldName] == gw["CNY"](0).value().getJson(JsonOptions::none)); BEAST_EXPECT( lines[1u][sfBalance.fieldName] == - (STAmount{Issue{to_currency("USD"), noAccount()}, -50}.value().getJson(JsonOptions::none))); - BEAST_EXPECT(lines[1u][sfHighLimit.fieldName] == alice["USD"](1000).value().getJson(JsonOptions::none)); - BEAST_EXPECT(lines[1u][sfLowLimit.fieldName] == gw["USD"](0).value().getJson(JsonOptions::none)); + (STAmount{Issue{to_currency("USD"), noAccount()}, -50}.value().getJson( + JsonOptions::none))); + BEAST_EXPECT( + lines[1u][sfHighLimit.fieldName] == + alice["USD"](1000).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + lines[1u][sfLowLimit.fieldName] == gw["USD"](0).value().getJson(JsonOptions::none)); if (!BEAST_EXPECT(result[jss::current].isMember(jss::offers))) return; @@ -140,8 +157,10 @@ class OwnerInfo_test : public beast::unit_test::suite BEAST_EXPECT(offers[1u] == result[jss::accepted][jss::offers][0u]); BEAST_EXPECT(offers[0u][jss::Account] == alice.human()); - BEAST_EXPECT(offers[0u][sfTakerGets.fieldName] == XRP(1000).value().getJson(JsonOptions::none)); - BEAST_EXPECT(offers[0u][sfTakerPays.fieldName] == CNY(2).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + offers[0u][sfTakerGets.fieldName] == XRP(1000).value().getJson(JsonOptions::none)); + BEAST_EXPECT( + offers[0u][sfTakerPays.fieldName] == CNY(2).value().getJson(JsonOptions::none)); } public: diff --git a/src/test/rpc/Peers_test.cpp b/src/test/rpc/Peers_test.cpp index 8e4e560f3e..d72ad6d878 100644 --- a/src/test/rpc/Peers_test.cpp +++ b/src/test/rpc/Peers_test.cpp @@ -53,8 +53,8 @@ class Peers_test : public beast::unit_test::suite continue; if (!BEAST_EXPECT((*it).isMember(jss::tag))) continue; - auto tag = (*it)[jss::tag].asString(); - BEAST_EXPECTS((*it)[jss::tag].asString() == nodes[key], key); + auto const tag = (*it)[jss::tag].asString(); + BEAST_EXPECTS(tag == nodes[key], key); } BEAST_EXPECT(peers.isMember(jss::peers) && peers[jss::peers].isNull()); } diff --git a/src/test/rpc/RPCCall_test.cpp b/src/test/rpc/RPCCall_test.cpp index 26f7ed0f18..e4ef3aca6a 100644 --- a/src/test/rpc/RPCCall_test.cpp +++ b/src/test/rpc/RPCCall_test.cpp @@ -78,7 +78,9 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_channels: account and ledger hash.", __LINE__, - {"account_channels", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rD5MbavGfiSC5m7mkxy1FANuT7s3HxqpoF"}, + {"account_channels", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rD5MbavGfiSC5m7mkxy1FANuT7s3HxqpoF"}, RPCCallTestData::no_exception, R"({ "method" : "account_channels", @@ -92,7 +94,9 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_channels: account and ledger index.", __LINE__, - {"account_channels", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "r9emE59aTWb85t64dAebKrxYMBTpzK5yR7"}, + {"account_channels", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "r9emE59aTWb85t64dAebKrxYMBTpzK5yR7"}, RPCCallTestData::no_exception, R"({ "method" : "account_channels", @@ -106,7 +110,9 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_channels: two accounts.", __LINE__, - {"account_channels", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA"}, + {"account_channels", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA"}, RPCCallTestData::no_exception, R"({ "method" : "account_channels", @@ -138,7 +144,10 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_channels: two accounts and ledger index.", __LINE__, - {"account_channels", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "90210"}, + {"account_channels", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "90210"}, RPCCallTestData::no_exception, R"({ "method" : "account_channels", @@ -491,7 +500,10 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_lines: peer and numeric ledger index.", __LINE__, - {"account_lines", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "888888888"}, + {"account_lines", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "888888888"}, RPCCallTestData::no_exception, R"({ "method" : "account_lines", @@ -506,7 +518,10 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_lines: peer and text ledger index.", __LINE__, - {"account_lines", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "closed"}, + {"account_lines", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "closed"}, RPCCallTestData::no_exception, R"({ "method" : "account_lines", @@ -655,7 +670,10 @@ static RPCCallTestData const rpcCallTestArray[] = { { "account_lines: invalid ledger selector.", __LINE__, - {"account_lines", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "not_a_ledger"}, + {"account_lines", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "not_a_ledger"}, RPCCallTestData::no_exception, R"({ "method" : "account_lines", @@ -1121,7 +1139,13 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_tx: ledger_index_min and _max plus trailing params.", __LINE__, - {"account_tx", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "-1", "413", "binary", "count", "descending"}, + {"account_tx", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "-1", + "413", + "binary", + "count", + "descending"}, RPCCallTestData::no_exception, R"({ "method" : "account_tx", @@ -1155,7 +1179,14 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_tx: ledger_index_min and _max, limit, trailing args.", __LINE__, - {"account_tx", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "247", "-1", "300", "count", "descending", "binary"}, + {"account_tx", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "247", + "-1", + "300", + "count", + "descending", + "binary"}, RPCCallTestData::no_exception, R"({ "method" : "account_tx", @@ -1191,7 +1222,14 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"account_tx: ledger_index_min and _max, limit, offset, trailing.", __LINE__, - {"account_tx", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "589", "590", "67", "45", "descending", "count"}, + {"account_tx", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "589", + "590", + "67", + "45", + "descending", + "count"}, RPCCallTestData::no_exception, R"({ "method" : "account_tx", @@ -1342,7 +1380,14 @@ static RPCCallTestData const rpcCallTestArray[] = { {// Note: this really shouldn't throw, but does at the moment. "account_tx: RIPD-1570.", __LINE__, - {"account_tx", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "-1", "-1", "2", "false", "false", "false"}, + {"account_tx", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "-1", + "-1", + "2", + "false", + "false", + "false"}, RPCCallTestData::bad_cast, R"()"}, @@ -1417,7 +1462,11 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"book_offers: add issuer and numeric ledger index.", __LINE__, - {"book_offers", "USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "EUR", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "666"}, + {"book_offers", + "USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "EUR", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "666"}, RPCCallTestData::no_exception, R"({ "method" : "book_offers", @@ -1438,7 +1487,11 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"book_offers: add issuer and text ledger index.", __LINE__, - {"book_offers", "USD", "EUR/rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "current"}, + {"book_offers", + "USD", + "EUR/rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "current"}, RPCCallTestData::no_exception, R"({ "method" : "book_offers", @@ -2332,7 +2385,10 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"deposit_authorized: with text ledger index.", __LINE__, - {"deposit_authorized", "source_account_NotValidated", "destination_account_NotValidated", "validated"}, + {"deposit_authorized", + "source_account_NotValidated", + "destination_account_NotValidated", + "validated"}, RPCCallTestData::no_exception, R"({ "method" : "deposit_authorized", @@ -2836,7 +2892,11 @@ static RPCCallTestData const rpcCallTestArray[] = { }, RPCCallTestData::bad_cast, R"()"}, - {"get_counts: count too large.", __LINE__, {"get_counts", "4294967296"}, RPCCallTestData::bad_cast, R"()"}, + {"get_counts: count too large.", + __LINE__, + {"get_counts", "4294967296"}, + RPCCallTestData::bad_cast, + R"()"}, // json // ------------------------------------------------------------------------ @@ -4537,7 +4597,12 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"sign: too many arguments.", __LINE__, - {"sign", "my_secret", R"({"json_argument":true})", "offline", "CounterpartySignature", "extra"}, + {"sign", + "my_secret", + R"({"json_argument":true})", + "offline", + "CounterpartySignature", + "extra"}, RPCCallTestData::no_exception, R"({ "method" : "sign", @@ -4612,7 +4677,11 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"sign_for: offline.", __LINE__, - {"sign_for", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "my_secret", R"({"json_argument":true})", "offline"}, + {"sign_for", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "my_secret", + R"({"json_argument":true})", + "offline"}, RPCCallTestData::no_exception, R"({ "method" : "sign_for", @@ -4648,7 +4717,12 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"sign_for: too many arguments.", __LINE__, - {"sign_for", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "my_secret", R"({"json_argument":true})", "offline", "extra"}, + {"sign_for", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "my_secret", + R"({"json_argument":true})", + "offline", + "extra"}, RPCCallTestData::no_exception, R"({ "method" : "sign_for", @@ -4681,7 +4755,11 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"sign_for: invalid final argument.", __LINE__, - {"sign_for", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "my_secret", R"({"json_argument":true})", "ofline"}, + {"sign_for", + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "my_secret", + R"({"json_argument":true})", + "ofline"}, RPCCallTestData::no_exception, R"({ "method" : "sign_for", @@ -4784,7 +4862,12 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"submit: too many arguments.", __LINE__, - {"submit", "my_secret", R"({"json_argument":true})", "offline", "CounterpartySignature", "extra"}, + {"submit", + "my_secret", + R"({"json_argument":true})", + "offline", + "CounterpartySignature", + "extra"}, RPCCallTestData::no_exception, R"({ "method" : "submit", @@ -5049,7 +5132,9 @@ static RPCCallTestData const rpcCallTestArray[] = { // ----------------------------------------------------------- {"transaction_entry: ledger index.", __LINE__, - {"transaction_entry", "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", "4294967295"}, + {"transaction_entry", + "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", + "4294967295"}, RPCCallTestData::no_exception, R"({ "method" : "transaction_entry", @@ -5063,7 +5148,9 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"transaction_entry: text ledger index.", __LINE__, - {"transaction_entry", "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", "current"}, + {"transaction_entry", + "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", + "current"}, RPCCallTestData::no_exception, R"({ "method" : "transaction_entry", @@ -5110,7 +5197,10 @@ static RPCCallTestData const rpcCallTestArray[] = { })"}, {"transaction_entry: too many arguments.", __LINE__, - {"transaction_entry", "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", "validated", "extra"}, + {"transaction_entry", + "0123456789ABCDEFGHIJKLMNOPQRSTUV0123456789ABCDEFGHIJKLMNOPQRSTUV", + "validated", + "extra"}, RPCCallTestData::no_exception, R"({ "method" : "transaction_entry", @@ -5759,7 +5849,9 @@ public: testRPCCall(unsigned apiVersion) { testcase << "RPCCall API version " << apiVersion; - if (!BEAST_EXPECT(apiVersion >= RPC::apiMinimumSupportedVersion && apiVersion <= RPC::apiMaximumValidVersion)) + if (!BEAST_EXPECT( + apiVersion >= RPC::apiMinimumSupportedVersion && + apiVersion <= RPC::apiMaximumValidVersion)) return; test::jtx::Env env(*this, makeNetworkConfig(11111)); // Used only for its Journal. @@ -5772,7 +5864,8 @@ public: std::vector const args{rpcCallTest.args.begin(), rpcCallTest.args.end()}; - char const* const expVersioned = (apiVersion - RPC::apiMinimumSupportedVersion) < rpcCallTest.exp.size() + char const* const expVersioned = + (apiVersion - RPC::apiMinimumSupportedVersion) < rpcCallTest.exp.size() ? rpcCallTest.exp[apiVersion - RPC::apiMinimumSupportedVersion] : rpcCallTest.exp.back(); @@ -5785,7 +5878,8 @@ public: } catch (std::bad_cast const&) { - if ((rpcCallTest.throwsWhat == RPCCallTestData::bad_cast) && (std::strlen(expVersioned) == 0)) + if ((rpcCallTest.throwsWhat == RPCCallTestData::bad_cast) && + (std::strlen(expVersioned) == 0)) { pass(); } @@ -5803,8 +5897,8 @@ public: // Lambda to remove the "params[0u]:error_code" field if present. // Error codes are not expected to be stable between releases. auto rmErrorCode = [](Json::Value& json) { - if (json.isMember(jss::params) && json[jss::params].isArray() && json[jss::params].size() > 0 && - json[jss::params][0u].isObject()) + if (json.isMember(jss::params) && json[jss::params].isArray() && + json[jss::params].size() > 0 && json[jss::params][0u].isObject()) { json[jss::params][0u].removeMember(jss::error_code); } diff --git a/src/test/rpc/RPCOverload_test.cpp b/src/test/rpc/RPCOverload_test.cpp index c8f02cd0a3..e5558af82c 100644 --- a/src/test/rpc/RPCOverload_test.cpp +++ b/src/test/rpc/RPCOverload_test.cpp @@ -45,7 +45,9 @@ public: jv = jv[jss::result]; // When booted, we just get a null json response if (jv.isNull()) + { booted = true; + } else if (!(jv.isMember(jss::status) && (jv[jss::status] == "success"))) { // Don't use BEAST_EXPECT above b/c it will be called a diff --git a/src/test/rpc/RobustTransaction_test.cpp b/src/test/rpc/RobustTransaction_test.cpp index 0934543fe0..0d47a39573 100644 --- a/src/test/rpc/RobustTransaction_test.cpp +++ b/src/test/rpc/RobustTransaction_test.cpp @@ -185,8 +185,8 @@ public: } // Check balance - auto ff = - jv[jss::result][jss::transactions][0u][jss::meta]["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"]; + auto ff = jv[jss::result][jss::transactions][0u][jss::meta]["AffectedNodes"][1u] + ["ModifiedNode"]["FinalFields"]; BEAST_EXPECT(ff[jss::Account] == Account("bob").human()); BEAST_EXPECT(ff["Balance"] == "10001000000"); } @@ -261,7 +261,8 @@ public: // Wait for the jobqueue to process everything env.app().getJobQueue().rendezvous(); - BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); + BEAST_EXPECT(wsc->findMsg( + 5s, [&](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); } { @@ -313,7 +314,8 @@ public: // Wait for the jobqueue to process everything env.app().getJobQueue().rendezvous(); - BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); + BEAST_EXPECT(wsc->findMsg( + 5s, [&](auto const& jval) { return jval[jss::type] == "ledgerClosed"; })); } { @@ -348,8 +350,8 @@ public: } // Check balance - auto ff = - jv[jss::result][jss::transactions][0u][jss::meta]["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"]; + auto ff = jv[jss::result][jss::transactions][0u][jss::meta]["AffectedNodes"][1u] + ["ModifiedNode"]["FinalFields"]; BEAST_EXPECT(ff[jss::Account] == Account("bob").human()); BEAST_EXPECT(ff["Balance"] == "10001000000"); } @@ -398,8 +400,9 @@ public: { // Check stream update - BEAST_EXPECT(wsc->findMsg( - 5s, [&](auto const& jv) { return jv[jss::transaction][jss::TransactionType] == jss::AccountSet; })); + BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { + return jv[jss::transaction][jss::TransactionType] == jss::AccountSet; + })); } { diff --git a/src/test/rpc/Roles_test.cpp b/src/test/rpc/Roles_test.cpp index 1750532a10..a4f9de2284 100644 --- a/src/test/rpc/Roles_test.cpp +++ b/src/test/rpc/Roles_test.cpp @@ -29,7 +29,8 @@ class Roles_test : public beast::unit_test::suite Env env(*this); BEAST_EXPECT(env.rpc("ping")["result"]["role"] == "admin"); - BEAST_EXPECT(makeWSClient(env.app().config())->invoke("ping")["result"]["unlimited"].asBool()); + BEAST_EXPECT( + makeWSClient(env.app().config())->invoke("ping")["result"]["unlimited"].asBool()); } { Env env{*this, envconfig(no_admin)}; @@ -225,7 +226,8 @@ class Roles_test : public beast::unit_test::suite { Env env{*this, envconfig(admin_localnet)}; BEAST_EXPECT(env.rpc("ping")["result"]["role"] == "admin"); - BEAST_EXPECT(makeWSClient(env.app().config())->invoke("ping")["result"]["unlimited"].asBool()); + BEAST_EXPECT( + makeWSClient(env.app().config())->invoke("ping")["result"]["unlimited"].asBool()); } { diff --git a/src/test/rpc/ServerDefinitions_test.cpp b/src/test/rpc/ServerDefinitions_test.cpp index 20bd6ba363..a2c45cfa65 100644 --- a/src/test/rpc/ServerDefinitions_test.cpp +++ b/src/test/rpc/ServerDefinitions_test.cpp @@ -1,6 +1,9 @@ #include #include +#include +#include +#include #include namespace xrpl { @@ -42,8 +45,10 @@ public: BEAST_EXPECT(firstField[1][jss::type].asString() == "Unknown"); } - BEAST_EXPECT(result[jss::result][jss::LEDGER_ENTRY_TYPES]["AccountRoot"].asUInt() == 97); - BEAST_EXPECT(result[jss::result][jss::TRANSACTION_RESULTS]["tecDIR_FULL"].asUInt() == 121); + BEAST_EXPECT( + result[jss::result][jss::LEDGER_ENTRY_TYPES]["AccountRoot"].asUInt() == 97); + BEAST_EXPECT( + result[jss::result][jss::TRANSACTION_RESULTS]["tecDIR_FULL"].asUInt() == 121); BEAST_EXPECT(result[jss::result][jss::TRANSACTION_TYPES]["Payment"].asUInt() == 0); BEAST_EXPECT(result[jss::result][jss::TYPES]["AccountID"].asUInt() == 8); @@ -79,43 +84,371 @@ public: BEAST_EXPECT(types["Hash384"].asUInt() == 22); BEAST_EXPECT(types["Hash512"].asUInt() == 23); } - } - // test providing the same hash - { - Env env(*this); - auto const firstResult = env.rpc("server_definitions"); - auto const hash = firstResult[jss::result][jss::hash].asString(); - auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + // test the properties of the LEDGER_ENTRY_FLAGS section + { + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS]; - auto const result = env.rpc("json", "server_definitions", hashParam); - BEAST_EXPECT(!result[jss::result].isMember(jss::error)); - BEAST_EXPECT(result[jss::result][jss::status] == "success"); - BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS)); - BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_RESULTS)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES)); - BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::hash)); - } + // sanity test the mapped value of a few arbitrarily chosen flags + BEAST_EXPECT(leFlags["AccountRoot"]["lsfDisallowXRP"] == 0x00080000); + BEAST_EXPECT(leFlags["AccountRoot"]["lsfDepositAuth"] == 0x01000000); + BEAST_EXPECT(leFlags["AccountRoot"]["lsfAllowTrustLineClawback"] == 0x80000000); - // test providing a different hash - { - Env env(*this); - std::string const hash = - "54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749" - "D1"; - auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + BEAST_EXPECT(leFlags["RippleState"]["lsfHighFreeze"] == 0x00800000); + BEAST_EXPECT(leFlags["RippleState"]["lsfAMMNode"] == 0x01000000); - auto const result = env.rpc("json", "server_definitions", hashParam); - BEAST_EXPECT(!result[jss::result].isMember(jss::error)); - BEAST_EXPECT(result[jss::result][jss::status] == "success"); - BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS)); - BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_RESULTS)); - BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::TYPES)); - BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + BEAST_EXPECT(leFlags["DirNode"]["lsfNFTokenBuyOffers"] == 0x00000001); + BEAST_EXPECT(leFlags["MPTokenIssuance"]["lsfMPTCanTrade"] == 0x00000010); + BEAST_EXPECT(leFlags["Credential"]["lsfAccepted"] == 0x00010000); + BEAST_EXPECT(leFlags["Loan"]["lsfLoanImpaired"] == 0x00020000); + BEAST_EXPECT(leFlags["Vault"]["lsfVaultPrivate"] == 0x00010000); + BEAST_EXPECT(leFlags["MPToken"]["lsfMPTAuthorized"] == 0x00000002); + } + + // validate the correctness of few chosen transaction flags + { + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS]; + + BEAST_EXPECT(txFlags["universal"]["tfFullyCanonicalSig"] == 0x80000000); + BEAST_EXPECT(txFlags["universal"]["tfInnerBatchTxn"] == 0x40000000); + + BEAST_EXPECT(txFlags["AccountSet"]["tfRequireAuth"] == 0x00040000); + BEAST_EXPECT(txFlags["AccountSet"]["tfAllowXRP"] == 0x00200000); + + BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTLock"] == 0x00000001); + BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTUnlock"] == 0x00000002); + + BEAST_EXPECT(txFlags["AMMDeposit"]["tfLPToken"] == 0x00010000); + BEAST_EXPECT(txFlags["AMMDeposit"]["tfLimitLPToken"] == 0x00400000); + } + + // validate the correctness of the AccountSpecificFlags section + { + BEAST_EXPECT(result[jss::result].isMember(jss::ACCOUNT_SET_FLAGS)); + Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS]; + + BEAST_EXPECT(asFlags["asfDisallowXRP"] == 3); + BEAST_EXPECT(asFlags["asfGlobalFreeze"] == 7); + BEAST_EXPECT(asFlags["asfDisallowIncomingNFTokenOffer"] == 12); + BEAST_EXPECT(asFlags["asfDisallowIncomingTrustline"] == 15); + } + + // test the response fields of the TRANSACTION_FORMATS section + { + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + Json::Value const& txnFormats = result[jss::result][jss::TRANSACTION_FORMATS]; + + // first validate the contents of "common" + { + BEAST_EXPECT(txnFormats.isMember("common")); + Json::Value const& section = txnFormats["common"]; + + BEAST_EXPECT(section[0u][jss::name] == "TransactionType"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Flags"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "SourceTag"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "Account"); + BEAST_EXPECT(section[3u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[4u][jss::name] == "Sequence"); + BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[5u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[6u][jss::name] == "LastLedgerSequence"); + BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[7u][jss::name] == "AccountTxnID"); + BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[8u][jss::name] == "Fee"); + BEAST_EXPECT(section[8u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[9u][jss::name] == "OperationLimit"); + BEAST_EXPECT(section[9u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[10u][jss::name] == "Memos"); + BEAST_EXPECT(section[10u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[11u][jss::name] == "SigningPubKey"); + BEAST_EXPECT(section[11u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[12u][jss::name] == "TicketSequence"); + BEAST_EXPECT(section[12u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[13u][jss::name] == "TxnSignature"); + BEAST_EXPECT(section[13u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[14u][jss::name] == "Signers"); + BEAST_EXPECT(section[14u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[15u][jss::name] == "NetworkID"); + BEAST_EXPECT(section[15u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[16u][jss::name] == "Delegate"); + BEAST_EXPECT(section[16u][jss::optionality] == soeOPTIONAL); + } + + // validate the contents of four arbitrarily selected transactions validate the + // format of the OracleSet transaction + { + BEAST_EXPECT(txnFormats.isMember("OracleSet")); + Json::Value const& section = txnFormats["OracleSet"]; + + BEAST_EXPECT(section[0u][jss::name] == "OracleDocumentID"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Provider"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "URI"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "AssetClass"); + BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[4u][jss::name] == "LastUpdateTime"); + BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[5u][jss::name] == "PriceDataSeries"); + BEAST_EXPECT(section[5u][jss::optionality] == soeREQUIRED); + } + + // validate the format of the PermissionedDomainDelete transaction + { + BEAST_EXPECT(txnFormats.isMember("PermissionedDomainDelete")); + Json::Value const& section = txnFormats["PermissionedDomainDelete"]; + + BEAST_EXPECT(section[0u][jss::name] == "DomainID"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + } + + // validate the format of the Clawback transaction + { + BEAST_EXPECT(txnFormats.isMember("Clawback")); + Json::Value const& section = txnFormats["Clawback"]; + + BEAST_EXPECT(section[0u][jss::name] == "Amount"); + BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(section[1u][jss::name] == "Holder"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + } + + // validate the format of the SetFee transaction + { + BEAST_EXPECT(txnFormats.isMember("SetFee")); + Json::Value const& section = txnFormats["SetFee"]; + + BEAST_EXPECT(section[0u][jss::name] == "LedgerSequence"); + BEAST_EXPECT(section[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[1u][jss::name] == "BaseFee"); + BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[2u][jss::name] == "ReferenceFeeUnits"); + BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[3u][jss::name] == "ReserveBase"); + BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[4u][jss::name] == "ReserveIncrement"); + BEAST_EXPECT(section[4u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[5u][jss::name] == "BaseFeeDrops"); + BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[6u][jss::name] == "ReserveBaseDrops"); + BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(section[7u][jss::name] == "ReserveIncrementDrops"); + BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL); + } + } + + // test the properties of the LEDGER_ENTRY_FORMATS section in server_definitions + // response + { + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + + // Note: For the purposes of software maintenance, this test does not exhaustively + // validate all the LEDGER_ENTRY_FORMATS + + // check "common" first + { + Json::Value const& observedCommonLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["common"]; + + BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::name] == "LedgerIndex"); + BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::name] == "LedgerEntryType"); + BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::name] == "Flags"); + BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::optionality] == soeREQUIRED); + } + + // test the contents of an arbitrary ledger-entry (DID) + { + Json::Value const& observedDIDLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["DID"]; + + BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::name] == "Account"); + BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::name] == "DIDDocument"); + BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::name] == "URI"); + BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::name] == "Data"); + BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::name] == "OwnerNode"); + BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::optionality] == soeREQUIRED); + + BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::name] == "PreviousTxnLgrSeq"); + BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::optionality] == soeREQUIRED); + } + + // test the contents of an arbitrary ledger-entry (NegativeUNL) + { + Json::Value const& observedNunlLedgerEntry = + result[jss::result][jss::LEDGER_ENTRY_FORMATS]["NegativeUNL"]; + + BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::name] == "DisabledValidators"); + BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::name] == "ValidatorToDisable"); + BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::name] == "ValidatorToReEnable"); + BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::name] == "PreviousTxnID"); + BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::optionality] == soeOPTIONAL); + + BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::name] == "PreviousTxnLgrSeq"); + BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::optionality] == soeOPTIONAL); + } + } + + // Exhaustive test: verify all transaction flags from getAllTxFlags() appear in the + // output + { + Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS]; + + for (auto const& [txName, flagMap] : getAllTxFlags()) + { + BEAST_EXPECT(txFlags.isMember(txName)); + if (txFlags.isMember(txName)) + { + for (auto const& [flagName, flagValue] : flagMap) + { + BEAST_EXPECT(txFlags[txName].isMember(flagName)); + if (txFlags[txName].isMember(flagName)) + { + BEAST_EXPECT(txFlags[txName][flagName].asUInt() == flagValue); + } + } + } + } + } + + // Exhaustive test: verify all ledger entry flags from getAllLedgerFlags() appear in the + // output + { + Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS]; + + for (auto const& [ledgerType, flagMap] : getAllLedgerFlags()) + { + BEAST_EXPECT(leFlags.isMember(ledgerType)); + if (leFlags.isMember(ledgerType)) + { + for (auto const& [flagName, flagValue] : flagMap) + { + BEAST_EXPECT(leFlags[ledgerType].isMember(flagName)); + if (leFlags[ledgerType].isMember(flagName)) + { + BEAST_EXPECT(leFlags[ledgerType][flagName].asUInt() == flagValue); + } + } + } + } + } + + // Exhaustive test: verify all AccountSet flags from getAsfFlagMap() appear in the + // output + { + Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS]; + + for (auto const& [flagName, flagValue] : getAsfFlagMap()) + { + BEAST_EXPECT(asFlags.isMember(flagName)); + if (asFlags.isMember(flagName)) + { + BEAST_EXPECT(asFlags[flagName].asInt() == flagValue); + } + } + } + + // test providing the same hash + { + Env env(*this); + auto const firstResult = env.rpc("server_definitions"); + auto const hash = firstResult[jss::result][jss::hash].asString(); + auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + + auto const result = env.rpc("json", "server_definitions", hashParam); + BEAST_EXPECT(!result[jss::result].isMember(jss::error)); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); + BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_RESULTS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + } + + // test providing a different hash + { + Env env(*this); + std::string const hash = + "54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749" + "D1"; + auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}"; + + auto const result = env.rpc("json", "server_definitions", hashParam); + BEAST_EXPECT(!result[jss::result].isMember(jss::error)); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); + BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS)); + BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_RESULTS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FORMATS)); + BEAST_EXPECT(result[jss::result].isMember(jss::TYPES)); + BEAST_EXPECT(result[jss::result].isMember(jss::hash)); + } } } diff --git a/src/test/rpc/ServerInfo_test.cpp b/src/test/rpc/ServerInfo_test.cpp index 054c3e563a..5bb2c31757 100644 --- a/src/test/rpc/ServerInfo_test.cpp +++ b/src/test/rpc/ServerInfo_test.cpp @@ -1,10 +1,10 @@ #include -#include #include #include #include +#include #include @@ -81,10 +81,11 @@ admin = 127.0.0.1 auto const& git = info[jss::git]; BEAST_EXPECT(git.isMember(jss::hash) || git.isMember(jss::branch)); BEAST_EXPECT( - !git.isMember(jss::hash) || (git[jss::hash].isString() && git[jss::hash].asString().size() == 40)); + !git.isMember(jss::hash) || + (git[jss::hash].isString() && git[jss::hash].asString().size() == 40)); BEAST_EXPECT( !git.isMember(jss::branch) || - (git[jss::branch].isString() && git[jss::branch].asString().size() != 0)); + (git[jss::branch].isString() && !git[jss::branch].asString().empty())); } } @@ -115,7 +116,9 @@ admin = 127.0.0.1 BEAST_EXPECT(!result[jss::result].isMember(jss::error)); BEAST_EXPECT(result[jss::result][jss::status] == "success"); BEAST_EXPECT(result[jss::result].isMember(jss::info)); - BEAST_EXPECT(result[jss::result][jss::info][jss::pubkey_validator] == validator_data::public_key); + BEAST_EXPECT( + result[jss::result][jss::info][jss::pubkey_validator] == + validator_data::public_key); auto const& ports = result[jss::result][jss::info][jss::ports]; BEAST_EXPECT(ports.isArray() && ports.size() == 3); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 194b0296ec..74f9b03111 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -41,7 +41,7 @@ class Simulate_test : public beast::unit_test::suite else { auto const unHexed = strUnHex(result[jss::tx_blob].asString()); - SerialIter sitTrans(makeSlice(*unHexed)); + SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) tx_json = STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::none); } BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]); @@ -59,7 +59,8 @@ class Simulate_test : public beast::unit_test::suite int const expectedSequence, XRPAmount const& expectedFee) { - return checkBasicReturnValidity(result, tx, expectedSequence, expectedFee.jsonClipped().asString()); + checkBasicReturnValidity( + result, tx, expectedSequence, expectedFee.jsonClipped().asString()); } void @@ -86,6 +87,7 @@ class Simulate_test : public beast::unit_test::suite // It is technically not a valid STObject, so the following line // will crash STParsedJSONObject const parsed(std::string(jss::tx_json), tx); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const tx_blob = strHex(parsed.object->getSerializer().peekData()); if (BEAST_EXPECT(parsed.object.has_value())) { @@ -106,8 +108,11 @@ class Simulate_test : public beast::unit_test::suite testTxJsonMetadataField( jtx::Env& env, Json::Value const& tx, - std::function const& - validate, + std::function const& validate, Json::Value const& expectedMetadataKey, Json::Value const& expectedMetadataValue) { @@ -115,8 +120,13 @@ class Simulate_test : public beast::unit_test::suite Json::Value params; params[jss::tx_json] = tx; - validate(env.rpc("json", "simulate", to_string(params)), tx, expectedMetadataKey, expectedMetadataValue); - validate(env.rpc("simulate", to_string(tx)), tx, expectedMetadataKey, expectedMetadataValue); + validate( + env.rpc("json", "simulate", to_string(params)), + tx, + expectedMetadataKey, + expectedMetadataValue); + validate( + env.rpc("simulate", to_string(tx)), tx, expectedMetadataKey, expectedMetadataValue); BEAST_EXPECTS(env.current()->txCount() == 0, std::to_string(env.current()->txCount())); } @@ -127,7 +137,7 @@ class Simulate_test : public beast::unit_test::suite if (txResult.isMember(jss::meta_blob)) { auto unHexed = strUnHex(txResult[jss::meta_blob].asString()); - SerialIter sitTrans(makeSlice(*unHexed)); + SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) return STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::none); } @@ -147,7 +157,9 @@ class Simulate_test : public beast::unit_test::suite // No params Json::Value const params = Json::objectValue; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Neither `tx_blob` nor `tx_json` included."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == + "Neither `tx_blob` nor `tx_json` included."); } { // Providing both `tx_json` and `tx_blob` @@ -156,7 +168,9 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_blob] = "1200"; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Can only include one of `tx_blob` and `tx_json`."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == + "Can only include one of `tx_blob` and `tx_json`."); } { // `binary` isn't a boolean @@ -180,7 +194,8 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = Json::objectValue; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Missing field 'tx.TransactionType'."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Missing field 'tx.TransactionType'."); } { // No tx.Account @@ -214,7 +229,8 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = ""; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx_json', not object."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Invalid field 'tx_json', not object."); } { // `seed` field included @@ -269,7 +285,9 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = tx_json; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_exception] == "Field 'Destination' is required but missing."); + BEAST_EXPECT( + resp[jss::result][jss::error_exception] == + "Field 'Destination' is required but missing."); } { // Bad account @@ -281,7 +299,8 @@ class Simulate_test : public beast::unit_test::suite auto const resp = env.rpc("json", "simulate", to_string(params)); BEAST_EXPECTS( - resp[jss::result][jss::error] == "srcActMalformed", resp[jss::result][jss::error].toStyledString()); + resp[jss::result][jss::error] == "srcActMalformed", + resp[jss::result][jss::error].toStyledString()); BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx.Account'."); } { @@ -330,7 +349,8 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = tx_json; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Field 'tx_json.foo' is unknown."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Field 'tx_json.foo' is unknown."); } { // non-`"binary"` second param for CLI @@ -350,7 +370,8 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = tx_json; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Transaction should not be signed."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Transaction should not be signed."); } { // Signed multisig transaction @@ -371,7 +392,8 @@ class Simulate_test : public beast::unit_test::suite params[jss::tx_json] = tx_json; auto const resp = env.rpc("json", "simulate", to_string(params)); - BEAST_EXPECT(resp[jss::result][jss::error_message] == "Transaction should not be signed."); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Transaction should not be signed."); } } @@ -459,7 +481,8 @@ class Simulate_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); BEAST_EXPECT(result[jss::engine_result_code] == 0); BEAST_EXPECT( - result[jss::engine_result_message] == "The simulated transaction would have been applied."); + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) { @@ -581,11 +604,14 @@ class Simulate_test : public beast::unit_test::suite auto finalFields = modifiedNode[sfFinalFields]; BEAST_EXPECT( finalFields[sfBalance] == - std::to_string(100'000'000'000'000'000 - env.current()->fees().base.drops())); + std::to_string( + 100'000'000'000'000'000 - + env.current()->fees().base.drops())); } } BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0); - BEAST_EXPECT(metadata[sfTransactionResult.jsonName] == "tecNO_DST_INSUF_XRP"); + BEAST_EXPECT( + metadata[sfTransactionResult.jsonName] == "tecNO_DST_INSUF_XRP"); } }; @@ -634,12 +660,14 @@ class Simulate_test : public beast::unit_test::suite result, tx, env.seq(alice), - tx.isMember(jss::Signers) ? env.current()->fees().base * 2 : env.current()->fees().base); + tx.isMember(jss::Signers) ? env.current()->fees().base * 2 + : env.current()->fees().base); BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); BEAST_EXPECT(result[jss::engine_result_code] == 0); BEAST_EXPECT( - result[jss::engine_result_message] == "The simulated transaction would have been applied."); + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) { @@ -713,7 +741,8 @@ class Simulate_test : public beast::unit_test::suite std::function const& testSimulation = [&](Json::Value const& resp, Json::Value const& tx) { auto result = resp[jss::result]; - checkBasicReturnValidity(result, tx, env.seq(env.master), env.current()->fees().base); + checkBasicReturnValidity( + result, tx, env.seq(env.master), env.current()->fees().base); BEAST_EXPECT(result[jss::engine_result] == "tefMASTER_DISABLED"); BEAST_EXPECT(result[jss::engine_result_code] == -188); @@ -766,11 +795,13 @@ class Simulate_test : public beast::unit_test::suite std::function const& testSimulation = [&](Json::Value const& resp, Json::Value const& tx) { auto result = resp[jss::result]; - checkBasicReturnValidity(result, tx, env.seq(env.master), env.current()->fees().base * 2); + checkBasicReturnValidity( + result, tx, env.seq(env.master), env.current()->fees().base * 2); BEAST_EXPECT(result[jss::engine_result] == "temINVALID"); BEAST_EXPECT(result[jss::engine_result_code] == -277); - BEAST_EXPECT(result[jss::engine_result_message] == "The transaction is ill-formed."); + BEAST_EXPECT( + result[jss::engine_result_message] == "The transaction is ill-formed."); BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob)); }; @@ -826,12 +857,16 @@ class Simulate_test : public beast::unit_test::suite { auto validateOutput = [&](Json::Value const& resp, Json::Value const& tx) { auto result = resp[jss::result]; - checkBasicReturnValidity(result, tx, env.seq(alice), env.current()->fees().base * 2); + checkBasicReturnValidity( + result, tx, env.seq(alice), env.current()->fees().base * 2); BEAST_EXPECTS( - result[jss::engine_result] == "tefBAD_SIGNATURE", result[jss::engine_result].toStyledString()); + result[jss::engine_result] == "tefBAD_SIGNATURE", + result[jss::engine_result].toStyledString()); BEAST_EXPECT(result[jss::engine_result_code] == -186); - BEAST_EXPECT(result[jss::engine_result_message] == "A signature is provided for a non-signer."); + BEAST_EXPECT( + result[jss::engine_result_message] == + "A signature is provided for a non-signer."); BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob)); }; @@ -913,12 +948,15 @@ class Simulate_test : public beast::unit_test::suite for (auto const& node : metadata[sfAffectedNodes.jsonName]) { if (node.isMember(sfDeletedNode.jsonName) && - node[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString() == "Credential") + node[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName] + .asString() == "Credential") { - auto const deleted = node[sfDeletedNode.jsonName][sfFinalFields.jsonName]; + auto const deleted = + node[sfDeletedNode.jsonName][sfFinalFields.jsonName]; found = deleted[jss::Issuer] == issuer.human() && deleted[jss::Subject] == subject.human() && - deleted["CredentialType"] == strHex(std::string_view(credType)); + deleted["CredentialType"] == + strHex(std::string_view(credType)); break; } } @@ -952,7 +990,8 @@ class Simulate_test : public beast::unit_test::suite auto const jle = credentials::ledgerEntry(env, subject, issuer, credType); BEAST_EXPECT( jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) && - jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember("LedgerEntryType") && + jle[jss::result].isMember(jss::node) && + jle[jss::result][jss::node].isMember("LedgerEntryType") && jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential && jle[jss::result][jss::node][jss::Issuer] == issuer.human() && jle[jss::result][jss::node][jss::Subject] == subject.human() && @@ -982,7 +1021,8 @@ class Simulate_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); BEAST_EXPECT(result[jss::engine_result_code] == 0); BEAST_EXPECT( - result[jss::engine_result_message] == "The simulated transaction would have been applied."); + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) { @@ -1056,7 +1096,8 @@ class Simulate_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); BEAST_EXPECT(result[jss::engine_result_code] == 0); BEAST_EXPECT( - result[jss::engine_result_message] == "The simulated transaction would have been applied."); + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) { @@ -1098,7 +1139,8 @@ class Simulate_test : public beast::unit_test::suite Json::Value mptIssuanceId = to_string(makeMptID(env.seq(alice), alice)); // test mpt issuance id - testTxJsonMetadataField(env, tx, validateOutput, jss::mpt_issuance_id, mptIssuanceId); + testTxJsonMetadataField( + env, tx, validateOutput, jss::mpt_issuance_id, mptIssuanceId); } } } diff --git a/src/test/rpc/Status_test.cpp b/src/test/rpc/Status_test.cpp index ac387196fe..01fc81430f 100644 --- a/src/test/rpc/Status_test.cpp +++ b/src/test/rpc/Status_test.cpp @@ -112,7 +112,11 @@ private: template void - expectFill(std::string const& label, Type status, Status::Strings messages, std::string const& message) + expectFill( + std::string const& label, + Type status, + Status::Strings messages, + std::string const& message) { value_.clear(); fillJson(Status(status, messages)); @@ -124,14 +128,18 @@ private: expect(bool(error), prefix + "No error."); auto code = error[jss::code].asInt(); - expect(status == code, prefix + "Wrong status " + std::to_string(code) + " != " + std::to_string(status)); + expect( + status == code, + prefix + "Wrong status " + std::to_string(code) + " != " + std::to_string(status)); auto m = error[jss::message].asString(); expect(m == message, m + " != " + message); auto d = error[jss::data]; size_t s1 = d.size(), s2 = messages.size(); - expect(s1 == s2, prefix + "Data sizes differ " + std::to_string(s1) + " != " + std::to_string(s2)); + expect( + s1 == s2, + prefix + "Data sizes differ " + std::to_string(s1) + " != " + std::to_string(s2)); for (auto i = 0; i < std::min(s1, s2); ++i) { auto ds = d[i].asString(); @@ -145,7 +153,11 @@ private: testcase("error"); expectFill("temBAD_AMOUNT", temBAD_AMOUNT, {}, "temBAD_AMOUNT: Malformed: Bad amount."); - expectFill("rpcBAD_SYNTAX", rpcBAD_SYNTAX, {"An error.", "Another error."}, "badSyntax: Syntax error."); + expectFill( + "rpcBAD_SYNTAX", + rpcBAD_SYNTAX, + {"An error.", "Another error."}, + "badSyntax: Syntax error."); expectFill("integer message", 23, {"Stuff."}, "23"); } diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index f15001a7bf..dcb5ab8afd 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -3,14 +3,15 @@ #include #include -#include -#include #include #include +#include #include #include #include +#include +#include #include @@ -25,7 +26,7 @@ public: { using namespace std::chrono_literals; using namespace jtx; - Env env(*this); + Env env{*this, single_thread_io(envconfig())}; auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -57,7 +58,8 @@ public: env.app().getOPs().reportFeeChange(); // Check stream update - BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { return jv[jss::type] == "serverStatus"; })); + BEAST_EXPECT( + wsc->findMsg(5s, [&](auto const& jv) { return jv[jss::type] == "serverStatus"; })); } { @@ -90,7 +92,7 @@ public: { using namespace std::chrono_literals; using namespace jtx; - Env env(*this); + Env env{*this, single_thread_io(envconfig())}; auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -106,26 +108,29 @@ public: BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5); } BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2); - BEAST_EXPECT(jv[jss::result][jss::network_id] == env.app().config().NETWORK_ID); + BEAST_EXPECT( + jv[jss::result][jss::network_id] == env.app().getNetworkIDService().getNetworkID()); } { // Accept a ledger - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream update BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::ledger_index] == 3 && jv[jss::network_id] == env.app().config().NETWORK_ID; + return jv[jss::ledger_index] == 3 && + jv[jss::network_id] == env.app().getNetworkIDService().getNetworkID(); })); } { // Accept another ledger - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream update BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::ledger_index] == 4 && jv[jss::network_id] == env.app().config().NETWORK_ID; + return jv[jss::ledger_index] == 4 && + jv[jss::network_id] == env.app().getNetworkIDService().getNetworkID(); })); } @@ -145,7 +150,7 @@ public: { using namespace std::chrono_literals; using namespace jtx; - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto baseFee = env.current()->fees().base.drops(); auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -166,11 +171,12 @@ public: { env.fund(XRP(10000), "alice"); - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream update for payment transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"][jss::Account] // + return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"] + [jss::Account] // == Account("alice").human() && jv[jss::transaction][jss::TransactionType] // == jss::Payment && @@ -184,16 +190,17 @@ public: // Check stream update for accountset transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"][jss::Account] == - Account("alice").human(); + return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"] + [jss::Account] == Account("alice").human(); })); env.fund(XRP(10000), "bob"); - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream update for payment transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"][jss::Account] // + return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"] + [jss::Account] // == Account("bob").human() && jv[jss::transaction][jss::TransactionType] // == jss::Payment && @@ -207,8 +214,8 @@ public: // Check stream update for accountset transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"][jss::Account] == - Account("bob").human(); + return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"] + [jss::Account] == Account("bob").human(); })); } @@ -242,22 +249,22 @@ public: { // Transaction that does not affect stream env.fund(XRP(10000), "carol"); - env.close(); + BEAST_EXPECT(env.syncClose()); BEAST_EXPECT(!wsc->getMsg(10ms)); // Transactions concerning alice env.trust(Account("bob")["USD"](100), "alice"); - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream updates BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"][jss::Account] == - Account("alice").human(); + return jv[jss::meta]["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"] + [jss::Account] == Account("alice").human(); })); BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"]["LowLimit"][jss::issuer] == - Account("alice").human(); + return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"]["LowLimit"] + [jss::issuer] == Account("alice").human(); })); } @@ -281,6 +288,7 @@ public: using namespace jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { cfg->FEES.reference_fee = 10; + cfg = single_thread_io(std::move(cfg)); return cfg; })); auto wsc = makeWSClient(env.app().config()); @@ -303,11 +311,12 @@ public: { env.fund(XRP(10000), "alice"); - env.close(); + BEAST_EXPECT(env.syncClose()); // Check stream update for payment transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"][jss::Account] // + return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]["NewFields"] + [jss::Account] // == Account("alice").human() && jv[jss::close_time_iso] // == "2000-01-01T00:00:10Z" && @@ -330,8 +339,8 @@ public: // Check stream update for accountset transaction BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { - return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"][jss::Account] == - Account("alice").human(); + return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]["FinalFields"] + [jss::Account] == Account("alice").human(); })); } @@ -352,7 +361,7 @@ public: testManifests() { using namespace jtx; - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -386,17 +395,18 @@ public: { using namespace jtx; - Env env{*this, envconfig(validator, ""), features}; + Env env{*this, single_thread_io(envconfig(validator, "")), features}; auto& cfg = env.app().config(); if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty())) return; auto const parsedseed = parseBase58(cfg.section(SECTION_VALIDATION_SEED).values()[0]); - if (!BEAST_EXPECT(parsedseed)) + if (BEAST_EXPECT(parsedseed); not parsedseed.has_value()) return; std::string const valPublicKey = toBase58( TokenType::NodePublic, - derivePublicKey(KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, *parsedseed))); + derivePublicKey( + KeyType::secp256k1, generateSecretKey(KeyType::secp256k1, *parsedseed))); auto wsc = makeWSClient(env.app().config()); Json::Value stream; @@ -451,7 +461,7 @@ public: if (!jv.isMember(jss::validated_hash)) return false; - uint32_t netID = env.app().config().NETWORK_ID; + uint32_t netID = env.app().getNetworkIDService().getNetworkID(); if (!jv.isMember(jss::network_id) || jv[jss::network_id] != netID) return false; @@ -490,7 +500,7 @@ public: // at least one flag ledger. while (env.closed()->header().seq < 300) { - env.close(); + BEAST_EXPECT(env.syncClose()); using namespace std::chrono_literals; BEAST_EXPECT(wsc->findMsg(5s, validValidationFields)); } @@ -512,7 +522,7 @@ public: { using namespace jtx; testcase("Subscribe by url"); - Env env{*this}; + Env env{*this, single_thread_io(envconfig())}; Json::Value jv; jv[jss::url] = "http://localhost/events"; @@ -526,7 +536,7 @@ public: jv[jss::streams][0u] = "ledger"; jr = env.rpc("json", "subscribe", to_string(jv))[jss::result]; BEAST_EXPECT(jr[jss::status] == "success"); - BEAST_EXPECT(jr[jss::network_id] == env.app().config().NETWORK_ID); + BEAST_EXPECT(jr[jss::network_id] == env.app().getNetworkIDService().getNetworkID()); jr = env.rpc("json", "unsubscribe", to_string(jv))[jss::result]; BEAST_EXPECT(jr[jss::status] == "success"); @@ -543,7 +553,7 @@ public: auto const method = subscribe ? "subscribe" : "unsubscribe"; testcase << "Error cases for " << method; - Env env{*this}; + Env env{*this, single_thread_io(envconfig())}; auto wsc = makeWSClient(env.app().config()); { @@ -579,7 +589,7 @@ public: } { - Env env_nonadmin{*this, no_admin(envconfig())}; + Env env_nonadmin{*this, single_thread_io(no_admin(envconfig()))}; Json::Value jv; jv[jss::url] = "no-url"; auto jr = env_nonadmin.rpc("json", method, to_string(jv))[jss::result]; @@ -694,7 +704,8 @@ public: // NOTE: this error is slightly incongruous with the // equivalent source currency error BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed"); - BEAST_EXPECT(jr[jss::error_message] == "Destination amount/currency/issuer is malformed."); + BEAST_EXPECT( + jr[jss::error_message] == "Destination amount/currency/issuer is malformed."); } { @@ -708,7 +719,8 @@ public: // NOTE: this error is slightly incongruous with the // equivalent source currency error BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed"); - BEAST_EXPECT(jr[jss::error_message] == "Destination amount/currency/issuer is malformed."); + BEAST_EXPECT( + jr[jss::error_message] == "Destination amount/currency/issuer is malformed."); } { @@ -806,13 +818,17 @@ public: * return {true, true} if received numReplies replies and also * received a tx with the account_history_tx_first == true */ - auto getTxHash = [](WSClient& wsc, IdxHashVec& v, int numReplies) -> std::pair { + auto getTxHash = [](WSClient& wsc, + IdxHashVec& v, + int numReplies, + std::chrono::milliseconds timeout = + std::chrono::milliseconds{5000}) -> std::pair { bool first_flag = false; for (int i = 0; i < numReplies; ++i) { std::uint32_t idx{0}; - auto reply = wsc.getMsg(100ms); + auto reply = wsc.getMsg(timeout); if (reply) { auto r = *reply; @@ -839,12 +855,13 @@ public: * send payments between the two accounts a and b, * and close ledgersToClose ledgers */ - auto sendPayments = [](Env& env, - Account const& a, - Account const& b, - int newTxns, - std::uint32_t ledgersToClose, - int numXRP = 10) { + auto sendPayments = [this]( + Env& env, + Account const& a, + Account const& b, + int newTxns, + std::uint32_t ledgersToClose, + int numXRP = 10) { env.memoize(a); env.memoize(b); for (int i = 0; i < newTxns; ++i) @@ -857,7 +874,7 @@ public: jtx::sig(jtx::autofill)); } for (int i = 0; i < ledgersToClose; ++i) - env.close(); + BEAST_EXPECT(env.syncClose()); return newTxns; }; @@ -866,7 +883,9 @@ public: * and in the same order. * If sizeCompare is false, txHistoryVec is allowed to be larger. */ - auto hashCompare = [](IdxHashVec const& accountVec, IdxHashVec const& txHistoryVec, bool sizeCompare) -> bool { + auto hashCompare = [](IdxHashVec const& accountVec, + IdxHashVec const& txHistoryVec, + bool sizeCompare) -> bool { if (accountVec.empty() || txHistoryVec.empty()) return false; if (sizeCompare && accountVec.size() != (txHistoryVec.size())) @@ -948,7 +967,7 @@ public: * * also test subscribe to the account before it is created */ - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto wscTxHistory = makeWSClient(env.app().config()); Json::Value request; request[jss::account_history_tx_stream] = Json::objectValue; @@ -983,7 +1002,7 @@ public: BEAST_EXPECT(goodSubRPC(jv)); sendPayments(env, env.master, alice, 1, 1); - r = getTxHash(*wscTxHistory, vec, 1); + r = getTxHash(*wscTxHistory, vec, 1, 10ms); BEAST_EXPECT(!r.first); } { @@ -991,16 +1010,18 @@ public: * subscribe genesis account tx history without txns * subscribe to bob's account after it is created */ - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto wscTxHistory = makeWSClient(env.app().config()); Json::Value request; request[jss::account_history_tx_stream] = Json::objectValue; - request[jss::account_history_tx_stream][jss::account] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + request[jss::account_history_tx_stream][jss::account] = + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; auto jv = wscTxHistory->invoke("subscribe", request); if (!BEAST_EXPECT(goodSubRPC(jv))) return; IdxHashVec genesisFullHistoryVec; - if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first)) + BEAST_EXPECT(env.syncClose()); + if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first)) return; /* @@ -1018,10 +1039,12 @@ public: if (!BEAST_EXPECT(goodSubRPC(jv))) return; IdxHashVec bobFullHistoryVec; + BEAST_EXPECT(env.syncClose()); r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1); if (!BEAST_EXPECT(r.first && r.second)) return; - BEAST_EXPECT(std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back())); + BEAST_EXPECT( + std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back())); /* * unsubscribe to prepare next test @@ -1029,7 +1052,8 @@ public: jv = wscTxHistory->invoke("unsubscribe", request); if (!BEAST_EXPECT(goodSubRPC(jv))) return; - request[jss::account_history_tx_stream][jss::account] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + request[jss::account_history_tx_stream][jss::account] = + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; jv = wscTxHistory->invoke("unsubscribe", request); BEAST_EXPECT(goodSubRPC(jv)); @@ -1046,13 +1070,16 @@ public: BEAST_EXPECT(getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second); jv = wscTxHistory->invoke("unsubscribe", request); - request[jss::account_history_tx_stream][jss::account] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + request[jss::account_history_tx_stream][jss::account] = + "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; jv = wscTxHistory->invoke("subscribe", request); genesisFullHistoryVec.clear(); + BEAST_EXPECT(env.syncClose()); BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second); jv = wscTxHistory->invoke("unsubscribe", request); - BEAST_EXPECT(std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back())); + BEAST_EXPECT( + std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back())); } { @@ -1060,13 +1087,13 @@ public: * subscribe account and subscribe account tx history * and compare txns streamed */ - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto wscAccount = makeWSClient(env.app().config()); auto wscTxHistory = makeWSClient(env.app().config()); std::array accounts = {alice, bob}; env.fund(XRP(222222), accounts); - env.close(); + BEAST_EXPECT(env.syncClose()); // subscribe account Json::Value stream = Json::objectValue; @@ -1129,18 +1156,18 @@ public: * alice issues USD to carol * mix USD and XRP payments */ - Env env(*this); + Env env(*this, single_thread_io(envconfig())); auto const USD_a = alice["USD"]; std::array accounts = {alice, carol}; env.fund(XRP(333333), accounts); env.trust(USD_a(20000), carol); - env.close(); + BEAST_EXPECT(env.syncClose()); auto mixedPayments = [&]() -> int { sendPayments(env, alice, carol, 1, 0); env(pay(alice, carol, USD_a(100))); - env.close(); + BEAST_EXPECT(env.syncClose()); return 2; }; @@ -1150,10 +1177,11 @@ public: request[jss::account_history_tx_stream][jss::account] = carol.human(); auto ws = makeWSClient(env.app().config()); auto jv = ws->invoke("subscribe", request); + BEAST_EXPECT(env.syncClose()); { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*ws, tempVec, 100); + getTxHash(*ws, tempVec, 100, 1000ms); } auto count = mixedPayments(); @@ -1167,13 +1195,15 @@ public: /* * long transaction history */ - Env env(*this); + Env env(*this, single_thread_io(envconfig())); std::array accounts = {alice, carol}; env.fund(XRP(444444), accounts); - env.close(); + BEAST_EXPECT(env.syncClose()); // many payments, and close lots of ledgers - auto oneRound = [&](int numPayments) { return sendPayments(env, alice, carol, numPayments, 300); }; + auto oneRound = [&](int numPayments) { + return sendPayments(env, alice, carol, numPayments, 300); + }; // subscribe Json::Value request; @@ -1181,10 +1211,11 @@ public: request[jss::account_history_tx_stream][jss::account] = carol.human(); auto wscLong = makeWSClient(env.app().config()); auto jv = wscLong->invoke("subscribe", request); + BEAST_EXPECT(env.syncClose()); { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*wscLong, tempVec, 100); + getTxHash(*wscLong, tempVec, 100, 1000ms); } // repeat the payments many rounds @@ -1215,9 +1246,10 @@ public: using namespace jtx; using namespace std::chrono_literals; FeatureBitset const all{ - jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | featurePermissionedDEX}; + jtx::testable_amendments() | featurePermissionedDomains | featureCredentials | + featurePermissionedDEX}; - Env env(*this, all); + Env env(*this, single_thread_io(envconfig()), all); PermissionedDEX permDex(env); auto const alice = permDex.alice; auto const bob = permDex.bob; @@ -1236,10 +1268,10 @@ public: if (!BEAST_EXPECT(jv[jss::status] == "success")) return; env(offer(alice, XRP(10), USD(10)), domain(domainID), txflags(tfHybrid)); - env.close(); + BEAST_EXPECT(env.syncClose()); env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID)); - env.close(); + BEAST_EXPECT(env.syncClose()); BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) { if (jv[jss::changes].size() != 1) @@ -1247,7 +1279,8 @@ public: auto const jrOffer = jv[jss::changes][0u]; return (jv[jss::changes][0u][jss::domain]).asString() == strHex(domainID) && - jrOffer[jss::currency_a].asString() == "XRP_drops" && jrOffer[jss::volume_a].asString() == "5000000" && + jrOffer[jss::currency_a].asString() == "XRP_drops" && + jrOffer[jss::volume_a].asString() == "5000000" && jrOffer[jss::currency_b].asString() == "rHUKYAZyUFn8PCZWbPfwHfbVQXTYrYKkHb/USD" && jrOffer[jss::volume_b].asString() == "5"; })); @@ -1278,9 +1311,9 @@ public: Account const bob{"bob"}; Account const broker{"broker"}; - Env env{*this, features}; + Env env{*this, single_thread_io(envconfig()), features}; env.fund(XRP(10000), alice, bob, broker); - env.close(); + BEAST_EXPECT(env.syncClose()); auto wsc = test::makeWSClient(env.app().config()); Json::Value stream; @@ -1344,12 +1377,12 @@ public: // Verify the NFTokenIDs are correct in the NFTokenMint tx meta uint256 const nftId1{token::getNextID(env, alice, 0u, tfTransferable)}; env(token::mint(alice, 0u), txflags(tfTransferable)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId1); uint256 const nftId2{token::getNextID(env, alice, 0u, tfTransferable)}; env(token::mint(alice, 0u), txflags(tfTransferable)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId2); // Alice creates one sell offer for each NFT @@ -1357,32 +1390,32 @@ public: // meta uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::createOffer(alice, nftId1, drops(1)), txflags(tfSellNFToken)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(aliceOfferIndex1); uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::createOffer(alice, nftId2, drops(1)), txflags(tfSellNFToken)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(aliceOfferIndex2); // Alice cancels two offers she created // Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx // meta env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2})); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenIDsInCancelOffer({nftId1, nftId2}); // Bobs creates a buy offer for nftId1 // Verify the offer id is correct in the NFTokenCreateOffer tx meta auto const bobBuyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key; env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(bobBuyOfferIndex); // Alice accepts bob's buy offer // Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta env(token::acceptBuyOffer(alice, bobBuyOfferIndex)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId1); } @@ -1391,24 +1424,26 @@ public: // Alice mints a NFT uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)}; env(token::mint(alice, 0u), txflags(tfTransferable)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId); // Alice creates sell offer and set broker as destination uint256 const offerAliceToBroker = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, drops(1)), token::destination(broker), txflags(tfSellNFToken)); - env.close(); + env(token::createOffer(alice, nftId, drops(1)), + token::destination(broker), + txflags(tfSellNFToken)); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(offerAliceToBroker); // Bob creates buy offer uint256 const offerBobToBroker = keylet::nftoffer(bob, env.seq(bob)).key; env(token::createOffer(bob, nftId, drops(1)), token::owner(alice)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(offerBobToBroker); // Check NFTokenID meta for NFTokenAcceptOffer in brokered mode env(token::brokerOffers(broker, offerBobToBroker, offerAliceToBroker)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId); } @@ -1418,24 +1453,24 @@ public: // Alice mints a NFT uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)}; env(token::mint(alice, 0u), txflags(tfTransferable)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenID(nftId); // Alice creates 2 sell offers for the same NFT uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(aliceOfferIndex1); uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken)); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(aliceOfferIndex2); // Make sure the metadata only has 1 nft id, since both offers are // for the same nft env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2})); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenIDsInCancelOffer({nftId}); } @@ -1443,7 +1478,7 @@ public: { uint256 const aliceMintWithOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::mint(alice), token::amount(XRP(0))); - env.close(); + BEAST_EXPECT(env.syncClose()); verifyNFTokenOfferID(aliceMintWithOfferIndex1); } } diff --git a/src/test/rpc/TransactionEntry_test.cpp b/src/test/rpc/TransactionEntry_test.cpp index 6302de4efa..14e75d04da 100644 --- a/src/test/rpc/TransactionEntry_test.cpp +++ b/src/test/rpc/TransactionEntry_test.cpp @@ -55,7 +55,8 @@ class TransactionEntry_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::status] == "error"); } - std::string const txHash{"E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"}; + std::string const txHash{ + "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"}; // Command line format { @@ -194,17 +195,19 @@ class TransactionEntry_test : public beast::unit_test::suite params[jss::ledger_hash] = resIndex[jss::ledger_hash]; params[jss::tx_hash] = txhash; params[jss::api_version] = apiVersion; - Json::Value const resHash = env.client().invoke("transaction_entry", params)[jss::result]; + Json::Value const resHash = + env.client().invoke("transaction_entry", params)[jss::result]; BEAST_EXPECT(resHash == resIndex); } // Use the command line form with the index. - Json::Value const clIndex{env.rpc(apiVersion, "transaction_entry", txhash, std::to_string(index))}; + Json::Value const clIndex{ + env.rpc(apiVersion, "transaction_entry", txhash, std::to_string(index))}; BEAST_EXPECT(clIndex["result"] == resIndex); // Use the command line form with the ledger_hash. - Json::Value const clHash{ - env.rpc(apiVersion, "transaction_entry", txhash, resIndex[jss::ledger_hash].asString())}; + Json::Value const clHash{env.rpc( + apiVersion, "transaction_entry", txhash, resIndex[jss::ledger_hash].asString())}; BEAST_EXPECT(clHash["result"] == resIndex); }; @@ -213,11 +216,13 @@ class TransactionEntry_test : public beast::unit_test::suite env.fund(XRP(10000), A1); auto fund_1_tx = to_string(env.tx()->getTransactionID()); - BEAST_EXPECT(fund_1_tx == "F4E9DF90D829A9E8B423FF68C34413E240D8D8BB0EFD080DF08114ED398E2506"); + BEAST_EXPECT( + fund_1_tx == "F4E9DF90D829A9E8B423FF68C34413E240D8D8BB0EFD080DF08114ED398E2506"); env.fund(XRP(10000), A2); auto fund_2_tx = to_string(env.tx()->getTransactionID()); - BEAST_EXPECT(fund_2_tx == "6853CD8226A05068C951CB1F54889FF4E40C5B440DC1C5BA38F114C4E0B1E705"); + BEAST_EXPECT( + fund_2_tx == "6853CD8226A05068C951CB1F54889FF4E40C5B440DC1C5BA38F114C4E0B1E705"); env.close(); @@ -257,7 +262,8 @@ class TransactionEntry_test : public beast::unit_test::suite // refunds fees with a payment after TrustSet..so just ignore the type // in the check below auto trust_tx = to_string(env.tx()->getTransactionID()); - BEAST_EXPECT(trust_tx == "C992D97D88FF444A1AB0C06B27557EC54B7F7DA28254778E60238BEA88E0C101"); + BEAST_EXPECT( + trust_tx == "C992D97D88FF444A1AB0C06B27557EC54B7F7DA28254778E60238BEA88E0C101"); env(pay(A2, A1, A2["USD"](5))); auto pay_tx = to_string(env.tx()->getTransactionID()); @@ -305,7 +311,8 @@ class TransactionEntry_test : public beast::unit_test::suite env(offer(A2, XRP(100), A2["USD"](1))); auto offer_tx = to_string(env.tx()->getTransactionID()); - BEAST_EXPECT(offer_tx == "5FCC1A27A7664F82A0CC4BE5766FBBB7C560D52B93AA7B550CD33B27AEC7EFFB"); + BEAST_EXPECT( + offer_tx == "5FCC1A27A7664F82A0CC4BE5766FBBB7C560D52B93AA7B550CD33B27AEC7EFFB"); env.close(); check_tx( diff --git a/src/test/rpc/Transaction_test.cpp b/src/test/rpc/Transaction_test.cpp index 3b289d73ca..2709900bf1 100644 --- a/src/test/rpc/Transaction_test.cpp +++ b/src/test/rpc/Transaction_test.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -65,7 +66,11 @@ class Transaction_test : public beast::unit_test::suite auto const& tx = txns[i]; auto const& meta = metas[i]; auto const result = env.rpc( - COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(startLegSeq), to_string(endLegSeq)); + COMMAND, + to_string(tx->getTransactionID()), + BINARY, + to_string(startLegSeq), + to_string(endLegSeq)); BEAST_EXPECT(result[jss::result][jss::status] == jss::success); BEAST_EXPECT(result[jss::result][jss::tx] == strHex(tx->getSerializer().getData())); @@ -83,12 +88,17 @@ class Transaction_test : public beast::unit_test::suite to_string(endLegSeq + deltaEndSeq)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); if (deltaEndSeq) + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } else + { BEAST_EXPECT(result[jss::result][jss::searched_all].asBool()); + } } // Find transactions outside of provided range. @@ -108,8 +118,7 @@ class Transaction_test : public beast::unit_test::suite auto const deletedLedger = (startLegSeq + endLegSeq) / 2; { // Remove one of the ledgers from the database directly - dynamic_cast(&env.app().getRelationalDatabase()) - ->deleteTransactionByLedgerSeq(deletedLedger); + env.app().getRelationalDatabase().deleteTransactionByLedgerSeq(deletedLedger); } for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq) @@ -122,18 +131,23 @@ class Transaction_test : public beast::unit_test::suite to_string(endLegSeq + deltaEndSeq)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); } // Provide range without providing the `binary` // field. (Tests parameter parsing) { - auto const result = - env.rpc(COMMAND, to_string(tx->getTransactionID()), to_string(startLegSeq), to_string(endLegSeq)); + auto const result = env.rpc( + COMMAND, + to_string(tx->getTransactionID()), + to_string(startLegSeq), + to_string(endLegSeq)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); } @@ -142,10 +156,14 @@ class Transaction_test : public beast::unit_test::suite // field. (Tests parameter parsing) { auto const result = env.rpc( - COMMAND, to_string(tx->getTransactionID()), to_string(startLegSeq), to_string(deletedLedger - 1)); + COMMAND, + to_string(tx->getTransactionID()), + to_string(startLegSeq), + to_string(deletedLedger - 1)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); BEAST_EXPECT(result[jss::result][jss::searched_all].asBool()); } @@ -154,7 +172,10 @@ class Transaction_test : public beast::unit_test::suite // field. (Tests parameter parsing) { auto const result = env.rpc( - COMMAND, to_string(txns[0]->getTransactionID()), to_string(startLegSeq), to_string(deletedLedger - 1)); + COMMAND, + to_string(txns[0]->getTransactionID()), + to_string(startLegSeq), + to_string(deletedLedger - 1)); BEAST_EXPECT(result[jss::result][jss::status] == jss::success); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); @@ -169,7 +190,9 @@ class Transaction_test : public beast::unit_test::suite to_string(deletedLedger - 1), to_string(startLegSeq)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -177,28 +200,39 @@ class Transaction_test : public beast::unit_test::suite // Provide an invalid range: (min < 0) { auto const result = env.rpc( - COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(-1), to_string(deletedLedger - 1)); + COMMAND, + to_string(tx->getTransactionID()), + BINARY, + to_string(-1), + to_string(deletedLedger - 1)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } // Provide an invalid range: (min < 0, max < 0) { - auto const result = - env.rpc(COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(-20), to_string(-10)); + auto const result = env.rpc( + COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(-20), to_string(-10)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } // Provide an invalid range: (only one value) { - auto const result = env.rpc(COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(20)); + auto const result = + env.rpc(COMMAND, to_string(tx->getTransactionID()), BINARY, to_string(20)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -227,7 +261,8 @@ class Transaction_test : public beast::unit_test::suite to_string(startLegSeq + 1001)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == EXCESSIVE); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == EXCESSIVE); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -248,7 +283,7 @@ class Transaction_test : public beast::unit_test::suite char const* EXCESSIVE = RPC::get_error_info(rpcEXCESSIVE_LGR_RANGE).token; Env env{*this, makeNetworkConfig(11111)}; - uint32_t netID = env.app().config().NETWORK_ID; + uint32_t netID = env.app().getNetworkIDService().getNetworkID(); auto const alice = Account("alice"); env.fund(XRP(1000), alice); @@ -274,6 +309,7 @@ class Transaction_test : public beast::unit_test::suite uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(startLegSeq + i, txnIdx, netID), BINARY, to_string(startLegSeq), @@ -285,19 +321,25 @@ class Transaction_test : public beast::unit_test::suite } auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ctid = *RPC::encodeCTID(endLegSeq, tx->getSeqValue(), netID); for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq) { - auto const result = - env.rpc(COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq)); + auto const result = env.rpc( + COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); if (deltaEndSeq) + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } else + { BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } } // Find transactions outside of provided range. @@ -308,6 +350,7 @@ class Transaction_test : public beast::unit_test::suite uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(startLegSeq + i, txnIdx, netID), BINARY, to_string(endLegSeq + 1), @@ -320,27 +363,29 @@ class Transaction_test : public beast::unit_test::suite auto const deletedLedger = (startLegSeq + endLegSeq) / 2; { // Remove one of the ledgers from the database directly - dynamic_cast(&env.app().getRelationalDatabase()) - ->deleteTransactionByLedgerSeq(deletedLedger); + env.app().getRelationalDatabase().deleteTransactionByLedgerSeq(deletedLedger); } for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq) + { + auto const result = env.rpc( + COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq)); + + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); + BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); + } + + // Provide range without providing the `binary` + // field. (Tests parameter parsing) { auto const result = - env.rpc(COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq)); + env.rpc(COMMAND, ctid, to_string(startLegSeq), to_string(endLegSeq)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); - BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); - } - - // Provide range without providing the `binary` - // field. (Tests parameter parsing) - { - auto const result = env.rpc(COMMAND, ctid, to_string(startLegSeq), to_string(endLegSeq)); - - BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); } @@ -348,10 +393,12 @@ class Transaction_test : public beast::unit_test::suite // Provide range without providing the `binary` // field. (Tests parameter parsing) { - auto const result = env.rpc(COMMAND, ctid, to_string(startLegSeq), to_string(deletedLedger - 1)); + auto const result = + env.rpc(COMMAND, ctid, to_string(startLegSeq), to_string(deletedLedger - 1)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == NOT_FOUND); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == NOT_FOUND); BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool()); } @@ -363,6 +410,7 @@ class Transaction_test : public beast::unit_test::suite uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex); auto const result = env.rpc( COMMAND, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *RPC::encodeCTID(endLegSeq, txnIdx, netID), to_string(startLegSeq), to_string(deletedLedger - 1)); @@ -373,18 +421,24 @@ class Transaction_test : public beast::unit_test::suite // Provide an invalid range: (min > max) { - auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(deletedLedger - 1), to_string(startLegSeq)); + auto const result = env.rpc( + COMMAND, ctid, BINARY, to_string(deletedLedger - 1), to_string(startLegSeq)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } // Provide an invalid range: (min < 0) { - auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(-1), to_string(deletedLedger - 1)); + auto const result = + env.rpc(COMMAND, ctid, BINARY, to_string(-1), to_string(deletedLedger - 1)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -393,7 +447,9 @@ class Transaction_test : public beast::unit_test::suite { auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(-20), to_string(-10)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -402,7 +458,9 @@ class Transaction_test : public beast::unit_test::suite { auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(20)); - BEAST_EXPECT(result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == INVALID); + BEAST_EXPECT( + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == INVALID); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -423,10 +481,12 @@ class Transaction_test : public beast::unit_test::suite // Provide an invalid range: (max - min > 1000) { - auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(startLegSeq + 1001)); + auto const result = env.rpc( + COMMAND, ctid, BINARY, to_string(startLegSeq), to_string(startLegSeq + 1001)); BEAST_EXPECT( - result[jss::result][jss::status] == jss::error && result[jss::result][jss::error] == EXCESSIVE); + result[jss::result][jss::status] == jss::error && + result[jss::result][jss::error] == EXCESSIVE); BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all)); } @@ -462,12 +522,14 @@ class Transaction_test : public beast::unit_test::suite BEAST_EXPECT(!RPC::encodeCTID(0x0FFF'FFFFUL, 0xFFFFU, 0x1'0000U)); // Test case 5: Valid input values - auto const expected51 = std::optional>(std::make_tuple(0, 0, 0)); + auto const expected51 = + std::optional>(std::make_tuple(0, 0, 0)); BEAST_EXPECT(RPC::decodeCTID("C000000000000000") == expected51); - auto const expected52 = std::optional>(std::make_tuple(1U, 2U, 3U)); + auto const expected52 = + std::optional>(std::make_tuple(1U, 2U, 3U)); BEAST_EXPECT(RPC::decodeCTID("C000000100020003") == expected52); - auto const expected53 = - std::optional>(std::make_tuple(13249191UL, 12911U, 49221U)); + auto const expected53 = std::optional>( + std::make_tuple(13249191UL, 12911U, 49221U)); BEAST_EXPECT(RPC::decodeCTID("C0CA2AA7326FC045") == expected53); // Test case 6: ctid not a string or big int @@ -488,7 +550,8 @@ class Transaction_test : public beast::unit_test::suite // Test case 11: Valid input values BEAST_EXPECT( (RPC::decodeCTID(0xCFFF'FFFF'FFFF'FFFFULL) == - std::optional>(std::make_tuple(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU)))); + std::optional>( + std::make_tuple(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU)))); BEAST_EXPECT( (RPC::decodeCTID(0xC000'0000'0000'0000ULL) == std::optional>(std::make_tuple(0, 0, 0)))); @@ -497,7 +560,8 @@ class Transaction_test : public beast::unit_test::suite std::optional>(std::make_tuple(1U, 2U, 3U)))); BEAST_EXPECT( (RPC::decodeCTID(0xC0CA'2AA7'326F'C045ULL) == - std::optional>(std::make_tuple(1324'9191UL, 12911U, 49221U)))); + std::optional>( + std::make_tuple(1324'9191UL, 12911U, 49221U)))); // Test case 12: ctid not exactly 16 nibbles BEAST_EXPECT(!RPC::decodeCTID(0xC003'FFFF'FFFF'FFF)); @@ -522,7 +586,7 @@ class Transaction_test : public beast::unit_test::suite for (uint32_t netID : {11111, 65535, 65536}) { Env env{*this, makeNetworkConfig(netID)}; - BEAST_EXPECT(netID == env.app().config().NETWORK_ID); + BEAST_EXPECT(netID == env.app().getNetworkIDService().getNetworkID()); auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -542,7 +606,7 @@ class Transaction_test : public beast::unit_test::suite Json::Value jsonTx; jsonTx[jss::binary] = false; - jsonTx[jss::ctid] = *ctid; + jsonTx[jss::ctid] = *ctid; // NOLINT(bugprone-unchecked-optional-access) jsonTx[jss::id] = 1; auto const jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result]; BEAST_EXPECT(jrr[jss::ctid] == ctid); @@ -552,7 +616,7 @@ class Transaction_test : public beast::unit_test::suite // test querying with mixed case ctid { Env env{*this, makeNetworkConfig(11111)}; - std::uint32_t const netID = env.app().config().NETWORK_ID; + std::uint32_t const netID = env.app().getNetworkIDService().getNetworkID(); Account const alice = Account("alice"); Account const bob = Account("bob"); @@ -562,6 +626,7 @@ class Transaction_test : public beast::unit_test::suite env(pay(alice, bob, XRP(10))); env.close(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) std::string const ctid = *RPC::encodeCTID(startLegSeq, 0, netID); auto isUpper = [](char c) { return std::isupper(c) != 0; }; @@ -593,7 +658,7 @@ class Transaction_test : public beast::unit_test::suite for (uint32_t netID : {2, 1024, 65535, 65536}) { Env env{*this, makeNetworkConfig(netID)}; - BEAST_EXPECT(netID == env.app().config().NETWORK_ID); + BEAST_EXPECT(netID == env.app().getNetworkIDService().getNetworkID()); auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -618,14 +683,15 @@ class Transaction_test : public beast::unit_test::suite if (jrr.isMember(jss::ctid)) { auto const ctid = RPC::encodeCTID(ledgerSeq, 0, netID); - BEAST_EXPECT(jrr[jss::ctid] == *ctid); + BEAST_EXPECT( + jrr[jss::ctid] == *ctid); // NOLINT(bugprone-unchecked-optional-access) } } // test the wrong network ID was submitted { Env env{*this, makeNetworkConfig(21337)}; - uint32_t netID = env.app().config().NETWORK_ID; + uint32_t netID = env.app().getNetworkIDService().getNetworkID(); auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -635,6 +701,7 @@ class Transaction_test : public beast::unit_test::suite env(pay(alice, bob, XRP(10))); env.close(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID + 1); Json::Value jsonTx; jsonTx[jss::binary] = false; @@ -678,7 +745,8 @@ class Transaction_test : public beast::unit_test::suite std::shared_ptr txn = env.tx(); env.close(); - std::shared_ptr meta = env.closed()->txRead(env.tx()->getTransactionID()).second; + std::shared_ptr meta = + env.closed()->txRead(env.tx()->getTransactionID()).second; Json::Value expected = txn->getJson(JsonOptions::none); expected[jss::DeliverMax] = expected[jss::Amount]; @@ -712,7 +780,8 @@ class Transaction_test : public beast::unit_test::suite for (auto memberIt = expected.begin(); memberIt != expected.end(); memberIt++) { std::string const name = memberIt.memberName(); - auto const& result_transaction = (apiVersion > 1 ? result[jss::result][jss::tx_json] : result[jss::result]); + auto const& result_transaction = + (apiVersion > 1 ? result[jss::result][jss::tx_json] : result[jss::result]); if (BEAST_EXPECT(result_transaction.isMember(name))) { auto const received = result_transaction[name]; @@ -745,7 +814,8 @@ class Transaction_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, gw); std::shared_ptr const txn = env.tx(); BEAST_EXPECT( - to_string(txn->getTransactionID()) == "3F8BDE5A5F82C4F4708E5E9255B713E303E6E1A371FD5C7A704AFD1387C23981"); + to_string(txn->getTransactionID()) == + "3F8BDE5A5F82C4F4708E5E9255B713E303E6E1A371FD5C7A704AFD1387C23981"); env.close(); std::shared_ptr meta = env.closed()->txRead(txn->getTransactionID()).second; diff --git a/src/test/rpc/ValidatorInfo_test.cpp b/src/test/rpc/ValidatorInfo_test.cpp index 41fa95940b..d4769f40fb 100644 --- a/src/test/rpc/ValidatorInfo_test.cpp +++ b/src/test/rpc/ValidatorInfo_test.cpp @@ -46,27 +46,15 @@ public: using namespace jtx; std::vector const tokenBlob = { - " " - "eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT" - "\n", - " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFua" - "WZl " - " \n", - "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZD" - "eE" - "\n", - "\t " - "hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG" - "\t " - "\t\n", - "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2" - "\n", - "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1" - "\n", - "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj" - "\n", - "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==" - "\n"}; + " eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n", + " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n", + "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n", + "\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n", + "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n", + "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n", + "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n", + "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n", + }; std::string const master_key = "nHBt9fsb4849WmZiCds4r5TXyBeQjqnH5kzPtqgMAQMgi39YZRPa"; std::string const ephemeral_key = "n9KsDYGKhABVc4wK5u3MnVhgPinyJimyKGpr9VJYuBaY8EnJXR2x"; diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index a46d198b5a..43ba188f6a 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -130,10 +130,11 @@ public: BEAST_EXPECT(jrrnUnlSize == 2); for (std::uint32_t x = 0; x < jrrnUnlSize; ++x) { - auto parsedKey = parseBase58(TokenType::NodePublic, jrrnUnl[x].asString()); + auto parsedKey = + parseBase58(TokenType::NodePublic, jrrnUnl[x].asString()); BEAST_EXPECT(parsedKey); if (parsedKey) - BEAST_EXPECT(disabledKeys.find(*parsedKey) != disabledKeys.end()); + BEAST_EXPECT(disabledKeys.contains(*parsedKey)); } disabledKeys.clear(); @@ -148,7 +149,9 @@ public: { using namespace test::jtx; - auto toStr = [](PublicKey const& publicKey) { return toBase58(TokenType::NodePublic, publicKey); }; + auto toStr = [](PublicKey const& publicKey) { + return toBase58(TokenType::NodePublic, publicKey); + }; // Validator keys that will be in the published list std::vector validators = { @@ -164,7 +167,13 @@ public: NetClock::time_point const validFrom2{validUntil - 60s}; NetClock::time_point const validUntil2{validFrom2 + 3600s}; auto server = make_TrustedPublisherServer( - worker.get_io_context(), validators, validUntil, {{validFrom2, validUntil2}}, false, 1, false); + worker.get_io_context(), + validators, + validUntil, + {{validFrom2, validUntil2}}, + false, + 1, + false); //---------------------------------------------------------------------- // Publisher list site unavailable v1 @@ -177,7 +186,8 @@ public: *this, envconfig([&](std::unique_ptr cfg) { cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS).append(strHex(server->publisherPublic())); + cfg->section(SECTION_VALIDATOR_LIST_KEYS) + .append(strHex(server->publisherPublic())); return cfg; }), }; @@ -195,7 +205,9 @@ public: } { auto const jrr = env.rpc("validators")[jss::result]; - BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == std::numeric_limits::max()); + BEAST_EXPECT( + jrr[jss::validation_quorum].asUInt() == + std::numeric_limits::max()); BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0); BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == 0); BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "unknown"); @@ -234,7 +246,8 @@ public: *this, envconfig([&](std::unique_ptr cfg) { cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS).append(strHex(server->publisherPublic())); + cfg->section(SECTION_VALIDATOR_LIST_KEYS) + .append(strHex(server->publisherPublic())); return cfg; }), }; @@ -252,7 +265,9 @@ public: } { auto const jrr = env.rpc("validators")[jss::result]; - BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == std::numeric_limits::max()); + BEAST_EXPECT( + jrr[jss::validation_quorum].asUInt() == + std::numeric_limits::max()); BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0); BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == 0); BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "unknown"); @@ -294,7 +309,8 @@ public: *this, envconfig([&](std::unique_ptr cfg) { cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS).append(strHex(server->publisherPublic())); + cfg->section(SECTION_VALIDATOR_LIST_KEYS) + .append(strHex(server->publisherPublic())); return cfg; }), }; @@ -306,16 +322,22 @@ public: startKeys.insert(calcNodeID(val.masterPublic)); env.app().validators().updateTrusted( - startKeys, env.timeKeeper().now(), env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); + startKeys, + env.timeKeeper().now(), + env.app().getOPs(), + env.app().overlay(), + env.app().getHashRouter()); { auto const jrr = env.rpc("server_info")[jss::result]; - BEAST_EXPECT(jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil)); + BEAST_EXPECT( + jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil)); } { auto const jrr = env.rpc("server_state")[jss::result]; BEAST_EXPECT( - jrr[jss::state][jss::validator_list_expires].asUInt() == validUntil.time_since_epoch().count()); + jrr[jss::state][jss::validator_list_expires].asUInt() == + validUntil.time_since_epoch().count()); } { auto const jrr = env.rpc("validators")[jss::result]; @@ -380,7 +402,8 @@ public: *this, envconfig([&](std::unique_ptr cfg) { cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS).append(strHex(server->publisherPublic())); + cfg->section(SECTION_VALIDATOR_LIST_KEYS) + .append(strHex(server->publisherPublic())); return cfg; }), }; @@ -392,16 +415,22 @@ public: startKeys.insert(calcNodeID(val.masterPublic)); env.app().validators().updateTrusted( - startKeys, env.timeKeeper().now(), env.app().getOPs(), env.app().overlay(), env.app().getHashRouter()); + startKeys, + env.timeKeeper().now(), + env.app().getOPs(), + env.app().overlay(), + env.app().getHashRouter()); { auto const jrr = env.rpc("server_info")[jss::result]; - BEAST_EXPECT(jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil2)); + BEAST_EXPECT( + jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil2)); } { auto const jrr = env.rpc("server_state")[jss::result]; BEAST_EXPECT( - jrr[jss::state][jss::validator_list_expires].asUInt() == validUntil2.time_since_epoch().count()); + jrr[jss::state][jss::validator_list_expires].asUInt() == + validUntil2.time_since_epoch().count()); } { auto const jrr = env.rpc("validators")[jss::result]; @@ -433,7 +462,8 @@ public: BEAST_EXPECT(jp[jss::pubkey_publisher] == strHex(server->publisherPublic())); BEAST_EXPECT(jp[jss::expiration] == to_string(validUntil)); BEAST_EXPECT(jp[jss::version] == 2); - if (BEAST_EXPECT(jp.isMember(jss::remaining)) && BEAST_EXPECT(jp[jss::remaining].isArray()) && + if (BEAST_EXPECT(jp.isMember(jss::remaining)) && + BEAST_EXPECT(jp[jss::remaining].isArray()) && BEAST_EXPECT(jp[jss::remaining].size() == 1)) { auto const& r = jp[jss::remaining][0u]; @@ -483,7 +513,8 @@ public: Env env{*this}; auto result = env.rpc("validation_create"); BEAST_EXPECT(result.isMember(jss::result) && result[jss::result][jss::status] == "success"); - result = env.rpc("validation_create", "BAWL MAN JADE MOON DOVE GEM SON NOW HAD ADEN GLOW TIRE"); + result = + env.rpc("validation_create", "BAWL MAN JADE MOON DOVE GEM SON NOW HAD ADEN GLOW TIRE"); BEAST_EXPECT(result.isMember(jss::result) && result[jss::result][jss::status] == "success"); } diff --git a/src/test/rpc/Version_test.cpp b/src/test/rpc/Version_test.cpp index 1dec8627ed..7fa6720f83 100644 --- a/src/test/rpc/Version_test.cpp +++ b/src/test/rpc/Version_test.cpp @@ -24,7 +24,8 @@ class Version_test : public beast::unit_test::suite auto jrr = env.rpc( "json", "version", - "{\"api_version\": " + std::to_string(RPC::apiMaximumSupportedVersion) + "}")[jss::result]; + "{\"api_version\": " + std::to_string(RPC::apiMaximumSupportedVersion) + + "}")[jss::result]; BEAST_EXPECT(isCorrectReply(jrr)); jrr = env.rpc("version")[jss::result]; @@ -41,15 +42,19 @@ class Version_test : public beast::unit_test::suite auto badVersion = [](Json::Value const& re) -> bool { if (re.isMember("error_what")) + { if (re["error_what"].isString()) { return re["error_what"].asString().find(jss::invalid_API_version.c_str()) == 0; } + } return false; }; auto re = env.rpc( - "json", "version", "{\"api_version\": " + std::to_string(RPC::apiMinimumSupportedVersion - 1) + "}"); + "json", + "version", + "{\"api_version\": " + std::to_string(RPC::apiMinimumSupportedVersion - 1) + "}"); BEAST_EXPECT(badVersion(re)); BEAST_EXPECT(env.app().config().BETA_RPC_API); @@ -57,7 +62,10 @@ class Version_test : public beast::unit_test::suite "json", "version", "{\"api_version\": " + - std::to_string(std::max(RPC::apiMaximumSupportedVersion.value, RPC::apiBetaVersion.value) + 1) + "}"); + std::to_string( + std::max(RPC::apiMaximumSupportedVersion.value, RPC::apiBetaVersion.value) + + 1) + + "}"); BEAST_EXPECT(badVersion(re)); re = env.rpc("json", "version", "{\"api_version\": \"a\"}"); @@ -69,7 +77,8 @@ class Version_test : public beast::unit_test::suite { testcase("test getAPIVersionNumber function"); - unsigned int versionIfUnspecified = RPC::apiVersionIfUnspecified < RPC::apiMinimumSupportedVersion + unsigned int versionIfUnspecified = + RPC::apiVersionIfUnspecified < RPC::apiMinimumSupportedVersion ? RPC::apiInvalidVersion : RPC::apiVersionIfUnspecified; @@ -150,14 +159,17 @@ class Version_test : public beast::unit_test::suite "\"id\": 5, " "\"method\": \"version\", " "\"params\": {}}"; - auto const with_wrong_api_verion = std::string("{ ") + + auto const with_wrong_api_verion = + std::string("{ ") + "\"jsonrpc\": \"2.0\", " "\"ripplerpc\": \"2.0\", " "\"id\": 6, " "\"method\": \"version\", " "\"params\": { " "\"api_version\": " + - std::to_string(std::max(RPC::apiMaximumSupportedVersion.value, RPC::apiBetaVersion.value) + 1) + "}}"; + std::to_string( + std::max(RPC::apiMaximumSupportedVersion.value, RPC::apiBetaVersion.value) + 1) + + "}}"; auto re = env.rpc("json2", '[' + without_api_verion + ", " + with_wrong_api_verion + ']'); if (!BEAST_EXPECT(re.isArray())) @@ -203,12 +215,15 @@ class Version_test : public beast::unit_test::suite if (!BEAST_EXPECT(env.app().config().BETA_RPC_API == true)) return; - auto jrr = - env.rpc("json", "version", "{\"api_version\": " + std::to_string(RPC::apiBetaVersion) + "}")[jss::result]; + auto jrr = env.rpc( + "json", + "version", + "{\"api_version\": " + std::to_string(RPC::apiBetaVersion) + "}")[jss::result]; if (!BEAST_EXPECT(jrr.isMember(jss::version))) return; - if (!BEAST_EXPECT(jrr[jss::version].isMember(jss::first)) && jrr[jss::version].isMember(jss::last)) + if (!BEAST_EXPECT(jrr[jss::version].isMember(jss::first)) && + jrr[jss::version].isMember(jss::last)) return; BEAST_EXPECT(jrr[jss::version][jss::first] == RPC::apiMinimumSupportedVersion.value); BEAST_EXPECT(jrr[jss::version][jss::last] == RPC::apiBetaVersion.value); diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index ccfdf2fd2b..178b85b3e4 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -4,13 +4,13 @@ #include #include -#include -#include #include #include #include #include +#include +#include #include #include @@ -87,7 +87,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en std::random_device rd; std::mt19937 e{rd()}; std::uniform_int_distribution<> d(0, 255); - std::array key; + std::array key{}; for (auto& v : key) v = d(e); req.insert("Sec-WebSocket-Key", base64_encode(key.data(), key.size())); @@ -98,7 +98,11 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en } auto - makeHTTPRequest(std::string const& host, uint16_t port, std::string const& body, myFields const& fields) + makeHTTPRequest( + std::string const& host, + uint16_t port, + std::string const& body, + myFields const& fields) { using namespace boost::asio; using namespace boost::beast::http; @@ -128,7 +132,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en void doRequest( boost::asio::yield_context& yield, - boost::beast::http::request&& req, + boost::beast::http::request const& req, std::string const& host, uint16_t port, bool secure, @@ -191,6 +195,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en { auto const port = env.app().config()["port_ws"].get("port"); auto ip = env.app().config()["port_ws"].get("ip"); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec); return; } @@ -207,6 +212,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en { auto const port = env.app().config()["port_rpc"].get("port"); auto const ip = env.app().config()["port_rpc"].get("ip"); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeHTTPRequest(*ip, *port, body, fields), *ip, *port, secure, resp, ec); return; } @@ -259,8 +265,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en void testAdminRequest(std::string const& proto, bool admin, bool credentials) { - testcase << "Admin request over " << proto << ", config " << (admin ? "enabled" : "disabled") - << ", credentials " << (credentials ? "" : "not ") << "set"; + testcase << "Admin request over " << proto << ", config " + << (admin ? "enabled" : "disabled") << ", credentials " + << (credentials ? "" : "not ") << "set"; using namespace jtx; Env env{*this, makeConfig(proto, admin, credentials)}; @@ -272,36 +279,47 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en if (admin && credentials) { - auto const user = env.app().config()[proto_ws ? "port_ws" : "port_rpc"].get("admin_user"); + auto const user = + env.app().config()[proto_ws ? "port_ws" : "port_rpc"].get( + "admin_user"); auto const password = - env.app().config()[proto_ws ? "port_ws" : "port_rpc"].get("admin_password"); + env.app().config()[proto_ws ? "port_ws" : "port_rpc"].get( + "admin_password"); // 1 - FAILS with wrong pass + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password + "_")[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( - jrr["error_message"] == proto_ws ? "Bad credentials." : "You don't have permission for this command."); + jrr["error_message"] == proto_ws ? "Bad credentials." + : "You don't have permission for this command."); // 2 - FAILS with password in an object + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password, true)[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( - jrr["error_message"] == proto_ws ? "Bad credentials." : "You don't have permission for this command."); + jrr["error_message"] == proto_ws ? "Bad credentials." + : "You don't have permission for this command."); // 3 - FAILS with wrong user + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user + "_", *password)[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( - jrr["error_message"] == proto_ws ? "Bad credentials." : "You don't have permission for this command."); + jrr["error_message"] == proto_ws ? "Bad credentials." + : "You don't have permission for this command."); // 4 - FAILS no credentials jrr = makeAdminRequest(env, proto, "", "")[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( - jrr["error_message"] == proto_ws ? "Bad credentials." : "You don't have permission for this command."); + jrr["error_message"] == proto_ws ? "Bad credentials." + : "You don't have permission for this command."); // 5 - SUCCEEDS with proper credentials + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) jrr = makeAdminRequest(env, proto, *user, *password)[jss::result]; BEAST_EXPECT(jrr["status"] == "success"); } @@ -321,7 +339,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en jrr = makeAdminRequest(env, proto, "", "")[jss::result]; BEAST_EXPECT(jrr["error"] == proto_ws ? "forbidden" : "noPermission"); BEAST_EXPECT( - jrr["error_message"] == proto_ws ? "Bad credentials." : "You don't have permission for this command."); + jrr["error_message"] == proto_ws ? "Bad credentials." + : "You don't have permission for this command."); } } @@ -405,7 +424,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en boost::system::error_code ec; response resp; - auto req = makeWSUpgrade(*ip, *port); + auto req = makeWSUpgrade(*ip, *port); // NOLINT(bugprone-unchecked-optional-access) // truncate the request message to near the value of the version header auto req_string = boost::lexical_cast(req); @@ -415,7 +434,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en ip::tcp::resolver r{ios}; boost::beast::multi_buffer sb; - auto it = r.async_resolve(*ip, std::to_string(*port), yield[ec]); + auto it = r.async_resolve( + *ip, std::to_string(*port), yield[ec]); // NOLINT(bugprone-unchecked-optional-access) if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -441,7 +461,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // The essence of this test is to have a client and server configured // out-of-phase with respect to ssl (secure client and insecure server // or vice-versa) - testcase << "Connect fails: " << client_protocol << " client to " << server_protocol << " server"; + testcase << "Connect fails: " << client_protocol << " client to " << server_protocol + << " server"; using namespace jtx; Env env{*this, makeConfig(server_protocol)}; @@ -454,7 +475,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en } else { - doWSRequest(env, yield, client_protocol == "wss" || client_protocol == "wss2", resp, ec); + doWSRequest( + env, yield, client_protocol == "wss" || client_protocol == "wss2", resp, ec); BEAST_EXPECT(ec); } } @@ -494,8 +516,11 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth); BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const user = env.app().config().section("port_rpc").get("user").value(); - auto const pass = env.app().config().section("port_rpc").get("password").value(); + auto const pass = + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + env.app().config().section("port_rpc").get("password").value(); // try with the correct user/pass, but not encoded auth.set("Authorization", "Basic " + user + ":" + pass); @@ -522,7 +547,10 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en return cfg; })}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const port = env.app().config()["port_rpc"].get("port").value(); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ip = env.app().config()["port_rpc"].get("ip").value(); boost::system::error_code ec; @@ -547,7 +575,8 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en int testTo = (limit == 0) ? 50 : limit + 1; while (connectionCount < testTo) { - clients.emplace_back(std::make_pair(ip::tcp::socket{ios}, boost::beast::multi_buffer{})); + clients.emplace_back( + std::make_pair(ip::tcp::socket{ios}, boost::beast::multi_buffer{})); async_connect(clients.back().first, it, yield[ec]); BEAST_EXPECT(!ec); auto req = makeHTTPRequest(ip, port, to_string(jr), {}); @@ -579,14 +608,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en return cfg; })}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const port = env.app().config()["port_ws"].get("port").value(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ip = env.app().config()["port_ws"].get("ip").value(); boost::beast::http::response resp; boost::system::error_code ec; doRequest(yield, makeWSUpgrade(ip, port), ip, port, true, resp, ec); BEAST_EXPECT(resp.result() == boost::beast::http::status::switching_protocols); - BEAST_EXPECT(resp.find("Upgrade") != resp.end() && resp["Upgrade"] == "websocket"); - BEAST_EXPECT(resp.find("Connection") != resp.end() && boost::iequals(resp["Connection"], "upgrade")); + BEAST_EXPECT(resp.contains("Upgrade") && resp["Upgrade"] == "websocket"); + BEAST_EXPECT(resp.contains("Connection") && boost::iequals(resp["Connection"], "upgrade")); } void @@ -597,7 +628,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en using namespace test::jtx; Env env{*this}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const port = env.app().config()["port_ws"].get("port").value(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ip = env.app().config()["port_ws"].get("ip").value(); boost::beast::http::response resp; boost::system::error_code ec; @@ -618,7 +651,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en using namespace boost::beast::http; Env env{*this}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const port = env.app().config()["port_ws"].get("port").value(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) auto const ip = env.app().config()["port_ws"].get("ip").value(); boost::system::error_code ec; @@ -650,8 +685,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en Json::Value resp; Json::Reader jr; - if (!BEAST_EXPECT( - jr.parse(boost::lexical_cast(boost::beast::make_printable(sb.data())), resp))) + if (!BEAST_EXPECT(jr.parse( + boost::lexical_cast(boost::beast::make_printable(sb.data())), + resp))) return Json::objectValue; sb.consume(sb.size()); return resp; @@ -728,7 +764,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en boost::system::error_code ec; response resp; - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -763,7 +809,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en si[jss::state][jss::warnings][0u][jss::id].asInt() == warnRPC_UNSUPPORTED_MAJORITY); // but status does not indicate a problem - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -773,7 +829,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // with ELB_SUPPORT, status still does not indicate a problem env.app().config().ELB_SUPPORT = true; - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -826,7 +892,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en boost::system::error_code ec; response resp; - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -844,7 +920,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // RPC request server_info again, now AB should be returned si = env.rpc("server_info")[jss::result]; BEAST_EXPECT(si.isMember(jss::info)); - BEAST_EXPECT(si[jss::info].isMember(jss::amendment_blocked) && si[jss::info][jss::amendment_blocked] == true); + BEAST_EXPECT( + si[jss::info].isMember(jss::amendment_blocked) && + si[jss::info][jss::amendment_blocked] == true); BEAST_EXPECT( si[jss::info].isMember(jss::warnings) && si[jss::info][jss::warnings].isArray() && si[jss::info][jss::warnings].size() == 1 && @@ -852,7 +930,9 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // RPC request server_state again, now AB should be returned si = env.rpc("server_state")[jss::result]; - BEAST_EXPECT(si[jss::state].isMember(jss::amendment_blocked) && si[jss::state][jss::amendment_blocked] == true); + BEAST_EXPECT( + si[jss::state].isMember(jss::amendment_blocked) && + si[jss::state][jss::amendment_blocked] == true); BEAST_EXPECT( si[jss::state].isMember(jss::warnings) && si[jss::state][jss::warnings].isArray() && si[jss::state][jss::warnings].size() == 1 && @@ -860,7 +940,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // but status does not indicate because it still relies on ELB // being enabled - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -869,7 +959,17 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en env.app().config().ELB_SUPPORT = true; - doRequest(yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + doRequest( + yield, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + makeHTTPRequest(*ip_ws, *port_ws, "", {}), + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *ip_ws, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + *port_ws, + false, + resp, + ec); if (!BEAST_EXPECTS(!ec, ec.message())) return; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 7347bf238c..bce8bced60 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -35,7 +35,8 @@ public: { private: boost::asio::io_context io_context_; - std::optional> work_; + std::optional> + work_; std::thread thread_; public: @@ -65,7 +66,8 @@ public: beast::unit_test::suite& suite_; public: - explicit TestSink(beast::unit_test::suite& suite) : Sink(beast::severities::kWarning, false), suite_(suite) + explicit TestSink(beast::unit_test::suite& suite) + : Sink(beast::severities::kWarning, false), suite_(suite) { } @@ -98,15 +100,18 @@ public: Handoff onHandoff( Session& session, - std::unique_ptr&& bundle, - http_request_type&& request, + std::unique_ptr const& bundle, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } Handoff - onHandoff(Session& session, http_request_type&& request, boost::asio::ip::tcp::endpoint remote_address) + onHandoff( + Session& session, + http_request_type const& request, + boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } @@ -116,13 +121,19 @@ public: { session.write(std::string("Hello, world!\n")); if (beast::rfc2616::is_keep_alive(session.request())) + { session.complete(); + } else + { session.close(true); + } } void - onWSMessage(std::shared_ptr session, std::vector const&) + onWSMessage( + std::shared_ptr session, + std::vector const&) { } @@ -225,7 +236,7 @@ public: return; boost::system::error_code ec; - s.shutdown(socket::shutdown_both, ec); + s.shutdown(socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -261,7 +272,7 @@ public: return; boost::system::error_code ec; - s.shutdown(socket::shutdown_both, ec); + s.shutdown(socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) } void @@ -275,7 +286,8 @@ public: TestHandler handler; auto s = make_Server(handler, thread.get_io_context(), journal); std::vector serverPort(1); - serverPort.back().ip = boost::asio::ip::make_address(getEnvLocalhostAddr()), serverPort.back().port = 0; + serverPort.back().ip = boost::asio::ip::make_address(getEnvLocalhostAddr()), + serverPort.back().port = 0; serverPort.back().protocol.insert("http"); auto eps = s->ports(serverPort); test_request(eps.begin()->second); @@ -300,15 +312,18 @@ public: Handoff onHandoff( Session& session, - std::unique_ptr&& bundle, - http_request_type&& request, + std::unique_ptr const& bundle, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } Handoff - onHandoff(Session& session, http_request_type&& request, boost::asio::ip::tcp::endpoint remote_address) + onHandoff( + Session& session, + http_request_type const& request, + boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; } @@ -319,7 +334,9 @@ public: } void - onWSMessage(std::shared_ptr session, std::vector const& buffers) + onWSMessage( + std::shared_ptr session, + std::vector const& buffers) { } @@ -343,7 +360,8 @@ public: TestThread thread; auto s = make_Server(h, thread.get_io_context(), journal); std::vector serverPort(1); - serverPort.back().ip = boost::asio::ip::make_address(getEnvLocalhostAddr()), serverPort.back().port = 0; + serverPort.back().ip = boost::asio::ip::make_address(getEnvLocalhostAddr()), + serverPort.back().port = 0; serverPort.back().protocol.insert("http"); s->ports(serverPort); } @@ -392,7 +410,8 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Invalid value '0' for key 'port' in [port_rpc]") == std::string::npos); + BEAST_EXPECT( + messages.find("Invalid value '0' for key 'port' in [port_rpc]") == std::string::npos); except([&] { Env env{ @@ -403,7 +422,8 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Invalid value '0' for key 'port' in [server]") != std::string::npos); + BEAST_EXPECT( + messages.find("Invalid value '0' for key 'port' in [server]") != std::string::npos); except([&] { Env env{ diff --git a/src/test/shamap/FetchPack_test.cpp b/src/test/shamap/FetchPack_test.cpp index 8919241996..471ad91e95 100644 --- a/src/test/shamap/FetchPack_test.cpp +++ b/src/test/shamap/FetchPack_test.cpp @@ -45,7 +45,7 @@ public: bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, - Blob&& nodeData, + Blob&& nodeData, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) SHAMapNodeType type) const override { } diff --git a/src/test/shamap/SHAMapSync_test.cpp b/src/test/shamap/SHAMapSync_test.cpp index 1add7a629e..a8f3a478b2 100644 --- a/src/test/shamap/SHAMapSync_test.cpp +++ b/src/test/shamap/SHAMapSync_test.cpp @@ -107,9 +107,10 @@ public: BEAST_EXPECT(source.getNodeFat(SHAMapNodeID(), a, rand_bool(eng_), rand_int(eng_, 2))); - unexpected(a.size() < 1, "NodeSize"); + unexpected(a.empty(), "NodeSize"); - BEAST_EXPECT(destination.addRootNode(source.getHash(), makeSlice(a[0].second), nullptr).isGood()); + BEAST_EXPECT(destination.addRootNode(source.getHash(), makeSlice(a[0].second), nullptr) + .isGood()); } do @@ -145,7 +146,8 @@ public: // Don't use BEAST_EXPECT here b/c it will be called a // non-deterministic number of times and the number of tests run // should be deterministic - if (!destination.addKnownNode(b[i].first, makeSlice(b[i].second), nullptr).isUseful()) + if (!destination.addKnownNode(b[i].first, makeSlice(b[i].second), nullptr) + .isUseful()) fail("", __FILE__, __LINE__); } } while (true); diff --git a/src/test/shamap/SHAMap_test.cpp b/src/test/shamap/SHAMap_test.cpp index c91cc63749..b16f7b157f 100644 --- a/src/test/shamap/SHAMap_test.cpp +++ b/src/test/shamap/SHAMap_test.cpp @@ -110,9 +110,13 @@ public: run(bool backed, beast::Journal const& journal) { if (backed) + { testcase("add/traverse backed"); + } else + { testcase("add/traverse unbacked"); + } tests::TestNodeFamily f(journal); @@ -163,9 +167,13 @@ public: unexpected(i != e, "bad traverse"); if (backed) + { testcase("snapshot backed"); + } else + { testcase("snapshot unbacked"); + } SHAMapHash mapHash = sMap.getHash(); std::shared_ptr map2 = sMap.snapShot(false); @@ -191,45 +199,65 @@ public: sMap.dump(); if (backed) + { testcase("build/tear backed"); + } else + { testcase("build/tear unbacked"); + } { constexpr std::array keys{ - uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8")}; + uint256( + "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8")}; constexpr std::array hashes{ - uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C" - "64B6281F7F"), - uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546" - "7FB2ECE266"), - uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A" - "E756795B75"), - uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC" - "6C74F93E07"), - uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596" - "462B0E3A3E"), - uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9" - "8A84D9DDE4"), - uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9" - "4361143615"), - uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0" - "2BBE7230E5")}; + uint256( + "B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C" + "64B6281F7F"), + uint256( + "FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546" + "7FB2ECE266"), + uint256( + "4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A" + "E756795B75"), + uint256( + "7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC" + "6C74F93E07"), + uint256( + "395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596" + "462B0E3A3E"), + uint256( + "D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9" + "8A84D9DDE4"), + uint256( + "76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9" + "4361143615"), + uint256( + "DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0" + "2BBE7230E5")}; SHAMap map(SHAMapType::FREE, f); if (!backed) @@ -238,7 +266,8 @@ public: BEAST_EXPECT(map.getHash() == beast::zero); for (int k = 0; k < keys.size(); ++k) { - BEAST_EXPECT(map.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(keys[k], IntToVUC(k)))); + BEAST_EXPECT(map.addItem( + SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(keys[k], IntToVUC(k)))); BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]); map.invariants(); } @@ -252,28 +281,40 @@ public: } if (backed) + { testcase("iterate backed"); + } else + { testcase("iterate unbacked"); + } { constexpr std::array keys{ - uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8"), - uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" - "5a772c6ca8")}; + uint256( + "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8"), + uint256( + "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e" + "5a772c6ca8")}; tests::TestNodeFamily tf{journal}; SHAMap map{SHAMapType::FREE, tf}; @@ -313,7 +354,8 @@ class SHAMapPathProof_test : public beast::unit_test::suite for (unsigned char c = 1; c < 100; ++c) { uint256 k(c); - map.addItem(SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(k, Slice{k.data(), k.size()})); + map.addItem( + SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(k, Slice{k.data(), k.size()})); map.invariants(); auto root = map.getHash().as_uint256(); diff --git a/src/test/shamap/common.h b/src/test/shamap/common.h index fe2a1884da..8284051d44 100644 --- a/src/test/shamap/common.h +++ b/src/test/shamap/common.h @@ -25,13 +25,19 @@ public: TestNodeFamily(beast::Journal j) : fbCache_(std::make_shared("App family full below cache", clock_, j)) , tnCache_( - std::make_shared("App family tree node cache", 65536, std::chrono::minutes{1}, clock_, j)) + std::make_shared( + "App family tree node cache", + 65536, + std::chrono::minutes{1}, + clock_, + j)) , j_(j) { Section testSection; testSection.set("type", "memory"); testSection.set("path", "SHAMap_test"); - db_ = NodeStore::Manager::instance().make_Database(megabytes(4), scheduler_, 1, testSection, j); + db_ = NodeStore::Manager::instance().make_Database( + megabytes(4), scheduler_, 1, testSection, j); } NodeStore::Database& diff --git a/src/test/unit_test/FileDirGuard.h b/src/test/unit_test/FileDirGuard.h index cf2eab7adb..b551b9389d 100644 --- a/src/test/unit_test/FileDirGuard.h +++ b/src/test/unit_test/FileDirGuard.h @@ -32,7 +32,8 @@ protected: if (is_directory(toRm) && is_empty(toRm)) remove(toRm); else - test_.log << "Expected " << toRm.string() << " to be an empty existing directory." << std::endl; + test_.log << "Expected " << toRm.string() << " to be an empty existing directory." + << std::endl; } public: @@ -128,7 +129,8 @@ public: else { if (created_) - test_.log << "Expected " << file_.string() << " to be an existing file." << std::endl; + test_.log << "Expected " << file_.string() << " to be an existing file." + << std::endl; } } catch (std::exception& e) diff --git a/src/test/unit_test/SuiteJournal.h b/src/test/unit_test/SuiteJournal.h index ad5f298b74..1eeefb68aa 100644 --- a/src/test/unit_test/SuiteJournal.h +++ b/src/test/unit_test/SuiteJournal.h @@ -105,7 +105,8 @@ class StreamSink : public beast::Journal::Sink std::stringstream strm_; public: - StreamSink(beast::severities::Severity threshold = beast::severities::kDebug) : Sink(threshold, false) + StreamSink(beast::severities::Severity threshold = beast::severities::kDebug) + : Sink(threshold, false) { } diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp index eefd0c8250..239564bd7c 100644 --- a/src/test/unit_test/multi_runner.cpp +++ b/src/test/unit_test/multi_runner.cpp @@ -49,7 +49,10 @@ results::add(suite_results const& r) if (elapsed >= std::chrono::seconds{1}) { auto const iter = std::lower_bound( - top.begin(), top.end(), elapsed, [](run_time const& t1, typename clock_type::duration const& t2) { + top.begin(), + top.end(), + elapsed, + [](run_time const& t1, typename clock_type::duration const& t2) { return t1.second > t2; }); @@ -105,7 +108,7 @@ results::print(S& s) { using namespace beast::unit_test; - if (top.size() > 0) + if (!top.empty()) { s << "Longest suite times:\n"; for (auto const& [name, dur] : top) @@ -207,7 +210,10 @@ multi_runner_base::multi_runner_base() } shared_mem_ = boost::interprocess::shared_memory_object{ - std::conditional_t{}, + std::conditional_t< + IsParent, + boost::interprocess::create_only_t, + boost::interprocess::open_only_t>{}, shared_mem_name_, boost::interprocess::read_write}; @@ -228,9 +234,13 @@ multi_runner_base::multi_runner_base() region_ = boost::interprocess::mapped_region{shared_mem_, boost::interprocess::read_write}; if (IsParent) + { inner_ = new (region_.get_address()) inner{}; + } else + { inner_ = reinterpret_cast(region_.get_address()); + } } catch (...) { @@ -457,7 +467,10 @@ multi_runner_parent::add_failures(std::size_t failures) //------------------------------------------------------------------------------ multi_runner_child::multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log) - : job_index_{checkout_job_index()}, num_jobs_{num_jobs}, quiet_{quiet}, print_log_{!quiet || print_log} + : job_index_{checkout_job_index()} + , num_jobs_{num_jobs} + , quiet_{quiet} + , print_log_{!quiet || print_log} { if (num_jobs_ > 1) { @@ -478,7 +491,8 @@ multi_runner_child::multi_runner_child(std::size_t num_jobs, bool quiet, bool pr if (cur_count == last_count) { // assume parent process is no longer alive - std::cerr << "multi_runner_child " << job_index_ << ": Assuming parent died, exiting.\n"; + std::cerr << "multi_runner_child " << job_index_ + << ": Assuming parent died, exiting.\n"; std::exit(EXIT_FAILURE); } } @@ -533,8 +547,8 @@ multi_runner_child::on_suite_end() std::stringstream s; if (num_jobs_ > 1) s << job_index_ << "> "; - s << (suite_results_.failed > 0 ? "failed: " : "") << suite_results_.name << " had " << suite_results_.failed - << " failures." << std::endl; + s << (suite_results_.failed > 0 ? "failed: " : "") << suite_results_.name << " had " + << suite_results_.failed << " failures." << std::endl; message_queue_send(MessageType::log, s.str()); } results_.add(suite_results_); @@ -552,7 +566,8 @@ multi_runner_child::on_case_begin(std::string const& name) std::stringstream s; if (num_jobs_ > 1) s << job_index_ << "> "; - s << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) << '\n'; + s << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) + << '\n'; message_queue_send(MessageType::log, s.str()); } diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h index 28ecaf85f9..8148079292 100644 --- a/src/test/unit_test/multi_runner.h +++ b/src/test/unit_test/multi_runner.h @@ -231,7 +231,8 @@ public: /** A class to run a subset of unit tests */ -class multi_runner_child : public beast::unit_test::runner, private detail::multi_runner_base +class multi_runner_child : public beast::unit_test::runner, + private detail::multi_runner_base { private: std::size_t job_index_; diff --git a/src/test/unit_test/utils.h b/src/test/unit_test/utils.h new file mode 100644 index 0000000000..1f6ee58436 --- /dev/null +++ b/src/test/unit_test/utils.h @@ -0,0 +1,19 @@ +#include + +#include + +namespace xrpl { +namespace test { + +/// Compare two SecretKey objects for equality. +/// SecretKey::operator== is deleted, so a named function is used +/// to avoid member-function lookup shadowing free-function overloads. +inline bool +equal(SecretKey const& lhs, SecretKey const& rhs) +{ + return lhs.size() == SecretKey::size_ && rhs.size() == SecretKey::size_ && + std::memcmp(lhs.data(), rhs.data(), SecretKey::size_) == 0; +} + +} // namespace test +} // namespace xrpl diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index cfa056b0aa..a82ed1472f 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -14,7 +14,10 @@ target_link_libraries(xrpl.helpers.test PRIVATE xrpl.libxrpl) # Common library dependencies for the rest of the tests. add_library(xrpl.imports.test INTERFACE) -target_link_libraries(xrpl.imports.test INTERFACE gtest::gtest xrpl.libxrpl xrpl.helpers.test) +target_link_libraries( + xrpl.imports.test + INTERFACE gtest::gtest xrpl.libxrpl xrpl.helpers.test +) # One test for each module. xrpl_add_test(basics) @@ -29,9 +32,24 @@ xrpl_add_test(json) target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test) add_dependencies(xrpl.tests xrpl.test.json) +# protocol_autogen tests use explicit source list (not GLOB) because sources are generated +# Mark generated sources so CMake knows they'll be created at build time +set_source_files_properties( + ${PROTOCOL_AUTOGEN_TEST_SOURCES} + PROPERTIES GENERATED TRUE +) +add_executable(xrpl.test.protocol_autogen ${PROTOCOL_AUTOGEN_TEST_SOURCES}) +target_link_libraries(xrpl.test.protocol_autogen PRIVATE xrpl.imports.test) +add_dependencies(xrpl.tests xrpl.test.protocol_autogen) +add_test(NAME xrpl.test.protocol_autogen COMMAND xrpl.test.protocol_autogen) +# Ensure code generation runs before compiling tests +if(TARGET protocol_autogen_generate) + add_dependencies(xrpl.test.protocol_autogen protocol_autogen_generate) +endif() + # Network unit tests are currently not supported on Windows -if (NOT WIN32) +if(NOT WIN32) xrpl_add_test(net) target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test) add_dependencies(xrpl.tests xrpl.test.net) -endif () +endif() diff --git a/src/tests/libxrpl/basics/MallocTrim.cpp b/src/tests/libxrpl/basics/MallocTrim.cpp new file mode 100644 index 0000000000..483cf37fe2 --- /dev/null +++ b/src/tests/libxrpl/basics/MallocTrim.cpp @@ -0,0 +1,209 @@ +#include + +#include + +#include + +using namespace xrpl; + +// cSpell:ignore statm + +#if defined(__GLIBC__) && BOOST_OS_LINUX +namespace xrpl::detail { +long +parseStatmRSSkB(std::string const& statm); +} // namespace xrpl::detail +#endif + +TEST(MallocTrimReport, structure) +{ + // Test default construction + MallocTrimReport report; + EXPECT_EQ(report.supported, false); + EXPECT_EQ(report.trimResult, -1); + EXPECT_EQ(report.rssBeforeKB, -1); + EXPECT_EQ(report.rssAfterKB, -1); + EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1}); + EXPECT_EQ(report.minfltDelta, -1); + EXPECT_EQ(report.majfltDelta, -1); + EXPECT_EQ(report.deltaKB(), 0); + + // Test deltaKB calculation - memory freed + report.rssBeforeKB = 1000; + report.rssAfterKB = 800; + EXPECT_EQ(report.deltaKB(), -200); + + // Test deltaKB calculation - memory increased + report.rssBeforeKB = 500; + report.rssAfterKB = 600; + EXPECT_EQ(report.deltaKB(), 100); + + // Test deltaKB calculation - no change + report.rssBeforeKB = 1234; + report.rssAfterKB = 1234; + EXPECT_EQ(report.deltaKB(), 0); +} + +#if defined(__GLIBC__) && BOOST_OS_LINUX +TEST(parseStatmRSSkB, standard_format) +{ + using xrpl::detail::parseStatmRSSkB; + + // Test standard format: size resident shared text lib data dt + // Assuming 4KB page size: resident=1000 pages = 4000 KB + { + std::string statm = "25365 1000 2377 0 0 5623 0"; + long result = parseStatmRSSkB(statm); + // Note: actual result depends on system page size + // On most systems it's 4KB, so 1000 pages = 4000 KB + EXPECT_GT(result, 0); + } + + // Test with newline + { + std::string statm = "12345 2000 1234 0 0 3456 0\n"; + long result = parseStatmRSSkB(statm); + EXPECT_GT(result, 0); + } + + // Test with tabs + { + std::string statm = "12345\t2000\t1234\t0\t0\t3456\t0"; + long result = parseStatmRSSkB(statm); + EXPECT_GT(result, 0); + } + + // Test zero resident pages + { + std::string statm = "25365 0 2377 0 0 5623 0"; + long result = parseStatmRSSkB(statm); + EXPECT_EQ(result, 0); + } + + // Test with extra whitespace + { + std::string statm = " 25365 1000 2377 "; + long result = parseStatmRSSkB(statm); + EXPECT_GT(result, 0); + } + + // Test empty string + { + std::string statm; + long result = parseStatmRSSkB(statm); + EXPECT_EQ(result, -1); + } + + // Test malformed data (only one field) + { + std::string statm = "25365"; + long result = parseStatmRSSkB(statm); + EXPECT_EQ(result, -1); + } + + // Test malformed data (non-numeric) + { + std::string statm = "abc def ghi"; + long result = parseStatmRSSkB(statm); + EXPECT_EQ(result, -1); + } + + // Test malformed data (second field non-numeric) + { + std::string statm = "25365 abc 2377"; + long result = parseStatmRSSkB(statm); + EXPECT_EQ(result, -1); + } +} +#endif + +TEST(mallocTrim, without_debug_logging) +{ + beast::Journal journal{beast::Journal::getNullSink()}; + + MallocTrimReport report = mallocTrim("without_debug", journal); + +#if defined(__GLIBC__) && BOOST_OS_LINUX + EXPECT_EQ(report.supported, true); + EXPECT_GE(report.trimResult, 0); + EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1}); + EXPECT_EQ(report.minfltDelta, -1); + EXPECT_EQ(report.majfltDelta, -1); +#else + EXPECT_EQ(report.supported, false); + EXPECT_EQ(report.trimResult, -1); + EXPECT_EQ(report.rssBeforeKB, -1); + EXPECT_EQ(report.rssAfterKB, -1); + EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1}); + EXPECT_EQ(report.minfltDelta, -1); + EXPECT_EQ(report.majfltDelta, -1); +#endif +} + +TEST(mallocTrim, empty_tag) +{ + beast::Journal journal{beast::Journal::getNullSink()}; + MallocTrimReport report = mallocTrim("", journal); + +#if defined(__GLIBC__) && BOOST_OS_LINUX + EXPECT_EQ(report.supported, true); + EXPECT_GE(report.trimResult, 0); +#else + EXPECT_EQ(report.supported, false); +#endif +} + +TEST(mallocTrim, with_debug_logging) +{ + struct DebugSink : public beast::Journal::Sink + { + DebugSink() : Sink(beast::severities::kDebug, false) + { + } + void + write(beast::severities::Severity, std::string const&) override + { + } + void + writeAlways(beast::severities::Severity, std::string const&) override + { + } + }; + + DebugSink sink; + beast::Journal journal{sink}; + + MallocTrimReport report = mallocTrim("debug_test", journal); + +#if defined(__GLIBC__) && BOOST_OS_LINUX + EXPECT_EQ(report.supported, true); + EXPECT_GE(report.trimResult, 0); + EXPECT_GE(report.durationUs.count(), 0); + EXPECT_GE(report.minfltDelta, 0); + EXPECT_GE(report.majfltDelta, 0); +#else + EXPECT_EQ(report.supported, false); + EXPECT_EQ(report.trimResult, -1); + EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1}); + EXPECT_EQ(report.minfltDelta, -1); + EXPECT_EQ(report.majfltDelta, -1); +#endif +} + +TEST(mallocTrim, repeated_calls) +{ + beast::Journal journal{beast::Journal::getNullSink()}; + + // Call malloc_trim multiple times to ensure it's safe + for (int i = 0; i < 5; ++i) + { + MallocTrimReport report = mallocTrim("iteration_" + std::to_string(i), journal); + +#if defined(__GLIBC__) && BOOST_OS_LINUX + EXPECT_EQ(report.supported, true); + EXPECT_GE(report.trimResult, 0); +#else + EXPECT_EQ(report.supported, false); +#endif + } +} diff --git a/src/tests/libxrpl/basics/Mutex.cpp b/src/tests/libxrpl/basics/Mutex.cpp new file mode 100644 index 0000000000..3bcee92276 --- /dev/null +++ b/src/tests/libxrpl/basics/Mutex.cpp @@ -0,0 +1,288 @@ +#include + +#include + +#include +#include +#include +#include +#include + +using namespace xrpl; + +struct MutexMakeTest : ::testing::Test +{ +}; + +TEST_F(MutexMakeTest, default_constructor) +{ + auto m = Mutex::make(); + auto lock = m.lock(); + EXPECT_EQ(*lock, 0); +} + +TEST_F(MutexMakeTest, single_argument) +{ + auto m = Mutex::make(42); + auto lock = m.lock(); + EXPECT_EQ(*lock, 42); +} + +TEST_F(MutexMakeTest, string_argument) +{ + auto m = Mutex::make("test"); + auto lock = m.lock(); + EXPECT_EQ(*lock, "test"); +} + +TEST_F(MutexMakeTest, move_only_type) +{ + auto m = Mutex>::make(std::make_unique(100)); + auto lock = m.lock(); + EXPECT_NE(lock->get(), nullptr); + EXPECT_EQ(**lock, 100); +} + +struct MutexDirectConstructionTest : ::testing::Test +{ +}; + +TEST_F(MutexDirectConstructionTest, default_constructor) +{ + // Test default construction with a type that has a non-trivial + // default constructor + Mutex m; + auto lock = m.lock(); + EXPECT_TRUE(lock->empty()); +} + +TEST_F(MutexDirectConstructionTest, default_initialization) +{ + Mutex m; + auto lock = m.lock(); + EXPECT_EQ(*lock, 0); +} + +TEST_F(MutexDirectConstructionTest, constructor_with_value) +{ + Mutex m(42); + auto lock = m.lock(); + EXPECT_EQ(*lock, 42); +} + +TEST_F(MutexDirectConstructionTest, constructor_with_string) +{ + Mutex m(std::string("hello")); + auto lock = m.lock(); + EXPECT_EQ(*lock, "hello"); +} + +struct MutexLockNonConstTest : ::testing::Test +{ +}; + +TEST_F(MutexLockNonConstTest, operator_star) +{ + Mutex m(10); + { + auto lock = m.lock(); + EXPECT_EQ(*lock, 10); + *lock = 20; + } + auto lock = m.lock(); + EXPECT_EQ(*lock, 20); +} + +TEST_F(MutexLockNonConstTest, get_method) +{ + Mutex m(10); + { + auto lock = m.lock(); + EXPECT_EQ(lock.get(), 10); + lock.get() = 30; + } + auto lock = m.lock(); + EXPECT_EQ(lock.get(), 30); +} + +TEST_F(MutexLockNonConstTest, operator_arrow) +{ + Mutex m(std::string("test")); + { + auto lock = m.lock(); + EXPECT_EQ(lock->size(), 4); + lock->append(" string"); + } + auto lock = m.lock(); + EXPECT_EQ(*lock, "test string"); +} + +TEST_F(MutexLockNonConstTest, multiple_modifications) +{ + Mutex m(10); + { + auto lock = m.lock(); + *lock = 20; + } + { + auto lock = m.lock(); + EXPECT_EQ(lock.get(), 20); + lock.get() = 30; + } + { + auto lock = m.lock(); + EXPECT_EQ(*lock, 30); + } +} + +struct MutexLockConstTest : ::testing::Test +{ +}; + +TEST_F(MutexLockConstTest, operator_star) +{ + Mutex const m(42); + auto lock = m.lock(); + static_assert(std::is_const_v>); + EXPECT_EQ(*lock, 42); +} + +TEST_F(MutexLockConstTest, get_method) +{ + Mutex const m(42); + auto lock = m.lock(); + static_assert(std::is_const_v>); + EXPECT_EQ(lock.get(), 42); +} + +TEST_F(MutexLockConstTest, operator_arrow) +{ + Mutex const m(std::string("test")); + auto lock = m.lock(); + static_assert(std::is_const_v>); + EXPECT_EQ(lock->size(), 4); + EXPECT_EQ(lock->at(0), 't'); +} + +struct MutexConstCorrectnessTest : ::testing::Test +{ +}; + +TEST_F(MutexConstCorrectnessTest, non_const_allows_modification) +{ + Mutex> m({1, 2, 3, 4, 5}); + { + auto lock = m.lock(); + EXPECT_EQ(lock->size(), 5); + lock->push_back(6); + } + auto lock = m.lock(); + EXPECT_EQ(lock->size(), 6); + EXPECT_EQ(lock->back(), 6); +} + +TEST_F(MutexConstCorrectnessTest, const_reference_provides_const_access) +{ + Mutex> m({1, 2, 3, 4, 5, 6}); + Mutex> const& const_ref = m; + auto lock = const_ref.lock(); + static_assert(std::is_const_v>); + EXPECT_EQ(lock->size(), 6); + EXPECT_EQ(lock->at(5), 6); +} + +struct MutexDifferentLockTypesTest : ::testing::Test +{ +}; + +TEST_F(MutexDifferentLockTypesTest, lock_guard) +{ + Mutex m(0); + { + auto lock = m.lock(); + *lock = 1; + } + auto lock = m.lock(); + EXPECT_EQ(*lock, 1); +} + +TEST_F(MutexDifferentLockTypesTest, unique_lock) +{ + Mutex m(0); + auto lock = m.lock(); + EXPECT_EQ(*lock, 0); + *lock = 2; + + std::unique_lock& ul = lock; + ul.unlock(); + ul.lock(); + EXPECT_EQ(*lock, 2); +} + +struct MutexSharedMutexTest : ::testing::Test +{ +}; + +TEST_F(MutexSharedMutexTest, shared_lock_for_const_access) +{ + Mutex m(100); + Mutex const& const_ref = m; + auto lock = const_ref.lock(); + EXPECT_EQ(*lock, 100); +} + +TEST_F(MutexSharedMutexTest, unique_lock_for_mutable_access) +{ + Mutex m(100); + { + auto lock = m.lock(); + *lock = 200; + } + auto lock = m.lock(); + EXPECT_EQ(*lock, 200); +} + +struct MutexComplexTypeTest : ::testing::Test +{ + struct Data + { + int x; + std::string y; + + Data(int x_, std::string y_) : x(x_), y(std::move(y_)) + { + } + }; +}; + +TEST_F(MutexComplexTypeTest, construct_and_access) +{ + auto m = Mutex::make(42, "hello"); + auto lock = m.lock(); + EXPECT_EQ(lock->x, 42); + EXPECT_EQ(lock->y, "hello"); +} + +TEST_F(MutexComplexTypeTest, modify_fields) +{ + auto m = Mutex::make(42, "hello"); + { + auto lock = m.lock(); + lock->x = 100; + lock->y = "world"; + } + { + auto lock = m.lock(); + EXPECT_EQ(lock->x, 100); + EXPECT_EQ(lock->y, "world"); + } +} + +TEST_F(MutexComplexTypeTest, const_access_to_fields) +{ + auto const m = Mutex::make(42, "hello"); + auto lock = m.lock(); + static_assert(std::is_const_v>); + EXPECT_EQ(lock->x, 42); + EXPECT_EQ(lock->y, "hello"); +} diff --git a/src/tests/libxrpl/basics/Slice.cpp b/src/tests/libxrpl/basics/Slice.cpp index 4373f98168..72f2d081c7 100644 --- a/src/tests/libxrpl/basics/Slice.cpp +++ b/src/tests/libxrpl/basics/Slice.cpp @@ -7,9 +7,9 @@ using namespace xrpl; -static std::uint8_t const data[] = {0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a, - 0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, - 0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; +static std::uint8_t const data[] = { + 0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a, 0x18, 0xb4, 0x70, 0xcb, 0xf5, + 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, 0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3}; TEST(Slice, equality_and_inequality) { @@ -28,24 +28,32 @@ TEST(Slice, equality_and_inequality) EXPECT_NE(s1.data(), nullptr); if (i == 0) + { EXPECT_EQ(s1, s0); + } else + { EXPECT_NE(s1, s0); + } for (std::size_t j = 0; j != sizeof(data); ++j) { Slice const s2{data, j}; if (i == j) + { EXPECT_EQ(s1, s2); + } else + { EXPECT_NE(s1, s2); + } } } // Test slices of equal size but pointing to different data: - std::array a; - std::array b; + std::array a{}; + std::array b{}; for (std::size_t i = 0; i != sizeof(data); ++i) a[i] = b[i] = data[i]; diff --git a/src/tests/libxrpl/basics/mulDiv.cpp b/src/tests/libxrpl/basics/mulDiv.cpp index c98c3fd61a..725bef399e 100644 --- a/src/tests/libxrpl/basics/mulDiv.cpp +++ b/src/tests/libxrpl/basics/mulDiv.cpp @@ -14,30 +14,30 @@ TEST(mulDiv, mulDiv) auto result = mulDiv(85, 20, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 340); + EXPECT_EQ(*result, 340); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(20, 85, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 340); + EXPECT_EQ(*result, 340); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(0, max - 1, max - 3); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 0); + EXPECT_EQ(*result, 0); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max - 1, 0, max - 3); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 0); + EXPECT_EQ(*result, 0); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 2, max / 2); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 4); + EXPECT_EQ(*result, 4); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 1000, max / 1000); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 1000000); + EXPECT_EQ(*result, 1000000); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max, 1000, max / 1001); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 1001000); + EXPECT_EQ(*result, 1001000); // NOLINT(bugprone-unchecked-optional-access) result = mulDiv(max32 + 1, max32 + 1, 5); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*result, 3689348814741910323); + EXPECT_EQ(*result, 3689348814741910323); // NOLINT(bugprone-unchecked-optional-access) // Overflow result = mulDiv(max - 1, max - 2, 5); diff --git a/src/tests/libxrpl/basics/scope.cpp b/src/tests/libxrpl/basics/scope.cpp index 309a41ec04..8efa4a84b1 100644 --- a/src/tests/libxrpl/basics/scope.cpp +++ b/src/tests/libxrpl/basics/scope.cpp @@ -35,7 +35,7 @@ TEST(scope, scope_exit) scope_exit x{[&i]() { i = 5; }}; throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } @@ -47,7 +47,7 @@ TEST(scope, scope_exit) x.release(); throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } @@ -85,7 +85,7 @@ TEST(scope, scope_fail) scope_fail x{[&i]() { i = 5; }}; throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } @@ -97,7 +97,7 @@ TEST(scope, scope_fail) x.release(); throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } @@ -135,7 +135,7 @@ TEST(scope, scope_success) scope_success x{[&i]() { i = 5; }}; throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } @@ -147,7 +147,7 @@ TEST(scope, scope_success) x.release(); throw 1; } - catch (...) + catch (...) // NOLINT(bugprone-empty-catch) { } } diff --git a/src/tests/libxrpl/basics/tagged_integer.cpp b/src/tests/libxrpl/basics/tagged_integer.cpp index e24b299442..09f8b6787b 100644 --- a/src/tests/libxrpl/basics/tagged_integer.cpp +++ b/src/tests/libxrpl/basics/tagged_integer.cpp @@ -53,15 +53,25 @@ static_assert( !std::is_assignable::value, "TagUInt3 should not be assignable with a std::uint64_t"); -static_assert(std::is_assignable::value, "TagUInt1 should be assignable with a TagUInt1"); +static_assert( + std::is_assignable::value, + "TagUInt1 should be assignable with a TagUInt1"); -static_assert(!std::is_assignable::value, "TagUInt1 should not be assignable with a TagUInt2"); +static_assert( + !std::is_assignable::value, + "TagUInt1 should not be assignable with a TagUInt2"); -static_assert(std::is_assignable::value, "TagUInt3 should be assignable with a TagUInt1"); +static_assert( + std::is_assignable::value, + "TagUInt3 should be assignable with a TagUInt1"); -static_assert(!std::is_assignable::value, "TagUInt1 should not be assignable with a TagUInt3"); +static_assert( + !std::is_assignable::value, + "TagUInt1 should not be assignable with a TagUInt3"); -static_assert(!std::is_assignable::value, "TagUInt3 should not be assignable with a TagUInt1"); +static_assert( + !std::is_assignable::value, + "TagUInt3 should not be assignable with a TagUInt1"); // Check convertibility of tagged_integers static_assert( @@ -80,11 +90,17 @@ static_assert( !std::is_convertible::value, "std::uint64_t should not be convertible to a TagUInt2"); -static_assert(!std::is_convertible::value, "TagUInt1 should not be convertible to TagUInt2"); +static_assert( + !std::is_convertible::value, + "TagUInt1 should not be convertible to TagUInt2"); -static_assert(!std::is_convertible::value, "TagUInt1 should not be convertible to TagUInt3"); +static_assert( + !std::is_convertible::value, + "TagUInt1 should not be convertible to TagUInt3"); -static_assert(!std::is_convertible::value, "TagUInt2 should not be convertible to a TagUInt3"); +static_assert( + !std::is_convertible::value, + "TagUInt2 should not be convertible to a TagUInt3"); using TagInt = tagged_integer; diff --git a/src/tests/libxrpl/json/Value.cpp b/src/tests/libxrpl/json/Value.cpp index 1d1131b209..a7b7387ba1 100644 --- a/src/tests/libxrpl/json/Value.cpp +++ b/src/tests/libxrpl/json/Value.cpp @@ -201,7 +201,11 @@ TEST(json_value, different_types) TEST(json_value, compare_strings) { - auto doCompare = [&](Json::Value const& lhs, Json::Value const& rhs, bool lhsEqRhs, bool lhsLtRhs, int line) { + auto doCompare = [&](Json::Value const& lhs, + Json::Value const& rhs, + bool lhsEqRhs, + bool lhsLtRhs, + int line) { SCOPED_TRACE(line); EXPECT_EQ((lhs == rhs), lhsEqRhs); EXPECT_NE((lhs != rhs), lhsEqRhs); @@ -757,16 +761,16 @@ TEST(json_value, move) EXPECT_EQ(v1.asDouble(), 2.5); Json::Value v2 = std::move(v1); - EXPECT_FALSE(v1); + EXPECT_FALSE(v1); // NOLINT(bugprone-use-after-move) EXPECT_TRUE(v2.isDouble()); EXPECT_EQ(v2.asDouble(), 2.5); - EXPECT_NE(v1, v2); + EXPECT_NE(v1, v2); // NOLINT(bugprone-use-after-move) v1 = std::move(v2); EXPECT_TRUE(v1.isDouble()); EXPECT_EQ(v1.asDouble(), 2.5); - EXPECT_FALSE(v2); - EXPECT_NE(v1, v2); + EXPECT_FALSE(v2); // NOLINT(bugprone-use-after-move) + EXPECT_NE(v1, v2); // NOLINT(bugprone-use-after-move) } TEST(json_value, comparisons) @@ -1344,7 +1348,7 @@ TEST(json_value, memory_leak) // Note that the type() == nullValue check is implementation // specific and not guaranteed to be valid in the future. - EXPECT_EQ(temp.type(), Json::nullValue); + EXPECT_EQ(temp.type(), Json::nullValue); // NOLINT(bugprone-use-after-move) } } diff --git a/src/tests/libxrpl/json/Writer.cpp b/src/tests/libxrpl/json/Writer.cpp index 54c3221858..7016b4322d 100644 --- a/src/tests/libxrpl/json/Writer.cpp +++ b/src/tests/libxrpl/json/Writer.cpp @@ -156,7 +156,8 @@ TEST_F(WriterFixture, complex_object) writer->startSet(Writer::array, "subarray"); writer->append(23.5); writer->finishAll(); - checkOutputAndReset(R"({"hello":"world","array":[true,12,[{"goodbye":"cruel world.","subarray":[23.5]}]]})"); + checkOutputAndReset( + R"({"hello":"world","array":[true,12,[{"goodbye":"cruel world.","subarray":[23.5]}]]})"); } TEST_F(WriterFixture, json_value) diff --git a/src/tests/libxrpl/net/HTTPClient.cpp b/src/tests/libxrpl/net/HTTPClient.cpp index 72a351f9b7..36159cb089 100644 --- a/src/tests/libxrpl/net/HTTPClient.cpp +++ b/src/tests/libxrpl/net/HTTPClient.cpp @@ -33,7 +33,7 @@ private: boost::asio::ip::tcp::endpoint endpoint_; bool running_{true}; bool finished_{false}; - unsigned short port_; + unsigned short port_{0}; // Custom headers to return std::map customHeaders_; @@ -43,7 +43,7 @@ private: beast::Journal j_; public: - TestHTTPServer() : acceptor_(ioc_), port_(0), j_(TestSink::instance()) + TestHTTPServer() : acceptor_(ioc_), j_(TestSink::instance()) { // Bind to any available port endpoint_ = {boost::asio::ip::tcp::v4(), 0}; @@ -147,7 +147,8 @@ private: boost::beast::http::request req; // Read the HTTP request asynchronously - co_await boost::beast::http::async_read(socket, buffer, req, boost::asio::use_awaitable); + co_await boost::beast::http::async_read( + socket, buffer, req, boost::asio::use_awaitable); // Create response boost::beast::http::response res; @@ -172,6 +173,8 @@ private: // Shutdown socket gracefully boost::system::error_code shutdownEc; + + // NOLINTNEXTLINE(bugprone-unused-return-value) socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, shutdownEc); } catch (std::exception const& e) @@ -182,60 +185,86 @@ private: } }; -// Helper function to run HTTP client test -bool -runHTTPTest( - TestHTTPServer& server, - std::string const& path, - bool& completed, - int& resultStatus, - std::string& resultData, - boost::system::error_code& resultError) -{ - // Create a null journal for testing - beast::Journal j{TestSink::instance()}; - - // Initialize HTTPClient SSL context - HTTPClient::initializeSSLContext("", "", false, j); - - HTTPClient::get( - false, // no SSL - server.ioc(), - "127.0.0.1", - server.port(), - path, - 1024, // max response size - std::chrono::seconds(5), - [&](boost::system::error_code const& ec, int status, std::string const& data) -> bool { - resultError = ec; - resultStatus = status; - resultData = data; - completed = true; - return false; // don't retry - }, - j); - - // Run the IO context until completion - auto start = std::chrono::steady_clock::now(); - while (server.ioc().run_one() != 0) - { - if (std::chrono::steady_clock::now() - start >= std::chrono::seconds(10) || server.finished()) - { - break; - } - - if (completed) - { - server.stop(); - } - } - - return completed; -} - } // anonymous namespace -TEST(HTTPClient, case_insensitive_content_length) +// Test fixture that manages the SSL context lifecycle via RAII. +// SetUp() initializes the context before each test and TearDown() +// cleans it up afterwards, so individual tests don't need to worry +// about resource management. +class HTTPClientTest : public ::testing::Test +{ +protected: + // Shared journal for SSL context initialization and HTTP requests. + beast::Journal j_{TestSink::instance()}; + + // Initialize the global SSL context used by HTTPClient. + void + SetUp() override + { + HTTPClient::initializeSSLContext( + "" /* sslVerifyDir*/, "" /*sslVerifyFile */, false /* sslVerify */, j_ /* journal */); + } + + // Release the global SSL context to prevent memory leaks. + void + TearDown() override + { + HTTPClient::cleanupSSLContext(); + } + + // Issue an HTTP GET to the given test server and drive the + // io_context until a response arrives or a timeout is reached. + // Returns true when the completion handler was invoked. + bool + runHTTPTest( + TestHTTPServer& server, + std::string const& path, + bool& completed, + int& resultStatus, + std::string& resultData, + boost::system::error_code& resultError) + { + HTTPClient::get( + false, // no SSL + server.ioc(), + "127.0.0.1", + server.port(), + path, + 1024, // max response size + std::chrono::seconds(5), + [&](boost::system::error_code const& ec, int status, std::string const& data) -> bool { + resultError = ec; + resultStatus = status; + resultData = data; + completed = true; + return false; // don't retry + }, + j_); + + // Run the IO context until completion + auto start = std::chrono::steady_clock::now(); + while (server.ioc().run_one() != 0) + { + if (std::chrono::steady_clock::now() - start >= std::chrono::seconds(10) || + server.finished()) + { + break; + } + + if (completed) + { + server.stop(); + } + } + + // Drain any remaining handlers to ensure proper cleanup of HTTPClientImp + server.ioc().poll(); + + return completed; + } +}; + +TEST_F(HTTPClientTest, case_insensitive_content_length) { // Test different cases of Content-Length header std::vector headerCases = { @@ -258,8 +287,8 @@ TEST(HTTPClient, case_insensitive_content_length) std::string resultData; boost::system::error_code resultError; - bool testCompleted = runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError); - + bool testCompleted = + runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError); // Verify results EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); @@ -268,7 +297,7 @@ TEST(HTTPClient, case_insensitive_content_length) } } -TEST(HTTPClient, basic_http_request) +TEST_F(HTTPClientTest, basic_http_request) { TestHTTPServer server; std::string testBody = "Test response body"; @@ -280,7 +309,8 @@ TEST(HTTPClient, basic_http_request) std::string resultData; boost::system::error_code resultError; - bool testCompleted = runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError); + bool testCompleted = + runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); @@ -288,7 +318,7 @@ TEST(HTTPClient, basic_http_request) EXPECT_EQ(resultData, testBody); } -TEST(HTTPClient, empty_response) +TEST_F(HTTPClientTest, empty_response) { TestHTTPServer server; server.setResponseBody(""); // Empty body @@ -299,7 +329,8 @@ TEST(HTTPClient, empty_response) std::string resultData; boost::system::error_code resultError; - bool testCompleted = runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError); + bool testCompleted = + runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); @@ -307,7 +338,7 @@ TEST(HTTPClient, empty_response) EXPECT_TRUE(resultData.empty()); } -TEST(HTTPClient, different_status_codes) +TEST_F(HTTPClientTest, different_status_codes) { std::vector statusCodes = {200, 404, 500}; @@ -322,7 +353,8 @@ TEST(HTTPClient, different_status_codes) std::string resultData; boost::system::error_code resultError; - bool testCompleted = runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError); + bool testCompleted = + runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError); EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); diff --git a/src/tests/libxrpl/protocol_autogen/STObjectValidation.cpp b/src/tests/libxrpl/protocol_autogen/STObjectValidation.cpp new file mode 100644 index 0000000000..5c100364dc --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/STObjectValidation.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +#include + +namespace xrpl { +TEST(STObjectValidation, validate_required_field) +{ + SOTemplate format{{sfFlags, soeREQUIRED}}; + STObject obj(sfGeneric); + obj.setFieldU32(sfFlags, 0); + EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_missing_required_field) +{ + SOTemplate format{{sfFlags, soeREQUIRED}}; + STObject obj(sfGeneric); + EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_optional_field) +{ + SOTemplate format{{sfFlags, soeOPTIONAL}}; + STObject obj(sfGeneric); + obj.setFieldU32(sfFlags, 0); + EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_missing_optional_field) +{ + SOTemplate format{{sfFlags, soeOPTIONAL}}; + STObject obj(sfGeneric); + EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_mpt_amount_supported) +{ + SOTemplate format{{sfAmount, soeREQUIRED, soeMPTSupported}}; + STObject obj(sfGeneric); + obj.setFieldAmount(sfAmount, STAmount{MPTAmount{Number{1}}, MPTIssue{}}); + EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_mpt_amount_not_supported) +{ + SOTemplate format{{sfAmount, soeREQUIRED, soeMPTNotSupported}}; + STObject obj(sfGeneric); + obj.setFieldAmount(sfAmount, STAmount{MPTAmount{Number{1}}, MPTIssue{}}); + EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_mpt_issue_supported) +{ + SOTemplate format{{sfAsset, soeREQUIRED, soeMPTSupported}}; + STObject obj(sfGeneric); + obj.setFieldIssue(sfAsset, STIssue{sfAsset, MPTIssue{}}); + EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format)); +} + +TEST(STObjectValidation, validate_mpt_issue_not_supported) +{ + SOTemplate format{{sfAsset, soeREQUIRED, soeMPTNotSupported}}; + STObject obj(sfGeneric); + obj.setFieldIssue(sfAsset, STIssue{sfAsset, MPTIssue{}}); + EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format)); +} +} // namespace xrpl diff --git a/src/tests/libxrpl/protocol_autogen/TestHelpers.h b/src/tests/libxrpl/protocol_autogen/TestHelpers.h new file mode 100644 index 0000000000..cf2d7ffe6a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/TestHelpers.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace xrpl { + +// Typed field canonical values + +using Uint8Value = std::decay_t; +inline Uint8Value +canonical_UINT8() +{ + return Uint8Value{1}; +} + +using Uint16Value = std::decay_t; +inline Uint16Value +canonical_UINT16() +{ + return Uint16Value{1}; +} + +using Uint32Value = std::decay_t; +inline Uint32Value +canonical_UINT32() +{ + return Uint32Value{1}; +} + +using Uint64Value = std::decay_t; +inline Uint64Value +canonical_UINT64() +{ + return Uint64Value{1}; +} + +using Uint128Value = std::decay_t; +inline Uint128Value +canonical_UINT128() +{ + return Uint128Value{1}; +} + +using Uint160Value = std::decay_t; +inline Uint160Value +canonical_UINT160() +{ + return Uint160Value{1}; +} + +using Uint192Value = std::decay_t; +inline Uint192Value +canonical_UINT192() +{ + return Uint192Value{1}; +} + +using Uint256Value = std::decay_t; +inline Uint256Value +canonical_UINT256() +{ + return Uint256Value{1}; +} + +using Int32Value = std::decay_t; +inline Int32Value +canonical_INT32() +{ + return Int32Value{42}; +} + +using NumberValue = std::decay_t; +inline NumberValue +canonical_NUMBER() +{ + return NumberValue{123}; +} + +using AmountValue = std::decay_t; +inline AmountValue +canonical_AMOUNT() +{ + return AmountValue{XRPAmount{1}}; +} + +using AccountValue = std::decay_t; +inline AccountValue +canonical_ACCOUNT() +{ + return xrpAccount(); +} + +using CurrencyValue = std::decay_t; +inline CurrencyValue +canonical_CURRENCY() +{ + return xrpCurrency(); +} + +using IssueValue = std::decay_t; +inline IssueValue +canonical_ISSUE() +{ + return IssueValue{xrpIssue()}; +} + +using Vector256Value = std::decay_t; +inline Vector256Value +canonical_VECTOR256() +{ + return Vector256Value{uint256{1}}; +} + +using BlobValue = std::decay_t; +inline BlobValue +canonical_VL() +{ + static constexpr std::array data{{'a', 'b', 'c'}}; + return BlobValue{data.data(), data.size()}; +} + +using XChainBridgeValue = std::decay_t; +inline XChainBridgeValue +canonical_XCHAIN_BRIDGE() +{ + return XChainBridgeValue{xrpAccount(), xrpIssue(), xrpAccount(), xrpIssue()}; +} + +// Untyped field canonical values + +inline STArray +canonical_ARRAY() +{ + return STArray{}; +} + +inline STObject +canonical_OBJECT() +{ + return STObject{sfGeneric}; +} + +inline STPathSet +canonical_PATHSET() +{ + STPathSet result{}; + result.push_back(STPath{}); + return result; +} + +// Field comparison helpers for generated tests + +template +void +expectEqualField(T const& expected, T const& actual, char const* fieldName) +{ + EXPECT_EQ(expected, actual) << "Field " << fieldName << " mismatch"; +} + +// Specialization for STObject (no operator==, use isEquivalent) +template <> +inline void +expectEqualField(STObject const& expected, STObject const& actual, char const* fieldName) +{ + EXPECT_TRUE(expected.isEquivalent(actual)) << "Field " << fieldName << " mismatch"; +} + +// Specialization for STPathSet (no operator==, use isEquivalent) +template <> +inline void +expectEqualField( + STPathSet const& expected, + STPathSet const& actual, + char const* fieldName) +{ + EXPECT_TRUE(expected.isEquivalent(actual)) << "Field " << fieldName << " mismatch"; +} + +// Overloads for std::reference_wrapper (used by optional ARRAY/PATHSET fields) +template +void +expectEqualField(T const& expected, std::reference_wrapper actual, char const* fieldName) +{ + expectEqualField(expected, actual.get(), fieldName); +} + +} // namespace xrpl diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp new file mode 100644 index 0000000000..0bb80c41e6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp @@ -0,0 +1,361 @@ +// Auto-generated unit tests for ledger entry AMM + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AMMTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + auto const voteSlotsValue = canonical_ARRAY(); + auto const auctionSlotValue = canonical_OBJECT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AMMBuilder builder{ + accountValue, + lPTokenBalanceValue, + assetValue, + asset2Value, + ownerNodeValue + }; + + builder.setTradingFee(tradingFeeValue); + builder.setVoteSlots(voteSlotsValue); + builder.setAuctionSlot(auctionSlotValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = lPTokenBalanceValue; + auto const actual = entry.getLPTokenBalance(); + expectEqualField(expected, actual, "sfLPTokenBalance"); + } + + { + auto const& expected = assetValue; + auto const actual = entry.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = entry.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = entry.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTradingFee"); + EXPECT_TRUE(entry.hasTradingFee()); + } + + { + auto const& expected = voteSlotsValue; + auto const actualOpt = entry.getVoteSlots(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfVoteSlots"); + EXPECT_TRUE(entry.hasVoteSlots()); + } + + { + auto const& expected = auctionSlotValue; + auto const actualOpt = entry.getAuctionSlot(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuctionSlot"); + EXPECT_TRUE(entry.hasAuctionSlot()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AMMTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + auto const voteSlotsValue = canonical_ARRAY(); + auto const auctionSlotValue = canonical_OBJECT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(AMM::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfTradingFee) = tradingFeeValue; + sle->setFieldArray(sfVoteSlots, voteSlotsValue); + sle->setFieldObject(sfAuctionSlot, auctionSlotValue); + sle->at(sfLPTokenBalance) = lPTokenBalanceValue; + sle->at(sfAsset) = STIssue(sfAsset, assetValue); + sle->at(sfAsset2) = STIssue(sfAsset2, asset2Value); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + AMMBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + AMM entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = lPTokenBalanceValue; + + auto const fromSle = entryFromSle.getLPTokenBalance(); + auto const fromBuilder = entryFromBuilder.getLPTokenBalance(); + + expectEqualField(expected, fromSle, "sfLPTokenBalance"); + expectEqualField(expected, fromBuilder, "sfLPTokenBalance"); + } + + { + auto const& expected = assetValue; + + auto const fromSle = entryFromSle.getAsset(); + auto const fromBuilder = entryFromBuilder.getAsset(); + + expectEqualField(expected, fromSle, "sfAsset"); + expectEqualField(expected, fromBuilder, "sfAsset"); + } + + { + auto const& expected = asset2Value; + + auto const fromSle = entryFromSle.getAsset2(); + auto const fromBuilder = entryFromBuilder.getAsset2(); + + expectEqualField(expected, fromSle, "sfAsset2"); + expectEqualField(expected, fromBuilder, "sfAsset2"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = tradingFeeValue; + + auto const fromSleOpt = entryFromSle.getTradingFee(); + auto const fromBuilderOpt = entryFromBuilder.getTradingFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTradingFee"); + expectEqualField(expected, *fromBuilderOpt, "sfTradingFee"); + } + + { + auto const& expected = voteSlotsValue; + + auto const fromSleOpt = entryFromSle.getVoteSlots(); + auto const fromBuilderOpt = entryFromBuilder.getVoteSlots(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfVoteSlots"); + expectEqualField(expected, *fromBuilderOpt, "sfVoteSlots"); + } + + { + auto const& expected = auctionSlotValue; + + auto const fromSleOpt = entryFromSle.getAuctionSlot(); + auto const fromBuilderOpt = entryFromBuilder.getAuctionSlot(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuctionSlot"); + expectEqualField(expected, *fromBuilderOpt, "sfAuctionSlot"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AMMTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AMM{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AMMTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AMMBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AMMTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const lPTokenBalanceValue = canonical_AMOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const ownerNodeValue = canonical_UINT64(); + + AMMBuilder builder{ + accountValue, + lPTokenBalanceValue, + assetValue, + asset2Value, + ownerNodeValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasTradingFee()); + EXPECT_FALSE(entry.getTradingFee().has_value()); + EXPECT_FALSE(entry.hasVoteSlots()); + EXPECT_FALSE(entry.getVoteSlots().has_value()); + EXPECT_FALSE(entry.hasAuctionSlot()); + EXPECT_FALSE(entry.getAuctionSlot().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp new file mode 100644 index 0000000000..e967b614b7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp @@ -0,0 +1,707 @@ +// Auto-generated unit tests for ledger entry AccountRoot + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AccountRootTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const accountTxnIDValue = canonical_UINT256(); + auto const regularKeyValue = canonical_ACCOUNT(); + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const domainValue = canonical_VL(); + auto const tickSizeValue = canonical_UINT8(); + auto const ticketCountValue = canonical_UINT32(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + auto const mintedNFTokensValue = canonical_UINT32(); + auto const burnedNFTokensValue = canonical_UINT32(); + auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const aMMIDValue = canonical_UINT256(); + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + + AccountRootBuilder builder{ + accountValue, + sequenceValue, + balanceValue, + ownerCountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setAccountTxnID(accountTxnIDValue); + builder.setRegularKey(regularKeyValue); + builder.setEmailHash(emailHashValue); + builder.setWalletLocator(walletLocatorValue); + builder.setWalletSize(walletSizeValue); + builder.setMessageKey(messageKeyValue); + builder.setTransferRate(transferRateValue); + builder.setDomain(domainValue); + builder.setTickSize(tickSizeValue); + builder.setTicketCount(ticketCountValue); + builder.setNFTokenMinter(nFTokenMinterValue); + builder.setMintedNFTokens(mintedNFTokensValue); + builder.setBurnedNFTokens(burnedNFTokensValue); + builder.setFirstNFTokenSequence(firstNFTokenSequenceValue); + builder.setAMMID(aMMIDValue); + builder.setVaultID(vaultIDValue); + builder.setLoanBrokerID(loanBrokerIDValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = ownerCountValue; + auto const actual = entry.getOwnerCount(); + expectEqualField(expected, actual, "sfOwnerCount"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = accountTxnIDValue; + auto const actualOpt = entry.getAccountTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAccountTxnID"); + EXPECT_TRUE(entry.hasAccountTxnID()); + } + + { + auto const& expected = regularKeyValue; + auto const actualOpt = entry.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfRegularKey"); + EXPECT_TRUE(entry.hasRegularKey()); + } + + { + auto const& expected = emailHashValue; + auto const actualOpt = entry.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfEmailHash"); + EXPECT_TRUE(entry.hasEmailHash()); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = entry.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + EXPECT_TRUE(entry.hasWalletLocator()); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = entry.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfWalletSize"); + EXPECT_TRUE(entry.hasWalletSize()); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = entry.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMessageKey"); + EXPECT_TRUE(entry.hasMessageKey()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = entry.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(entry.hasTransferRate()); + } + + { + auto const& expected = domainValue; + auto const actualOpt = entry.getDomain(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomain"); + EXPECT_TRUE(entry.hasDomain()); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = entry.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTickSize"); + EXPECT_TRUE(entry.hasTickSize()); + } + + { + auto const& expected = ticketCountValue; + auto const actualOpt = entry.getTicketCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTicketCount"); + EXPECT_TRUE(entry.hasTicketCount()); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = entry.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + EXPECT_TRUE(entry.hasNFTokenMinter()); + } + + { + auto const& expected = mintedNFTokensValue; + auto const actualOpt = entry.getMintedNFTokens(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMintedNFTokens"); + EXPECT_TRUE(entry.hasMintedNFTokens()); + } + + { + auto const& expected = burnedNFTokensValue; + auto const actualOpt = entry.getBurnedNFTokens(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBurnedNFTokens"); + EXPECT_TRUE(entry.hasBurnedNFTokens()); + } + + { + auto const& expected = firstNFTokenSequenceValue; + auto const actualOpt = entry.getFirstNFTokenSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFirstNFTokenSequence"); + EXPECT_TRUE(entry.hasFirstNFTokenSequence()); + } + + { + auto const& expected = aMMIDValue; + auto const actualOpt = entry.getAMMID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAMMID"); + EXPECT_TRUE(entry.hasAMMID()); + } + + { + auto const& expected = vaultIDValue; + auto const actualOpt = entry.getVaultID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfVaultID"); + EXPECT_TRUE(entry.hasVaultID()); + } + + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = entry.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(entry.hasLoanBrokerID()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AccountRootTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const accountTxnIDValue = canonical_UINT256(); + auto const regularKeyValue = canonical_ACCOUNT(); + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const domainValue = canonical_VL(); + auto const tickSizeValue = canonical_UINT8(); + auto const ticketCountValue = canonical_UINT32(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + auto const mintedNFTokensValue = canonical_UINT32(); + auto const burnedNFTokensValue = canonical_UINT32(); + auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const aMMIDValue = canonical_UINT256(); + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + + auto sle = std::make_shared(AccountRoot::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfBalance) = balanceValue; + sle->at(sfOwnerCount) = ownerCountValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfAccountTxnID) = accountTxnIDValue; + sle->at(sfRegularKey) = regularKeyValue; + sle->at(sfEmailHash) = emailHashValue; + sle->at(sfWalletLocator) = walletLocatorValue; + sle->at(sfWalletSize) = walletSizeValue; + sle->at(sfMessageKey) = messageKeyValue; + sle->at(sfTransferRate) = transferRateValue; + sle->at(sfDomain) = domainValue; + sle->at(sfTickSize) = tickSizeValue; + sle->at(sfTicketCount) = ticketCountValue; + sle->at(sfNFTokenMinter) = nFTokenMinterValue; + sle->at(sfMintedNFTokens) = mintedNFTokensValue; + sle->at(sfBurnedNFTokens) = burnedNFTokensValue; + sle->at(sfFirstNFTokenSequence) = firstNFTokenSequenceValue; + sle->at(sfAMMID) = aMMIDValue; + sle->at(sfVaultID) = vaultIDValue; + sle->at(sfLoanBrokerID) = loanBrokerIDValue; + + AccountRootBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + AccountRoot entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = ownerCountValue; + + auto const fromSle = entryFromSle.getOwnerCount(); + auto const fromBuilder = entryFromBuilder.getOwnerCount(); + + expectEqualField(expected, fromSle, "sfOwnerCount"); + expectEqualField(expected, fromBuilder, "sfOwnerCount"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = accountTxnIDValue; + + auto const fromSleOpt = entryFromSle.getAccountTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getAccountTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAccountTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfAccountTxnID"); + } + + { + auto const& expected = regularKeyValue; + + auto const fromSleOpt = entryFromSle.getRegularKey(); + auto const fromBuilderOpt = entryFromBuilder.getRegularKey(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfRegularKey"); + expectEqualField(expected, *fromBuilderOpt, "sfRegularKey"); + } + + { + auto const& expected = emailHashValue; + + auto const fromSleOpt = entryFromSle.getEmailHash(); + auto const fromBuilderOpt = entryFromBuilder.getEmailHash(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfEmailHash"); + expectEqualField(expected, *fromBuilderOpt, "sfEmailHash"); + } + + { + auto const& expected = walletLocatorValue; + + auto const fromSleOpt = entryFromSle.getWalletLocator(); + auto const fromBuilderOpt = entryFromBuilder.getWalletLocator(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfWalletLocator"); + expectEqualField(expected, *fromBuilderOpt, "sfWalletLocator"); + } + + { + auto const& expected = walletSizeValue; + + auto const fromSleOpt = entryFromSle.getWalletSize(); + auto const fromBuilderOpt = entryFromBuilder.getWalletSize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfWalletSize"); + expectEqualField(expected, *fromBuilderOpt, "sfWalletSize"); + } + + { + auto const& expected = messageKeyValue; + + auto const fromSleOpt = entryFromSle.getMessageKey(); + auto const fromBuilderOpt = entryFromBuilder.getMessageKey(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMessageKey"); + expectEqualField(expected, *fromBuilderOpt, "sfMessageKey"); + } + + { + auto const& expected = transferRateValue; + + auto const fromSleOpt = entryFromSle.getTransferRate(); + auto const fromBuilderOpt = entryFromBuilder.getTransferRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferRate"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferRate"); + } + + { + auto const& expected = domainValue; + + auto const fromSleOpt = entryFromSle.getDomain(); + auto const fromBuilderOpt = entryFromBuilder.getDomain(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomain"); + expectEqualField(expected, *fromBuilderOpt, "sfDomain"); + } + + { + auto const& expected = tickSizeValue; + + auto const fromSleOpt = entryFromSle.getTickSize(); + auto const fromBuilderOpt = entryFromBuilder.getTickSize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTickSize"); + expectEqualField(expected, *fromBuilderOpt, "sfTickSize"); + } + + { + auto const& expected = ticketCountValue; + + auto const fromSleOpt = entryFromSle.getTicketCount(); + auto const fromBuilderOpt = entryFromBuilder.getTicketCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTicketCount"); + expectEqualField(expected, *fromBuilderOpt, "sfTicketCount"); + } + + { + auto const& expected = nFTokenMinterValue; + + auto const fromSleOpt = entryFromSle.getNFTokenMinter(); + auto const fromBuilderOpt = entryFromBuilder.getNFTokenMinter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNFTokenMinter"); + expectEqualField(expected, *fromBuilderOpt, "sfNFTokenMinter"); + } + + { + auto const& expected = mintedNFTokensValue; + + auto const fromSleOpt = entryFromSle.getMintedNFTokens(); + auto const fromBuilderOpt = entryFromBuilder.getMintedNFTokens(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMintedNFTokens"); + expectEqualField(expected, *fromBuilderOpt, "sfMintedNFTokens"); + } + + { + auto const& expected = burnedNFTokensValue; + + auto const fromSleOpt = entryFromSle.getBurnedNFTokens(); + auto const fromBuilderOpt = entryFromBuilder.getBurnedNFTokens(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBurnedNFTokens"); + expectEqualField(expected, *fromBuilderOpt, "sfBurnedNFTokens"); + } + + { + auto const& expected = firstNFTokenSequenceValue; + + auto const fromSleOpt = entryFromSle.getFirstNFTokenSequence(); + auto const fromBuilderOpt = entryFromBuilder.getFirstNFTokenSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFirstNFTokenSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfFirstNFTokenSequence"); + } + + { + auto const& expected = aMMIDValue; + + auto const fromSleOpt = entryFromSle.getAMMID(); + auto const fromBuilderOpt = entryFromBuilder.getAMMID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAMMID"); + expectEqualField(expected, *fromBuilderOpt, "sfAMMID"); + } + + { + auto const& expected = vaultIDValue; + + auto const fromSleOpt = entryFromSle.getVaultID(); + auto const fromBuilderOpt = entryFromBuilder.getVaultID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfVaultID"); + expectEqualField(expected, *fromBuilderOpt, "sfVaultID"); + } + + { + auto const& expected = loanBrokerIDValue; + + auto const fromSleOpt = entryFromSle.getLoanBrokerID(); + auto const fromBuilderOpt = entryFromBuilder.getLoanBrokerID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanBrokerID"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanBrokerID"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AccountRootTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AccountRoot{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AccountRootTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AccountRootBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AccountRootTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const balanceValue = canonical_AMOUNT(); + auto const ownerCountValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AccountRootBuilder builder{ + accountValue, + sequenceValue, + balanceValue, + ownerCountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAccountTxnID()); + EXPECT_FALSE(entry.getAccountTxnID().has_value()); + EXPECT_FALSE(entry.hasRegularKey()); + EXPECT_FALSE(entry.getRegularKey().has_value()); + EXPECT_FALSE(entry.hasEmailHash()); + EXPECT_FALSE(entry.getEmailHash().has_value()); + EXPECT_FALSE(entry.hasWalletLocator()); + EXPECT_FALSE(entry.getWalletLocator().has_value()); + EXPECT_FALSE(entry.hasWalletSize()); + EXPECT_FALSE(entry.getWalletSize().has_value()); + EXPECT_FALSE(entry.hasMessageKey()); + EXPECT_FALSE(entry.getMessageKey().has_value()); + EXPECT_FALSE(entry.hasTransferRate()); + EXPECT_FALSE(entry.getTransferRate().has_value()); + EXPECT_FALSE(entry.hasDomain()); + EXPECT_FALSE(entry.getDomain().has_value()); + EXPECT_FALSE(entry.hasTickSize()); + EXPECT_FALSE(entry.getTickSize().has_value()); + EXPECT_FALSE(entry.hasTicketCount()); + EXPECT_FALSE(entry.getTicketCount().has_value()); + EXPECT_FALSE(entry.hasNFTokenMinter()); + EXPECT_FALSE(entry.getNFTokenMinter().has_value()); + EXPECT_FALSE(entry.hasMintedNFTokens()); + EXPECT_FALSE(entry.getMintedNFTokens().has_value()); + EXPECT_FALSE(entry.hasBurnedNFTokens()); + EXPECT_FALSE(entry.getBurnedNFTokens().has_value()); + EXPECT_FALSE(entry.hasFirstNFTokenSequence()); + EXPECT_FALSE(entry.getFirstNFTokenSequence().has_value()); + EXPECT_FALSE(entry.hasAMMID()); + EXPECT_FALSE(entry.getAMMID().has_value()); + EXPECT_FALSE(entry.hasVaultID()); + EXPECT_FALSE(entry.getVaultID().has_value()); + EXPECT_FALSE(entry.hasLoanBrokerID()); + EXPECT_FALSE(entry.getLoanBrokerID().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp new file mode 100644 index 0000000000..17da755a3d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp @@ -0,0 +1,224 @@ +// Auto-generated unit tests for ledger entry Amendments + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(AmendmentsTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const amendmentsValue = canonical_VECTOR256(); + auto const majoritiesValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + AmendmentsBuilder builder{ + }; + + builder.setAmendments(amendmentsValue); + builder.setMajorities(majoritiesValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = amendmentsValue; + auto const actualOpt = entry.getAmendments(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAmendments"); + EXPECT_TRUE(entry.hasAmendments()); + } + + { + auto const& expected = majoritiesValue; + auto const actualOpt = entry.getMajorities(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMajorities"); + EXPECT_TRUE(entry.hasMajorities()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(AmendmentsTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const amendmentsValue = canonical_VECTOR256(); + auto const majoritiesValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Amendments::entryType, index); + + sle->at(sfAmendments) = amendmentsValue; + sle->setFieldArray(sfMajorities, majoritiesValue); + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + AmendmentsBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Amendments entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = amendmentsValue; + + auto const fromSleOpt = entryFromSle.getAmendments(); + auto const fromBuilderOpt = entryFromBuilder.getAmendments(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAmendments"); + expectEqualField(expected, *fromBuilderOpt, "sfAmendments"); + } + + { + auto const& expected = majoritiesValue; + + auto const fromSleOpt = entryFromSle.getMajorities(); + auto const fromBuilderOpt = entryFromBuilder.getMajorities(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMajorities"); + expectEqualField(expected, *fromBuilderOpt, "sfMajorities"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(AmendmentsTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Amendments{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(AmendmentsTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(AmendmentsBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(AmendmentsTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + AmendmentsBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAmendments()); + EXPECT_FALSE(entry.getAmendments().has_value()); + EXPECT_FALSE(entry.hasMajorities()); + EXPECT_FALSE(entry.getMajorities().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp new file mode 100644 index 0000000000..1d4cfc00b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp @@ -0,0 +1,341 @@ +// Auto-generated unit tests for ledger entry Bridge + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(BridgeTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + BridgeBuilder builder{ + accountValue, + signatureRewardValue, + xChainBridgeValue, + xChainClaimIDValue, + xChainAccountCreateCountValue, + xChainAccountClaimCountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = entry.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = entry.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = entry.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainAccountClaimCountValue; + auto const actual = entry.getXChainAccountClaimCount(); + expectEqualField(expected, actual, "sfXChainAccountClaimCount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = entry.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(entry.hasMinAccountCreateAmount()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(BridgeTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Bridge::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSignatureReward) = signatureRewardValue; + sle->at(sfMinAccountCreateAmount) = minAccountCreateAmountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainClaimID) = xChainClaimIDValue; + sle->at(sfXChainAccountCreateCount) = xChainAccountCreateCountValue; + sle->at(sfXChainAccountClaimCount) = xChainAccountClaimCountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + BridgeBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Bridge entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = signatureRewardValue; + + auto const fromSle = entryFromSle.getSignatureReward(); + auto const fromBuilder = entryFromBuilder.getSignatureReward(); + + expectEqualField(expected, fromSle, "sfSignatureReward"); + expectEqualField(expected, fromBuilder, "sfSignatureReward"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + + auto const fromSle = entryFromSle.getXChainClaimID(); + auto const fromBuilder = entryFromBuilder.getXChainClaimID(); + + expectEqualField(expected, fromSle, "sfXChainClaimID"); + expectEqualField(expected, fromBuilder, "sfXChainClaimID"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + + auto const fromSle = entryFromSle.getXChainAccountCreateCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountCreateCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountCreateCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainAccountClaimCountValue; + + auto const fromSle = entryFromSle.getXChainAccountClaimCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountClaimCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountClaimCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountClaimCount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = minAccountCreateAmountValue; + + auto const fromSleOpt = entryFromSle.getMinAccountCreateAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMinAccountCreateAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMinAccountCreateAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMinAccountCreateAmount"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(BridgeTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Bridge{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(BridgeTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(BridgeBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(BridgeTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainAccountClaimCountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + BridgeBuilder builder{ + accountValue, + signatureRewardValue, + xChainBridgeValue, + xChainClaimIDValue, + xChainAccountCreateCountValue, + xChainAccountClaimCountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasMinAccountCreateAmount()); + EXPECT_FALSE(entry.getMinAccountCreateAmount().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp new file mode 100644 index 0000000000..f14b4fb4e4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp @@ -0,0 +1,400 @@ +// Auto-generated unit tests for ledger entry Check + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(CheckTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const expirationValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CheckBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + ownerNodeValue, + destinationNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setInvoiceID(invoiceIDValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = entry.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = destinationNodeValue; + auto const actual = entry.getDestinationNode(); + expectEqualField(expected, actual, "sfDestinationNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = entry.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(entry.hasInvoiceID()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(CheckTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const expirationValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Check::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfSendMax) = sendMaxValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfDestinationNode) = destinationNodeValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfInvoiceID) = invoiceIDValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + CheckBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Check entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + + auto const fromSle = entryFromSle.getSendMax(); + auto const fromBuilder = entryFromBuilder.getSendMax(); + + expectEqualField(expected, fromSle, "sfSendMax"); + expectEqualField(expected, fromBuilder, "sfSendMax"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSle = entryFromSle.getDestinationNode(); + auto const fromBuilder = entryFromBuilder.getDestinationNode(); + + expectEqualField(expected, fromSle, "sfDestinationNode"); + expectEqualField(expected, fromBuilder, "sfDestinationNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = invoiceIDValue; + + auto const fromSleOpt = entryFromSle.getInvoiceID(); + auto const fromBuilderOpt = entryFromBuilder.getInvoiceID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfInvoiceID"); + expectEqualField(expected, *fromBuilderOpt, "sfInvoiceID"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(CheckTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Check{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(CheckTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(CheckBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(CheckTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const destinationNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CheckBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + ownerNodeValue, + destinationNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasInvoiceID()); + EXPECT_FALSE(entry.getInvoiceID().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp new file mode 100644 index 0000000000..4fbf6d2a26 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp @@ -0,0 +1,329 @@ +// Auto-generated unit tests for ledger entry Credential + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(CredentialTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const subjectNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CredentialBuilder builder{ + subjectValue, + issuerValue, + credentialTypeValue, + issuerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setURI(uRIValue); + builder.setSubjectNode(subjectNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = subjectValue; + auto const actual = entry.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = issuerValue; + auto const actual = entry.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = entry.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + { + auto const& expected = issuerNodeValue; + auto const actual = entry.getIssuerNode(); + expectEqualField(expected, actual, "sfIssuerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + { + auto const& expected = subjectNodeValue; + auto const actualOpt = entry.getSubjectNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSubjectNode"); + EXPECT_TRUE(entry.hasSubjectNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(CredentialTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const subjectNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Credential::entryType, index); + + sle->at(sfSubject) = subjectValue; + sle->at(sfIssuer) = issuerValue; + sle->at(sfCredentialType) = credentialTypeValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfURI) = uRIValue; + sle->at(sfIssuerNode) = issuerNodeValue; + sle->at(sfSubjectNode) = subjectNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + CredentialBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Credential entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = subjectValue; + + auto const fromSle = entryFromSle.getSubject(); + auto const fromBuilder = entryFromBuilder.getSubject(); + + expectEqualField(expected, fromSle, "sfSubject"); + expectEqualField(expected, fromBuilder, "sfSubject"); + } + + { + auto const& expected = issuerValue; + + auto const fromSle = entryFromSle.getIssuer(); + auto const fromBuilder = entryFromBuilder.getIssuer(); + + expectEqualField(expected, fromSle, "sfIssuer"); + expectEqualField(expected, fromBuilder, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + + auto const fromSle = entryFromSle.getCredentialType(); + auto const fromBuilder = entryFromBuilder.getCredentialType(); + + expectEqualField(expected, fromSle, "sfCredentialType"); + expectEqualField(expected, fromBuilder, "sfCredentialType"); + } + + { + auto const& expected = issuerNodeValue; + + auto const fromSle = entryFromSle.getIssuerNode(); + auto const fromBuilder = entryFromBuilder.getIssuerNode(); + + expectEqualField(expected, fromSle, "sfIssuerNode"); + expectEqualField(expected, fromBuilder, "sfIssuerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + { + auto const& expected = subjectNodeValue; + + auto const fromSleOpt = entryFromSle.getSubjectNode(); + auto const fromBuilderOpt = entryFromBuilder.getSubjectNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSubjectNode"); + expectEqualField(expected, *fromBuilderOpt, "sfSubjectNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(CredentialTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Credential{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(CredentialTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(CredentialBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(CredentialTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const issuerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + CredentialBuilder builder{ + subjectValue, + issuerValue, + credentialTypeValue, + issuerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); + EXPECT_FALSE(entry.hasSubjectNode()); + EXPECT_FALSE(entry.getSubjectNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp new file mode 100644 index 0000000000..29d49a47f2 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp @@ -0,0 +1,285 @@ +// Auto-generated unit tests for ledger entry DID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DIDBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setDIDDocument(dIDDocumentValue); + builder.setURI(uRIValue); + builder.setData(dataValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = entry.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + EXPECT_TRUE(entry.hasDIDDocument()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + { + auto const& expected = dataValue; + auto const actualOpt = entry.getData(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfData"); + EXPECT_TRUE(entry.hasData()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(DID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDIDDocument) = dIDDocumentValue; + sle->at(sfURI) = uRIValue; + sle->at(sfData) = dataValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + DIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = dIDDocumentValue; + + auto const fromSleOpt = entryFromSle.getDIDDocument(); + auto const fromBuilderOpt = entryFromBuilder.getDIDDocument(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDIDDocument"); + expectEqualField(expected, *fromBuilderOpt, "sfDIDDocument"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + { + 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"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DIDTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DIDBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDIDDocument()); + EXPECT_FALSE(entry.getDIDDocument().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp new file mode 100644 index 0000000000..68d0e6c08e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp @@ -0,0 +1,223 @@ +// Auto-generated unit tests for ledger entry Delegate + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DelegateTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DelegateBuilder builder{ + accountValue, + authorizeValue, + permissionsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = authorizeValue; + auto const actual = entry.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = entry.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DelegateTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Delegate::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfAuthorize) = authorizeValue; + sle->setFieldArray(sfPermissions, permissionsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + DelegateBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Delegate entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = authorizeValue; + + auto const fromSle = entryFromSle.getAuthorize(); + auto const fromBuilder = entryFromBuilder.getAuthorize(); + + expectEqualField(expected, fromSle, "sfAuthorize"); + expectEqualField(expected, fromBuilder, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + + auto const fromSle = entryFromSle.getPermissions(); + auto const fromBuilder = entryFromBuilder.getPermissions(); + + expectEqualField(expected, fromSle, "sfPermissions"); + expectEqualField(expected, fromBuilder, "sfPermissions"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DelegateTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Delegate{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DelegateTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DelegateBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp new file mode 100644 index 0000000000..9e6144d5b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp @@ -0,0 +1,258 @@ +// Auto-generated unit tests for ledger entry DepositPreauth + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DepositPreauthTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + + DepositPreauthBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setAuthorize(authorizeValue); + builder.setAuthorizeCredentials(authorizeCredentialsValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = authorizeValue; + auto const actualOpt = entry.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuthorize"); + EXPECT_TRUE(entry.hasAuthorize()); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = entry.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + EXPECT_TRUE(entry.hasAuthorizeCredentials()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DepositPreauthTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const authorizeValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + + auto sle = std::make_shared(DepositPreauth::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfAuthorize) = authorizeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->setFieldArray(sfAuthorizeCredentials, authorizeCredentialsValue); + + DepositPreauthBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DepositPreauth entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = authorizeValue; + + auto const fromSleOpt = entryFromSle.getAuthorize(); + auto const fromBuilderOpt = entryFromBuilder.getAuthorize(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuthorize"); + expectEqualField(expected, *fromBuilderOpt, "sfAuthorize"); + } + + { + auto const& expected = authorizeCredentialsValue; + + auto const fromSleOpt = entryFromSle.getAuthorizeCredentials(); + auto const fromBuilderOpt = entryFromBuilder.getAuthorizeCredentials(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAuthorizeCredentials"); + expectEqualField(expected, *fromBuilderOpt, "sfAuthorizeCredentials"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DepositPreauthTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DepositPreauth{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DepositPreauthTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DepositPreauthBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DepositPreauthTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + DepositPreauthBuilder builder{ + accountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasAuthorize()); + EXPECT_FALSE(entry.getAuthorize().has_value()); + EXPECT_FALSE(entry.hasAuthorizeCredentials()); + EXPECT_FALSE(entry.getAuthorizeCredentials().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp new file mode 100644 index 0000000000..d1b6edc704 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp @@ -0,0 +1,484 @@ +// Auto-generated unit tests for ledger entry DirectoryNode + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(DirectoryNodeTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const takerPaysCurrencyValue = canonical_UINT160(); + auto const takerPaysIssuerValue = canonical_UINT160(); + auto const takerGetsCurrencyValue = canonical_UINT160(); + auto const takerGetsIssuerValue = canonical_UINT160(); + auto const exchangeRateValue = canonical_UINT64(); + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + auto const indexNextValue = canonical_UINT64(); + auto const indexPreviousValue = canonical_UINT64(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + DirectoryNodeBuilder builder{ + indexesValue, + rootIndexValue + }; + + builder.setOwner(ownerValue); + builder.setTakerPaysCurrency(takerPaysCurrencyValue); + builder.setTakerPaysIssuer(takerPaysIssuerValue); + builder.setTakerGetsCurrency(takerGetsCurrencyValue); + builder.setTakerGetsIssuer(takerGetsIssuerValue); + builder.setExchangeRate(exchangeRateValue); + builder.setIndexNext(indexNextValue); + builder.setIndexPrevious(indexPreviousValue); + builder.setNFTokenID(nFTokenIDValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + builder.setDomainID(domainIDValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = indexesValue; + auto const actual = entry.getIndexes(); + expectEqualField(expected, actual, "sfIndexes"); + } + + { + auto const& expected = rootIndexValue; + auto const actual = entry.getRootIndex(); + expectEqualField(expected, actual, "sfRootIndex"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = entry.getOwner(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(entry.hasOwner()); + } + + { + auto const& expected = takerPaysCurrencyValue; + auto const actualOpt = entry.getTakerPaysCurrency(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerPaysCurrency"); + EXPECT_TRUE(entry.hasTakerPaysCurrency()); + } + + { + auto const& expected = takerPaysIssuerValue; + auto const actualOpt = entry.getTakerPaysIssuer(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerPaysIssuer"); + EXPECT_TRUE(entry.hasTakerPaysIssuer()); + } + + { + auto const& expected = takerGetsCurrencyValue; + auto const actualOpt = entry.getTakerGetsCurrency(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerGetsCurrency"); + EXPECT_TRUE(entry.hasTakerGetsCurrency()); + } + + { + auto const& expected = takerGetsIssuerValue; + auto const actualOpt = entry.getTakerGetsIssuer(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTakerGetsIssuer"); + EXPECT_TRUE(entry.hasTakerGetsIssuer()); + } + + { + auto const& expected = exchangeRateValue; + auto const actualOpt = entry.getExchangeRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExchangeRate"); + EXPECT_TRUE(entry.hasExchangeRate()); + } + + { + auto const& expected = indexNextValue; + auto const actualOpt = entry.getIndexNext(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIndexNext"); + EXPECT_TRUE(entry.hasIndexNext()); + } + + { + auto const& expected = indexPreviousValue; + auto const actualOpt = entry.getIndexPrevious(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIndexPrevious"); + EXPECT_TRUE(entry.hasIndexPrevious()); + } + + { + auto const& expected = nFTokenIDValue; + auto const actualOpt = entry.getNFTokenID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNFTokenID"); + EXPECT_TRUE(entry.hasNFTokenID()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(DirectoryNodeTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const takerPaysCurrencyValue = canonical_UINT160(); + auto const takerPaysIssuerValue = canonical_UINT160(); + auto const takerGetsCurrencyValue = canonical_UINT160(); + auto const takerGetsIssuerValue = canonical_UINT160(); + auto const exchangeRateValue = canonical_UINT64(); + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + auto const indexNextValue = canonical_UINT64(); + auto const indexPreviousValue = canonical_UINT64(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + auto sle = std::make_shared(DirectoryNode::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfTakerPaysCurrency) = takerPaysCurrencyValue; + sle->at(sfTakerPaysIssuer) = takerPaysIssuerValue; + sle->at(sfTakerGetsCurrency) = takerGetsCurrencyValue; + sle->at(sfTakerGetsIssuer) = takerGetsIssuerValue; + sle->at(sfExchangeRate) = exchangeRateValue; + sle->at(sfIndexes) = indexesValue; + sle->at(sfRootIndex) = rootIndexValue; + sle->at(sfIndexNext) = indexNextValue; + sle->at(sfIndexPrevious) = indexPreviousValue; + sle->at(sfNFTokenID) = nFTokenIDValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDomainID) = domainIDValue; + + DirectoryNodeBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + DirectoryNode entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = indexesValue; + + auto const fromSle = entryFromSle.getIndexes(); + auto const fromBuilder = entryFromBuilder.getIndexes(); + + expectEqualField(expected, fromSle, "sfIndexes"); + expectEqualField(expected, fromBuilder, "sfIndexes"); + } + + { + auto const& expected = rootIndexValue; + + auto const fromSle = entryFromSle.getRootIndex(); + auto const fromBuilder = entryFromBuilder.getRootIndex(); + + expectEqualField(expected, fromSle, "sfRootIndex"); + expectEqualField(expected, fromBuilder, "sfRootIndex"); + } + + { + auto const& expected = ownerValue; + + auto const fromSleOpt = entryFromSle.getOwner(); + auto const fromBuilderOpt = entryFromBuilder.getOwner(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwner"); + expectEqualField(expected, *fromBuilderOpt, "sfOwner"); + } + + { + auto const& expected = takerPaysCurrencyValue; + + auto const fromSleOpt = entryFromSle.getTakerPaysCurrency(); + auto const fromBuilderOpt = entryFromBuilder.getTakerPaysCurrency(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerPaysCurrency"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysCurrency"); + } + + { + auto const& expected = takerPaysIssuerValue; + + auto const fromSleOpt = entryFromSle.getTakerPaysIssuer(); + auto const fromBuilderOpt = entryFromBuilder.getTakerPaysIssuer(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerPaysIssuer"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysIssuer"); + } + + { + auto const& expected = takerGetsCurrencyValue; + + auto const fromSleOpt = entryFromSle.getTakerGetsCurrency(); + auto const fromBuilderOpt = entryFromBuilder.getTakerGetsCurrency(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerGetsCurrency"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsCurrency"); + } + + { + auto const& expected = takerGetsIssuerValue; + + auto const fromSleOpt = entryFromSle.getTakerGetsIssuer(); + auto const fromBuilderOpt = entryFromBuilder.getTakerGetsIssuer(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTakerGetsIssuer"); + expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsIssuer"); + } + + { + auto const& expected = exchangeRateValue; + + auto const fromSleOpt = entryFromSle.getExchangeRate(); + auto const fromBuilderOpt = entryFromBuilder.getExchangeRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExchangeRate"); + expectEqualField(expected, *fromBuilderOpt, "sfExchangeRate"); + } + + { + auto const& expected = indexNextValue; + + auto const fromSleOpt = entryFromSle.getIndexNext(); + auto const fromBuilderOpt = entryFromBuilder.getIndexNext(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIndexNext"); + expectEqualField(expected, *fromBuilderOpt, "sfIndexNext"); + } + + { + auto const& expected = indexPreviousValue; + + auto const fromSleOpt = entryFromSle.getIndexPrevious(); + auto const fromBuilderOpt = entryFromBuilder.getIndexPrevious(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIndexPrevious"); + expectEqualField(expected, *fromBuilderOpt, "sfIndexPrevious"); + } + + { + auto const& expected = nFTokenIDValue; + + auto const fromSleOpt = entryFromSle.getNFTokenID(); + auto const fromBuilderOpt = entryFromBuilder.getNFTokenID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNFTokenID"); + expectEqualField(expected, *fromBuilderOpt, "sfNFTokenID"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(DirectoryNodeTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DirectoryNode{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(DirectoryNodeTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(DirectoryNodeBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(DirectoryNodeTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const indexesValue = canonical_VECTOR256(); + auto const rootIndexValue = canonical_UINT256(); + + DirectoryNodeBuilder builder{ + indexesValue, + rootIndexValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOwner()); + EXPECT_FALSE(entry.getOwner().has_value()); + EXPECT_FALSE(entry.hasTakerPaysCurrency()); + EXPECT_FALSE(entry.getTakerPaysCurrency().has_value()); + EXPECT_FALSE(entry.hasTakerPaysIssuer()); + EXPECT_FALSE(entry.getTakerPaysIssuer().has_value()); + EXPECT_FALSE(entry.hasTakerGetsCurrency()); + EXPECT_FALSE(entry.getTakerGetsCurrency().has_value()); + EXPECT_FALSE(entry.hasTakerGetsIssuer()); + EXPECT_FALSE(entry.getTakerGetsIssuer().has_value()); + EXPECT_FALSE(entry.hasExchangeRate()); + EXPECT_FALSE(entry.getExchangeRate().has_value()); + EXPECT_FALSE(entry.hasIndexNext()); + EXPECT_FALSE(entry.getIndexNext().has_value()); + EXPECT_FALSE(entry.hasIndexPrevious()); + EXPECT_FALSE(entry.getIndexPrevious().has_value()); + EXPECT_FALSE(entry.hasNFTokenID()); + EXPECT_FALSE(entry.getNFTokenID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp new file mode 100644 index 0000000000..2dbb450e28 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp @@ -0,0 +1,491 @@ +// Auto-generated unit tests for ledger entry Escrow + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(EscrowTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + auto const transferRateValue = canonical_UINT32(); + auto const issuerNodeValue = canonical_UINT64(); + + EscrowBuilder builder{ + accountValue, + destinationValue, + amountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setSequence(sequenceValue); + builder.setCondition(conditionValue); + builder.setCancelAfter(cancelAfterValue); + builder.setFinishAfter(finishAfterValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + builder.setDestinationNode(destinationNodeValue); + builder.setTransferRate(transferRateValue); + builder.setIssuerNode(issuerNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actualOpt = entry.getSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSequence"); + EXPECT_TRUE(entry.hasSequence()); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = entry.getCondition(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(entry.hasCondition()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = entry.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(entry.hasCancelAfter()); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = entry.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + EXPECT_TRUE(entry.hasFinishAfter()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + { + auto const& expected = destinationNodeValue; + auto const actualOpt = entry.getDestinationNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationNode"); + EXPECT_TRUE(entry.hasDestinationNode()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = entry.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(entry.hasTransferRate()); + } + + { + auto const& expected = issuerNodeValue; + auto const actualOpt = entry.getIssuerNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfIssuerNode"); + EXPECT_TRUE(entry.hasIssuerNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(EscrowTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const conditionValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const finishAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + auto const transferRateValue = canonical_UINT32(); + auto const issuerNodeValue = canonical_UINT64(); + + auto sle = std::make_shared(Escrow::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfAmount) = amountValue; + sle->at(sfCondition) = conditionValue; + sle->at(sfCancelAfter) = cancelAfterValue; + sle->at(sfFinishAfter) = finishAfterValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDestinationNode) = destinationNodeValue; + sle->at(sfTransferRate) = transferRateValue; + sle->at(sfIssuerNode) = issuerNodeValue; + + EscrowBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Escrow entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSleOpt = entryFromSle.getSequence(); + auto const fromBuilderOpt = entryFromBuilder.getSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfSequence"); + } + + { + auto const& expected = conditionValue; + + auto const fromSleOpt = entryFromSle.getCondition(); + auto const fromBuilderOpt = entryFromBuilder.getCondition(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCondition"); + expectEqualField(expected, *fromBuilderOpt, "sfCondition"); + } + + { + auto const& expected = cancelAfterValue; + + auto const fromSleOpt = entryFromSle.getCancelAfter(); + auto const fromBuilderOpt = entryFromBuilder.getCancelAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCancelAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfCancelAfter"); + } + + { + auto const& expected = finishAfterValue; + + auto const fromSleOpt = entryFromSle.getFinishAfter(); + auto const fromBuilderOpt = entryFromBuilder.getFinishAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFinishAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfFinishAfter"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSleOpt = entryFromSle.getDestinationNode(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationNode"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationNode"); + } + + { + auto const& expected = transferRateValue; + + auto const fromSleOpt = entryFromSle.getTransferRate(); + auto const fromBuilderOpt = entryFromBuilder.getTransferRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferRate"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferRate"); + } + + { + auto const& expected = issuerNodeValue; + + auto const fromSleOpt = entryFromSle.getIssuerNode(); + auto const fromBuilderOpt = entryFromBuilder.getIssuerNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfIssuerNode"); + expectEqualField(expected, *fromBuilderOpt, "sfIssuerNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(EscrowTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Escrow{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(EscrowTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(EscrowBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(EscrowTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + EscrowBuilder builder{ + accountValue, + destinationValue, + amountValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasSequence()); + EXPECT_FALSE(entry.getSequence().has_value()); + EXPECT_FALSE(entry.hasCondition()); + EXPECT_FALSE(entry.getCondition().has_value()); + EXPECT_FALSE(entry.hasCancelAfter()); + EXPECT_FALSE(entry.getCancelAfter().has_value()); + EXPECT_FALSE(entry.hasFinishAfter()); + EXPECT_FALSE(entry.getFinishAfter().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); + EXPECT_FALSE(entry.hasDestinationNode()); + EXPECT_FALSE(entry.getDestinationNode().has_value()); + EXPECT_FALSE(entry.hasTransferRate()); + EXPECT_FALSE(entry.getTransferRate().has_value()); + EXPECT_FALSE(entry.hasIssuerNode()); + EXPECT_FALSE(entry.getIssuerNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp new file mode 100644 index 0000000000..479d0c56c6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp @@ -0,0 +1,359 @@ +// Auto-generated unit tests for ledger entry FeeSettings + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(FeeSettingsTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + FeeSettingsBuilder builder{ + }; + + builder.setBaseFee(baseFeeValue); + builder.setReferenceFeeUnits(referenceFeeUnitsValue); + builder.setReserveBase(reserveBaseValue); + builder.setReserveIncrement(reserveIncrementValue); + builder.setBaseFeeDrops(baseFeeDropsValue); + builder.setReserveBaseDrops(reserveBaseDropsValue); + builder.setReserveIncrementDrops(reserveIncrementDropsValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = baseFeeValue; + auto const actualOpt = entry.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBaseFee"); + EXPECT_TRUE(entry.hasBaseFee()); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = entry.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + EXPECT_TRUE(entry.hasReferenceFeeUnits()); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = entry.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveBase"); + EXPECT_TRUE(entry.hasReserveBase()); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = entry.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + EXPECT_TRUE(entry.hasReserveIncrement()); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = entry.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + EXPECT_TRUE(entry.hasBaseFeeDrops()); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = entry.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + EXPECT_TRUE(entry.hasReserveBaseDrops()); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = entry.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + EXPECT_TRUE(entry.hasReserveIncrementDrops()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(FeeSettingsTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(FeeSettings::entryType, index); + + sle->at(sfBaseFee) = baseFeeValue; + sle->at(sfReferenceFeeUnits) = referenceFeeUnitsValue; + sle->at(sfReserveBase) = reserveBaseValue; + sle->at(sfReserveIncrement) = reserveIncrementValue; + sle->at(sfBaseFeeDrops) = baseFeeDropsValue; + sle->at(sfReserveBaseDrops) = reserveBaseDropsValue; + sle->at(sfReserveIncrementDrops) = reserveIncrementDropsValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + FeeSettingsBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + FeeSettings entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = baseFeeValue; + + auto const fromSleOpt = entryFromSle.getBaseFee(); + auto const fromBuilderOpt = entryFromBuilder.getBaseFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBaseFee"); + expectEqualField(expected, *fromBuilderOpt, "sfBaseFee"); + } + + { + auto const& expected = referenceFeeUnitsValue; + + auto const fromSleOpt = entryFromSle.getReferenceFeeUnits(); + auto const fromBuilderOpt = entryFromBuilder.getReferenceFeeUnits(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReferenceFeeUnits"); + expectEqualField(expected, *fromBuilderOpt, "sfReferenceFeeUnits"); + } + + { + auto const& expected = reserveBaseValue; + + auto const fromSleOpt = entryFromSle.getReserveBase(); + auto const fromBuilderOpt = entryFromBuilder.getReserveBase(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveBase"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveBase"); + } + + { + auto const& expected = reserveIncrementValue; + + auto const fromSleOpt = entryFromSle.getReserveIncrement(); + auto const fromBuilderOpt = entryFromBuilder.getReserveIncrement(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveIncrement"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveIncrement"); + } + + { + auto const& expected = baseFeeDropsValue; + + auto const fromSleOpt = entryFromSle.getBaseFeeDrops(); + auto const fromBuilderOpt = entryFromBuilder.getBaseFeeDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfBaseFeeDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfBaseFeeDrops"); + } + + { + auto const& expected = reserveBaseDropsValue; + + auto const fromSleOpt = entryFromSle.getReserveBaseDrops(); + auto const fromBuilderOpt = entryFromBuilder.getReserveBaseDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveBaseDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveBaseDrops"); + } + + { + auto const& expected = reserveIncrementDropsValue; + + auto const fromSleOpt = entryFromSle.getReserveIncrementDrops(); + auto const fromBuilderOpt = entryFromBuilder.getReserveIncrementDrops(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReserveIncrementDrops"); + expectEqualField(expected, *fromBuilderOpt, "sfReserveIncrementDrops"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(FeeSettingsTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(FeeSettings{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(FeeSettingsTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(FeeSettingsBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(FeeSettingsTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + FeeSettingsBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasBaseFee()); + EXPECT_FALSE(entry.getBaseFee().has_value()); + EXPECT_FALSE(entry.hasReferenceFeeUnits()); + EXPECT_FALSE(entry.getReferenceFeeUnits().has_value()); + EXPECT_FALSE(entry.hasReserveBase()); + EXPECT_FALSE(entry.getReserveBase().has_value()); + EXPECT_FALSE(entry.hasReserveIncrement()); + EXPECT_FALSE(entry.getReserveIncrement().has_value()); + EXPECT_FALSE(entry.hasBaseFeeDrops()); + EXPECT_FALSE(entry.getBaseFeeDrops().has_value()); + EXPECT_FALSE(entry.hasReserveBaseDrops()); + EXPECT_FALSE(entry.getReserveBaseDrops().has_value()); + EXPECT_FALSE(entry.hasReserveIncrementDrops()); + EXPECT_FALSE(entry.getReserveIncrementDrops().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp new file mode 100644 index 0000000000..8a7ae38749 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp @@ -0,0 +1,192 @@ +// Auto-generated unit tests for ledger entry LedgerHashes + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LedgerHashesTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const firstLedgerSequenceValue = canonical_UINT32(); + auto const lastLedgerSequenceValue = canonical_UINT32(); + auto const hashesValue = canonical_VECTOR256(); + + LedgerHashesBuilder builder{ + hashesValue + }; + + builder.setFirstLedgerSequence(firstLedgerSequenceValue); + builder.setLastLedgerSequence(lastLedgerSequenceValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = hashesValue; + auto const actual = entry.getHashes(); + expectEqualField(expected, actual, "sfHashes"); + } + + { + auto const& expected = firstLedgerSequenceValue; + auto const actualOpt = entry.getFirstLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFirstLedgerSequence"); + EXPECT_TRUE(entry.hasFirstLedgerSequence()); + } + + { + auto const& expected = lastLedgerSequenceValue; + auto const actualOpt = entry.getLastLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLastLedgerSequence"); + EXPECT_TRUE(entry.hasLastLedgerSequence()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LedgerHashesTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const firstLedgerSequenceValue = canonical_UINT32(); + auto const lastLedgerSequenceValue = canonical_UINT32(); + auto const hashesValue = canonical_VECTOR256(); + + auto sle = std::make_shared(LedgerHashes::entryType, index); + + sle->at(sfFirstLedgerSequence) = firstLedgerSequenceValue; + sle->at(sfLastLedgerSequence) = lastLedgerSequenceValue; + sle->at(sfHashes) = hashesValue; + + LedgerHashesBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + LedgerHashes entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = hashesValue; + + auto const fromSle = entryFromSle.getHashes(); + auto const fromBuilder = entryFromBuilder.getHashes(); + + expectEqualField(expected, fromSle, "sfHashes"); + expectEqualField(expected, fromBuilder, "sfHashes"); + } + + { + auto const& expected = firstLedgerSequenceValue; + + auto const fromSleOpt = entryFromSle.getFirstLedgerSequence(); + auto const fromBuilderOpt = entryFromBuilder.getFirstLedgerSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFirstLedgerSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfFirstLedgerSequence"); + } + + { + auto const& expected = lastLedgerSequenceValue; + + auto const fromSleOpt = entryFromSle.getLastLedgerSequence(); + auto const fromBuilderOpt = entryFromBuilder.getLastLedgerSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLastLedgerSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfLastLedgerSequence"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LedgerHashesTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LedgerHashes{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LedgerHashesTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LedgerHashesBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LedgerHashesTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const hashesValue = canonical_VECTOR256(); + + LedgerHashesBuilder builder{ + hashesValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasFirstLedgerSequence()); + EXPECT_FALSE(entry.getFirstLedgerSequence().has_value()); + EXPECT_FALSE(entry.hasLastLedgerSequence()); + EXPECT_FALSE(entry.getLastLedgerSequence().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp new file mode 100644 index 0000000000..fba8c539d2 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp @@ -0,0 +1,530 @@ +// Auto-generated unit tests for ledger entry LoanBroker + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LoanBrokerTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const ownerCountValue = canonical_UINT32(); + auto const debtTotalValue = canonical_NUMBER(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverAvailableValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + LoanBrokerBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + vaultNodeValue, + vaultIDValue, + accountValue, + ownerValue, + loanSequenceValue + }; + + builder.setData(dataValue); + builder.setManagementFeeRate(managementFeeRateValue); + builder.setOwnerCount(ownerCountValue); + builder.setDebtTotal(debtTotalValue); + builder.setDebtMaximum(debtMaximumValue); + builder.setCoverAvailable(coverAvailableValue); + builder.setCoverRateMinimum(coverRateMinimumValue); + builder.setCoverRateLiquidation(coverRateLiquidationValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = vaultNodeValue; + auto const actual = entry.getVaultNode(); + expectEqualField(expected, actual, "sfVaultNode"); + } + + { + auto const& expected = vaultIDValue; + auto const actual = entry.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = loanSequenceValue; + auto const actual = entry.getLoanSequence(); + expectEqualField(expected, actual, "sfLoanSequence"); + } + + { + 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 = managementFeeRateValue; + auto const actualOpt = entry.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + EXPECT_TRUE(entry.hasManagementFeeRate()); + } + + { + auto const& expected = ownerCountValue; + auto const actualOpt = entry.getOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwnerCount"); + EXPECT_TRUE(entry.hasOwnerCount()); + } + + { + auto const& expected = debtTotalValue; + auto const actualOpt = entry.getDebtTotal(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDebtTotal"); + EXPECT_TRUE(entry.hasDebtTotal()); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = entry.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + EXPECT_TRUE(entry.hasDebtMaximum()); + } + + { + auto const& expected = coverAvailableValue; + auto const actualOpt = entry.getCoverAvailable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverAvailable"); + EXPECT_TRUE(entry.hasCoverAvailable()); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = entry.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + EXPECT_TRUE(entry.hasCoverRateMinimum()); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = entry.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + EXPECT_TRUE(entry.hasCoverRateLiquidation()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LoanBrokerTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const ownerCountValue = canonical_UINT32(); + auto const debtTotalValue = canonical_NUMBER(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverAvailableValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + auto sle = std::make_shared(LoanBroker::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfVaultNode) = vaultNodeValue; + sle->at(sfVaultID) = vaultIDValue; + sle->at(sfAccount) = accountValue; + sle->at(sfOwner) = ownerValue; + sle->at(sfLoanSequence) = loanSequenceValue; + sle->at(sfData) = dataValue; + sle->at(sfManagementFeeRate) = managementFeeRateValue; + sle->at(sfOwnerCount) = ownerCountValue; + sle->at(sfDebtTotal) = debtTotalValue; + sle->at(sfDebtMaximum) = debtMaximumValue; + sle->at(sfCoverAvailable) = coverAvailableValue; + sle->at(sfCoverRateMinimum) = coverRateMinimumValue; + sle->at(sfCoverRateLiquidation) = coverRateLiquidationValue; + + LoanBrokerBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + LoanBroker entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = vaultNodeValue; + + auto const fromSle = entryFromSle.getVaultNode(); + auto const fromBuilder = entryFromBuilder.getVaultNode(); + + expectEqualField(expected, fromSle, "sfVaultNode"); + expectEqualField(expected, fromBuilder, "sfVaultNode"); + } + + { + auto const& expected = vaultIDValue; + + auto const fromSle = entryFromSle.getVaultID(); + auto const fromBuilder = entryFromBuilder.getVaultID(); + + expectEqualField(expected, fromSle, "sfVaultID"); + expectEqualField(expected, fromBuilder, "sfVaultID"); + } + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = loanSequenceValue; + + auto const fromSle = entryFromSle.getLoanSequence(); + auto const fromBuilder = entryFromBuilder.getLoanSequence(); + + expectEqualField(expected, fromSle, "sfLoanSequence"); + expectEqualField(expected, fromBuilder, "sfLoanSequence"); + } + + { + 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 = managementFeeRateValue; + + auto const fromSleOpt = entryFromSle.getManagementFeeRate(); + auto const fromBuilderOpt = entryFromBuilder.getManagementFeeRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfManagementFeeRate"); + expectEqualField(expected, *fromBuilderOpt, "sfManagementFeeRate"); + } + + { + auto const& expected = ownerCountValue; + + auto const fromSleOpt = entryFromSle.getOwnerCount(); + auto const fromBuilderOpt = entryFromBuilder.getOwnerCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwnerCount"); + expectEqualField(expected, *fromBuilderOpt, "sfOwnerCount"); + } + + { + auto const& expected = debtTotalValue; + + auto const fromSleOpt = entryFromSle.getDebtTotal(); + auto const fromBuilderOpt = entryFromBuilder.getDebtTotal(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDebtTotal"); + expectEqualField(expected, *fromBuilderOpt, "sfDebtTotal"); + } + + { + auto const& expected = debtMaximumValue; + + auto const fromSleOpt = entryFromSle.getDebtMaximum(); + auto const fromBuilderOpt = entryFromBuilder.getDebtMaximum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDebtMaximum"); + expectEqualField(expected, *fromBuilderOpt, "sfDebtMaximum"); + } + + { + auto const& expected = coverAvailableValue; + + auto const fromSleOpt = entryFromSle.getCoverAvailable(); + auto const fromBuilderOpt = entryFromBuilder.getCoverAvailable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverAvailable"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverAvailable"); + } + + { + auto const& expected = coverRateMinimumValue; + + auto const fromSleOpt = entryFromSle.getCoverRateMinimum(); + auto const fromBuilderOpt = entryFromBuilder.getCoverRateMinimum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverRateMinimum"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverRateMinimum"); + } + + { + auto const& expected = coverRateLiquidationValue; + + auto const fromSleOpt = entryFromSle.getCoverRateLiquidation(); + auto const fromBuilderOpt = entryFromBuilder.getCoverRateLiquidation(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCoverRateLiquidation"); + expectEqualField(expected, *fromBuilderOpt, "sfCoverRateLiquidation"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LoanBrokerTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBroker{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LoanBrokerTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBrokerBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LoanBrokerTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const vaultNodeValue = canonical_UINT64(); + auto const vaultIDValue = canonical_UINT256(); + auto const accountValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const loanSequenceValue = canonical_UINT32(); + + LoanBrokerBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + vaultNodeValue, + vaultIDValue, + accountValue, + ownerValue, + loanSequenceValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); + EXPECT_FALSE(entry.hasManagementFeeRate()); + EXPECT_FALSE(entry.getManagementFeeRate().has_value()); + EXPECT_FALSE(entry.hasOwnerCount()); + EXPECT_FALSE(entry.getOwnerCount().has_value()); + EXPECT_FALSE(entry.hasDebtTotal()); + EXPECT_FALSE(entry.getDebtTotal().has_value()); + EXPECT_FALSE(entry.hasDebtMaximum()); + EXPECT_FALSE(entry.getDebtMaximum().has_value()); + EXPECT_FALSE(entry.hasCoverAvailable()); + EXPECT_FALSE(entry.getCoverAvailable().has_value()); + EXPECT_FALSE(entry.hasCoverRateMinimum()); + EXPECT_FALSE(entry.getCoverRateMinimum().has_value()); + EXPECT_FALSE(entry.hasCoverRateLiquidation()); + EXPECT_FALSE(entry.getCoverRateLiquidation().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp new file mode 100644 index 0000000000..845a8337b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp @@ -0,0 +1,795 @@ +// Auto-generated unit tests for ledger entry Loan + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(LoanTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + auto const previousPaymentDueDateValue = canonical_UINT32(); + auto const nextPaymentDueDateValue = canonical_UINT32(); + auto const paymentRemainingValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + auto const principalOutstandingValue = canonical_NUMBER(); + auto const totalValueOutstandingValue = canonical_NUMBER(); + auto const managementFeeOutstandingValue = canonical_NUMBER(); + auto const loanScaleValue = canonical_INT32(); + + LoanBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerNodeValue, + loanBrokerNodeValue, + loanBrokerIDValue, + loanSequenceValue, + borrowerValue, + startDateValue, + paymentIntervalValue, + periodicPaymentValue + }; + + builder.setLoanOriginationFee(loanOriginationFeeValue); + builder.setLoanServiceFee(loanServiceFeeValue); + builder.setLatePaymentFee(latePaymentFeeValue); + builder.setClosePaymentFee(closePaymentFeeValue); + builder.setOverpaymentFee(overpaymentFeeValue); + builder.setInterestRate(interestRateValue); + builder.setLateInterestRate(lateInterestRateValue); + builder.setCloseInterestRate(closeInterestRateValue); + builder.setOverpaymentInterestRate(overpaymentInterestRateValue); + builder.setGracePeriod(gracePeriodValue); + builder.setPreviousPaymentDueDate(previousPaymentDueDateValue); + builder.setNextPaymentDueDate(nextPaymentDueDateValue); + builder.setPaymentRemaining(paymentRemainingValue); + builder.setPrincipalOutstanding(principalOutstandingValue); + builder.setTotalValueOutstanding(totalValueOutstandingValue); + builder.setManagementFeeOutstanding(managementFeeOutstandingValue); + builder.setLoanScale(loanScaleValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = loanBrokerNodeValue; + auto const actual = entry.getLoanBrokerNode(); + expectEqualField(expected, actual, "sfLoanBrokerNode"); + } + + { + auto const& expected = loanBrokerIDValue; + auto const actual = entry.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = loanSequenceValue; + auto const actual = entry.getLoanSequence(); + expectEqualField(expected, actual, "sfLoanSequence"); + } + + { + auto const& expected = borrowerValue; + auto const actual = entry.getBorrower(); + expectEqualField(expected, actual, "sfBorrower"); + } + + { + auto const& expected = startDateValue; + auto const actual = entry.getStartDate(); + expectEqualField(expected, actual, "sfStartDate"); + } + + { + auto const& expected = paymentIntervalValue; + auto const actual = entry.getPaymentInterval(); + expectEqualField(expected, actual, "sfPaymentInterval"); + } + + { + auto const& expected = periodicPaymentValue; + auto const actual = entry.getPeriodicPayment(); + expectEqualField(expected, actual, "sfPeriodicPayment"); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = entry.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + EXPECT_TRUE(entry.hasLoanOriginationFee()); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = entry.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + EXPECT_TRUE(entry.hasLoanServiceFee()); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = entry.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + EXPECT_TRUE(entry.hasLatePaymentFee()); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = entry.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + EXPECT_TRUE(entry.hasClosePaymentFee()); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = entry.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + EXPECT_TRUE(entry.hasOverpaymentFee()); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = entry.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfInterestRate"); + EXPECT_TRUE(entry.hasInterestRate()); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = entry.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + EXPECT_TRUE(entry.hasLateInterestRate()); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = entry.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + EXPECT_TRUE(entry.hasCloseInterestRate()); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = entry.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + EXPECT_TRUE(entry.hasOverpaymentInterestRate()); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = entry.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + EXPECT_TRUE(entry.hasGracePeriod()); + } + + { + auto const& expected = previousPaymentDueDateValue; + auto const actualOpt = entry.getPreviousPaymentDueDate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousPaymentDueDate"); + EXPECT_TRUE(entry.hasPreviousPaymentDueDate()); + } + + { + auto const& expected = nextPaymentDueDateValue; + auto const actualOpt = entry.getNextPaymentDueDate(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNextPaymentDueDate"); + EXPECT_TRUE(entry.hasNextPaymentDueDate()); + } + + { + auto const& expected = paymentRemainingValue; + auto const actualOpt = entry.getPaymentRemaining(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPaymentRemaining"); + EXPECT_TRUE(entry.hasPaymentRemaining()); + } + + { + auto const& expected = principalOutstandingValue; + auto const actualOpt = entry.getPrincipalOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPrincipalOutstanding"); + EXPECT_TRUE(entry.hasPrincipalOutstanding()); + } + + { + auto const& expected = totalValueOutstandingValue; + auto const actualOpt = entry.getTotalValueOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTotalValueOutstanding"); + EXPECT_TRUE(entry.hasTotalValueOutstanding()); + } + + { + auto const& expected = managementFeeOutstandingValue; + auto const actualOpt = entry.getManagementFeeOutstanding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfManagementFeeOutstanding"); + EXPECT_TRUE(entry.hasManagementFeeOutstanding()); + } + + { + auto const& expected = loanScaleValue; + auto const actualOpt = entry.getLoanScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLoanScale"); + EXPECT_TRUE(entry.hasLoanScale()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(LoanTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + auto const previousPaymentDueDateValue = canonical_UINT32(); + auto const nextPaymentDueDateValue = canonical_UINT32(); + auto const paymentRemainingValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + auto const principalOutstandingValue = canonical_NUMBER(); + auto const totalValueOutstandingValue = canonical_NUMBER(); + auto const managementFeeOutstandingValue = canonical_NUMBER(); + auto const loanScaleValue = canonical_INT32(); + + auto sle = std::make_shared(Loan::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfLoanBrokerNode) = loanBrokerNodeValue; + sle->at(sfLoanBrokerID) = loanBrokerIDValue; + sle->at(sfLoanSequence) = loanSequenceValue; + sle->at(sfBorrower) = borrowerValue; + sle->at(sfLoanOriginationFee) = loanOriginationFeeValue; + sle->at(sfLoanServiceFee) = loanServiceFeeValue; + sle->at(sfLatePaymentFee) = latePaymentFeeValue; + sle->at(sfClosePaymentFee) = closePaymentFeeValue; + sle->at(sfOverpaymentFee) = overpaymentFeeValue; + sle->at(sfInterestRate) = interestRateValue; + sle->at(sfLateInterestRate) = lateInterestRateValue; + sle->at(sfCloseInterestRate) = closeInterestRateValue; + sle->at(sfOverpaymentInterestRate) = overpaymentInterestRateValue; + sle->at(sfStartDate) = startDateValue; + sle->at(sfPaymentInterval) = paymentIntervalValue; + sle->at(sfGracePeriod) = gracePeriodValue; + sle->at(sfPreviousPaymentDueDate) = previousPaymentDueDateValue; + sle->at(sfNextPaymentDueDate) = nextPaymentDueDateValue; + sle->at(sfPaymentRemaining) = paymentRemainingValue; + sle->at(sfPeriodicPayment) = periodicPaymentValue; + sle->at(sfPrincipalOutstanding) = principalOutstandingValue; + sle->at(sfTotalValueOutstanding) = totalValueOutstandingValue; + sle->at(sfManagementFeeOutstanding) = managementFeeOutstandingValue; + sle->at(sfLoanScale) = loanScaleValue; + + LoanBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Loan entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = loanBrokerNodeValue; + + auto const fromSle = entryFromSle.getLoanBrokerNode(); + auto const fromBuilder = entryFromBuilder.getLoanBrokerNode(); + + expectEqualField(expected, fromSle, "sfLoanBrokerNode"); + expectEqualField(expected, fromBuilder, "sfLoanBrokerNode"); + } + + { + auto const& expected = loanBrokerIDValue; + + auto const fromSle = entryFromSle.getLoanBrokerID(); + auto const fromBuilder = entryFromBuilder.getLoanBrokerID(); + + expectEqualField(expected, fromSle, "sfLoanBrokerID"); + expectEqualField(expected, fromBuilder, "sfLoanBrokerID"); + } + + { + auto const& expected = loanSequenceValue; + + auto const fromSle = entryFromSle.getLoanSequence(); + auto const fromBuilder = entryFromBuilder.getLoanSequence(); + + expectEqualField(expected, fromSle, "sfLoanSequence"); + expectEqualField(expected, fromBuilder, "sfLoanSequence"); + } + + { + auto const& expected = borrowerValue; + + auto const fromSle = entryFromSle.getBorrower(); + auto const fromBuilder = entryFromBuilder.getBorrower(); + + expectEqualField(expected, fromSle, "sfBorrower"); + expectEqualField(expected, fromBuilder, "sfBorrower"); + } + + { + auto const& expected = startDateValue; + + auto const fromSle = entryFromSle.getStartDate(); + auto const fromBuilder = entryFromBuilder.getStartDate(); + + expectEqualField(expected, fromSle, "sfStartDate"); + expectEqualField(expected, fromBuilder, "sfStartDate"); + } + + { + auto const& expected = paymentIntervalValue; + + auto const fromSle = entryFromSle.getPaymentInterval(); + auto const fromBuilder = entryFromBuilder.getPaymentInterval(); + + expectEqualField(expected, fromSle, "sfPaymentInterval"); + expectEqualField(expected, fromBuilder, "sfPaymentInterval"); + } + + { + auto const& expected = periodicPaymentValue; + + auto const fromSle = entryFromSle.getPeriodicPayment(); + auto const fromBuilder = entryFromBuilder.getPeriodicPayment(); + + expectEqualField(expected, fromSle, "sfPeriodicPayment"); + expectEqualField(expected, fromBuilder, "sfPeriodicPayment"); + } + + { + auto const& expected = loanOriginationFeeValue; + + auto const fromSleOpt = entryFromSle.getLoanOriginationFee(); + auto const fromBuilderOpt = entryFromBuilder.getLoanOriginationFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanOriginationFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanOriginationFee"); + } + + { + auto const& expected = loanServiceFeeValue; + + auto const fromSleOpt = entryFromSle.getLoanServiceFee(); + auto const fromBuilderOpt = entryFromBuilder.getLoanServiceFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanServiceFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanServiceFee"); + } + + { + auto const& expected = latePaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getLatePaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getLatePaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLatePaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfLatePaymentFee"); + } + + { + auto const& expected = closePaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getClosePaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getClosePaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfClosePaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfClosePaymentFee"); + } + + { + auto const& expected = overpaymentFeeValue; + + auto const fromSleOpt = entryFromSle.getOverpaymentFee(); + auto const fromBuilderOpt = entryFromBuilder.getOverpaymentFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOverpaymentFee"); + expectEqualField(expected, *fromBuilderOpt, "sfOverpaymentFee"); + } + + { + auto const& expected = interestRateValue; + + auto const fromSleOpt = entryFromSle.getInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfInterestRate"); + } + + { + auto const& expected = lateInterestRateValue; + + auto const fromSleOpt = entryFromSle.getLateInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getLateInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLateInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfLateInterestRate"); + } + + { + auto const& expected = closeInterestRateValue; + + auto const fromSleOpt = entryFromSle.getCloseInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getCloseInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCloseInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfCloseInterestRate"); + } + + { + auto const& expected = overpaymentInterestRateValue; + + auto const fromSleOpt = entryFromSle.getOverpaymentInterestRate(); + auto const fromBuilderOpt = entryFromBuilder.getOverpaymentInterestRate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOverpaymentInterestRate"); + expectEqualField(expected, *fromBuilderOpt, "sfOverpaymentInterestRate"); + } + + { + auto const& expected = gracePeriodValue; + + auto const fromSleOpt = entryFromSle.getGracePeriod(); + auto const fromBuilderOpt = entryFromBuilder.getGracePeriod(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfGracePeriod"); + expectEqualField(expected, *fromBuilderOpt, "sfGracePeriod"); + } + + { + auto const& expected = previousPaymentDueDateValue; + + auto const fromSleOpt = entryFromSle.getPreviousPaymentDueDate(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousPaymentDueDate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousPaymentDueDate"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousPaymentDueDate"); + } + + { + auto const& expected = nextPaymentDueDateValue; + + auto const fromSleOpt = entryFromSle.getNextPaymentDueDate(); + auto const fromBuilderOpt = entryFromBuilder.getNextPaymentDueDate(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNextPaymentDueDate"); + expectEqualField(expected, *fromBuilderOpt, "sfNextPaymentDueDate"); + } + + { + auto const& expected = paymentRemainingValue; + + auto const fromSleOpt = entryFromSle.getPaymentRemaining(); + auto const fromBuilderOpt = entryFromBuilder.getPaymentRemaining(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPaymentRemaining"); + expectEqualField(expected, *fromBuilderOpt, "sfPaymentRemaining"); + } + + { + auto const& expected = principalOutstandingValue; + + auto const fromSleOpt = entryFromSle.getPrincipalOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getPrincipalOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPrincipalOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfPrincipalOutstanding"); + } + + { + auto const& expected = totalValueOutstandingValue; + + auto const fromSleOpt = entryFromSle.getTotalValueOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getTotalValueOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTotalValueOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfTotalValueOutstanding"); + } + + { + auto const& expected = managementFeeOutstandingValue; + + auto const fromSleOpt = entryFromSle.getManagementFeeOutstanding(); + auto const fromBuilderOpt = entryFromBuilder.getManagementFeeOutstanding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfManagementFeeOutstanding"); + expectEqualField(expected, *fromBuilderOpt, "sfManagementFeeOutstanding"); + } + + { + auto const& expected = loanScaleValue; + + auto const fromSleOpt = entryFromSle.getLoanScale(); + auto const fromBuilderOpt = entryFromBuilder.getLoanScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLoanScale"); + expectEqualField(expected, *fromBuilderOpt, "sfLoanScale"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(LoanTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Loan{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(LoanTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(LoanBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(LoanTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const loanBrokerNodeValue = canonical_UINT64(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const loanSequenceValue = canonical_UINT32(); + auto const borrowerValue = canonical_ACCOUNT(); + auto const startDateValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const periodicPaymentValue = canonical_NUMBER(); + + LoanBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerNodeValue, + loanBrokerNodeValue, + loanBrokerIDValue, + loanSequenceValue, + borrowerValue, + startDateValue, + paymentIntervalValue, + periodicPaymentValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasLoanOriginationFee()); + EXPECT_FALSE(entry.getLoanOriginationFee().has_value()); + EXPECT_FALSE(entry.hasLoanServiceFee()); + EXPECT_FALSE(entry.getLoanServiceFee().has_value()); + EXPECT_FALSE(entry.hasLatePaymentFee()); + EXPECT_FALSE(entry.getLatePaymentFee().has_value()); + EXPECT_FALSE(entry.hasClosePaymentFee()); + EXPECT_FALSE(entry.getClosePaymentFee().has_value()); + EXPECT_FALSE(entry.hasOverpaymentFee()); + EXPECT_FALSE(entry.getOverpaymentFee().has_value()); + EXPECT_FALSE(entry.hasInterestRate()); + EXPECT_FALSE(entry.getInterestRate().has_value()); + EXPECT_FALSE(entry.hasLateInterestRate()); + EXPECT_FALSE(entry.getLateInterestRate().has_value()); + EXPECT_FALSE(entry.hasCloseInterestRate()); + EXPECT_FALSE(entry.getCloseInterestRate().has_value()); + EXPECT_FALSE(entry.hasOverpaymentInterestRate()); + EXPECT_FALSE(entry.getOverpaymentInterestRate().has_value()); + EXPECT_FALSE(entry.hasGracePeriod()); + EXPECT_FALSE(entry.getGracePeriod().has_value()); + EXPECT_FALSE(entry.hasPreviousPaymentDueDate()); + EXPECT_FALSE(entry.getPreviousPaymentDueDate().has_value()); + EXPECT_FALSE(entry.hasNextPaymentDueDate()); + EXPECT_FALSE(entry.getNextPaymentDueDate().has_value()); + EXPECT_FALSE(entry.hasPaymentRemaining()); + EXPECT_FALSE(entry.getPaymentRemaining().has_value()); + EXPECT_FALSE(entry.hasPrincipalOutstanding()); + EXPECT_FALSE(entry.getPrincipalOutstanding().has_value()); + EXPECT_FALSE(entry.hasTotalValueOutstanding()); + EXPECT_FALSE(entry.getTotalValueOutstanding().has_value()); + EXPECT_FALSE(entry.hasManagementFeeOutstanding()); + EXPECT_FALSE(entry.getManagementFeeOutstanding().has_value()); + EXPECT_FALSE(entry.hasLoanScale()); + EXPECT_FALSE(entry.getLoanScale().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp new file mode 100644 index 0000000000..e74af94a5f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp @@ -0,0 +1,437 @@ +// Auto-generated unit tests for ledger entry MPTokenIssuance + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const ownerNodeValue = canonical_UINT64(); + auto const assetScaleValue = canonical_UINT8(); + auto const maximumAmountValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceBuilder builder{ + issuerValue, + sequenceValue, + ownerNodeValue, + outstandingAmountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setTransferFee(transferFeeValue); + builder.setAssetScale(assetScaleValue); + builder.setMaximumAmount(maximumAmountValue); + builder.setLockedAmount(lockedAmountValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setMutableFlags(mutableFlagsValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = issuerValue; + auto const actual = entry.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = outstandingAmountValue; + auto const actual = entry.getOutstandingAmount(); + expectEqualField(expected, actual, "sfOutstandingAmount"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = entry.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(entry.hasTransferFee()); + } + + { + auto const& expected = assetScaleValue; + auto const actualOpt = entry.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetScale"); + EXPECT_TRUE(entry.hasAssetScale()); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = entry.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + EXPECT_TRUE(entry.hasMaximumAmount()); + } + + { + auto const& expected = lockedAmountValue; + auto const actualOpt = entry.getLockedAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLockedAmount"); + EXPECT_TRUE(entry.hasLockedAmount()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = entry.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(entry.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = entry.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(entry.hasMutableFlags()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const ownerNodeValue = canonical_UINT64(); + auto const assetScaleValue = canonical_UINT8(); + auto const maximumAmountValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + auto sle = std::make_shared(MPTokenIssuance::entryType, index); + + sle->at(sfIssuer) = issuerValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfTransferFee) = transferFeeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfAssetScale) = assetScaleValue; + sle->at(sfMaximumAmount) = maximumAmountValue; + sle->at(sfOutstandingAmount) = outstandingAmountValue; + sle->at(sfLockedAmount) = lockedAmountValue; + sle->at(sfMPTokenMetadata) = mPTokenMetadataValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDomainID) = domainIDValue; + sle->at(sfMutableFlags) = mutableFlagsValue; + + MPTokenIssuanceBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + MPTokenIssuance entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = issuerValue; + + auto const fromSle = entryFromSle.getIssuer(); + auto const fromBuilder = entryFromBuilder.getIssuer(); + + expectEqualField(expected, fromSle, "sfIssuer"); + expectEqualField(expected, fromBuilder, "sfIssuer"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = outstandingAmountValue; + + auto const fromSle = entryFromSle.getOutstandingAmount(); + auto const fromBuilder = entryFromBuilder.getOutstandingAmount(); + + expectEqualField(expected, fromSle, "sfOutstandingAmount"); + expectEqualField(expected, fromBuilder, "sfOutstandingAmount"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = transferFeeValue; + + auto const fromSleOpt = entryFromSle.getTransferFee(); + auto const fromBuilderOpt = entryFromBuilder.getTransferFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfTransferFee"); + expectEqualField(expected, *fromBuilderOpt, "sfTransferFee"); + } + + { + auto const& expected = assetScaleValue; + + auto const fromSleOpt = entryFromSle.getAssetScale(); + auto const fromBuilderOpt = entryFromBuilder.getAssetScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetScale"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetScale"); + } + + { + auto const& expected = maximumAmountValue; + + auto const fromSleOpt = entryFromSle.getMaximumAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMaximumAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMaximumAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMaximumAmount"); + } + + { + auto const& expected = lockedAmountValue; + + auto const fromSleOpt = entryFromSle.getLockedAmount(); + auto const fromBuilderOpt = entryFromBuilder.getLockedAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLockedAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfLockedAmount"); + } + + { + auto const& expected = mPTokenMetadataValue; + + auto const fromSleOpt = entryFromSle.getMPTokenMetadata(); + auto const fromBuilderOpt = entryFromBuilder.getMPTokenMetadata(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMPTokenMetadata"); + expectEqualField(expected, *fromBuilderOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + { + auto const& expected = mutableFlagsValue; + + auto const fromSleOpt = entryFromSle.getMutableFlags(); + auto const fromBuilderOpt = entryFromBuilder.getMutableFlags(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMutableFlags"); + expectEqualField(expected, *fromBuilderOpt, "sfMutableFlags"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(MPTokenIssuanceTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenIssuance{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(MPTokenIssuanceTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenIssuanceBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const issuerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const outstandingAmountValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenIssuanceBuilder builder{ + issuerValue, + sequenceValue, + ownerNodeValue, + outstandingAmountValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasTransferFee()); + EXPECT_FALSE(entry.getTransferFee().has_value()); + EXPECT_FALSE(entry.hasAssetScale()); + EXPECT_FALSE(entry.getAssetScale().has_value()); + EXPECT_FALSE(entry.hasMaximumAmount()); + EXPECT_FALSE(entry.getMaximumAmount().has_value()); + EXPECT_FALSE(entry.hasLockedAmount()); + EXPECT_FALSE(entry.getLockedAmount().has_value()); + EXPECT_FALSE(entry.hasMPTokenMetadata()); + EXPECT_FALSE(entry.getMPTokenMetadata().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); + EXPECT_FALSE(entry.hasMutableFlags()); + EXPECT_FALSE(entry.getMutableFlags().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp new file mode 100644 index 0000000000..c104e7b365 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp @@ -0,0 +1,280 @@ +// Auto-generated unit tests for ledger entry MPToken + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(MPTokenTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setMPTAmount(mPTAmountValue); + builder.setLockedAmount(lockedAmountValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = entry.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = mPTAmountValue; + auto const actualOpt = entry.getMPTAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMPTAmount"); + EXPECT_TRUE(entry.hasMPTAmount()); + } + + { + auto const& expected = lockedAmountValue; + auto const actualOpt = entry.getLockedAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLockedAmount"); + EXPECT_TRUE(entry.hasLockedAmount()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(MPTokenTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const lockedAmountValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(MPToken::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfMPTokenIssuanceID) = mPTokenIssuanceIDValue; + sle->at(sfMPTAmount) = mPTAmountValue; + sle->at(sfLockedAmount) = lockedAmountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + MPTokenBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + MPToken entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = mPTokenIssuanceIDValue; + + auto const fromSle = entryFromSle.getMPTokenIssuanceID(); + auto const fromBuilder = entryFromBuilder.getMPTokenIssuanceID(); + + expectEqualField(expected, fromSle, "sfMPTokenIssuanceID"); + expectEqualField(expected, fromBuilder, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = mPTAmountValue; + + auto const fromSleOpt = entryFromSle.getMPTAmount(); + auto const fromBuilderOpt = entryFromBuilder.getMPTAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMPTAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfMPTAmount"); + } + + { + auto const& expected = lockedAmountValue; + + auto const fromSleOpt = entryFromSle.getLockedAmount(); + auto const fromBuilderOpt = entryFromBuilder.getLockedAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLockedAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfLockedAmount"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(MPTokenTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPToken{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(MPTokenTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(MPTokenBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(MPTokenTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + MPTokenBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasMPTAmount()); + EXPECT_FALSE(entry.getMPTAmount().has_value()); + EXPECT_FALSE(entry.hasLockedAmount()); + EXPECT_FALSE(entry.getLockedAmount().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp new file mode 100644 index 0000000000..9d039b2c38 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp @@ -0,0 +1,324 @@ +// Auto-generated unit tests for ledger entry NFTokenOffer + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NFTokenOfferTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenOfferBuilder builder{ + ownerValue, + nFTokenIDValue, + amountValue, + ownerNodeValue, + nFTokenOfferNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setDestination(destinationValue); + builder.setExpiration(expirationValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = nFTokenIDValue; + auto const actual = entry.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = nFTokenOfferNodeValue; + auto const actual = entry.getNFTokenOfferNode(); + expectEqualField(expected, actual, "sfNFTokenOfferNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = entry.getDestination(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(entry.hasDestination()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NFTokenOfferTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NFTokenOffer::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfNFTokenID) = nFTokenIDValue; + sle->at(sfAmount) = amountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfNFTokenOfferNode) = nFTokenOfferNodeValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NFTokenOfferBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NFTokenOffer entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = nFTokenIDValue; + + auto const fromSle = entryFromSle.getNFTokenID(); + auto const fromBuilder = entryFromBuilder.getNFTokenID(); + + expectEqualField(expected, fromSle, "sfNFTokenID"); + expectEqualField(expected, fromBuilder, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = nFTokenOfferNodeValue; + + auto const fromSle = entryFromSle.getNFTokenOfferNode(); + auto const fromBuilder = entryFromBuilder.getNFTokenOfferNode(); + + expectEqualField(expected, fromSle, "sfNFTokenOfferNode"); + expectEqualField(expected, fromBuilder, "sfNFTokenOfferNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = destinationValue; + + auto const fromSleOpt = entryFromSle.getDestination(); + auto const fromBuilderOpt = entryFromBuilder.getDestination(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestination"); + expectEqualField(expected, *fromBuilderOpt, "sfDestination"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NFTokenOfferTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenOffer{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NFTokenOfferTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenOfferBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NFTokenOfferTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const nFTokenOfferNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenOfferBuilder builder{ + ownerValue, + nFTokenIDValue, + amountValue, + ownerNodeValue, + nFTokenOfferNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDestination()); + EXPECT_FALSE(entry.getDestination().has_value()); + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp new file mode 100644 index 0000000000..5909050169 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp @@ -0,0 +1,236 @@ +// Auto-generated unit tests for ledger entry NFTokenPage + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NFTokenPageTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousPageMinValue = canonical_UINT256(); + auto const nextPageMinValue = canonical_UINT256(); + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenPageBuilder builder{ + nFTokensValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setPreviousPageMin(previousPageMinValue); + builder.setNextPageMin(nextPageMinValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = nFTokensValue; + auto const actual = entry.getNFTokens(); + expectEqualField(expected, actual, "sfNFTokens"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = previousPageMinValue; + auto const actualOpt = entry.getPreviousPageMin(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousPageMin"); + EXPECT_TRUE(entry.hasPreviousPageMin()); + } + + { + auto const& expected = nextPageMinValue; + auto const actualOpt = entry.getNextPageMin(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfNextPageMin"); + EXPECT_TRUE(entry.hasNextPageMin()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NFTokenPageTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousPageMinValue = canonical_UINT256(); + auto const nextPageMinValue = canonical_UINT256(); + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NFTokenPage::entryType, index); + + sle->at(sfPreviousPageMin) = previousPageMinValue; + sle->at(sfNextPageMin) = nextPageMinValue; + sle->setFieldArray(sfNFTokens, nFTokensValue); + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NFTokenPageBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NFTokenPage entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = nFTokensValue; + + auto const fromSle = entryFromSle.getNFTokens(); + auto const fromBuilder = entryFromBuilder.getNFTokens(); + + expectEqualField(expected, fromSle, "sfNFTokens"); + expectEqualField(expected, fromBuilder, "sfNFTokens"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = previousPageMinValue; + + auto const fromSleOpt = entryFromSle.getPreviousPageMin(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousPageMin(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousPageMin"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousPageMin"); + } + + { + auto const& expected = nextPageMinValue; + + auto const fromSleOpt = entryFromSle.getNextPageMin(); + auto const fromBuilderOpt = entryFromBuilder.getNextPageMin(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfNextPageMin"); + expectEqualField(expected, *fromBuilderOpt, "sfNextPageMin"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NFTokenPageTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenPage{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NFTokenPageTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NFTokenPageBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NFTokenPageTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const nFTokensValue = canonical_ARRAY(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NFTokenPageBuilder builder{ + nFTokensValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasPreviousPageMin()); + EXPECT_FALSE(entry.getPreviousPageMin().has_value()); + EXPECT_FALSE(entry.hasNextPageMin()); + EXPECT_FALSE(entry.getNextPageMin().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp new file mode 100644 index 0000000000..48ff060c11 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp @@ -0,0 +1,251 @@ +// Auto-generated unit tests for ledger entry NegativeUNL + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(NegativeUNLTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const disabledValidatorsValue = canonical_ARRAY(); + auto const validatorToDisableValue = canonical_VL(); + auto const validatorToReEnableValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + NegativeUNLBuilder builder{ + }; + + builder.setDisabledValidators(disabledValidatorsValue); + builder.setValidatorToDisable(validatorToDisableValue); + builder.setValidatorToReEnable(validatorToReEnableValue); + builder.setPreviousTxnID(previousTxnIDValue); + builder.setPreviousTxnLgrSeq(previousTxnLgrSeqValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = disabledValidatorsValue; + auto const actualOpt = entry.getDisabledValidators(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDisabledValidators"); + EXPECT_TRUE(entry.hasDisabledValidators()); + } + + { + auto const& expected = validatorToDisableValue; + auto const actualOpt = entry.getValidatorToDisable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfValidatorToDisable"); + EXPECT_TRUE(entry.hasValidatorToDisable()); + } + + { + auto const& expected = validatorToReEnableValue; + auto const actualOpt = entry.getValidatorToReEnable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfValidatorToReEnable"); + EXPECT_TRUE(entry.hasValidatorToReEnable()); + } + + { + auto const& expected = previousTxnIDValue; + auto const actualOpt = entry.getPreviousTxnID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnID"); + EXPECT_TRUE(entry.hasPreviousTxnID()); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actualOpt = entry.getPreviousTxnLgrSeq(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfPreviousTxnLgrSeq"); + EXPECT_TRUE(entry.hasPreviousTxnLgrSeq()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(NegativeUNLTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const disabledValidatorsValue = canonical_ARRAY(); + auto const validatorToDisableValue = canonical_VL(); + auto const validatorToReEnableValue = canonical_VL(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(NegativeUNL::entryType, index); + + sle->setFieldArray(sfDisabledValidators, disabledValidatorsValue); + sle->at(sfValidatorToDisable) = validatorToDisableValue; + sle->at(sfValidatorToReEnable) = validatorToReEnableValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + NegativeUNLBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + NegativeUNL entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = disabledValidatorsValue; + + auto const fromSleOpt = entryFromSle.getDisabledValidators(); + auto const fromBuilderOpt = entryFromBuilder.getDisabledValidators(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDisabledValidators"); + expectEqualField(expected, *fromBuilderOpt, "sfDisabledValidators"); + } + + { + auto const& expected = validatorToDisableValue; + + auto const fromSleOpt = entryFromSle.getValidatorToDisable(); + auto const fromBuilderOpt = entryFromBuilder.getValidatorToDisable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfValidatorToDisable"); + expectEqualField(expected, *fromBuilderOpt, "sfValidatorToDisable"); + } + + { + auto const& expected = validatorToReEnableValue; + + auto const fromSleOpt = entryFromSle.getValidatorToReEnable(); + auto const fromBuilderOpt = entryFromBuilder.getValidatorToReEnable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfValidatorToReEnable"); + expectEqualField(expected, *fromBuilderOpt, "sfValidatorToReEnable"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnID(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnID"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSleOpt = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilderOpt = entryFromBuilder.getPreviousTxnLgrSeq(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, *fromBuilderOpt, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(NegativeUNLTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NegativeUNL{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(NegativeUNLTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(NegativeUNLBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(NegativeUNLTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + + NegativeUNLBuilder builder{ + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasDisabledValidators()); + EXPECT_FALSE(entry.getDisabledValidators().has_value()); + EXPECT_FALSE(entry.hasValidatorToDisable()); + EXPECT_FALSE(entry.getValidatorToDisable().has_value()); + EXPECT_FALSE(entry.hasValidatorToReEnable()); + EXPECT_FALSE(entry.getValidatorToReEnable().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnID()); + EXPECT_FALSE(entry.getPreviousTxnID().has_value()); + EXPECT_FALSE(entry.hasPreviousTxnLgrSeq()); + EXPECT_FALSE(entry.getPreviousTxnLgrSeq().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp new file mode 100644 index 0000000000..5cb1bbd804 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp @@ -0,0 +1,395 @@ +// Auto-generated unit tests for ledger entry Offer + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(OfferTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const additionalBooksValue = canonical_ARRAY(); + + OfferBuilder builder{ + accountValue, + sequenceValue, + takerPaysValue, + takerGetsValue, + bookDirectoryValue, + bookNodeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setExpiration(expirationValue); + builder.setDomainID(domainIDValue); + builder.setAdditionalBooks(additionalBooksValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = takerPaysValue; + auto const actual = entry.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = entry.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + { + auto const& expected = bookDirectoryValue; + auto const actual = entry.getBookDirectory(); + expectEqualField(expected, actual, "sfBookDirectory"); + } + + { + auto const& expected = bookNodeValue; + auto const actual = entry.getBookNode(); + expectEqualField(expected, actual, "sfBookNode"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = entry.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(entry.hasDomainID()); + } + + { + auto const& expected = additionalBooksValue; + auto const actualOpt = entry.getAdditionalBooks(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAdditionalBooks"); + EXPECT_TRUE(entry.hasAdditionalBooks()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(OfferTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + auto const additionalBooksValue = canonical_ARRAY(); + + auto sle = std::make_shared(Offer::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfTakerPays) = takerPaysValue; + sle->at(sfTakerGets) = takerGetsValue; + sle->at(sfBookDirectory) = bookDirectoryValue; + sle->at(sfBookNode) = bookNodeValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfDomainID) = domainIDValue; + sle->setFieldArray(sfAdditionalBooks, additionalBooksValue); + + OfferBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Offer entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = takerPaysValue; + + auto const fromSle = entryFromSle.getTakerPays(); + auto const fromBuilder = entryFromBuilder.getTakerPays(); + + expectEqualField(expected, fromSle, "sfTakerPays"); + expectEqualField(expected, fromBuilder, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + + auto const fromSle = entryFromSle.getTakerGets(); + auto const fromBuilder = entryFromBuilder.getTakerGets(); + + expectEqualField(expected, fromSle, "sfTakerGets"); + expectEqualField(expected, fromBuilder, "sfTakerGets"); + } + + { + auto const& expected = bookDirectoryValue; + + auto const fromSle = entryFromSle.getBookDirectory(); + auto const fromBuilder = entryFromBuilder.getBookDirectory(); + + expectEqualField(expected, fromSle, "sfBookDirectory"); + expectEqualField(expected, fromBuilder, "sfBookDirectory"); + } + + { + auto const& expected = bookNodeValue; + + auto const fromSle = entryFromSle.getBookNode(); + auto const fromBuilder = entryFromBuilder.getBookNode(); + + expectEqualField(expected, fromSle, "sfBookNode"); + expectEqualField(expected, fromBuilder, "sfBookNode"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = domainIDValue; + + auto const fromSleOpt = entryFromSle.getDomainID(); + auto const fromBuilderOpt = entryFromBuilder.getDomainID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDomainID"); + expectEqualField(expected, *fromBuilderOpt, "sfDomainID"); + } + + { + auto const& expected = additionalBooksValue; + + auto const fromSleOpt = entryFromSle.getAdditionalBooks(); + auto const fromBuilderOpt = entryFromBuilder.getAdditionalBooks(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAdditionalBooks"); + expectEqualField(expected, *fromBuilderOpt, "sfAdditionalBooks"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(OfferTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Offer{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(OfferTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(OfferBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(OfferTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); + auto const bookNodeValue = canonical_UINT64(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OfferBuilder builder{ + accountValue, + sequenceValue, + takerPaysValue, + takerGetsValue, + bookDirectoryValue, + bookNodeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasDomainID()); + EXPECT_FALSE(entry.getDomainID().has_value()); + EXPECT_FALSE(entry.hasAdditionalBooks()); + EXPECT_FALSE(entry.getAdditionalBooks().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp new file mode 100644 index 0000000000..837fd576bf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp @@ -0,0 +1,346 @@ +// Auto-generated unit tests for ledger entry Oracle + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(OracleTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OracleBuilder builder{ + ownerValue, + providerValue, + priceDataSeriesValue, + assetClassValue, + lastUpdateTimeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setOracleDocumentID(oracleDocumentIDValue); + builder.setURI(uRIValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = providerValue; + auto const actual = entry.getProvider(); + expectEqualField(expected, actual, "sfProvider"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = entry.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + { + auto const& expected = assetClassValue; + auto const actual = entry.getAssetClass(); + expectEqualField(expected, actual, "sfAssetClass"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = entry.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = oracleDocumentIDValue; + auto const actualOpt = entry.getOracleDocumentID(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOracleDocumentID"); + EXPECT_TRUE(entry.hasOracleDocumentID()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = entry.getURI(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(entry.hasURI()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(OracleTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Oracle::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfOracleDocumentID) = oracleDocumentIDValue; + sle->at(sfProvider) = providerValue; + sle->setFieldArray(sfPriceDataSeries, priceDataSeriesValue); + sle->at(sfAssetClass) = assetClassValue; + sle->at(sfLastUpdateTime) = lastUpdateTimeValue; + sle->at(sfURI) = uRIValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + OracleBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Oracle entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = providerValue; + + auto const fromSle = entryFromSle.getProvider(); + auto const fromBuilder = entryFromBuilder.getProvider(); + + expectEqualField(expected, fromSle, "sfProvider"); + expectEqualField(expected, fromBuilder, "sfProvider"); + } + + { + auto const& expected = priceDataSeriesValue; + + auto const fromSle = entryFromSle.getPriceDataSeries(); + auto const fromBuilder = entryFromBuilder.getPriceDataSeries(); + + expectEqualField(expected, fromSle, "sfPriceDataSeries"); + expectEqualField(expected, fromBuilder, "sfPriceDataSeries"); + } + + { + auto const& expected = assetClassValue; + + auto const fromSle = entryFromSle.getAssetClass(); + auto const fromBuilder = entryFromBuilder.getAssetClass(); + + expectEqualField(expected, fromSle, "sfAssetClass"); + expectEqualField(expected, fromBuilder, "sfAssetClass"); + } + + { + auto const& expected = lastUpdateTimeValue; + + auto const fromSle = entryFromSle.getLastUpdateTime(); + auto const fromBuilder = entryFromBuilder.getLastUpdateTime(); + + expectEqualField(expected, fromSle, "sfLastUpdateTime"); + expectEqualField(expected, fromBuilder, "sfLastUpdateTime"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = oracleDocumentIDValue; + + auto const fromSleOpt = entryFromSle.getOracleDocumentID(); + auto const fromBuilderOpt = entryFromBuilder.getOracleDocumentID(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOracleDocumentID"); + expectEqualField(expected, *fromBuilderOpt, "sfOracleDocumentID"); + } + + { + auto const& expected = uRIValue; + + auto const fromSleOpt = entryFromSle.getURI(); + auto const fromBuilderOpt = entryFromBuilder.getURI(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfURI"); + expectEqualField(expected, *fromBuilderOpt, "sfURI"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(OracleTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Oracle{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(OracleTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(OracleBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(OracleTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const providerValue = canonical_VL(); + auto const priceDataSeriesValue = canonical_ARRAY(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + OracleBuilder builder{ + ownerValue, + providerValue, + priceDataSeriesValue, + assetClassValue, + lastUpdateTimeValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOracleDocumentID()); + EXPECT_FALSE(entry.getOracleDocumentID().has_value()); + EXPECT_FALSE(entry.hasURI()); + EXPECT_FALSE(entry.getURI().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp new file mode 100644 index 0000000000..7c31252369 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp @@ -0,0 +1,476 @@ +// Auto-generated unit tests for ledger entry PayChannel + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(PayChannelTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const cancelAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + + PayChannelBuilder builder{ + accountValue, + destinationValue, + amountValue, + balanceValue, + publicKeyValue, + settleDelayValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setSequence(sequenceValue); + builder.setExpiration(expirationValue); + builder.setCancelAfter(cancelAfterValue); + builder.setSourceTag(sourceTagValue); + builder.setDestinationTag(destinationTagValue); + builder.setDestinationNode(destinationNodeValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = destinationValue; + auto const actual = entry.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = entry.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = entry.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = entry.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actualOpt = entry.getSequence(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSequence"); + EXPECT_TRUE(entry.hasSequence()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = entry.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(entry.hasExpiration()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = entry.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(entry.hasCancelAfter()); + } + + { + auto const& expected = sourceTagValue; + auto const actualOpt = entry.getSourceTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSourceTag"); + EXPECT_TRUE(entry.hasSourceTag()); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = entry.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + EXPECT_TRUE(entry.hasDestinationTag()); + } + + { + auto const& expected = destinationNodeValue; + auto const actualOpt = entry.getDestinationNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfDestinationNode"); + EXPECT_TRUE(entry.hasDestinationNode()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(PayChannelTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const expirationValue = canonical_UINT32(); + auto const cancelAfterValue = canonical_UINT32(); + auto const sourceTagValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const destinationNodeValue = canonical_UINT64(); + + auto sle = std::make_shared(PayChannel::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfDestination) = destinationValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfAmount) = amountValue; + sle->at(sfBalance) = balanceValue; + sle->at(sfPublicKey) = publicKeyValue; + sle->at(sfSettleDelay) = settleDelayValue; + sle->at(sfExpiration) = expirationValue; + sle->at(sfCancelAfter) = cancelAfterValue; + sle->at(sfSourceTag) = sourceTagValue; + sle->at(sfDestinationTag) = destinationTagValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfDestinationNode) = destinationNodeValue; + + PayChannelBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + PayChannel entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = destinationValue; + + auto const fromSle = entryFromSle.getDestination(); + auto const fromBuilder = entryFromBuilder.getDestination(); + + expectEqualField(expected, fromSle, "sfDestination"); + expectEqualField(expected, fromBuilder, "sfDestination"); + } + + { + auto const& expected = amountValue; + + auto const fromSle = entryFromSle.getAmount(); + auto const fromBuilder = entryFromBuilder.getAmount(); + + expectEqualField(expected, fromSle, "sfAmount"); + expectEqualField(expected, fromBuilder, "sfAmount"); + } + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = publicKeyValue; + + auto const fromSle = entryFromSle.getPublicKey(); + auto const fromBuilder = entryFromBuilder.getPublicKey(); + + expectEqualField(expected, fromSle, "sfPublicKey"); + expectEqualField(expected, fromBuilder, "sfPublicKey"); + } + + { + auto const& expected = settleDelayValue; + + auto const fromSle = entryFromSle.getSettleDelay(); + auto const fromBuilder = entryFromBuilder.getSettleDelay(); + + expectEqualField(expected, fromSle, "sfSettleDelay"); + expectEqualField(expected, fromBuilder, "sfSettleDelay"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSleOpt = entryFromSle.getSequence(); + auto const fromBuilderOpt = entryFromBuilder.getSequence(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSequence"); + expectEqualField(expected, *fromBuilderOpt, "sfSequence"); + } + + { + auto const& expected = expirationValue; + + auto const fromSleOpt = entryFromSle.getExpiration(); + auto const fromBuilderOpt = entryFromBuilder.getExpiration(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfExpiration"); + expectEqualField(expected, *fromBuilderOpt, "sfExpiration"); + } + + { + auto const& expected = cancelAfterValue; + + auto const fromSleOpt = entryFromSle.getCancelAfter(); + auto const fromBuilderOpt = entryFromBuilder.getCancelAfter(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfCancelAfter"); + expectEqualField(expected, *fromBuilderOpt, "sfCancelAfter"); + } + + { + auto const& expected = sourceTagValue; + + auto const fromSleOpt = entryFromSle.getSourceTag(); + auto const fromBuilderOpt = entryFromBuilder.getSourceTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSourceTag"); + expectEqualField(expected, *fromBuilderOpt, "sfSourceTag"); + } + + { + auto const& expected = destinationTagValue; + + auto const fromSleOpt = entryFromSle.getDestinationTag(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationTag(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationTag"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationTag"); + } + + { + auto const& expected = destinationNodeValue; + + auto const fromSleOpt = entryFromSle.getDestinationNode(); + auto const fromBuilderOpt = entryFromBuilder.getDestinationNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfDestinationNode"); + expectEqualField(expected, *fromBuilderOpt, "sfDestinationNode"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(PayChannelTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PayChannel{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(PayChannelTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PayChannelBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(PayChannelTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const settleDelayValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + PayChannelBuilder builder{ + accountValue, + destinationValue, + amountValue, + balanceValue, + publicKeyValue, + settleDelayValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasSequence()); + EXPECT_FALSE(entry.getSequence().has_value()); + EXPECT_FALSE(entry.hasExpiration()); + EXPECT_FALSE(entry.getExpiration().has_value()); + EXPECT_FALSE(entry.hasCancelAfter()); + EXPECT_FALSE(entry.getCancelAfter().has_value()); + EXPECT_FALSE(entry.hasSourceTag()); + EXPECT_FALSE(entry.getSourceTag().has_value()); + EXPECT_FALSE(entry.hasDestinationTag()); + EXPECT_FALSE(entry.getDestinationTag().has_value()); + EXPECT_FALSE(entry.hasDestinationNode()); + EXPECT_FALSE(entry.getDestinationNode().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp new file mode 100644 index 0000000000..c448d064cf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp @@ -0,0 +1,223 @@ +// Auto-generated unit tests for ledger entry PermissionedDomain + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(PermissionedDomainTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + PermissionedDomainBuilder builder{ + ownerValue, + sequenceValue, + acceptedCredentialsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = acceptedCredentialsValue; + auto const actual = entry.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(PermissionedDomainTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const sequenceValue = canonical_UINT32(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(PermissionedDomain::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfSequence) = sequenceValue; + sle->setFieldArray(sfAcceptedCredentials, acceptedCredentialsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + PermissionedDomainBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + PermissionedDomain entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = acceptedCredentialsValue; + + auto const fromSle = entryFromSle.getAcceptedCredentials(); + auto const fromBuilder = entryFromBuilder.getAcceptedCredentials(); + + expectEqualField(expected, fromSle, "sfAcceptedCredentials"); + expectEqualField(expected, fromBuilder, "sfAcceptedCredentials"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(PermissionedDomainTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PermissionedDomain{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(PermissionedDomainTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(PermissionedDomainBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp new file mode 100644 index 0000000000..a51ce55f6f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp @@ -0,0 +1,388 @@ +// Auto-generated unit tests for ledger entry RippleState + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(RippleStateTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const lowNodeValue = canonical_UINT64(); + auto const lowQualityInValue = canonical_UINT32(); + auto const lowQualityOutValue = canonical_UINT32(); + auto const highNodeValue = canonical_UINT64(); + auto const highQualityInValue = canonical_UINT32(); + auto const highQualityOutValue = canonical_UINT32(); + + RippleStateBuilder builder{ + balanceValue, + lowLimitValue, + highLimitValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setLowNode(lowNodeValue); + builder.setLowQualityIn(lowQualityInValue); + builder.setLowQualityOut(lowQualityOutValue); + builder.setHighNode(highNodeValue); + builder.setHighQualityIn(highQualityInValue); + builder.setHighQualityOut(highQualityOutValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = balanceValue; + auto const actual = entry.getBalance(); + expectEqualField(expected, actual, "sfBalance"); + } + + { + auto const& expected = lowLimitValue; + auto const actual = entry.getLowLimit(); + expectEqualField(expected, actual, "sfLowLimit"); + } + + { + auto const& expected = highLimitValue; + auto const actual = entry.getHighLimit(); + expectEqualField(expected, actual, "sfHighLimit"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = lowNodeValue; + auto const actualOpt = entry.getLowNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowNode"); + EXPECT_TRUE(entry.hasLowNode()); + } + + { + auto const& expected = lowQualityInValue; + auto const actualOpt = entry.getLowQualityIn(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowQualityIn"); + EXPECT_TRUE(entry.hasLowQualityIn()); + } + + { + auto const& expected = lowQualityOutValue; + auto const actualOpt = entry.getLowQualityOut(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowQualityOut"); + EXPECT_TRUE(entry.hasLowQualityOut()); + } + + { + auto const& expected = highNodeValue; + auto const actualOpt = entry.getHighNode(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighNode"); + EXPECT_TRUE(entry.hasHighNode()); + } + + { + auto const& expected = highQualityInValue; + auto const actualOpt = entry.getHighQualityIn(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighQualityIn"); + EXPECT_TRUE(entry.hasHighQualityIn()); + } + + { + auto const& expected = highQualityOutValue; + auto const actualOpt = entry.getHighQualityOut(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighQualityOut"); + EXPECT_TRUE(entry.hasHighQualityOut()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(RippleStateTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const lowNodeValue = canonical_UINT64(); + auto const lowQualityInValue = canonical_UINT32(); + auto const lowQualityOutValue = canonical_UINT32(); + auto const highNodeValue = canonical_UINT64(); + auto const highQualityInValue = canonical_UINT32(); + auto const highQualityOutValue = canonical_UINT32(); + + auto sle = std::make_shared(RippleState::entryType, index); + + sle->at(sfBalance) = balanceValue; + sle->at(sfLowLimit) = lowLimitValue; + sle->at(sfHighLimit) = highLimitValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfLowNode) = lowNodeValue; + sle->at(sfLowQualityIn) = lowQualityInValue; + sle->at(sfLowQualityOut) = lowQualityOutValue; + sle->at(sfHighNode) = highNodeValue; + sle->at(sfHighQualityIn) = highQualityInValue; + sle->at(sfHighQualityOut) = highQualityOutValue; + + RippleStateBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + RippleState entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = balanceValue; + + auto const fromSle = entryFromSle.getBalance(); + auto const fromBuilder = entryFromBuilder.getBalance(); + + expectEqualField(expected, fromSle, "sfBalance"); + expectEqualField(expected, fromBuilder, "sfBalance"); + } + + { + auto const& expected = lowLimitValue; + + auto const fromSle = entryFromSle.getLowLimit(); + auto const fromBuilder = entryFromBuilder.getLowLimit(); + + expectEqualField(expected, fromSle, "sfLowLimit"); + expectEqualField(expected, fromBuilder, "sfLowLimit"); + } + + { + auto const& expected = highLimitValue; + + auto const fromSle = entryFromSle.getHighLimit(); + auto const fromBuilder = entryFromBuilder.getHighLimit(); + + expectEqualField(expected, fromSle, "sfHighLimit"); + expectEqualField(expected, fromBuilder, "sfHighLimit"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = lowNodeValue; + + auto const fromSleOpt = entryFromSle.getLowNode(); + auto const fromBuilderOpt = entryFromBuilder.getLowNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowNode"); + expectEqualField(expected, *fromBuilderOpt, "sfLowNode"); + } + + { + auto const& expected = lowQualityInValue; + + auto const fromSleOpt = entryFromSle.getLowQualityIn(); + auto const fromBuilderOpt = entryFromBuilder.getLowQualityIn(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowQualityIn"); + expectEqualField(expected, *fromBuilderOpt, "sfLowQualityIn"); + } + + { + auto const& expected = lowQualityOutValue; + + auto const fromSleOpt = entryFromSle.getLowQualityOut(); + auto const fromBuilderOpt = entryFromBuilder.getLowQualityOut(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowQualityOut"); + expectEqualField(expected, *fromBuilderOpt, "sfLowQualityOut"); + } + + { + auto const& expected = highNodeValue; + + auto const fromSleOpt = entryFromSle.getHighNode(); + auto const fromBuilderOpt = entryFromBuilder.getHighNode(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighNode"); + expectEqualField(expected, *fromBuilderOpt, "sfHighNode"); + } + + { + auto const& expected = highQualityInValue; + + auto const fromSleOpt = entryFromSle.getHighQualityIn(); + auto const fromBuilderOpt = entryFromBuilder.getHighQualityIn(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighQualityIn"); + expectEqualField(expected, *fromBuilderOpt, "sfHighQualityIn"); + } + + { + auto const& expected = highQualityOutValue; + + auto const fromSleOpt = entryFromSle.getHighQualityOut(); + auto const fromBuilderOpt = entryFromBuilder.getHighQualityOut(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighQualityOut"); + expectEqualField(expected, *fromBuilderOpt, "sfHighQualityOut"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(RippleStateTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(RippleState{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(RippleStateTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(RippleStateBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(RippleStateTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const balanceValue = canonical_AMOUNT(); + auto const lowLimitValue = canonical_AMOUNT(); + auto const highLimitValue = canonical_AMOUNT(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + RippleStateBuilder builder{ + balanceValue, + lowLimitValue, + highLimitValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasLowNode()); + EXPECT_FALSE(entry.getLowNode().has_value()); + EXPECT_FALSE(entry.hasLowQualityIn()); + EXPECT_FALSE(entry.getLowQualityIn().has_value()); + EXPECT_FALSE(entry.hasLowQualityOut()); + EXPECT_FALSE(entry.getLowQualityOut().has_value()); + EXPECT_FALSE(entry.hasHighNode()); + EXPECT_FALSE(entry.getHighNode().has_value()); + EXPECT_FALSE(entry.hasHighQualityIn()); + EXPECT_FALSE(entry.getHighQualityIn().has_value()); + EXPECT_FALSE(entry.hasHighQualityOut()); + EXPECT_FALSE(entry.getHighQualityOut().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp new file mode 100644 index 0000000000..5ec2c4e10d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp @@ -0,0 +1,275 @@ +// Auto-generated unit tests for ledger entry SignerList + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(SignerListTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + SignerListBuilder builder{ + ownerNodeValue, + signerQuorumValue, + signerEntriesValue, + signerListIDValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + builder.setOwner(ownerValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = signerQuorumValue; + auto const actual = entry.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + { + auto const& expected = signerEntriesValue; + auto const actual = entry.getSignerEntries(); + expectEqualField(expected, actual, "sfSignerEntries"); + } + + { + auto const& expected = signerListIDValue; + auto const actual = entry.getSignerListID(); + expectEqualField(expected, actual, "sfSignerListID"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = entry.getOwner(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(entry.hasOwner()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(SignerListTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const ownerValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(SignerList::entryType, index); + + sle->at(sfOwner) = ownerValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfSignerQuorum) = signerQuorumValue; + sle->setFieldArray(sfSignerEntries, signerEntriesValue); + sle->at(sfSignerListID) = signerListIDValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + SignerListBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + SignerList entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = signerQuorumValue; + + auto const fromSle = entryFromSle.getSignerQuorum(); + auto const fromBuilder = entryFromBuilder.getSignerQuorum(); + + expectEqualField(expected, fromSle, "sfSignerQuorum"); + expectEqualField(expected, fromBuilder, "sfSignerQuorum"); + } + + { + auto const& expected = signerEntriesValue; + + auto const fromSle = entryFromSle.getSignerEntries(); + auto const fromBuilder = entryFromBuilder.getSignerEntries(); + + expectEqualField(expected, fromSle, "sfSignerEntries"); + expectEqualField(expected, fromBuilder, "sfSignerEntries"); + } + + { + auto const& expected = signerListIDValue; + + auto const fromSle = entryFromSle.getSignerListID(); + auto const fromBuilder = entryFromBuilder.getSignerListID(); + + expectEqualField(expected, fromSle, "sfSignerListID"); + expectEqualField(expected, fromBuilder, "sfSignerListID"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + + auto const fromSleOpt = entryFromSle.getOwner(); + auto const fromBuilderOpt = entryFromBuilder.getOwner(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfOwner"); + expectEqualField(expected, *fromBuilderOpt, "sfOwner"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(SignerListTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(SignerList{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(SignerListTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(SignerListBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(SignerListTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const ownerNodeValue = canonical_UINT64(); + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + auto const signerListIDValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + SignerListBuilder builder{ + ownerNodeValue, + signerQuorumValue, + signerEntriesValue, + signerListIDValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasOwner()); + EXPECT_FALSE(entry.getOwner().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp new file mode 100644 index 0000000000..1ac00288f0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp @@ -0,0 +1,209 @@ +// Auto-generated unit tests for ledger entry Ticket + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(TicketTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ticketSequenceValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + TicketBuilder builder{ + accountValue, + ownerNodeValue, + ticketSequenceValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = ticketSequenceValue; + auto const actual = entry.getTicketSequence(); + expectEqualField(expected, actual, "sfTicketSequence"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(TicketTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ticketSequenceValue = canonical_UINT32(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(Ticket::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfTicketSequence) = ticketSequenceValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + TicketBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Ticket entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = ticketSequenceValue; + + auto const fromSle = entryFromSle.getTicketSequence(); + auto const fromBuilder = entryFromBuilder.getTicketSequence(); + + expectEqualField(expected, fromSle, "sfTicketSequence"); + expectEqualField(expected, fromBuilder, "sfTicketSequence"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(TicketTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + CheckBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Ticket{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(TicketTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + CheckBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_ACCOUNT(), + canonical_AMOUNT(), + canonical_UINT32(), + canonical_UINT64(), + canonical_UINT64(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(TicketBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp new file mode 100644 index 0000000000..2697924d37 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp @@ -0,0 +1,476 @@ +// Auto-generated unit tests for ledger entry Vault + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(VaultTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const dataValue = canonical_VL(); + auto const assetValue = canonical_ISSUE(); + auto const assetsTotalValue = canonical_NUMBER(); + auto const assetsAvailableValue = canonical_NUMBER(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const lossUnrealizedValue = canonical_NUMBER(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const scaleValue = canonical_UINT8(); + + VaultBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + ownerValue, + accountValue, + assetValue, + shareMPTIDValue, + withdrawalPolicyValue + }; + + builder.setData(dataValue); + builder.setAssetsTotal(assetsTotalValue); + builder.setAssetsAvailable(assetsAvailableValue); + builder.setAssetsMaximum(assetsMaximumValue); + builder.setLossUnrealized(lossUnrealizedValue); + builder.setScale(scaleValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + auto const actual = entry.getSequence(); + expectEqualField(expected, actual, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = assetValue; + auto const actual = entry.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = shareMPTIDValue; + auto const actual = entry.getShareMPTID(); + expectEqualField(expected, actual, "sfShareMPTID"); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actual = entry.getWithdrawalPolicy(); + expectEqualField(expected, actual, "sfWithdrawalPolicy"); + } + + { + 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 = assetsTotalValue; + auto const actualOpt = entry.getAssetsTotal(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsTotal"); + EXPECT_TRUE(entry.hasAssetsTotal()); + } + + { + auto const& expected = assetsAvailableValue; + auto const actualOpt = entry.getAssetsAvailable(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsAvailable"); + EXPECT_TRUE(entry.hasAssetsAvailable()); + } + + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = entry.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(entry.hasAssetsMaximum()); + } + + { + auto const& expected = lossUnrealizedValue; + auto const actualOpt = entry.getLossUnrealized(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLossUnrealized"); + EXPECT_TRUE(entry.hasLossUnrealized()); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = entry.getScale(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfScale"); + EXPECT_TRUE(entry.hasScale()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(VaultTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const dataValue = canonical_VL(); + auto const assetValue = canonical_ISSUE(); + auto const assetsTotalValue = canonical_NUMBER(); + auto const assetsAvailableValue = canonical_NUMBER(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const lossUnrealizedValue = canonical_NUMBER(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const scaleValue = canonical_UINT8(); + + auto sle = std::make_shared(Vault::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfSequence) = sequenceValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfOwner) = ownerValue; + sle->at(sfAccount) = accountValue; + sle->at(sfData) = dataValue; + sle->at(sfAsset) = STIssue(sfAsset, assetValue); + sle->at(sfAssetsTotal) = assetsTotalValue; + sle->at(sfAssetsAvailable) = assetsAvailableValue; + sle->at(sfAssetsMaximum) = assetsMaximumValue; + sle->at(sfLossUnrealized) = lossUnrealizedValue; + sle->at(sfShareMPTID) = shareMPTIDValue; + sle->at(sfWithdrawalPolicy) = withdrawalPolicyValue; + sle->at(sfScale) = scaleValue; + + VaultBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Vault entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = sequenceValue; + + auto const fromSle = entryFromSle.getSequence(); + auto const fromBuilder = entryFromBuilder.getSequence(); + + expectEqualField(expected, fromSle, "sfSequence"); + expectEqualField(expected, fromBuilder, "sfSequence"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = assetValue; + + auto const fromSle = entryFromSle.getAsset(); + auto const fromBuilder = entryFromBuilder.getAsset(); + + expectEqualField(expected, fromSle, "sfAsset"); + expectEqualField(expected, fromBuilder, "sfAsset"); + } + + { + auto const& expected = shareMPTIDValue; + + auto const fromSle = entryFromSle.getShareMPTID(); + auto const fromBuilder = entryFromBuilder.getShareMPTID(); + + expectEqualField(expected, fromSle, "sfShareMPTID"); + expectEqualField(expected, fromBuilder, "sfShareMPTID"); + } + + { + auto const& expected = withdrawalPolicyValue; + + auto const fromSle = entryFromSle.getWithdrawalPolicy(); + auto const fromBuilder = entryFromBuilder.getWithdrawalPolicy(); + + expectEqualField(expected, fromSle, "sfWithdrawalPolicy"); + expectEqualField(expected, fromBuilder, "sfWithdrawalPolicy"); + } + + { + 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 = assetsTotalValue; + + auto const fromSleOpt = entryFromSle.getAssetsTotal(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsTotal(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsTotal"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsTotal"); + } + + { + auto const& expected = assetsAvailableValue; + + auto const fromSleOpt = entryFromSle.getAssetsAvailable(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsAvailable(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsAvailable"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsAvailable"); + } + + { + auto const& expected = assetsMaximumValue; + + auto const fromSleOpt = entryFromSle.getAssetsMaximum(); + auto const fromBuilderOpt = entryFromBuilder.getAssetsMaximum(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfAssetsMaximum"); + expectEqualField(expected, *fromBuilderOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = lossUnrealizedValue; + + auto const fromSleOpt = entryFromSle.getLossUnrealized(); + auto const fromBuilderOpt = entryFromBuilder.getLossUnrealized(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLossUnrealized"); + expectEqualField(expected, *fromBuilderOpt, "sfLossUnrealized"); + } + + { + auto const& expected = scaleValue; + + auto const fromSleOpt = entryFromSle.getScale(); + auto const fromBuilderOpt = entryFromBuilder.getScale(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfScale"); + expectEqualField(expected, *fromBuilderOpt, "sfScale"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(VaultTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Vault{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(VaultTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(VaultBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(VaultTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const sequenceValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const ownerValue = canonical_ACCOUNT(); + auto const accountValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const shareMPTIDValue = canonical_UINT192(); + auto const withdrawalPolicyValue = canonical_UINT8(); + + VaultBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + sequenceValue, + ownerNodeValue, + ownerValue, + accountValue, + assetValue, + shareMPTIDValue, + withdrawalPolicyValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasData()); + EXPECT_FALSE(entry.getData().has_value()); + EXPECT_FALSE(entry.hasAssetsTotal()); + EXPECT_FALSE(entry.getAssetsTotal().has_value()); + EXPECT_FALSE(entry.hasAssetsAvailable()); + EXPECT_FALSE(entry.getAssetsAvailable().has_value()); + EXPECT_FALSE(entry.hasAssetsMaximum()); + EXPECT_FALSE(entry.getAssetsMaximum().has_value()); + EXPECT_FALSE(entry.hasLossUnrealized()); + EXPECT_FALSE(entry.getLossUnrealized().has_value()); + EXPECT_FALSE(entry.hasScale()); + EXPECT_FALSE(entry.getScale().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp new file mode 100644 index 0000000000..4b6979b86a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp @@ -0,0 +1,283 @@ +// Auto-generated unit tests for ledger entry XChainOwnedClaimID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(XChainOwnedClaimIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const xChainClaimAttestationsValue = canonical_ARRAY(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + XChainOwnedClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + otherChainSourceValue, + xChainClaimAttestationsValue, + signatureRewardValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = entry.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = entry.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = xChainClaimAttestationsValue; + auto const actual = entry.getXChainClaimAttestations(); + expectEqualField(expected, actual, "sfXChainClaimAttestations"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = entry.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(XChainOwnedClaimIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const xChainClaimAttestationsValue = canonical_ARRAY(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(XChainOwnedClaimID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainClaimID) = xChainClaimIDValue; + sle->at(sfOtherChainSource) = otherChainSourceValue; + sle->setFieldArray(sfXChainClaimAttestations, xChainClaimAttestationsValue); + sle->at(sfSignatureReward) = signatureRewardValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + XChainOwnedClaimIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + XChainOwnedClaimID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + + auto const fromSle = entryFromSle.getXChainClaimID(); + auto const fromBuilder = entryFromBuilder.getXChainClaimID(); + + expectEqualField(expected, fromSle, "sfXChainClaimID"); + expectEqualField(expected, fromBuilder, "sfXChainClaimID"); + } + + { + auto const& expected = otherChainSourceValue; + + auto const fromSle = entryFromSle.getOtherChainSource(); + auto const fromBuilder = entryFromBuilder.getOtherChainSource(); + + expectEqualField(expected, fromSle, "sfOtherChainSource"); + expectEqualField(expected, fromBuilder, "sfOtherChainSource"); + } + + { + auto const& expected = xChainClaimAttestationsValue; + + auto const fromSle = entryFromSle.getXChainClaimAttestations(); + auto const fromBuilder = entryFromBuilder.getXChainClaimAttestations(); + + expectEqualField(expected, fromSle, "sfXChainClaimAttestations"); + expectEqualField(expected, fromBuilder, "sfXChainClaimAttestations"); + } + + { + auto const& expected = signatureRewardValue; + + auto const fromSle = entryFromSle.getSignatureReward(); + auto const fromBuilder = entryFromBuilder.getSignatureReward(); + + expectEqualField(expected, fromSle, "sfSignatureReward"); + expectEqualField(expected, fromBuilder, "sfSignatureReward"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(XChainOwnedClaimIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedClaimID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(XChainOwnedClaimIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedClaimIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp new file mode 100644 index 0000000000..0e4e1fb977 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp @@ -0,0 +1,243 @@ +// Auto-generated unit tests for ledger entry XChainOwnedCreateAccountClaimID + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainCreateAccountAttestationsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + XChainOwnedCreateAccountClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + xChainAccountCreateCountValue, + xChainCreateAccountAttestationsValue, + ownerNodeValue, + previousTxnIDValue, + previousTxnLgrSeqValue + }; + + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = accountValue; + auto const actual = entry.getAccount(); + expectEqualField(expected, actual, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + auto const actual = entry.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = entry.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainCreateAccountAttestationsValue; + auto const actual = entry.getXChainCreateAccountAttestations(); + expectEqualField(expected, actual, "sfXChainCreateAccountAttestations"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const accountValue = canonical_ACCOUNT(); + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const xChainCreateAccountAttestationsValue = canonical_ARRAY(); + auto const ownerNodeValue = canonical_UINT64(); + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + + auto sle = std::make_shared(XChainOwnedCreateAccountClaimID::entryType, index); + + sle->at(sfAccount) = accountValue; + sle->at(sfXChainBridge) = xChainBridgeValue; + sle->at(sfXChainAccountCreateCount) = xChainAccountCreateCountValue; + sle->setFieldArray(sfXChainCreateAccountAttestations, xChainCreateAccountAttestationsValue); + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + + XChainOwnedCreateAccountClaimIDBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + XChainOwnedCreateAccountClaimID entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = accountValue; + + auto const fromSle = entryFromSle.getAccount(); + auto const fromBuilder = entryFromBuilder.getAccount(); + + expectEqualField(expected, fromSle, "sfAccount"); + expectEqualField(expected, fromBuilder, "sfAccount"); + } + + { + auto const& expected = xChainBridgeValue; + + auto const fromSle = entryFromSle.getXChainBridge(); + auto const fromBuilder = entryFromBuilder.getXChainBridge(); + + expectEqualField(expected, fromSle, "sfXChainBridge"); + expectEqualField(expected, fromBuilder, "sfXChainBridge"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + + auto const fromSle = entryFromSle.getXChainAccountCreateCount(); + auto const fromBuilder = entryFromBuilder.getXChainAccountCreateCount(); + + expectEqualField(expected, fromSle, "sfXChainAccountCreateCount"); + expectEqualField(expected, fromBuilder, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = xChainCreateAccountAttestationsValue; + + auto const fromSle = entryFromSle.getXChainCreateAccountAttestations(); + auto const fromBuilder = entryFromBuilder.getXChainCreateAccountAttestations(); + + expectEqualField(expected, fromSle, "sfXChainCreateAccountAttestations"); + expectEqualField(expected, fromBuilder, "sfXChainCreateAccountAttestations"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(XChainOwnedCreateAccountClaimIDTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedCreateAccountClaimID{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(XChainOwnedCreateAccountClaimIDTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(XChainOwnedCreateAccountClaimIDBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/main.cpp b/src/tests/libxrpl/protocol_autogen/main.cpp new file mode 100644 index 0000000000..5142bbe08a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/main.cpp @@ -0,0 +1,8 @@ +#include + +int +main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp new file mode 100644 index 0000000000..c9db0cabcf --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMBidTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction AMMBid + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMBidTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBid")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const bidMinValue = canonical_AMOUNT(); + auto const bidMaxValue = canonical_AMOUNT(); + auto const authAccountsValue = canonical_ARRAY(); + + AMMBidBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setBidMin(bidMinValue); + builder.setBidMax(bidMaxValue); + builder.setAuthAccounts(authAccountsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = bidMinValue; + auto const actualOpt = tx.getBidMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMin should be present"; + expectEqualField(expected, *actualOpt, "sfBidMin"); + EXPECT_TRUE(tx.hasBidMin()); + } + + { + auto const& expected = bidMaxValue; + auto const actualOpt = tx.getBidMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMax should be present"; + expectEqualField(expected, *actualOpt, "sfBidMax"); + EXPECT_TRUE(tx.hasBidMax()); + } + + { + auto const& expected = authAccountsValue; + auto const actualOpt = tx.getAuthAccounts(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthAccounts should be present"; + expectEqualField(expected, *actualOpt, "sfAuthAccounts"); + EXPECT_TRUE(tx.hasAuthAccounts()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMBidTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBidFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const bidMinValue = canonical_AMOUNT(); + auto const bidMaxValue = canonical_AMOUNT(); + auto const authAccountsValue = canonical_ARRAY(); + + // Build an initial transaction + AMMBidBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setBidMin(bidMinValue); + initialBuilder.setBidMax(bidMaxValue); + initialBuilder.setAuthAccounts(authAccountsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMBidBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = bidMinValue; + auto const actualOpt = rebuiltTx.getBidMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMin should be present"; + expectEqualField(expected, *actualOpt, "sfBidMin"); + } + + { + auto const& expected = bidMaxValue; + auto const actualOpt = rebuiltTx.getBidMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBidMax should be present"; + expectEqualField(expected, *actualOpt, "sfBidMax"); + } + + { + auto const& expected = authAccountsValue; + auto const actualOpt = rebuiltTx.getAuthAccounts(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthAccounts should be present"; + expectEqualField(expected, *actualOpt, "sfAuthAccounts"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMBidTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMBid{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMBidTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMBidBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMBidTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMBidNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMBidBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasBidMin()); + EXPECT_FALSE(tx.getBidMin().has_value()); + EXPECT_FALSE(tx.hasBidMax()); + EXPECT_FALSE(tx.getBidMax().has_value()); + EXPECT_FALSE(tx.hasAuthAccounts()); + EXPECT_FALSE(tx.getAuthAccounts().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp new file mode 100644 index 0000000000..d01e840ce5 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMClawbackTests.cpp @@ -0,0 +1,231 @@ +// Auto-generated unit tests for transaction AMMClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + + AMMClawbackBuilder builder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = holderValue; + auto const actual = tx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + AMMClawbackBuilder initialBuilder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = holderValue; + auto const actual = rebuiltTx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const holderValue = canonical_ACCOUNT(); + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMClawbackBuilder builder{ + accountValue, + holderValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp new file mode 100644 index 0000000000..b55c9f377e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMCreateTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction AMMCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMCreateBuilder builder{ + accountValue, + amountValue, + amount2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actual = tx.getAmount2(); + expectEqualField(expected, actual, "sfAmount2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = tx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMCreateBuilder initialBuilder{ + accountValue, + amountValue, + amount2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actual = rebuiltTx.getAmount2(); + expectEqualField(expected, actual, "sfAmount2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = rebuiltTx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp new file mode 100644 index 0000000000..774d993a1a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMDeleteTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction AMMDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMDeleteBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + // Build an initial transaction + AMMDeleteBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp new file mode 100644 index 0000000000..9917848f0a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMDepositTests.cpp @@ -0,0 +1,297 @@ +// Auto-generated unit tests for transaction AMMDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenOutValue = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMDepositBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setAmount2(amount2Value); + builder.setEPrice(ePriceValue); + builder.setLPTokenOut(lPTokenOutValue); + builder.setTradingFee(tradingFeeValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = tx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + EXPECT_TRUE(tx.hasAmount2()); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = tx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + EXPECT_TRUE(tx.hasEPrice()); + } + + { + auto const& expected = lPTokenOutValue; + auto const actualOpt = tx.getLPTokenOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenOut should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenOut"); + EXPECT_TRUE(tx.hasLPTokenOut()); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = tx.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTradingFee should be present"; + expectEqualField(expected, *actualOpt, "sfTradingFee"); + EXPECT_TRUE(tx.hasTradingFee()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenOutValue = canonical_AMOUNT(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMDepositBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setAmount2(amount2Value); + initialBuilder.setEPrice(ePriceValue); + initialBuilder.setLPTokenOut(lPTokenOutValue); + initialBuilder.setTradingFee(tradingFeeValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = rebuiltTx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = rebuiltTx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + } + + { + auto const& expected = lPTokenOutValue; + auto const actualOpt = rebuiltTx.getLPTokenOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenOut should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenOut"); + } + + { + auto const& expected = tradingFeeValue; + auto const actualOpt = rebuiltTx.getTradingFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTradingFee should be present"; + expectEqualField(expected, *actualOpt, "sfTradingFee"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMDepositTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMDepositNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMDepositBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasAmount2()); + EXPECT_FALSE(tx.getAmount2().has_value()); + EXPECT_FALSE(tx.hasEPrice()); + EXPECT_FALSE(tx.getEPrice().has_value()); + EXPECT_FALSE(tx.hasLPTokenOut()); + EXPECT_FALSE(tx.getLPTokenOut().has_value()); + EXPECT_FALSE(tx.hasTradingFee()); + EXPECT_FALSE(tx.getTradingFee().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp new file mode 100644 index 0000000000..6a47b7613e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMVoteTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction AMMVote + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMVoteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMVote")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const tradingFeeValue = canonical_UINT16(); + + AMMVoteBuilder builder{ + accountValue, + assetValue, + asset2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = tx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMVoteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMVoteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const tradingFeeValue = canonical_UINT16(); + + // Build an initial transaction + AMMVoteBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + tradingFeeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMVoteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + { + auto const& expected = tradingFeeValue; + auto const actual = rebuiltTx.getTradingFee(); + expectEqualField(expected, actual, "sfTradingFee"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMVoteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMVote{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMVoteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMVoteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp new file mode 100644 index 0000000000..0fd529c282 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AMMWithdrawTests.cpp @@ -0,0 +1,276 @@ +// Auto-generated unit tests for transaction AMMWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAMMWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenInValue = canonical_AMOUNT(); + + AMMWithdrawBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setAmount2(amount2Value); + builder.setEPrice(ePriceValue); + builder.setLPTokenIn(lPTokenInValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = tx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = tx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + EXPECT_TRUE(tx.hasAmount2()); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = tx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + EXPECT_TRUE(tx.hasEPrice()); + } + + { + auto const& expected = lPTokenInValue; + auto const actualOpt = tx.getLPTokenIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenIn should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenIn"); + EXPECT_TRUE(tx.hasLPTokenIn()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAMMWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + auto const amountValue = canonical_AMOUNT(); + auto const amount2Value = canonical_AMOUNT(); + auto const ePriceValue = canonical_AMOUNT(); + auto const lPTokenInValue = canonical_AMOUNT(); + + // Build an initial transaction + AMMWithdrawBuilder initialBuilder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setAmount2(amount2Value); + initialBuilder.setEPrice(ePriceValue); + initialBuilder.setLPTokenIn(lPTokenInValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AMMWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + { + auto const& expected = asset2Value; + auto const actual = rebuiltTx.getAsset2(); + expectEqualField(expected, actual, "sfAsset2"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = amount2Value; + auto const actualOpt = rebuiltTx.getAmount2(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount2 should be present"; + expectEqualField(expected, *actualOpt, "sfAmount2"); + } + + { + auto const& expected = ePriceValue; + auto const actualOpt = rebuiltTx.getEPrice(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEPrice should be present"; + expectEqualField(expected, *actualOpt, "sfEPrice"); + } + + { + auto const& expected = lPTokenInValue; + auto const actualOpt = rebuiltTx.getLPTokenIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLPTokenIn should be present"; + expectEqualField(expected, *actualOpt, "sfLPTokenIn"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAMMWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAMMWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AMMWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAMMWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAMMWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + auto const asset2Value = canonical_ISSUE(); + + AMMWithdrawBuilder builder{ + accountValue, + assetValue, + asset2Value, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasAmount2()); + EXPECT_FALSE(tx.getAmount2().has_value()); + EXPECT_FALSE(tx.hasEPrice()); + EXPECT_FALSE(tx.getEPrice().has_value()); + EXPECT_FALSE(tx.hasLPTokenIn()); + EXPECT_FALSE(tx.getLPTokenIn().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp new file mode 100644 index 0000000000..0fb951221b --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AccountDeleteTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction AccountDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAccountDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const credentialIDsValue = canonical_VECTOR256(); + + AccountDeleteBuilder builder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestinationTag(destinationTagValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + // 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 = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAccountDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + AccountDeleteBuilder initialBuilder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AccountDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + // 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 = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAccountDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAccountDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAccountDeleteTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountDeleteNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + + AccountDeleteBuilder builder{ + accountValue, + destinationValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + 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.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp new file mode 100644 index 0000000000..993fe55742 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/AccountSetTests.cpp @@ -0,0 +1,366 @@ +// Auto-generated unit tests for transaction AccountSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsAccountSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const domainValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const setFlagValue = canonical_UINT32(); + auto const clearFlagValue = canonical_UINT32(); + auto const tickSizeValue = canonical_UINT8(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + + AccountSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setEmailHash(emailHashValue); + builder.setWalletLocator(walletLocatorValue); + builder.setWalletSize(walletSizeValue); + builder.setMessageKey(messageKeyValue); + builder.setDomain(domainValue); + builder.setTransferRate(transferRateValue); + builder.setSetFlag(setFlagValue); + builder.setClearFlag(clearFlagValue); + builder.setTickSize(tickSizeValue); + builder.setNFTokenMinter(nFTokenMinterValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = emailHashValue; + auto const actualOpt = tx.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEmailHash should be present"; + expectEqualField(expected, *actualOpt, "sfEmailHash"); + EXPECT_TRUE(tx.hasEmailHash()); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = tx.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletLocator should be present"; + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + EXPECT_TRUE(tx.hasWalletLocator()); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = tx.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletSize should be present"; + expectEqualField(expected, *actualOpt, "sfWalletSize"); + EXPECT_TRUE(tx.hasWalletSize()); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = tx.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMessageKey should be present"; + expectEqualField(expected, *actualOpt, "sfMessageKey"); + EXPECT_TRUE(tx.hasMessageKey()); + } + + { + auto const& expected = domainValue; + auto const actualOpt = tx.getDomain(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomain should be present"; + expectEqualField(expected, *actualOpt, "sfDomain"); + EXPECT_TRUE(tx.hasDomain()); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = tx.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferRate should be present"; + expectEqualField(expected, *actualOpt, "sfTransferRate"); + EXPECT_TRUE(tx.hasTransferRate()); + } + + { + auto const& expected = setFlagValue; + auto const actualOpt = tx.getSetFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSetFlag should be present"; + expectEqualField(expected, *actualOpt, "sfSetFlag"); + EXPECT_TRUE(tx.hasSetFlag()); + } + + { + auto const& expected = clearFlagValue; + auto const actualOpt = tx.getClearFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClearFlag should be present"; + expectEqualField(expected, *actualOpt, "sfClearFlag"); + EXPECT_TRUE(tx.hasClearFlag()); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = tx.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTickSize should be present"; + expectEqualField(expected, *actualOpt, "sfTickSize"); + EXPECT_TRUE(tx.hasTickSize()); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = tx.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenMinter should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + EXPECT_TRUE(tx.hasNFTokenMinter()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsAccountSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const emailHashValue = canonical_UINT128(); + auto const walletLocatorValue = canonical_UINT256(); + auto const walletSizeValue = canonical_UINT32(); + auto const messageKeyValue = canonical_VL(); + auto const domainValue = canonical_VL(); + auto const transferRateValue = canonical_UINT32(); + auto const setFlagValue = canonical_UINT32(); + auto const clearFlagValue = canonical_UINT32(); + auto const tickSizeValue = canonical_UINT8(); + auto const nFTokenMinterValue = canonical_ACCOUNT(); + + // Build an initial transaction + AccountSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setEmailHash(emailHashValue); + initialBuilder.setWalletLocator(walletLocatorValue); + initialBuilder.setWalletSize(walletSizeValue); + initialBuilder.setMessageKey(messageKeyValue); + initialBuilder.setDomain(domainValue); + initialBuilder.setTransferRate(transferRateValue); + initialBuilder.setSetFlag(setFlagValue); + initialBuilder.setClearFlag(clearFlagValue); + initialBuilder.setTickSize(tickSizeValue); + initialBuilder.setNFTokenMinter(nFTokenMinterValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + AccountSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = emailHashValue; + auto const actualOpt = rebuiltTx.getEmailHash(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfEmailHash should be present"; + expectEqualField(expected, *actualOpt, "sfEmailHash"); + } + + { + auto const& expected = walletLocatorValue; + auto const actualOpt = rebuiltTx.getWalletLocator(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletLocator should be present"; + expectEqualField(expected, *actualOpt, "sfWalletLocator"); + } + + { + auto const& expected = walletSizeValue; + auto const actualOpt = rebuiltTx.getWalletSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWalletSize should be present"; + expectEqualField(expected, *actualOpt, "sfWalletSize"); + } + + { + auto const& expected = messageKeyValue; + auto const actualOpt = rebuiltTx.getMessageKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMessageKey should be present"; + expectEqualField(expected, *actualOpt, "sfMessageKey"); + } + + { + auto const& expected = domainValue; + auto const actualOpt = rebuiltTx.getDomain(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomain should be present"; + expectEqualField(expected, *actualOpt, "sfDomain"); + } + + { + auto const& expected = transferRateValue; + auto const actualOpt = rebuiltTx.getTransferRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferRate should be present"; + expectEqualField(expected, *actualOpt, "sfTransferRate"); + } + + { + auto const& expected = setFlagValue; + auto const actualOpt = rebuiltTx.getSetFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSetFlag should be present"; + expectEqualField(expected, *actualOpt, "sfSetFlag"); + } + + { + auto const& expected = clearFlagValue; + auto const actualOpt = rebuiltTx.getClearFlag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClearFlag should be present"; + expectEqualField(expected, *actualOpt, "sfClearFlag"); + } + + { + auto const& expected = tickSizeValue; + auto const actualOpt = rebuiltTx.getTickSize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTickSize should be present"; + expectEqualField(expected, *actualOpt, "sfTickSize"); + } + + { + auto const& expected = nFTokenMinterValue; + auto const actualOpt = rebuiltTx.getNFTokenMinter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenMinter should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenMinter"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsAccountSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + OfferCancelBuilder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsAccountSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + OfferCancelBuilder wrongBuilder{account, canonical_UINT32(), 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(AccountSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsAccountSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testAccountSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + AccountSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasEmailHash()); + EXPECT_FALSE(tx.getEmailHash().has_value()); + EXPECT_FALSE(tx.hasWalletLocator()); + EXPECT_FALSE(tx.getWalletLocator().has_value()); + EXPECT_FALSE(tx.hasWalletSize()); + EXPECT_FALSE(tx.getWalletSize().has_value()); + EXPECT_FALSE(tx.hasMessageKey()); + EXPECT_FALSE(tx.getMessageKey().has_value()); + EXPECT_FALSE(tx.hasDomain()); + EXPECT_FALSE(tx.getDomain().has_value()); + EXPECT_FALSE(tx.hasTransferRate()); + EXPECT_FALSE(tx.getTransferRate().has_value()); + EXPECT_FALSE(tx.hasSetFlag()); + EXPECT_FALSE(tx.getSetFlag().has_value()); + EXPECT_FALSE(tx.hasClearFlag()); + EXPECT_FALSE(tx.getClearFlag().has_value()); + EXPECT_FALSE(tx.hasTickSize()); + EXPECT_FALSE(tx.getTickSize().has_value()); + EXPECT_FALSE(tx.hasNFTokenMinter()); + EXPECT_FALSE(tx.getNFTokenMinter().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp new file mode 100644 index 0000000000..c6818a26d7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/BatchTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction Batch + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsBatchTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatch")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const rawTransactionsValue = canonical_ARRAY(); + auto const batchSignersValue = canonical_ARRAY(); + + BatchBuilder builder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setBatchSigners(batchSignersValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = rawTransactionsValue; + auto const actual = tx.getRawTransactions(); + expectEqualField(expected, actual, "sfRawTransactions"); + } + + // Verify optional fields + { + auto const& expected = batchSignersValue; + auto const actualOpt = tx.getBatchSigners(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBatchSigners should be present"; + expectEqualField(expected, *actualOpt, "sfBatchSigners"); + EXPECT_TRUE(tx.hasBatchSigners()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsBatchTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatchFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const rawTransactionsValue = canonical_ARRAY(); + auto const batchSignersValue = canonical_ARRAY(); + + // Build an initial transaction + BatchBuilder initialBuilder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setBatchSigners(batchSignersValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + BatchBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = rawTransactionsValue; + auto const actual = rebuiltTx.getRawTransactions(); + expectEqualField(expected, actual, "sfRawTransactions"); + } + + // Verify optional fields + { + auto const& expected = batchSignersValue; + auto const actualOpt = rebuiltTx.getBatchSigners(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBatchSigners should be present"; + expectEqualField(expected, *actualOpt, "sfBatchSigners"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsBatchTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Batch{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsBatchTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(BatchBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsBatchTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testBatchNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const rawTransactionsValue = canonical_ARRAY(); + + BatchBuilder builder{ + accountValue, + rawTransactionsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasBatchSigners()); + EXPECT_FALSE(tx.getBatchSigners().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp new file mode 100644 index 0000000000..afdc2d6d66 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCancelTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction CheckCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + + CheckCancelBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = tx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + + // Build an initial transaction + CheckCancelBuilder initialBuilder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = rebuiltTx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp new file mode 100644 index 0000000000..b8c4dccd55 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCashTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction CheckCash + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCashTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCash")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const deliverMinValue = canonical_AMOUNT(); + + CheckCashBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setDeliverMin(deliverMinValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = tx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = tx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + EXPECT_TRUE(tx.hasDeliverMin()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCashTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCashFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const checkIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const deliverMinValue = canonical_AMOUNT(); + + // Build an initial transaction + CheckCashBuilder initialBuilder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setDeliverMin(deliverMinValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCashBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = checkIDValue; + auto const actual = rebuiltTx.getCheckID(); + expectEqualField(expected, actual, "sfCheckID"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = deliverMinValue; + auto const actualOpt = rebuiltTx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCashTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCash{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCashTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCashBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCheckCashTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCashNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const checkIDValue = canonical_UINT256(); + + CheckCashBuilder builder{ + accountValue, + checkIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasDeliverMin()); + EXPECT_FALSE(tx.getDeliverMin().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp new file mode 100644 index 0000000000..9b8a831913 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CheckCreateTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction CheckCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCheckCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + + CheckCreateBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setDestinationTag(destinationTagValue); + builder.setInvoiceID(invoiceIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = tx.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + 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 = invoiceIDValue; + auto const actualOpt = tx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(tx.hasInvoiceID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCheckCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + auto const invoiceIDValue = canonical_UINT256(); + + // Build an initial transaction + CheckCreateBuilder initialBuilder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setInvoiceID(invoiceIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CheckCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = sendMaxValue; + auto const actual = rebuiltTx.getSendMax(); + expectEqualField(expected, actual, "sfSendMax"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + 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 = invoiceIDValue; + auto const actualOpt = rebuiltTx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCheckCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCheckCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CheckCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCheckCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCheckCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + + CheckCreateBuilder builder{ + accountValue, + destinationValue, + sendMaxValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); + EXPECT_FALSE(tx.hasInvoiceID()); + EXPECT_FALSE(tx.getInvoiceID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp new file mode 100644 index 0000000000..2f9b7f1e3a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ClawbackTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction Clawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const holderValue = canonical_ACCOUNT(); + + ClawbackBuilder builder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const amountValue = canonical_AMOUNT(); + auto const holderValue = canonical_ACCOUNT(); + + // Build an initial transaction + ClawbackBuilder initialBuilder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Clawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const amountValue = canonical_AMOUNT(); + + ClawbackBuilder builder{ + accountValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp new file mode 100644 index 0000000000..5ae9c8a181 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialAcceptTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction CredentialAccept + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialAcceptTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialAccept")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialAcceptBuilder builder{ + accountValue, + issuerValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = issuerValue; + auto const actual = tx.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialAcceptTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialAcceptFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + // Build an initial transaction + CredentialAcceptBuilder initialBuilder{ + accountValue, + issuerValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialAcceptBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = issuerValue; + auto const actual = rebuiltTx.getIssuer(); + expectEqualField(expected, actual, "sfIssuer"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialAcceptTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialAccept{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialAcceptTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialAcceptBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp new file mode 100644 index 0000000000..0bfa9def33 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialCreateTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction CredentialCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + + CredentialCreateBuilder builder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setURI(uRIValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = subjectValue; + auto const actual = tx.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + auto const expirationValue = canonical_UINT32(); + auto const uRIValue = canonical_VL(); + + // Build an initial transaction + CredentialCreateBuilder initialBuilder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setURI(uRIValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = subjectValue; + auto const actual = rebuiltTx.getSubject(); + expectEqualField(expected, actual, "sfSubject"); + } + + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCredentialCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const subjectValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialCreateBuilder builder{ + accountValue, + subjectValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp new file mode 100644 index 0000000000..26bba2ba1a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/CredentialDeleteTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction CredentialDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsCredentialDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + CredentialDeleteBuilder builder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSubject(subjectValue); + builder.setIssuer(issuerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = credentialTypeValue; + auto const actual = tx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = subjectValue; + auto const actualOpt = tx.getSubject(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSubject should be present"; + expectEqualField(expected, *actualOpt, "sfSubject"); + EXPECT_TRUE(tx.hasSubject()); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = tx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + EXPECT_TRUE(tx.hasIssuer()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsCredentialDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const subjectValue = canonical_ACCOUNT(); + auto const issuerValue = canonical_ACCOUNT(); + auto const credentialTypeValue = canonical_VL(); + + // Build an initial transaction + CredentialDeleteBuilder initialBuilder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSubject(subjectValue); + initialBuilder.setIssuer(issuerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + CredentialDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = credentialTypeValue; + auto const actual = rebuiltTx.getCredentialType(); + expectEqualField(expected, actual, "sfCredentialType"); + } + + // Verify optional fields + { + auto const& expected = subjectValue; + auto const actualOpt = rebuiltTx.getSubject(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSubject should be present"; + expectEqualField(expected, *actualOpt, "sfSubject"); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = rebuiltTx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsCredentialDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsCredentialDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(CredentialDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsCredentialDeleteTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testCredentialDeleteNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const credentialTypeValue = canonical_VL(); + + CredentialDeleteBuilder builder{ + accountValue, + credentialTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSubject()); + EXPECT_FALSE(tx.getSubject().has_value()); + EXPECT_FALSE(tx.hasIssuer()); + EXPECT_FALSE(tx.getIssuer().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp new file mode 100644 index 0000000000..e60ef99fb1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DIDDeleteTests.cpp @@ -0,0 +1,130 @@ +// Auto-generated unit tests for transaction DIDDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDIDDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + + DIDDeleteBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDIDDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + + // Build an initial transaction + DIDDeleteBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DIDDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDIDDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDIDDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp new file mode 100644 index 0000000000..07215e6966 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DIDSetTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction DIDSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDIDSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + + DIDSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDIDDocument(dIDDocumentValue); + builder.setURI(uRIValue); + builder.setData(dataValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = tx.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDIDDocument should be present"; + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + EXPECT_TRUE(tx.hasDIDDocument()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDIDSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const dIDDocumentValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const dataValue = canonical_VL(); + + // Build an initial transaction + DIDSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDIDDocument(dIDDocumentValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setData(dataValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DIDSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = dIDDocumentValue; + auto const actualOpt = rebuiltTx.getDIDDocument(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDIDDocument should be present"; + expectEqualField(expected, *actualOpt, "sfDIDDocument"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDIDSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDIDSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DIDSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsDIDSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDIDSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + DIDSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDIDDocument()); + EXPECT_FALSE(tx.getDIDDocument().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp new file mode 100644 index 0000000000..8e7fed9419 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DelegateSetTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction DelegateSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDelegateSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDelegateSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + + DelegateSetBuilder builder{ + accountValue, + authorizeValue, + permissionsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = authorizeValue; + auto const actual = tx.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = tx.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDelegateSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDelegateSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const permissionsValue = canonical_ARRAY(); + + // Build an initial transaction + DelegateSetBuilder initialBuilder{ + accountValue, + authorizeValue, + permissionsValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DelegateSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = authorizeValue; + auto const actual = rebuiltTx.getAuthorize(); + expectEqualField(expected, actual, "sfAuthorize"); + } + + { + auto const& expected = permissionsValue; + auto const actual = rebuiltTx.getPermissions(); + expectEqualField(expected, actual, "sfPermissions"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDelegateSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DelegateSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDelegateSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DelegateSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp new file mode 100644 index 0000000000..36232eb220 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/DepositPreauthTests.cpp @@ -0,0 +1,240 @@ +// Auto-generated unit tests for transaction DepositPreauth + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsDepositPreauthTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauth")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const unauthorizeValue = canonical_ACCOUNT(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + auto const unauthorizeCredentialsValue = canonical_ARRAY(); + + DepositPreauthBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAuthorize(authorizeValue); + builder.setUnauthorize(unauthorizeValue); + builder.setAuthorizeCredentials(authorizeCredentialsValue); + builder.setUnauthorizeCredentials(unauthorizeCredentialsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = authorizeValue; + auto const actualOpt = tx.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorize should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorize"); + EXPECT_TRUE(tx.hasAuthorize()); + } + + { + auto const& expected = unauthorizeValue; + auto const actualOpt = tx.getUnauthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorize should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorize"); + EXPECT_TRUE(tx.hasUnauthorize()); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = tx.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + EXPECT_TRUE(tx.hasAuthorizeCredentials()); + } + + { + auto const& expected = unauthorizeCredentialsValue; + auto const actualOpt = tx.getUnauthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorizeCredentials"); + EXPECT_TRUE(tx.hasUnauthorizeCredentials()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsDepositPreauthTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauthFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const authorizeValue = canonical_ACCOUNT(); + auto const unauthorizeValue = canonical_ACCOUNT(); + auto const authorizeCredentialsValue = canonical_ARRAY(); + auto const unauthorizeCredentialsValue = canonical_ARRAY(); + + // Build an initial transaction + DepositPreauthBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAuthorize(authorizeValue); + initialBuilder.setUnauthorize(unauthorizeValue); + initialBuilder.setAuthorizeCredentials(authorizeCredentialsValue); + initialBuilder.setUnauthorizeCredentials(unauthorizeCredentialsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + DepositPreauthBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = authorizeValue; + auto const actualOpt = rebuiltTx.getAuthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorize should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorize"); + } + + { + auto const& expected = unauthorizeValue; + auto const actualOpt = rebuiltTx.getUnauthorize(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorize should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorize"); + } + + { + auto const& expected = authorizeCredentialsValue; + auto const actualOpt = rebuiltTx.getAuthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAuthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfAuthorizeCredentials"); + } + + { + auto const& expected = unauthorizeCredentialsValue; + auto const actualOpt = rebuiltTx.getUnauthorizeCredentials(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfUnauthorizeCredentials should be present"; + expectEqualField(expected, *actualOpt, "sfUnauthorizeCredentials"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsDepositPreauthTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DepositPreauth{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsDepositPreauthTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(DepositPreauthBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsDepositPreauthTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testDepositPreauthNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + DepositPreauthBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAuthorize()); + EXPECT_FALSE(tx.getAuthorize().has_value()); + EXPECT_FALSE(tx.hasUnauthorize()); + EXPECT_FALSE(tx.getUnauthorize().has_value()); + EXPECT_FALSE(tx.hasAuthorizeCredentials()); + EXPECT_FALSE(tx.getAuthorizeCredentials().has_value()); + EXPECT_FALSE(tx.hasUnauthorizeCredentials()); + EXPECT_FALSE(tx.getUnauthorizeCredentials().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp new file mode 100644 index 0000000000..e9dfe892e4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EnableAmendmentTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction EnableAmendment + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEnableAmendmentTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEnableAmendment")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const amendmentValue = canonical_UINT256(); + + EnableAmendmentBuilder builder{ + accountValue, + ledgerSequenceValue, + amendmentValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerSequenceValue; + auto const actual = tx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = amendmentValue; + auto const actual = tx.getAmendment(); + expectEqualField(expected, actual, "sfAmendment"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEnableAmendmentTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEnableAmendmentFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const amendmentValue = canonical_UINT256(); + + // Build an initial transaction + EnableAmendmentBuilder initialBuilder{ + accountValue, + ledgerSequenceValue, + amendmentValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EnableAmendmentBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerSequenceValue; + auto const actual = rebuiltTx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = amendmentValue; + auto const actual = rebuiltTx.getAmendment(); + expectEqualField(expected, actual, "sfAmendment"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEnableAmendmentTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EnableAmendment{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEnableAmendmentTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EnableAmendmentBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp new file mode 100644 index 0000000000..300a9b7615 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCancelTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction EscrowCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + EscrowCancelBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = tx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + // Build an initial transaction + EscrowCancelBuilder initialBuilder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = rebuiltTx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp new file mode 100644 index 0000000000..1f9b3d30a9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowCreateTests.cpp @@ -0,0 +1,276 @@ +// Auto-generated unit tests for transaction EscrowCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + 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(); + + EscrowCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setCondition(conditionValue); + builder.setCancelAfter(cancelAfterValue); + builder.setFinishAfter(finishAfterValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = conditionValue; + auto const actualOpt = tx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(tx.hasCondition()); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = tx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(tx.hasCancelAfter()); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = tx.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFinishAfter should be present"; + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + EXPECT_TRUE(tx.hasFinishAfter()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + 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(); + + // Build an initial transaction + EscrowCreateBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setCondition(conditionValue); + initialBuilder.setCancelAfter(cancelAfterValue); + initialBuilder.setFinishAfter(finishAfterValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = conditionValue; + auto const actualOpt = rebuiltTx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + } + + { + auto const& expected = cancelAfterValue; + auto const actualOpt = rebuiltTx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + } + + { + auto const& expected = finishAfterValue; + auto const actualOpt = rebuiltTx.getFinishAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFinishAfter should be present"; + expectEqualField(expected, *actualOpt, "sfFinishAfter"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsEscrowCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + EscrowCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + 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()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp new file mode 100644 index 0000000000..f729d0fda3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/EscrowFinishTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction EscrowFinish + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsEscrowFinishTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinish")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + auto const fulfillmentValue = canonical_VL(); + auto const conditionValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + EscrowFinishBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setFulfillment(fulfillmentValue); + builder.setCondition(conditionValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = tx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields + { + auto const& expected = fulfillmentValue; + auto const actualOpt = tx.getFulfillment(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFulfillment should be present"; + expectEqualField(expected, *actualOpt, "sfFulfillment"); + EXPECT_TRUE(tx.hasFulfillment()); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = tx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + EXPECT_TRUE(tx.hasCondition()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsEscrowFinishTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinishFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + auto const fulfillmentValue = canonical_VL(); + auto const conditionValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + EscrowFinishBuilder initialBuilder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + initialBuilder.setFulfillment(fulfillmentValue); + initialBuilder.setCondition(conditionValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + EscrowFinishBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ownerValue; + auto const actual = rebuiltTx.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields + { + auto const& expected = fulfillmentValue; + auto const actualOpt = rebuiltTx.getFulfillment(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFulfillment should be present"; + expectEqualField(expected, *actualOpt, "sfFulfillment"); + } + + { + auto const& expected = conditionValue; + auto const actualOpt = rebuiltTx.getCondition(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCondition should be present"; + expectEqualField(expected, *actualOpt, "sfCondition"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsEscrowFinishTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowFinish{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsEscrowFinishTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(EscrowFinishBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsEscrowFinishTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testEscrowFinishNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const ownerValue = canonical_ACCOUNT(); + auto const offerSequenceValue = canonical_UINT32(); + + EscrowFinishBuilder builder{ + accountValue, + ownerValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasFulfillment()); + EXPECT_FALSE(tx.getFulfillment().has_value()); + EXPECT_FALSE(tx.hasCondition()); + EXPECT_FALSE(tx.getCondition().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp new file mode 100644 index 0000000000..b366605a4f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction LedgerStateFix + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLedgerStateFixTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFix")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerFixTypeValue = canonical_UINT16(); + auto const ownerValue = canonical_ACCOUNT(); + + LedgerStateFixBuilder builder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerFixTypeValue; + auto const actual = tx.getLedgerFixType(); + expectEqualField(expected, actual, "sfLedgerFixType"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLedgerStateFixTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFixFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerFixTypeValue = canonical_UINT16(); + auto const ownerValue = canonical_ACCOUNT(); + + // Build an initial transaction + LedgerStateFixBuilder initialBuilder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LedgerStateFixBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ledgerFixTypeValue; + auto const actual = rebuiltTx.getLedgerFixType(); + expectEqualField(expected, actual, "sfLedgerFixType"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLedgerStateFixTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LedgerStateFix{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLedgerStateFixTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LedgerStateFixBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLedgerStateFixTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLedgerStateFixNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const ledgerFixTypeValue = canonical_UINT16(); + + LedgerStateFixBuilder builder{ + accountValue, + ledgerFixTypeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp new file mode 100644 index 0000000000..1e6c72f458 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverClawbackTests.cpp @@ -0,0 +1,198 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverClawbackBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLoanBrokerID(loanBrokerIDValue); + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = tx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(tx.hasLoanBrokerID()); + } + + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanBrokerCoverClawbackBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLoanBrokerID(loanBrokerIDValue); + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = rebuiltTx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerCoverClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + LoanBrokerCoverClawbackBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLoanBrokerID()); + EXPECT_FALSE(tx.getLoanBrokerID().has_value()); + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp new file mode 100644 index 0000000000..d6addb2fef --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverDepositTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverDepositBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanBrokerCoverDepositBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp new file mode 100644 index 0000000000..3e66944e39 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerCoverWithdrawTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction LoanBrokerCoverWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + LoanBrokerCoverWithdrawBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + LoanBrokerCoverWithdrawBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerCoverWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerCoverWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerCoverWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerCoverWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerCoverWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanBrokerCoverWithdrawBuilder builder{ + accountValue, + loanBrokerIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp new file mode 100644 index 0000000000..b557ab4397 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanBrokerDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + + LoanBrokerDeleteBuilder builder{ + accountValue, + loanBrokerIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanBrokerDeleteBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp new file mode 100644 index 0000000000..e270332f42 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanBrokerSetTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction LoanBrokerSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanBrokerSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + LoanBrokerSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLoanBrokerID(loanBrokerIDValue); + builder.setData(dataValue); + builder.setManagementFeeRate(managementFeeRateValue); + builder.setDebtMaximum(debtMaximumValue); + builder.setCoverRateMinimum(coverRateMinimumValue); + builder.setCoverRateLiquidation(coverRateLiquidationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = tx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + EXPECT_TRUE(tx.hasLoanBrokerID()); + } + + { + 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()); + } + + { + auto const& expected = managementFeeRateValue; + auto const actualOpt = tx.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfManagementFeeRate should be present"; + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + EXPECT_TRUE(tx.hasManagementFeeRate()); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = tx.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDebtMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + EXPECT_TRUE(tx.hasDebtMaximum()); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = tx.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateMinimum should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + EXPECT_TRUE(tx.hasCoverRateMinimum()); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = tx.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateLiquidation should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + EXPECT_TRUE(tx.hasCoverRateLiquidation()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanBrokerSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const managementFeeRateValue = canonical_UINT16(); + auto const debtMaximumValue = canonical_NUMBER(); + auto const coverRateMinimumValue = canonical_UINT32(); + auto const coverRateLiquidationValue = canonical_UINT32(); + + // Build an initial transaction + LoanBrokerSetBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLoanBrokerID(loanBrokerIDValue); + initialBuilder.setData(dataValue); + initialBuilder.setManagementFeeRate(managementFeeRateValue); + initialBuilder.setDebtMaximum(debtMaximumValue); + initialBuilder.setCoverRateMinimum(coverRateMinimumValue); + initialBuilder.setCoverRateLiquidation(coverRateLiquidationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanBrokerSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = loanBrokerIDValue; + auto const actualOpt = rebuiltTx.getLoanBrokerID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanBrokerID should be present"; + expectEqualField(expected, *actualOpt, "sfLoanBrokerID"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = managementFeeRateValue; + auto const actualOpt = rebuiltTx.getManagementFeeRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfManagementFeeRate should be present"; + expectEqualField(expected, *actualOpt, "sfManagementFeeRate"); + } + + { + auto const& expected = debtMaximumValue; + auto const actualOpt = rebuiltTx.getDebtMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDebtMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfDebtMaximum"); + } + + { + auto const& expected = coverRateMinimumValue; + auto const actualOpt = rebuiltTx.getCoverRateMinimum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateMinimum should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateMinimum"); + } + + { + auto const& expected = coverRateLiquidationValue; + auto const actualOpt = rebuiltTx.getCoverRateLiquidation(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCoverRateLiquidation should be present"; + expectEqualField(expected, *actualOpt, "sfCoverRateLiquidation"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanBrokerSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanBrokerSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanBrokerSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanBrokerSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + + LoanBrokerSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLoanBrokerID()); + EXPECT_FALSE(tx.getLoanBrokerID().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasManagementFeeRate()); + EXPECT_FALSE(tx.getManagementFeeRate().has_value()); + EXPECT_FALSE(tx.hasDebtMaximum()); + EXPECT_FALSE(tx.getDebtMaximum().has_value()); + EXPECT_FALSE(tx.hasCoverRateMinimum()); + EXPECT_FALSE(tx.getCoverRateMinimum().has_value()); + EXPECT_FALSE(tx.hasCoverRateLiquidation()); + EXPECT_FALSE(tx.getCoverRateLiquidation().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp new file mode 100644 index 0000000000..0edcdf4d06 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + LoanDeleteBuilder builder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanDeleteBuilder initialBuilder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp new file mode 100644 index 0000000000..bb842330d6 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanManageTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction LoanManage + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanManageTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanManage")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + LoanManageBuilder builder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanManageTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanManageFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + + // Build an initial transaction + LoanManageBuilder initialBuilder{ + accountValue, + loanIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanManageBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanManageTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanManage{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanManageTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanManageBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp new file mode 100644 index 0000000000..2b0f16f25c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanPayTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction LoanPay + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanPayTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanPay")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + LoanPayBuilder builder{ + accountValue, + loanIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = tx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanPayTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanPayFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + LoanPayBuilder initialBuilder{ + accountValue, + loanIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanPayBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanIDValue; + auto const actual = rebuiltTx.getLoanID(); + expectEqualField(expected, actual, "sfLoanID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanPayTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanPay{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanPayTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanPayBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp new file mode 100644 index 0000000000..4141a958a0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/LoanSetTests.cpp @@ -0,0 +1,507 @@ +// Auto-generated unit tests for transaction LoanSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsLoanSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const counterpartyValue = canonical_ACCOUNT(); + auto const counterpartySignatureValue = canonical_OBJECT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const principalRequestedValue = canonical_NUMBER(); + auto const paymentTotalValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + + LoanSetBuilder builder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setData(dataValue); + builder.setCounterparty(counterpartyValue); + builder.setCounterpartySignature(counterpartySignatureValue); + builder.setLoanOriginationFee(loanOriginationFeeValue); + builder.setLoanServiceFee(loanServiceFeeValue); + builder.setLatePaymentFee(latePaymentFeeValue); + builder.setClosePaymentFee(closePaymentFeeValue); + builder.setOverpaymentFee(overpaymentFeeValue); + builder.setInterestRate(interestRateValue); + builder.setLateInterestRate(lateInterestRateValue); + builder.setCloseInterestRate(closeInterestRateValue); + builder.setOverpaymentInterestRate(overpaymentInterestRateValue); + builder.setPaymentTotal(paymentTotalValue); + builder.setPaymentInterval(paymentIntervalValue); + builder.setGracePeriod(gracePeriodValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = tx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = principalRequestedValue; + auto const actual = tx.getPrincipalRequested(); + expectEqualField(expected, actual, "sfPrincipalRequested"); + } + + // Verify optional fields + { + 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()); + } + + { + auto const& expected = counterpartyValue; + auto const actualOpt = tx.getCounterparty(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present"; + expectEqualField(expected, *actualOpt, "sfCounterparty"); + EXPECT_TRUE(tx.hasCounterparty()); + } + + { + auto const& expected = counterpartySignatureValue; + auto const actualOpt = tx.getCounterpartySignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySignature"); + EXPECT_TRUE(tx.hasCounterpartySignature()); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = tx.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + EXPECT_TRUE(tx.hasLoanOriginationFee()); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = tx.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + EXPECT_TRUE(tx.hasLoanServiceFee()); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = tx.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + EXPECT_TRUE(tx.hasLatePaymentFee()); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = tx.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + EXPECT_TRUE(tx.hasClosePaymentFee()); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = tx.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + EXPECT_TRUE(tx.hasOverpaymentFee()); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = tx.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfInterestRate"); + EXPECT_TRUE(tx.hasInterestRate()); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = tx.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + EXPECT_TRUE(tx.hasLateInterestRate()); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = tx.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + EXPECT_TRUE(tx.hasCloseInterestRate()); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = tx.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + EXPECT_TRUE(tx.hasOverpaymentInterestRate()); + } + + { + auto const& expected = paymentTotalValue; + auto const actualOpt = tx.getPaymentTotal(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentTotal"); + EXPECT_TRUE(tx.hasPaymentTotal()); + } + + { + auto const& expected = paymentIntervalValue; + auto const actualOpt = tx.getPaymentInterval(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentInterval"); + EXPECT_TRUE(tx.hasPaymentInterval()); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = tx.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present"; + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + EXPECT_TRUE(tx.hasGracePeriod()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsLoanSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + auto const counterpartyValue = canonical_ACCOUNT(); + auto const counterpartySignatureValue = canonical_OBJECT(); + auto const loanOriginationFeeValue = canonical_NUMBER(); + auto const loanServiceFeeValue = canonical_NUMBER(); + auto const latePaymentFeeValue = canonical_NUMBER(); + auto const closePaymentFeeValue = canonical_NUMBER(); + auto const overpaymentFeeValue = canonical_UINT32(); + auto const interestRateValue = canonical_UINT32(); + auto const lateInterestRateValue = canonical_UINT32(); + auto const closeInterestRateValue = canonical_UINT32(); + auto const overpaymentInterestRateValue = canonical_UINT32(); + auto const principalRequestedValue = canonical_NUMBER(); + auto const paymentTotalValue = canonical_UINT32(); + auto const paymentIntervalValue = canonical_UINT32(); + auto const gracePeriodValue = canonical_UINT32(); + + // Build an initial transaction + LoanSetBuilder initialBuilder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + initialBuilder.setData(dataValue); + initialBuilder.setCounterparty(counterpartyValue); + initialBuilder.setCounterpartySignature(counterpartySignatureValue); + initialBuilder.setLoanOriginationFee(loanOriginationFeeValue); + initialBuilder.setLoanServiceFee(loanServiceFeeValue); + initialBuilder.setLatePaymentFee(latePaymentFeeValue); + initialBuilder.setClosePaymentFee(closePaymentFeeValue); + initialBuilder.setOverpaymentFee(overpaymentFeeValue); + initialBuilder.setInterestRate(interestRateValue); + initialBuilder.setLateInterestRate(lateInterestRateValue); + initialBuilder.setCloseInterestRate(closeInterestRateValue); + initialBuilder.setOverpaymentInterestRate(overpaymentInterestRateValue); + initialBuilder.setPaymentTotal(paymentTotalValue); + initialBuilder.setPaymentInterval(paymentIntervalValue); + initialBuilder.setGracePeriod(gracePeriodValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + LoanSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = loanBrokerIDValue; + auto const actual = rebuiltTx.getLoanBrokerID(); + expectEqualField(expected, actual, "sfLoanBrokerID"); + } + + { + auto const& expected = principalRequestedValue; + auto const actual = rebuiltTx.getPrincipalRequested(); + expectEqualField(expected, actual, "sfPrincipalRequested"); + } + + // Verify optional fields + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = counterpartyValue; + auto const actualOpt = rebuiltTx.getCounterparty(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present"; + expectEqualField(expected, *actualOpt, "sfCounterparty"); + } + + { + auto const& expected = counterpartySignatureValue; + auto const actualOpt = rebuiltTx.getCounterpartySignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySignature"); + } + + { + auto const& expected = loanOriginationFeeValue; + auto const actualOpt = rebuiltTx.getLoanOriginationFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanOriginationFee"); + } + + { + auto const& expected = loanServiceFeeValue; + auto const actualOpt = rebuiltTx.getLoanServiceFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present"; + expectEqualField(expected, *actualOpt, "sfLoanServiceFee"); + } + + { + auto const& expected = latePaymentFeeValue; + auto const actualOpt = rebuiltTx.getLatePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfLatePaymentFee"); + } + + { + auto const& expected = closePaymentFeeValue; + auto const actualOpt = rebuiltTx.getClosePaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfClosePaymentFee"); + } + + { + auto const& expected = overpaymentFeeValue; + auto const actualOpt = rebuiltTx.getOverpaymentFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentFee"); + } + + { + auto const& expected = interestRateValue; + auto const actualOpt = rebuiltTx.getInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfInterestRate"); + } + + { + auto const& expected = lateInterestRateValue; + auto const actualOpt = rebuiltTx.getLateInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfLateInterestRate"); + } + + { + auto const& expected = closeInterestRateValue; + auto const actualOpt = rebuiltTx.getCloseInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfCloseInterestRate"); + } + + { + auto const& expected = overpaymentInterestRateValue; + auto const actualOpt = rebuiltTx.getOverpaymentInterestRate(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present"; + expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate"); + } + + { + auto const& expected = paymentTotalValue; + auto const actualOpt = rebuiltTx.getPaymentTotal(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentTotal"); + } + + { + auto const& expected = paymentIntervalValue; + auto const actualOpt = rebuiltTx.getPaymentInterval(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present"; + expectEqualField(expected, *actualOpt, "sfPaymentInterval"); + } + + { + auto const& expected = gracePeriodValue; + auto const actualOpt = rebuiltTx.getGracePeriod(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present"; + expectEqualField(expected, *actualOpt, "sfGracePeriod"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsLoanSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsLoanSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(LoanSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsLoanSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const loanBrokerIDValue = canonical_UINT256(); + auto const principalRequestedValue = canonical_NUMBER(); + + LoanSetBuilder builder{ + accountValue, + loanBrokerIDValue, + principalRequestedValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasCounterparty()); + EXPECT_FALSE(tx.getCounterparty().has_value()); + EXPECT_FALSE(tx.hasCounterpartySignature()); + EXPECT_FALSE(tx.getCounterpartySignature().has_value()); + EXPECT_FALSE(tx.hasLoanOriginationFee()); + EXPECT_FALSE(tx.getLoanOriginationFee().has_value()); + EXPECT_FALSE(tx.hasLoanServiceFee()); + EXPECT_FALSE(tx.getLoanServiceFee().has_value()); + EXPECT_FALSE(tx.hasLatePaymentFee()); + EXPECT_FALSE(tx.getLatePaymentFee().has_value()); + EXPECT_FALSE(tx.hasClosePaymentFee()); + EXPECT_FALSE(tx.getClosePaymentFee().has_value()); + EXPECT_FALSE(tx.hasOverpaymentFee()); + EXPECT_FALSE(tx.getOverpaymentFee().has_value()); + EXPECT_FALSE(tx.hasInterestRate()); + EXPECT_FALSE(tx.getInterestRate().has_value()); + EXPECT_FALSE(tx.hasLateInterestRate()); + EXPECT_FALSE(tx.getLateInterestRate().has_value()); + EXPECT_FALSE(tx.hasCloseInterestRate()); + EXPECT_FALSE(tx.getCloseInterestRate().has_value()); + EXPECT_FALSE(tx.hasOverpaymentInterestRate()); + EXPECT_FALSE(tx.getOverpaymentInterestRate().has_value()); + EXPECT_FALSE(tx.hasPaymentTotal()); + EXPECT_FALSE(tx.getPaymentTotal().has_value()); + EXPECT_FALSE(tx.hasPaymentInterval()); + EXPECT_FALSE(tx.getPaymentInterval().has_value()); + EXPECT_FALSE(tx.hasGracePeriod()); + EXPECT_FALSE(tx.getGracePeriod().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp new file mode 100644 index 0000000000..bde6df05ff --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenAuthorizeTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction MPTokenAuthorize + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenAuthorizeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorize")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + + MPTokenAuthorizeBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenAuthorizeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorizeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + + // Build an initial transaction + MPTokenAuthorizeBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenAuthorizeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenAuthorizeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenAuthorize{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenAuthorizeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenAuthorizeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenAuthorizeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenAuthorizeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenAuthorizeBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp new file mode 100644 index 0000000000..dd4874e268 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp @@ -0,0 +1,282 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetScaleValue = canonical_UINT8(); + auto const transferFeeValue = canonical_UINT16(); + auto const maximumAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceCreateBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetScale(assetScaleValue); + builder.setTransferFee(transferFeeValue); + builder.setMaximumAmount(maximumAmountValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setMutableFlags(mutableFlagsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = assetScaleValue; + auto const actualOpt = tx.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetScale should be present"; + expectEqualField(expected, *actualOpt, "sfAssetScale"); + EXPECT_TRUE(tx.hasAssetScale()); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = tx.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaximumAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + EXPECT_TRUE(tx.hasMaximumAmount()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = tx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(tx.hasMutableFlags()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetScaleValue = canonical_UINT8(); + auto const transferFeeValue = canonical_UINT16(); + auto const maximumAmountValue = canonical_UINT64(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const mutableFlagsValue = canonical_UINT32(); + + // Build an initial transaction + MPTokenIssuanceCreateBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetScale(assetScaleValue); + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setMaximumAmount(maximumAmountValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setMutableFlags(mutableFlagsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = assetScaleValue; + auto const actualOpt = rebuiltTx.getAssetScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetScale should be present"; + expectEqualField(expected, *actualOpt, "sfAssetScale"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = maximumAmountValue; + auto const actualOpt = rebuiltTx.getMaximumAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaximumAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMaximumAmount"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = rebuiltTx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenIssuanceCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + MPTokenIssuanceCreateBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetScale()); + EXPECT_FALSE(tx.getAssetScale().has_value()); + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasMaximumAmount()); + EXPECT_FALSE(tx.getMaximumAmount().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasMutableFlags()); + EXPECT_FALSE(tx.getMutableFlags().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp new file mode 100644 index 0000000000..8a69c1f63a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceDestroyTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceDestroy + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceDestroy")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenIssuanceDestroyBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceDestroyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + // Build an initial transaction + MPTokenIssuanceDestroyBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceDestroyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceDestroyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceDestroy{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceDestroyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceDestroyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp new file mode 100644 index 0000000000..2ed9e42184 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp @@ -0,0 +1,279 @@ +// Auto-generated unit tests for transaction MPTokenIssuanceSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const domainIDValue = canonical_UINT256(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const transferFeeValue = canonical_UINT16(); + auto const mutableFlagsValue = canonical_UINT32(); + + MPTokenIssuanceSetBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setHolder(holderValue); + builder.setDomainID(domainIDValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setTransferFee(transferFeeValue); + builder.setMutableFlags(mutableFlagsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = tx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + EXPECT_TRUE(tx.hasHolder()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = tx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + EXPECT_TRUE(tx.hasMutableFlags()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const domainIDValue = canonical_UINT256(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const transferFeeValue = canonical_UINT16(); + auto const mutableFlagsValue = canonical_UINT32(); + + // Build an initial transaction + MPTokenIssuanceSetBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setHolder(holderValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setMutableFlags(mutableFlagsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + MPTokenIssuanceSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields + { + auto const& expected = holderValue; + auto const actualOpt = rebuiltTx.getHolder(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfHolder should be present"; + expectEqualField(expected, *actualOpt, "sfHolder"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = mutableFlagsValue; + auto const actualOpt = rebuiltTx.getMutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfMutableFlags"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsMPTokenIssuanceSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(MPTokenIssuanceSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testMPTokenIssuanceSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + MPTokenIssuanceSetBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolder()); + EXPECT_FALSE(tx.getHolder().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasMutableFlags()); + EXPECT_FALSE(tx.getMutableFlags().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp new file mode 100644 index 0000000000..e2fb8d29ec --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenAcceptOfferTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction NFTokenAcceptOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenBuyOfferValue = canonical_UINT256(); + auto const nFTokenSellOfferValue = canonical_UINT256(); + auto const nFTokenBrokerFeeValue = canonical_AMOUNT(); + + NFTokenAcceptOfferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setNFTokenBuyOffer(nFTokenBuyOfferValue); + builder.setNFTokenSellOffer(nFTokenSellOfferValue); + builder.setNFTokenBrokerFee(nFTokenBrokerFeeValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = nFTokenBuyOfferValue; + auto const actualOpt = tx.getNFTokenBuyOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBuyOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBuyOffer"); + EXPECT_TRUE(tx.hasNFTokenBuyOffer()); + } + + { + auto const& expected = nFTokenSellOfferValue; + auto const actualOpt = tx.getNFTokenSellOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenSellOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenSellOffer"); + EXPECT_TRUE(tx.hasNFTokenSellOffer()); + } + + { + auto const& expected = nFTokenBrokerFeeValue; + auto const actualOpt = tx.getNFTokenBrokerFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBrokerFee should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBrokerFee"); + EXPECT_TRUE(tx.hasNFTokenBrokerFee()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenBuyOfferValue = canonical_UINT256(); + auto const nFTokenSellOfferValue = canonical_UINT256(); + auto const nFTokenBrokerFeeValue = canonical_AMOUNT(); + + // Build an initial transaction + NFTokenAcceptOfferBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setNFTokenBuyOffer(nFTokenBuyOfferValue); + initialBuilder.setNFTokenSellOffer(nFTokenSellOfferValue); + initialBuilder.setNFTokenBrokerFee(nFTokenBrokerFeeValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenAcceptOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = nFTokenBuyOfferValue; + auto const actualOpt = rebuiltTx.getNFTokenBuyOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBuyOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBuyOffer"); + } + + { + auto const& expected = nFTokenSellOfferValue; + auto const actualOpt = rebuiltTx.getNFTokenSellOffer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenSellOffer should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenSellOffer"); + } + + { + auto const& expected = nFTokenBrokerFeeValue; + auto const actualOpt = rebuiltTx.getNFTokenBrokerFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfNFTokenBrokerFee should be present"; + expectEqualField(expected, *actualOpt, "sfNFTokenBrokerFee"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenAcceptOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenAcceptOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenAcceptOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenAcceptOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenAcceptOfferTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenAcceptOfferNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + NFTokenAcceptOfferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasNFTokenBuyOffer()); + EXPECT_FALSE(tx.getNFTokenBuyOffer().has_value()); + EXPECT_FALSE(tx.hasNFTokenSellOffer()); + EXPECT_FALSE(tx.getNFTokenSellOffer().has_value()); + EXPECT_FALSE(tx.hasNFTokenBrokerFee()); + EXPECT_FALSE(tx.getNFTokenBrokerFee().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp new file mode 100644 index 0000000000..128ff51794 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenBurnTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction NFTokenBurn + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenBurnTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurn")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + + NFTokenBurnBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenBurnTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurnFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + + // Build an initial transaction + NFTokenBurnBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenBurnBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenBurnTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenBurn{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenBurnTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenBurnBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenBurnTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenBurnNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + + NFTokenBurnBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp new file mode 100644 index 0000000000..f8ab0f7da1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCancelOfferTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction NFTokenCancelOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenCancelOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCancelOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenOffersValue = canonical_VECTOR256(); + + NFTokenCancelOfferBuilder builder{ + accountValue, + nFTokenOffersValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenOffersValue; + auto const actual = tx.getNFTokenOffers(); + expectEqualField(expected, actual, "sfNFTokenOffers"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenCancelOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCancelOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenOffersValue = canonical_VECTOR256(); + + // Build an initial transaction + NFTokenCancelOfferBuilder initialBuilder{ + accountValue, + nFTokenOffersValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenCancelOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenOffersValue; + auto const actual = rebuiltTx.getNFTokenOffers(); + expectEqualField(expected, actual, "sfNFTokenOffers"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCancelOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCancelOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCancelOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCancelOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp new file mode 100644 index 0000000000..c57ede25c4 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenCreateOfferTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction NFTokenCreateOffer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenCreateOfferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOffer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + NFTokenCreateOfferBuilder builder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setOwner(ownerValue); + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenCreateOfferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOfferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const ownerValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + NFTokenCreateOfferBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setOwner(ownerValue); + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenCreateOfferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCreateOfferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCreateOffer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenCreateOfferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenCreateOfferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenCreateOfferTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenCreateOfferNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + NFTokenCreateOfferBuilder builder{ + accountValue, + nFTokenIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp new file mode 100644 index 0000000000..e85dbd238c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenMintTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction NFTokenMint + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenMintTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMint")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenTaxonValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const issuerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + NFTokenMintBuilder builder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setTransferFee(transferFeeValue); + builder.setIssuer(issuerValue); + builder.setURI(uRIValue); + builder.setAmount(amountValue); + builder.setDestination(destinationValue); + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenTaxonValue; + auto const actual = tx.getNFTokenTaxon(); + expectEqualField(expected, actual, "sfNFTokenTaxon"); + } + + // Verify optional fields + { + auto const& expected = transferFeeValue; + auto const actualOpt = tx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + EXPECT_TRUE(tx.hasTransferFee()); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = tx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + EXPECT_TRUE(tx.hasIssuer()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenMintTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMintFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenTaxonValue = canonical_UINT32(); + auto const transferFeeValue = canonical_UINT16(); + auto const issuerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + NFTokenMintBuilder initialBuilder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + initialBuilder.setTransferFee(transferFeeValue); + initialBuilder.setIssuer(issuerValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setAmount(amountValue); + initialBuilder.setDestination(destinationValue); + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenMintBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenTaxonValue; + auto const actual = rebuiltTx.getNFTokenTaxon(); + expectEqualField(expected, actual, "sfNFTokenTaxon"); + } + + // Verify optional fields + { + auto const& expected = transferFeeValue; + auto const actualOpt = rebuiltTx.getTransferFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfTransferFee should be present"; + expectEqualField(expected, *actualOpt, "sfTransferFee"); + } + + { + auto const& expected = issuerValue; + auto const actualOpt = rebuiltTx.getIssuer(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfIssuer should be present"; + expectEqualField(expected, *actualOpt, "sfIssuer"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenMintTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenMint{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenMintTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenMintBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenMintTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenMintNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenTaxonValue = canonical_UINT32(); + + NFTokenMintBuilder builder{ + accountValue, + nFTokenTaxonValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasTransferFee()); + EXPECT_FALSE(tx.getTransferFee().has_value()); + EXPECT_FALSE(tx.hasIssuer()); + EXPECT_FALSE(tx.getIssuer().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp new file mode 100644 index 0000000000..9f5eeb052d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/NFTokenModifyTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction NFTokenModify + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsNFTokenModifyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModify")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + + NFTokenModifyBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOwner(ownerValue); + builder.setURI(uRIValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = tx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = tx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + EXPECT_TRUE(tx.hasOwner()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsNFTokenModifyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModifyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const nFTokenIDValue = canonical_UINT256(); + auto const ownerValue = canonical_ACCOUNT(); + auto const uRIValue = canonical_VL(); + + // Build an initial transaction + NFTokenModifyBuilder initialBuilder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOwner(ownerValue); + initialBuilder.setURI(uRIValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + NFTokenModifyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = nFTokenIDValue; + auto const actual = rebuiltTx.getNFTokenID(); + expectEqualField(expected, actual, "sfNFTokenID"); + } + + // Verify optional fields + { + auto const& expected = ownerValue; + auto const actualOpt = rebuiltTx.getOwner(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOwner should be present"; + expectEqualField(expected, *actualOpt, "sfOwner"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenModifyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenModify{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsNFTokenModifyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(NFTokenModifyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsNFTokenModifyTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testNFTokenModifyNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const nFTokenIDValue = canonical_UINT256(); + + NFTokenModifyBuilder builder{ + accountValue, + nFTokenIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOwner()); + EXPECT_FALSE(tx.getOwner().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp new file mode 100644 index 0000000000..0e3c8a4224 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OfferCancelTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction OfferCancel + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOfferCancelTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCancel")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const offerSequenceValue = canonical_UINT32(); + + OfferCancelBuilder builder{ + accountValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = offerSequenceValue; + auto const actual = tx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOfferCancelTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCancelFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const offerSequenceValue = canonical_UINT32(); + + // Build an initial transaction + OfferCancelBuilder initialBuilder{ + accountValue, + offerSequenceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OfferCancelBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = offerSequenceValue; + auto const actual = rebuiltTx.getOfferSequence(); + expectEqualField(expected, actual, "sfOfferSequence"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOfferCancelTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCancel{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOfferCancelTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCancelBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp new file mode 100644 index 0000000000..3dde69574e --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OfferCreateTests.cpp @@ -0,0 +1,255 @@ +// Auto-generated unit tests for transaction OfferCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOfferCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const offerSequenceValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + OfferCreateBuilder builder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + builder.setOfferSequence(offerSequenceValue); + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = takerPaysValue; + auto const actual = tx.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = tx.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + + { + auto const& expected = offerSequenceValue; + auto const actualOpt = tx.getOfferSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOfferSequence should be present"; + expectEqualField(expected, *actualOpt, "sfOfferSequence"); + EXPECT_TRUE(tx.hasOfferSequence()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOfferCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + auto const offerSequenceValue = canonical_UINT32(); + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + OfferCreateBuilder initialBuilder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + initialBuilder.setOfferSequence(offerSequenceValue); + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OfferCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = takerPaysValue; + auto const actual = rebuiltTx.getTakerPays(); + expectEqualField(expected, actual, "sfTakerPays"); + } + + { + auto const& expected = takerGetsValue; + auto const actual = rebuiltTx.getTakerGets(); + expectEqualField(expected, actual, "sfTakerGets"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + + { + auto const& expected = offerSequenceValue; + auto const actualOpt = rebuiltTx.getOfferSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOfferSequence should be present"; + expectEqualField(expected, *actualOpt, "sfOfferSequence"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOfferCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOfferCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OfferCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsOfferCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOfferCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const takerPaysValue = canonical_AMOUNT(); + auto const takerGetsValue = canonical_AMOUNT(); + + OfferCreateBuilder builder{ + accountValue, + takerPaysValue, + takerGetsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); + EXPECT_FALSE(tx.hasOfferSequence()); + EXPECT_FALSE(tx.getOfferSequence().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp new file mode 100644 index 0000000000..631ad0861a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OracleDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction OracleDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOracleDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + + OracleDeleteBuilder builder{ + accountValue, + oracleDocumentIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = tx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOracleDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + + // Build an initial transaction + OracleDeleteBuilder initialBuilder{ + accountValue, + oracleDocumentIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OracleDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = rebuiltTx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOracleDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOracleDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp new file mode 100644 index 0000000000..e13e8a8374 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/OracleSetTests.cpp @@ -0,0 +1,273 @@ +// Auto-generated unit tests for transaction OracleSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsOracleSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + OracleSetBuilder builder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setProvider(providerValue); + builder.setURI(uRIValue); + builder.setAssetClass(assetClassValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = tx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = tx.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = tx.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + // Verify optional fields + { + auto const& expected = providerValue; + auto const actualOpt = tx.getProvider(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfProvider should be present"; + expectEqualField(expected, *actualOpt, "sfProvider"); + EXPECT_TRUE(tx.hasProvider()); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = tx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + EXPECT_TRUE(tx.hasURI()); + } + + { + auto const& expected = assetClassValue; + auto const actualOpt = tx.getAssetClass(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetClass should be present"; + expectEqualField(expected, *actualOpt, "sfAssetClass"); + EXPECT_TRUE(tx.hasAssetClass()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsOracleSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const providerValue = canonical_VL(); + auto const uRIValue = canonical_VL(); + auto const assetClassValue = canonical_VL(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + // Build an initial transaction + OracleSetBuilder initialBuilder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + initialBuilder.setProvider(providerValue); + initialBuilder.setURI(uRIValue); + initialBuilder.setAssetClass(assetClassValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + OracleSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = oracleDocumentIDValue; + auto const actual = rebuiltTx.getOracleDocumentID(); + expectEqualField(expected, actual, "sfOracleDocumentID"); + } + + { + auto const& expected = lastUpdateTimeValue; + auto const actual = rebuiltTx.getLastUpdateTime(); + expectEqualField(expected, actual, "sfLastUpdateTime"); + } + + { + auto const& expected = priceDataSeriesValue; + auto const actual = rebuiltTx.getPriceDataSeries(); + expectEqualField(expected, actual, "sfPriceDataSeries"); + } + + // Verify optional fields + { + auto const& expected = providerValue; + auto const actualOpt = rebuiltTx.getProvider(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfProvider should be present"; + expectEqualField(expected, *actualOpt, "sfProvider"); + } + + { + auto const& expected = uRIValue; + auto const actualOpt = rebuiltTx.getURI(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfURI should be present"; + expectEqualField(expected, *actualOpt, "sfURI"); + } + + { + auto const& expected = assetClassValue; + auto const actualOpt = rebuiltTx.getAssetClass(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetClass should be present"; + expectEqualField(expected, *actualOpt, "sfAssetClass"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsOracleSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsOracleSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(OracleSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsOracleSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testOracleSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const oracleDocumentIDValue = canonical_UINT32(); + auto const lastUpdateTimeValue = canonical_UINT32(); + auto const priceDataSeriesValue = canonical_ARRAY(); + + OracleSetBuilder builder{ + accountValue, + oracleDocumentIDValue, + lastUpdateTimeValue, + priceDataSeriesValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasProvider()); + EXPECT_FALSE(tx.getProvider().has_value()); + EXPECT_FALSE(tx.hasURI()); + EXPECT_FALSE(tx.getURI().has_value()); + EXPECT_FALSE(tx.hasAssetClass()); + EXPECT_FALSE(tx.getAssetClass().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp new file mode 100644 index 0000000000..1b742196a5 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelClaimTests.cpp @@ -0,0 +1,279 @@ +// Auto-generated unit tests for transaction PaymentChannelClaim + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelClaimTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaim")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const signatureValue = canonical_VL(); + auto const publicKeyValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + PaymentChannelClaimBuilder builder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + builder.setBalance(balanceValue); + builder.setSignature(signatureValue); + builder.setPublicKey(publicKeyValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = tx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + + { + auto const& expected = balanceValue; + auto const actualOpt = tx.getBalance(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBalance should be present"; + expectEqualField(expected, *actualOpt, "sfBalance"); + EXPECT_TRUE(tx.hasBalance()); + } + + { + auto const& expected = signatureValue; + auto const actualOpt = tx.getSignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignature should be present"; + expectEqualField(expected, *actualOpt, "sfSignature"); + EXPECT_TRUE(tx.hasSignature()); + } + + { + auto const& expected = publicKeyValue; + auto const actualOpt = tx.getPublicKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPublicKey should be present"; + expectEqualField(expected, *actualOpt, "sfPublicKey"); + EXPECT_TRUE(tx.hasPublicKey()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelClaimTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaimFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const balanceValue = canonical_AMOUNT(); + auto const signatureValue = canonical_VL(); + auto const publicKeyValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + PaymentChannelClaimBuilder initialBuilder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + initialBuilder.setBalance(balanceValue); + initialBuilder.setSignature(signatureValue); + initialBuilder.setPublicKey(publicKeyValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelClaimBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = rebuiltTx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + + { + auto const& expected = balanceValue; + auto const actualOpt = rebuiltTx.getBalance(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBalance should be present"; + expectEqualField(expected, *actualOpt, "sfBalance"); + } + + { + auto const& expected = signatureValue; + auto const actualOpt = rebuiltTx.getSignature(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignature should be present"; + expectEqualField(expected, *actualOpt, "sfSignature"); + } + + { + auto const& expected = publicKeyValue; + auto const actualOpt = rebuiltTx.getPublicKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPublicKey should be present"; + expectEqualField(expected, *actualOpt, "sfPublicKey"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelClaimTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelClaim{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelClaimTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelClaimBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelClaimTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelClaimNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const channelValue = canonical_UINT256(); + + PaymentChannelClaimBuilder builder{ + accountValue, + channelValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); + EXPECT_FALSE(tx.hasBalance()); + EXPECT_FALSE(tx.getBalance().has_value()); + EXPECT_FALSE(tx.hasSignature()); + EXPECT_FALSE(tx.getSignature().has_value()); + EXPECT_FALSE(tx.hasPublicKey()); + EXPECT_FALSE(tx.getPublicKey().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp new file mode 100644 index 0000000000..e3ba999d0b --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelCreateTests.cpp @@ -0,0 +1,270 @@ +// Auto-generated unit tests for transaction PaymentChannelCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + PaymentChannelCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setCancelAfter(cancelAfterValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = tx.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + // Verify optional fields + { + auto const& expected = cancelAfterValue; + auto const actualOpt = tx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + EXPECT_TRUE(tx.hasCancelAfter()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + auto const cancelAfterValue = canonical_UINT32(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + PaymentChannelCreateBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + initialBuilder.setCancelAfter(cancelAfterValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = settleDelayValue; + auto const actual = rebuiltTx.getSettleDelay(); + expectEqualField(expected, actual, "sfSettleDelay"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + // Verify optional fields + { + auto const& expected = cancelAfterValue; + auto const actualOpt = rebuiltTx.getCancelAfter(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCancelAfter should be present"; + expectEqualField(expected, *actualOpt, "sfCancelAfter"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const settleDelayValue = canonical_UINT32(); + auto const publicKeyValue = canonical_VL(); + + PaymentChannelCreateBuilder builder{ + accountValue, + destinationValue, + amountValue, + settleDelayValue, + publicKeyValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasCancelAfter()); + EXPECT_FALSE(tx.getCancelAfter().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp new file mode 100644 index 0000000000..ce070c0761 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentChannelFundTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction PaymentChannelFund + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentChannelFundTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFund")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + + PaymentChannelFundBuilder builder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setExpiration(expirationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = tx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = tx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + EXPECT_TRUE(tx.hasExpiration()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentChannelFundTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFundFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const expirationValue = canonical_UINT32(); + + // Build an initial transaction + PaymentChannelFundBuilder initialBuilder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setExpiration(expirationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentChannelFundBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = channelValue; + auto const actual = rebuiltTx.getChannel(); + expectEqualField(expected, actual, "sfChannel"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = expirationValue; + auto const actualOpt = rebuiltTx.getExpiration(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfExpiration should be present"; + expectEqualField(expected, *actualOpt, "sfExpiration"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelFundTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelFund{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentChannelFundTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentChannelFundBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentChannelFundTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentChannelFundNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const channelValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + PaymentChannelFundBuilder builder{ + accountValue, + channelValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasExpiration()); + EXPECT_FALSE(tx.getExpiration().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp new file mode 100644 index 0000000000..7e5c046fca --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PaymentTests.cpp @@ -0,0 +1,339 @@ +// Auto-generated unit tests for transaction Payment + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPaymentTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPayment")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const pathsValue = canonical_PATHSET(); + auto const invoiceIDValue = canonical_UINT256(); + auto const destinationTagValue = canonical_UINT32(); + auto const deliverMinValue = canonical_AMOUNT(); + auto const credentialIDsValue = canonical_VECTOR256(); + auto const domainIDValue = canonical_UINT256(); + + PaymentBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSendMax(sendMaxValue); + builder.setPaths(pathsValue); + builder.setInvoiceID(invoiceIDValue); + builder.setDestinationTag(destinationTagValue); + builder.setDeliverMin(deliverMinValue); + builder.setCredentialIDs(credentialIDsValue); + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = sendMaxValue; + auto const actualOpt = tx.getSendMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present"; + expectEqualField(expected, *actualOpt, "sfSendMax"); + EXPECT_TRUE(tx.hasSendMax()); + } + + { + auto const& expected = pathsValue; + auto const actualOpt = tx.getPaths(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present"; + expectEqualField(expected, *actualOpt, "sfPaths"); + EXPECT_TRUE(tx.hasPaths()); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = tx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + EXPECT_TRUE(tx.hasInvoiceID()); + } + + { + 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 = deliverMinValue; + auto const actualOpt = tx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + EXPECT_TRUE(tx.hasDeliverMin()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPaymentTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const sendMaxValue = canonical_AMOUNT(); + auto const pathsValue = canonical_PATHSET(); + auto const invoiceIDValue = canonical_UINT256(); + auto const destinationTagValue = canonical_UINT32(); + auto const deliverMinValue = canonical_AMOUNT(); + auto const credentialIDsValue = canonical_VECTOR256(); + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + PaymentBuilder initialBuilder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSendMax(sendMaxValue); + initialBuilder.setPaths(pathsValue); + initialBuilder.setInvoiceID(invoiceIDValue); + initialBuilder.setDestinationTag(destinationTagValue); + initialBuilder.setDeliverMin(deliverMinValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PaymentBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = sendMaxValue; + auto const actualOpt = rebuiltTx.getSendMax(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSendMax should be present"; + expectEqualField(expected, *actualOpt, "sfSendMax"); + } + + { + auto const& expected = pathsValue; + auto const actualOpt = rebuiltTx.getPaths(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaths should be present"; + expectEqualField(expected, *actualOpt, "sfPaths"); + } + + { + auto const& expected = invoiceIDValue; + auto const actualOpt = rebuiltTx.getInvoiceID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInvoiceID should be present"; + expectEqualField(expected, *actualOpt, "sfInvoiceID"); + } + + { + 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 = deliverMinValue; + auto const actualOpt = rebuiltTx.getDeliverMin(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDeliverMin should be present"; + expectEqualField(expected, *actualOpt, "sfDeliverMin"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPaymentTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(Payment{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPaymentTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PaymentBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPaymentTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPaymentNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + PaymentBuilder builder{ + accountValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSendMax()); + EXPECT_FALSE(tx.getSendMax().has_value()); + EXPECT_FALSE(tx.hasPaths()); + EXPECT_FALSE(tx.getPaths().has_value()); + EXPECT_FALSE(tx.hasInvoiceID()); + EXPECT_FALSE(tx.getInvoiceID().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); + EXPECT_FALSE(tx.hasDeliverMin()); + EXPECT_FALSE(tx.getDeliverMin().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp new file mode 100644 index 0000000000..e9f92fb5b3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction PermissionedDomainDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + + PermissionedDomainDeleteBuilder builder{ + accountValue, + domainIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = domainIDValue; + auto const actual = tx.getDomainID(); + expectEqualField(expected, actual, "sfDomainID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + + // Build an initial transaction + PermissionedDomainDeleteBuilder initialBuilder{ + accountValue, + domainIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PermissionedDomainDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = domainIDValue; + auto const actual = rebuiltTx.getDomainID(); + expectEqualField(expected, actual, "sfDomainID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp new file mode 100644 index 0000000000..eeec170f4c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/PermissionedDomainSetTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction PermissionedDomainSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsPermissionedDomainSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + + PermissionedDomainSetBuilder builder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDomainID(domainIDValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = acceptedCredentialsValue; + auto const actual = tx.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + // Verify optional fields + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsPermissionedDomainSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const domainIDValue = canonical_UINT256(); + auto const acceptedCredentialsValue = canonical_ARRAY(); + + // Build an initial transaction + PermissionedDomainSetBuilder initialBuilder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDomainID(domainIDValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + PermissionedDomainSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = acceptedCredentialsValue; + auto const actual = rebuiltTx.getAcceptedCredentials(); + expectEqualField(expected, actual, "sfAcceptedCredentials"); + } + + // Verify optional fields + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsPermissionedDomainSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(PermissionedDomainSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsPermissionedDomainSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testPermissionedDomainSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const acceptedCredentialsValue = canonical_ARRAY(); + + PermissionedDomainSetBuilder builder{ + accountValue, + acceptedCredentialsValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp new file mode 100644 index 0000000000..981bf72ea7 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SetFeeTests.cpp @@ -0,0 +1,324 @@ +// Auto-generated unit tests for transaction SetFee + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSetFeeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFee")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + + SetFeeBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLedgerSequence(ledgerSequenceValue); + builder.setBaseFee(baseFeeValue); + builder.setReferenceFeeUnits(referenceFeeUnitsValue); + builder.setReserveBase(reserveBaseValue); + builder.setReserveIncrement(reserveIncrementValue); + builder.setBaseFeeDrops(baseFeeDropsValue); + builder.setReserveBaseDrops(reserveBaseDropsValue); + builder.setReserveIncrementDrops(reserveIncrementDropsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = ledgerSequenceValue; + auto const actualOpt = tx.getLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLedgerSequence should be present"; + expectEqualField(expected, *actualOpt, "sfLedgerSequence"); + EXPECT_TRUE(tx.hasLedgerSequence()); + } + + { + auto const& expected = baseFeeValue; + auto const actualOpt = tx.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFee should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFee"); + EXPECT_TRUE(tx.hasBaseFee()); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = tx.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReferenceFeeUnits should be present"; + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + EXPECT_TRUE(tx.hasReferenceFeeUnits()); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = tx.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBase should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBase"); + EXPECT_TRUE(tx.hasReserveBase()); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = tx.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrement should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + EXPECT_TRUE(tx.hasReserveIncrement()); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = tx.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFeeDrops should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + EXPECT_TRUE(tx.hasBaseFeeDrops()); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = tx.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBaseDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + EXPECT_TRUE(tx.hasReserveBaseDrops()); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = tx.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrementDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + EXPECT_TRUE(tx.hasReserveIncrementDrops()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSetFeeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFeeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ledgerSequenceValue = canonical_UINT32(); + auto const baseFeeValue = canonical_UINT64(); + auto const referenceFeeUnitsValue = canonical_UINT32(); + auto const reserveBaseValue = canonical_UINT32(); + auto const reserveIncrementValue = canonical_UINT32(); + auto const baseFeeDropsValue = canonical_AMOUNT(); + auto const reserveBaseDropsValue = canonical_AMOUNT(); + auto const reserveIncrementDropsValue = canonical_AMOUNT(); + + // Build an initial transaction + SetFeeBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLedgerSequence(ledgerSequenceValue); + initialBuilder.setBaseFee(baseFeeValue); + initialBuilder.setReferenceFeeUnits(referenceFeeUnitsValue); + initialBuilder.setReserveBase(reserveBaseValue); + initialBuilder.setReserveIncrement(reserveIncrementValue); + initialBuilder.setBaseFeeDrops(baseFeeDropsValue); + initialBuilder.setReserveBaseDrops(reserveBaseDropsValue); + initialBuilder.setReserveIncrementDrops(reserveIncrementDropsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SetFeeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = ledgerSequenceValue; + auto const actualOpt = rebuiltTx.getLedgerSequence(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLedgerSequence should be present"; + expectEqualField(expected, *actualOpt, "sfLedgerSequence"); + } + + { + auto const& expected = baseFeeValue; + auto const actualOpt = rebuiltTx.getBaseFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFee should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFee"); + } + + { + auto const& expected = referenceFeeUnitsValue; + auto const actualOpt = rebuiltTx.getReferenceFeeUnits(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReferenceFeeUnits should be present"; + expectEqualField(expected, *actualOpt, "sfReferenceFeeUnits"); + } + + { + auto const& expected = reserveBaseValue; + auto const actualOpt = rebuiltTx.getReserveBase(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBase should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBase"); + } + + { + auto const& expected = reserveIncrementValue; + auto const actualOpt = rebuiltTx.getReserveIncrement(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrement should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrement"); + } + + { + auto const& expected = baseFeeDropsValue; + auto const actualOpt = rebuiltTx.getBaseFeeDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBaseFeeDrops should be present"; + expectEqualField(expected, *actualOpt, "sfBaseFeeDrops"); + } + + { + auto const& expected = reserveBaseDropsValue; + auto const actualOpt = rebuiltTx.getReserveBaseDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveBaseDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveBaseDrops"); + } + + { + auto const& expected = reserveIncrementDropsValue; + auto const actualOpt = rebuiltTx.getReserveIncrementDrops(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveIncrementDrops should be present"; + expectEqualField(expected, *actualOpt, "sfReserveIncrementDrops"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSetFeeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetFee{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSetFeeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetFeeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSetFeeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetFeeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SetFeeBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLedgerSequence()); + EXPECT_FALSE(tx.getLedgerSequence().has_value()); + EXPECT_FALSE(tx.hasBaseFee()); + EXPECT_FALSE(tx.getBaseFee().has_value()); + EXPECT_FALSE(tx.hasReferenceFeeUnits()); + EXPECT_FALSE(tx.getReferenceFeeUnits().has_value()); + EXPECT_FALSE(tx.hasReserveBase()); + EXPECT_FALSE(tx.getReserveBase().has_value()); + EXPECT_FALSE(tx.hasReserveIncrement()); + EXPECT_FALSE(tx.getReserveIncrement().has_value()); + EXPECT_FALSE(tx.hasBaseFeeDrops()); + EXPECT_FALSE(tx.getBaseFeeDrops().has_value()); + EXPECT_FALSE(tx.hasReserveBaseDrops()); + EXPECT_FALSE(tx.getReserveBaseDrops().has_value()); + EXPECT_FALSE(tx.hasReserveIncrementDrops()); + EXPECT_FALSE(tx.getReserveIncrementDrops().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp new file mode 100644 index 0000000000..ce4f8ce2ef --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SetRegularKeyTests.cpp @@ -0,0 +1,177 @@ +// Auto-generated unit tests for transaction SetRegularKey + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSetRegularKeyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKey")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const regularKeyValue = canonical_ACCOUNT(); + + SetRegularKeyBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setRegularKey(regularKeyValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = regularKeyValue; + auto const actualOpt = tx.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRegularKey should be present"; + expectEqualField(expected, *actualOpt, "sfRegularKey"); + EXPECT_TRUE(tx.hasRegularKey()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSetRegularKeyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKeyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const regularKeyValue = canonical_ACCOUNT(); + + // Build an initial transaction + SetRegularKeyBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setRegularKey(regularKeyValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SetRegularKeyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = regularKeyValue; + auto const actualOpt = rebuiltTx.getRegularKey(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRegularKey should be present"; + expectEqualField(expected, *actualOpt, "sfRegularKey"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSetRegularKeyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetRegularKey{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSetRegularKeyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SetRegularKeyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSetRegularKeyTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSetRegularKeyNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SetRegularKeyBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasRegularKey()); + EXPECT_FALSE(tx.getRegularKey().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp new file mode 100644 index 0000000000..8d027823d0 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SignerListSetTests.cpp @@ -0,0 +1,195 @@ +// Auto-generated unit tests for transaction SignerListSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSignerListSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + + SignerListSetBuilder builder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSignerEntries(signerEntriesValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = signerQuorumValue; + auto const actual = tx.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + // Verify optional fields + { + auto const& expected = signerEntriesValue; + auto const actualOpt = tx.getSignerEntries(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignerEntries should be present"; + expectEqualField(expected, *actualOpt, "sfSignerEntries"); + EXPECT_TRUE(tx.hasSignerEntries()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSignerListSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const signerQuorumValue = canonical_UINT32(); + auto const signerEntriesValue = canonical_ARRAY(); + + // Build an initial transaction + SignerListSetBuilder initialBuilder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSignerEntries(signerEntriesValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SignerListSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = signerQuorumValue; + auto const actual = rebuiltTx.getSignerQuorum(); + expectEqualField(expected, actual, "sfSignerQuorum"); + } + + // Verify optional fields + { + auto const& expected = signerEntriesValue; + auto const actualOpt = rebuiltTx.getSignerEntries(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignerEntries should be present"; + expectEqualField(expected, *actualOpt, "sfSignerEntries"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSignerListSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SignerListSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSignerListSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SignerListSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSignerListSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testSignerListSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const signerQuorumValue = canonical_UINT32(); + + SignerListSetBuilder builder{ + accountValue, + signerQuorumValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSignerEntries()); + EXPECT_FALSE(tx.getSignerEntries().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp new file mode 100644 index 0000000000..bb07cdef84 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/TicketCreateTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction TicketCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsTicketCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTicketCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ticketCountValue = canonical_UINT32(); + + TicketCreateBuilder builder{ + accountValue, + ticketCountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ticketCountValue; + auto const actual = tx.getTicketCount(); + expectEqualField(expected, actual, "sfTicketCount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsTicketCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTicketCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const ticketCountValue = canonical_UINT32(); + + // Build an initial transaction + TicketCreateBuilder initialBuilder{ + accountValue, + ticketCountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + TicketCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = ticketCountValue; + auto const actual = rebuiltTx.getTicketCount(); + expectEqualField(expected, actual, "sfTicketCount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsTicketCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TicketCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsTicketCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TicketCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp new file mode 100644 index 0000000000..12b243ca5c --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/TrustSetTests.cpp @@ -0,0 +1,219 @@ +// Auto-generated unit tests for transaction TrustSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsTrustSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const limitAmountValue = canonical_AMOUNT(); + auto const qualityInValue = canonical_UINT32(); + auto const qualityOutValue = canonical_UINT32(); + + TrustSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setLimitAmount(limitAmountValue); + builder.setQualityIn(qualityInValue); + builder.setQualityOut(qualityOutValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = limitAmountValue; + auto const actualOpt = tx.getLimitAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLimitAmount should be present"; + expectEqualField(expected, *actualOpt, "sfLimitAmount"); + EXPECT_TRUE(tx.hasLimitAmount()); + } + + { + auto const& expected = qualityInValue; + auto const actualOpt = tx.getQualityIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityIn should be present"; + expectEqualField(expected, *actualOpt, "sfQualityIn"); + EXPECT_TRUE(tx.hasQualityIn()); + } + + { + auto const& expected = qualityOutValue; + auto const actualOpt = tx.getQualityOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityOut should be present"; + expectEqualField(expected, *actualOpt, "sfQualityOut"); + EXPECT_TRUE(tx.hasQualityOut()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsTrustSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const limitAmountValue = canonical_AMOUNT(); + auto const qualityInValue = canonical_UINT32(); + auto const qualityOutValue = canonical_UINT32(); + + // Build an initial transaction + TrustSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setLimitAmount(limitAmountValue); + initialBuilder.setQualityIn(qualityInValue); + initialBuilder.setQualityOut(qualityOutValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + TrustSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = limitAmountValue; + auto const actualOpt = rebuiltTx.getLimitAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLimitAmount should be present"; + expectEqualField(expected, *actualOpt, "sfLimitAmount"); + } + + { + auto const& expected = qualityInValue; + auto const actualOpt = rebuiltTx.getQualityIn(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityIn should be present"; + expectEqualField(expected, *actualOpt, "sfQualityIn"); + } + + { + auto const& expected = qualityOutValue; + auto const actualOpt = rebuiltTx.getQualityOut(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfQualityOut should be present"; + expectEqualField(expected, *actualOpt, "sfQualityOut"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsTrustSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TrustSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsTrustSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(TrustSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsTrustSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testTrustSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + TrustSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasLimitAmount()); + EXPECT_FALSE(tx.getLimitAmount().has_value()); + EXPECT_FALSE(tx.hasQualityIn()); + EXPECT_FALSE(tx.getQualityIn().has_value()); + EXPECT_FALSE(tx.hasQualityOut()); + EXPECT_FALSE(tx.getQualityOut().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp new file mode 100644 index 0000000000..3481460641 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/UNLModifyTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction UNLModify + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsUNLModifyTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testUNLModify")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const uNLModifyDisablingValue = canonical_UINT8(); + auto const ledgerSequenceValue = canonical_UINT32(); + auto const uNLModifyValidatorValue = canonical_VL(); + + UNLModifyBuilder builder{ + accountValue, + uNLModifyDisablingValue, + ledgerSequenceValue, + uNLModifyValidatorValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = uNLModifyDisablingValue; + auto const actual = tx.getUNLModifyDisabling(); + expectEqualField(expected, actual, "sfUNLModifyDisabling"); + } + + { + auto const& expected = ledgerSequenceValue; + auto const actual = tx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = uNLModifyValidatorValue; + auto const actual = tx.getUNLModifyValidator(); + expectEqualField(expected, actual, "sfUNLModifyValidator"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsUNLModifyTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testUNLModifyFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const uNLModifyDisablingValue = canonical_UINT8(); + auto const ledgerSequenceValue = canonical_UINT32(); + auto const uNLModifyValidatorValue = canonical_VL(); + + // Build an initial transaction + UNLModifyBuilder initialBuilder{ + accountValue, + uNLModifyDisablingValue, + ledgerSequenceValue, + uNLModifyValidatorValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + UNLModifyBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = uNLModifyDisablingValue; + auto const actual = rebuiltTx.getUNLModifyDisabling(); + expectEqualField(expected, actual, "sfUNLModifyDisabling"); + } + + { + auto const& expected = ledgerSequenceValue; + auto const actual = rebuiltTx.getLedgerSequence(); + expectEqualField(expected, actual, "sfLedgerSequence"); + } + + { + auto const& expected = uNLModifyValidatorValue; + auto const actual = rebuiltTx.getUNLModifyValidator(); + expectEqualField(expected, actual, "sfUNLModifyValidator"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsUNLModifyTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(UNLModify{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsUNLModifyTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(UNLModifyBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp new file mode 100644 index 0000000000..d089e3d097 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultClawbackTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction VaultClawback + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + VaultClawbackBuilder builder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAmount(amountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = holderValue; + auto const actual = tx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = tx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + EXPECT_TRUE(tx.hasAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + VaultClawbackBuilder initialBuilder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAmount(amountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = holderValue; + auto const actual = rebuiltTx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + // Verify optional fields + { + auto const& expected = amountValue; + auto const actualOpt = rebuiltTx.getAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultClawbackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultClawbackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + auto const holderValue = canonical_ACCOUNT(); + + VaultClawbackBuilder builder{ + accountValue, + vaultIDValue, + holderValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAmount()); + EXPECT_FALSE(tx.getAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp new file mode 100644 index 0000000000..da141ce812 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultCreateTests.cpp @@ -0,0 +1,300 @@ +// Auto-generated unit tests for transaction VaultCreate + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultCreateTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreate")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const dataValue = canonical_VL(); + auto const scaleValue = canonical_UINT8(); + + VaultCreateBuilder builder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetsMaximum(assetsMaximumValue); + builder.setMPTokenMetadata(mPTokenMetadataValue); + builder.setDomainID(domainIDValue); + builder.setWithdrawalPolicy(withdrawalPolicyValue); + builder.setData(dataValue); + builder.setScale(scaleValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = tx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = tx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(tx.hasAssetsMaximum()); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = tx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + EXPECT_TRUE(tx.hasMPTokenMetadata()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actualOpt = tx.getWithdrawalPolicy(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWithdrawalPolicy should be present"; + expectEqualField(expected, *actualOpt, "sfWithdrawalPolicy"); + EXPECT_TRUE(tx.hasWithdrawalPolicy()); + } + + { + 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()); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = tx.getScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfScale should be present"; + expectEqualField(expected, *actualOpt, "sfScale"); + EXPECT_TRUE(tx.hasScale()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultCreateTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreateFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const assetValue = canonical_ISSUE(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const mPTokenMetadataValue = canonical_VL(); + auto const domainIDValue = canonical_UINT256(); + auto const withdrawalPolicyValue = canonical_UINT8(); + auto const dataValue = canonical_VL(); + auto const scaleValue = canonical_UINT8(); + + // Build an initial transaction + VaultCreateBuilder initialBuilder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetsMaximum(assetsMaximumValue); + initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setWithdrawalPolicy(withdrawalPolicyValue); + initialBuilder.setData(dataValue); + initialBuilder.setScale(scaleValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultCreateBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = assetValue; + auto const actual = rebuiltTx.getAsset(); + expectEqualField(expected, actual, "sfAsset"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = rebuiltTx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = mPTokenMetadataValue; + auto const actualOpt = rebuiltTx.getMPTokenMetadata(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMPTokenMetadata should be present"; + expectEqualField(expected, *actualOpt, "sfMPTokenMetadata"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = withdrawalPolicyValue; + auto const actualOpt = rebuiltTx.getWithdrawalPolicy(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfWithdrawalPolicy should be present"; + expectEqualField(expected, *actualOpt, "sfWithdrawalPolicy"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + + { + auto const& expected = scaleValue; + auto const actualOpt = rebuiltTx.getScale(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfScale should be present"; + expectEqualField(expected, *actualOpt, "sfScale"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultCreateTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultCreate{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultCreateTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultCreateBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultCreateTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultCreateNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const assetValue = canonical_ISSUE(); + + VaultCreateBuilder builder{ + accountValue, + assetValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetsMaximum()); + EXPECT_FALSE(tx.getAssetsMaximum().has_value()); + EXPECT_FALSE(tx.hasMPTokenMetadata()); + EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasWithdrawalPolicy()); + EXPECT_FALSE(tx.getWithdrawalPolicy().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); + EXPECT_FALSE(tx.hasScale()); + EXPECT_FALSE(tx.getScale().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp new file mode 100644 index 0000000000..24c89d249a --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultDeleteTests.cpp @@ -0,0 +1,146 @@ +// Auto-generated unit tests for transaction VaultDelete + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultDeleteTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDelete")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + + VaultDeleteBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultDeleteTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDeleteFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + + // Build an initial transaction + VaultDeleteBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultDeleteBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultDeleteTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDelete{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultDeleteTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDeleteBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp new file mode 100644 index 0000000000..c18ee8bd62 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultDepositTests.cpp @@ -0,0 +1,162 @@ +// Auto-generated unit tests for transaction VaultDeposit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultDepositTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDeposit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + VaultDepositBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultDepositTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultDepositFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + VaultDepositBuilder initialBuilder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultDepositBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultDepositTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDeposit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultDepositTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultDepositBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp new file mode 100644 index 0000000000..995c1019f3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultSetTests.cpp @@ -0,0 +1,237 @@ +// Auto-generated unit tests for transaction VaultSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const domainIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + + VaultSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setAssetsMaximum(assetsMaximumValue); + builder.setDomainID(domainIDValue); + builder.setData(dataValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = tx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + EXPECT_TRUE(tx.hasAssetsMaximum()); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = tx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + EXPECT_TRUE(tx.hasDomainID()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const assetsMaximumValue = canonical_NUMBER(); + auto const domainIDValue = canonical_UINT256(); + auto const dataValue = canonical_VL(); + + // Build an initial transaction + VaultSetBuilder initialBuilder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setAssetsMaximum(assetsMaximumValue); + initialBuilder.setDomainID(domainIDValue); + initialBuilder.setData(dataValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + // Verify optional fields + { + auto const& expected = assetsMaximumValue; + auto const actualOpt = rebuiltTx.getAssetsMaximum(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfAssetsMaximum should be present"; + expectEqualField(expected, *actualOpt, "sfAssetsMaximum"); + } + + { + auto const& expected = domainIDValue; + auto const actualOpt = rebuiltTx.getDomainID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDomainID should be present"; + expectEqualField(expected, *actualOpt, "sfDomainID"); + } + + { + auto const& expected = dataValue; + auto const actualOpt = rebuiltTx.getData(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present"; + expectEqualField(expected, *actualOpt, "sfData"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + + VaultSetBuilder builder{ + accountValue, + vaultIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAssetsMaximum()); + EXPECT_FALSE(tx.getAssetsMaximum().has_value()); + EXPECT_FALSE(tx.hasDomainID()); + EXPECT_FALSE(tx.getDomainID().has_value()); + EXPECT_FALSE(tx.hasData()); + EXPECT_FALSE(tx.getData().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp new file mode 100644 index 0000000000..9d8a7fecb9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/VaultWithdrawTests.cpp @@ -0,0 +1,234 @@ +// Auto-generated unit tests for transaction VaultWithdraw + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsVaultWithdrawTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdraw")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + VaultWithdrawBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = tx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + + { + 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsVaultWithdrawTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdrawFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + + // Build an initial transaction + VaultWithdrawBuilder initialBuilder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + VaultWithdrawBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = vaultIDValue; + auto const actual = rebuiltTx.getVaultID(); + expectEqualField(expected, actual, "sfVaultID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + + { + auto const& expected = destinationTagValue; + auto const actualOpt = rebuiltTx.getDestinationTag(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present"; + expectEqualField(expected, *actualOpt, "sfDestinationTag"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsVaultWithdrawTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultWithdraw{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsVaultWithdrawTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(VaultWithdrawBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsVaultWithdrawTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testVaultWithdrawNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const vaultIDValue = canonical_UINT256(); + auto const amountValue = canonical_AMOUNT(); + + VaultWithdrawBuilder builder{ + accountValue, + vaultIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp new file mode 100644 index 0000000000..c3f2f11a00 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAccountCreateCommitTests.cpp @@ -0,0 +1,194 @@ +// Auto-generated unit tests for transaction XChainAccountCreateCommit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAccountCreateCommit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainAccountCreateCommitBuilder builder{ + accountValue, + xChainBridgeValue, + destinationValue, + amountValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAccountCreateCommitFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainAccountCreateCommitBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + destinationValue, + amountValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAccountCreateCommitBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAccountCreateCommitTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAccountCreateCommit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAccountCreateCommitTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAccountCreateCommitBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp new file mode 100644 index 0000000000..82f9c6afc1 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestationTests.cpp @@ -0,0 +1,306 @@ +// Auto-generated unit tests for transaction XChainAddAccountCreateAttestation + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddAccountCreateAttestation")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainAddAccountCreateAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainAccountCreateCountValue, + destinationValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = tx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = tx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = tx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = tx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = tx.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddAccountCreateAttestationFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainAccountCreateCountValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const signatureRewardValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainAddAccountCreateAttestationBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainAccountCreateCountValue, + destinationValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAddAccountCreateAttestationBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = rebuiltTx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = rebuiltTx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = rebuiltTx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = rebuiltTx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainAccountCreateCountValue; + auto const actual = rebuiltTx.getXChainAccountCreateCount(); + expectEqualField(expected, actual, "sfXChainAccountCreateCount"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddAccountCreateAttestationTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddAccountCreateAttestation{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddAccountCreateAttestationTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddAccountCreateAttestationBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp new file mode 100644 index 0000000000..6b9144fa68 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainAddClaimAttestationTests.cpp @@ -0,0 +1,339 @@ +// Auto-generated unit tests for transaction XChainAddClaimAttestation + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestation")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + + XChainAddClaimAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestination(destinationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = tx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = tx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = tx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = tx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = tx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = tx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + EXPECT_TRUE(tx.hasDestination()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestationFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainAddClaimAttestationBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestination(destinationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainAddClaimAttestationBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = attestationSignerAccountValue; + auto const actual = rebuiltTx.getAttestationSignerAccount(); + expectEqualField(expected, actual, "sfAttestationSignerAccount"); + } + + { + auto const& expected = publicKeyValue; + auto const actual = rebuiltTx.getPublicKey(); + expectEqualField(expected, actual, "sfPublicKey"); + } + + { + auto const& expected = signatureValue; + auto const actual = rebuiltTx.getSignature(); + expectEqualField(expected, actual, "sfSignature"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + { + auto const& expected = attestationRewardAccountValue; + auto const actual = rebuiltTx.getAttestationRewardAccount(); + expectEqualField(expected, actual, "sfAttestationRewardAccount"); + } + + { + auto const& expected = wasLockingChainSendValue; + auto const actual = rebuiltTx.getWasLockingChainSend(); + expectEqualField(expected, actual, "sfWasLockingChainSend"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + // Verify optional fields + { + auto const& expected = destinationValue; + auto const actualOpt = rebuiltTx.getDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestination should be present"; + expectEqualField(expected, *actualOpt, "sfDestination"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddClaimAttestationTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddClaimAttestation{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainAddClaimAttestationTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainAddClaimAttestationBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainAddClaimAttestationTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainAddClaimAttestationNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const attestationSignerAccountValue = canonical_ACCOUNT(); + auto const publicKeyValue = canonical_VL(); + auto const signatureValue = canonical_VL(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + auto const attestationRewardAccountValue = canonical_ACCOUNT(); + auto const wasLockingChainSendValue = canonical_UINT8(); + auto const xChainClaimIDValue = canonical_UINT64(); + + XChainAddClaimAttestationBuilder builder{ + accountValue, + xChainBridgeValue, + attestationSignerAccountValue, + publicKeyValue, + signatureValue, + otherChainSourceValue, + amountValue, + attestationRewardAccountValue, + wasLockingChainSendValue, + xChainClaimIDValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestination()); + EXPECT_FALSE(tx.getDestination().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp new file mode 100644 index 0000000000..69101c843d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainClaimTests.cpp @@ -0,0 +1,249 @@ +// Auto-generated unit tests for transaction XChainClaim + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainClaimTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaim")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + + XChainClaimBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setDestinationTag(destinationTagValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // 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()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainClaimTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaimFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const destinationTagValue = canonical_UINT32(); + auto const amountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainClaimBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setDestinationTag(destinationTagValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainClaimBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // 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"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainClaimTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainClaim{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainClaimTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainClaimBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainClaimTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainClaimNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const destinationValue = canonical_ACCOUNT(); + auto const amountValue = canonical_AMOUNT(); + + XChainClaimBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + destinationValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasDestinationTag()); + EXPECT_FALSE(tx.getDestinationTag().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp new file mode 100644 index 0000000000..e6c72ec908 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCommitTests.cpp @@ -0,0 +1,231 @@ +// Auto-generated unit tests for transaction XChainCommit + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCommitTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommit")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + auto const otherChainDestinationValue = canonical_ACCOUNT(); + + XChainCommitBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setOtherChainDestination(otherChainDestinationValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = tx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = amountValue; + auto const actual = tx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = otherChainDestinationValue; + auto const actualOpt = tx.getOtherChainDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOtherChainDestination should be present"; + expectEqualField(expected, *actualOpt, "sfOtherChainDestination"); + EXPECT_TRUE(tx.hasOtherChainDestination()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCommitTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommitFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + auto const otherChainDestinationValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainCommitBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setOtherChainDestination(otherChainDestinationValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCommitBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = xChainClaimIDValue; + auto const actual = rebuiltTx.getXChainClaimID(); + expectEqualField(expected, actual, "sfXChainClaimID"); + } + + { + auto const& expected = amountValue; + auto const actual = rebuiltTx.getAmount(); + expectEqualField(expected, actual, "sfAmount"); + } + + // Verify optional fields + { + auto const& expected = otherChainDestinationValue; + auto const actualOpt = rebuiltTx.getOtherChainDestination(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOtherChainDestination should be present"; + expectEqualField(expected, *actualOpt, "sfOtherChainDestination"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCommitTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCommit{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCommitTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCommitBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainCommitTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCommitNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const xChainClaimIDValue = canonical_UINT64(); + auto const amountValue = canonical_AMOUNT(); + + XChainCommitBuilder builder{ + accountValue, + xChainBridgeValue, + xChainClaimIDValue, + amountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasOtherChainDestination()); + EXPECT_FALSE(tx.getOtherChainDestination().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp new file mode 100644 index 0000000000..5f529440f3 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateBridgeTests.cpp @@ -0,0 +1,213 @@ +// Auto-generated unit tests for transaction XChainCreateBridge + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCreateBridgeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridge")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + XChainCreateBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = tx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(tx.hasMinAccountCreateAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCreateBridgeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridgeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainCreateBridgeBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + initialBuilder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCreateBridgeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + // Verify optional fields + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = rebuiltTx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateBridgeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateBridge{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateBridgeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateBridgeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainCreateBridgeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateBridgeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + + XChainCreateBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasMinAccountCreateAmount()); + EXPECT_FALSE(tx.getMinAccountCreateAmount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp new file mode 100644 index 0000000000..41c71b9d8d --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainCreateClaimIDTests.cpp @@ -0,0 +1,178 @@ +// Auto-generated unit tests for transaction XChainCreateClaimID + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainCreateClaimIDTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateClaimID")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + + XChainCreateClaimIDBuilder builder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + otherChainSourceValue, + sequenceValue, + feeValue + }; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = tx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = tx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainCreateClaimIDTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainCreateClaimIDFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const otherChainSourceValue = canonical_ACCOUNT(); + + // Build an initial transaction + XChainCreateClaimIDBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + signatureRewardValue, + otherChainSourceValue, + sequenceValue, + feeValue + }; + + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainCreateClaimIDBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + { + auto const& expected = signatureRewardValue; + auto const actual = rebuiltTx.getSignatureReward(); + expectEqualField(expected, actual, "sfSignatureReward"); + } + + { + auto const& expected = otherChainSourceValue; + auto const actual = rebuiltTx.getOtherChainSource(); + expectEqualField(expected, actual, "sfOtherChainSource"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateClaimIDTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateClaimID{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainCreateClaimIDTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainCreateClaimIDBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp new file mode 100644 index 0000000000..7b3c4be5ce --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/XChainModifyBridgeTests.cpp @@ -0,0 +1,216 @@ +// Auto-generated unit tests for transaction XChainModifyBridge + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsXChainModifyBridgeTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridge")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + XChainModifyBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setSignatureReward(signatureRewardValue); + builder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = tx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + // Verify optional fields + { + auto const& expected = signatureRewardValue; + auto const actualOpt = tx.getSignatureReward(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignatureReward should be present"; + expectEqualField(expected, *actualOpt, "sfSignatureReward"); + EXPECT_TRUE(tx.hasSignatureReward()); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = tx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + EXPECT_TRUE(tx.hasMinAccountCreateAmount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsXChainModifyBridgeTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridgeFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + auto const signatureRewardValue = canonical_AMOUNT(); + auto const minAccountCreateAmountValue = canonical_AMOUNT(); + + // Build an initial transaction + XChainModifyBridgeBuilder initialBuilder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + initialBuilder.setSignatureReward(signatureRewardValue); + initialBuilder.setMinAccountCreateAmount(minAccountCreateAmountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + XChainModifyBridgeBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = xChainBridgeValue; + auto const actual = rebuiltTx.getXChainBridge(); + expectEqualField(expected, actual, "sfXChainBridge"); + } + + // Verify optional fields + { + auto const& expected = signatureRewardValue; + auto const actualOpt = rebuiltTx.getSignatureReward(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSignatureReward should be present"; + expectEqualField(expected, *actualOpt, "sfSignatureReward"); + } + + { + auto const& expected = minAccountCreateAmountValue; + auto const actualOpt = rebuiltTx.getMinAccountCreateAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMinAccountCreateAmount should be present"; + expectEqualField(expected, *actualOpt, "sfMinAccountCreateAmount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsXChainModifyBridgeTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainModifyBridge{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsXChainModifyBridgeTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(XChainModifyBridgeBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsXChainModifyBridgeTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testXChainModifyBridgeNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); + + XChainModifyBridgeBuilder builder{ + accountValue, + xChainBridgeValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasSignatureReward()); + EXPECT_FALSE(tx.getSignatureReward().has_value()); + EXPECT_FALSE(tx.hasMinAccountCreateAmount()); + EXPECT_FALSE(tx.getMinAccountCreateAmount().has_value()); +} + +} diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 35f8eec1a3..5a3d53dae0 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -7,11 +7,7 @@ #include #include #include -#include -#include -#include #include -#include #include #include #include @@ -22,9 +18,13 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include #include @@ -41,7 +41,14 @@ RCLConsensus::RCLConsensus( Consensus::clock_type const& clock, ValidatorKeys const& validatorKeys, beast::Journal journal) - : adaptor_(app, std::move(feeVote), ledgerMaster, localTxs, inboundTransactions, validatorKeys, journal) + : adaptor_( + app, + std::move(feeVote), + ledgerMaster, + localTxs, + inboundTransactions, + validatorKeys, + journal) , consensus_(clock, adaptor_, journal) , j_(journal) { @@ -71,8 +78,6 @@ RCLConsensus::Adaptor::Adaptor( if (validatorKeys_.nodeID != beast::zero && validatorKeys_.keys) { - std::stringstream ss; - JLOG(j_.info()) << "Validator identity: " << toBase58(TokenType::NodePublic, validatorKeys_.keys->masterPublicKey); @@ -109,8 +114,11 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& hash) } XRPL_ASSERT( - !built->open() && built->isImmutable(), "xrpl::RCLConsensus::Adaptor::acquireLedger : valid ledger state"); - XRPL_ASSERT(built->header().hash == hash, "xrpl::RCLConsensus::Adaptor::acquireLedger : ledger hash match"); + !built->open() && built->isImmutable(), + "xrpl::RCLConsensus::Adaptor::acquireLedger : valid ledger state"); + XRPL_ASSERT( + built->header().hash == hash, + "xrpl::RCLConsensus::Adaptor::acquireLedger : ledger hash match"); // Notify inbound transactions of the new ledger sequence number inboundTransactions_.newRound(built->header().seq); @@ -164,7 +172,8 @@ void RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal) { JLOG(j_.trace()) << (proposal.isBowOut() ? "We bow out: " : "We propose: ") - << xrpl::to_string(proposal.prevLedger()) << " -> " << xrpl::to_string(proposal.position()); + << xrpl::to_string(proposal.prevLedger()) << " -> " + << xrpl::to_string(proposal.position()); protocol::TMProposeSet prop; @@ -189,7 +198,12 @@ RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal) prop.set_signature(sig.data(), sig.size()); auto const suppression = proposalUniqueId( - proposal.position(), proposal.prevLedger(), proposal.proposeSeq(), proposal.closeTime(), keys.publicKey, sig); + proposal.position(), + proposal.prevLedger(), + proposal.proposeSeq(), + proposal.closeTime(), + keys.publicKey, + sig); app_.getHashRouter().addSuppression(suppression); @@ -232,11 +246,15 @@ RCLConsensus::Adaptor::proposersFinished(RCLCxLedger const& ledger, LedgerHash c } uint256 -RCLConsensus::Adaptor::getPrevLedger(uint256 ledgerID, RCLCxLedger const& ledger, ConsensusMode mode) +RCLConsensus::Adaptor::getPrevLedger( + uint256 ledgerID, + RCLCxLedger const& ledger, + ConsensusMode mode) { RCLValidations& vals = app_.getValidations(); uint256 netLgr = vals.getPreferred( - RCLValidatedLedger{ledger.ledger_, vals.adaptor().journal()}, ledgerMaster_.getValidLedgerIndex()); + RCLValidatedLedger{ledger.ledger_, vals.adaptor().journal()}, + ledgerMaster_.getValidLedgerIndex()); if (netLgr != ledgerID) { @@ -250,8 +268,10 @@ RCLConsensus::Adaptor::getPrevLedger(uint256 ledgerID, RCLCxLedger const& ledger } auto -RCLConsensus::Adaptor::onClose(RCLCxLedger const& ledger, NetClock::time_point const& closeTime, ConsensusMode mode) - -> Result +RCLConsensus::Adaptor::onClose( + RCLCxLedger const& ledger, + NetClock::time_point const& closeTime, + ConsensusMode mode) -> Result { bool const wrongLCL = mode == ConsensusMode::wrongLedger; bool const proposing = mode == ConsensusMode::proposing; @@ -275,7 +295,9 @@ RCLConsensus::Adaptor::onClose(RCLCxLedger const& ledger, NetClock::time_point c JLOG(j_.trace()) << "Adding open ledger TX " << tx.first->getTransactionID(); Serializer s(2048); tx.first->add(s); - initialSet->addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(tx.first->getTransactionID(), s.slice())); + initialSet->addItem( + SHAMapNodeType::tnTRANSACTION_NM, + make_shamapitem(tx.first->getTransactionID(), s.slice())); } // Add pseudo-transactions to the set @@ -285,8 +307,9 @@ RCLConsensus::Adaptor::onClose(RCLCxLedger const& ledger, NetClock::time_point c { // previous ledger was flag ledger, add fee and amendment // pseudo-transactions - auto validations = app_.validators().negativeUNLFilter( - app_.getValidations().getTrustedForLedger(prevLedger->header().parentHash, prevLedger->seq() - 1)); + auto validations = + app_.validators().negativeUNLFilter(app_.getValidations().getTrustedForLedger( + prevLedger->header().parentHash, prevLedger->seq() - 1)); if (validations.size() >= app_.validators().quorum()) { feeVote_->doVoting(prevLedger, validations, initialSet); @@ -298,7 +321,11 @@ RCLConsensus::Adaptor::onClose(RCLCxLedger const& ledger, NetClock::time_point c // previous ledger was a voting ledger, // so the current consensus session is for a flag ledger, // add negative UNL pseudo-transactions - nUnlVote_.doVoting(prevLedger, app_.validators().getTrustedMasterKeys(), app_.getValidations(), initialSet); + nUnlVote_.doVoting( + prevLedger, + app_.validators().getTrustedMasterKeys(), + app_.getValidations(), + initialSet); } } @@ -310,9 +337,10 @@ RCLConsensus::Adaptor::onClose(RCLCxLedger const& ledger, NetClock::time_point c LedgerIndex const seq = prevLedger->header().seq + 1; RCLCensorshipDetector::TxIDSeqVec proposed; - initialSet->visitLeaves([&proposed, seq](boost::intrusive_ptr const& item) { - proposed.emplace_back(item->key(), seq); - }); + initialSet->visitLeaves( + [&proposed, seq](boost::intrusive_ptr const& item) { + proposed.emplace_back(item->key(), seq); + }); censorshipDetector_.propose(std::move(proposed)); } @@ -353,16 +381,20 @@ RCLConsensus::Adaptor::onAccept( Json::Value&& consensusJson, bool const validating) { - app_.getJobQueue().addJob(jtACCEPT, "AcceptLedger", [=, this, cj = std::move(consensusJson)]() mutable { - // Note that no lock is held or acquired during this job. - // This is because generic Consensus guarantees that once a ledger - // is accepted, the consensus results and capture by reference state - // will not change until startRound is called (which happens via - // endConsensus). - RclConsensusLogger clog("onAccept", validating, j_); - this->doAccept(result, prevLedger, closeResolution, rawCloseTimes, mode, std::move(cj)); - this->app_.getOPs().endConsensus(clog.ss()); - }); + app_.getJobQueue().addJob( + jtACCEPT, + "AcceptLedger", + // NOLINTNEXTLINE(cppcoreguidelines-misleading-capture-default-by-value) + [=, this, cj = std::move(consensusJson)]() mutable { + // Note that no lock is held or acquired during this job. + // This is because generic Consensus guarantees that once a ledger + // is accepted, the consensus results and capture by reference state + // will not change until startRound is called (which happens via + // endConsensus). + RclConsensusLogger clog("onAccept", validating, j_); + this->doAccept(result, prevLedger, closeResolution, rawCloseTimes, mode, std::move(cj)); + this->app_.getOPs().endConsensus(clog.ss()); + }); } void @@ -377,7 +409,7 @@ RCLConsensus::Adaptor::doAccept( prevProposers_ = result.proposers; prevRoundTime_ = result.roundTime.read(); - bool closeTimeCorrect; + bool closeTimeCorrect = false; bool const proposing = mode == ConsensusMode::proposing; bool const haveCorrectLCL = mode != ConsensusMode::wrongLedger; @@ -395,12 +427,15 @@ RCLConsensus::Adaptor::doAccept( else { // We agreed on a close time - consensusCloseTime = effCloseTime(consensusCloseTime, closeResolution, prevLedger.closeTime()); + consensusCloseTime = + effCloseTime(consensusCloseTime, closeResolution, prevLedger.closeTime()); closeTimeCorrect = true; } - JLOG(j_.debug()) << "Report: Prop=" << (proposing ? "yes" : "no") << " val=" << (validating_ ? "yes" : "no") - << " corLCL=" << (haveCorrectLCL ? "yes" : "no") << " fail=" << (consensusFail ? "yes" : "no"); + JLOG(j_.debug()) << "Report: Prop=" << (proposing ? "yes" : "no") + << " val=" << (validating_ ? "yes" : "no") + << " corLCL=" << (haveCorrectLCL ? "yes" : "no") + << " fail=" << (consensusFail ? "yes" : "no"); JLOG(j_.debug()) << "Report: Prev = " << prevLedger.id() << ":" << prevLedger.seq(); //-------------------------------------------------------------------------- @@ -451,7 +486,9 @@ RCLConsensus::Adaptor::doAccept( std::vector accepted; result.txns.map_->visitLeaves( - [&accepted](boost::intrusive_ptr const& item) { accepted.push_back(item->key()); }); + [&accepted](boost::intrusive_ptr const& item) { + accepted.push_back(item->key()); + }); // Track all the transactions which failed or were marked as retriable for (auto const& r : retriableTxs) @@ -459,8 +496,9 @@ RCLConsensus::Adaptor::doAccept( censorshipDetector_.check( std::move(accepted), - [curr = built.seq(), j = app_.journal("CensorshipDetector"), &failed](uint256 const& id, LedgerIndex seq) { - if (failed.count(id)) + [curr = built.seq(), j = app_.journal("CensorshipDetector"), &failed]( + uint256 const& id, LedgerIndex seq) { + if (failed.contains(id)) return true; auto const wait = curr - seq; @@ -468,7 +506,8 @@ RCLConsensus::Adaptor::doAccept( if (wait && (wait % censorshipWarnInternal == 0)) { std::ostringstream ss; - ss << "Potential Censorship: Eligible tx " << id << ", which we are tracking since ledger " << seq + ss << "Potential Censorship: Eligible tx " << id + << ", which we are tracking since ledger " << seq << " has not been included as of ledger " << curr << "."; JLOG(j.warn()) << ss.str(); @@ -546,9 +585,13 @@ RCLConsensus::Adaptor::doAccept( auto const lastVal = ledgerMaster_.getValidatedLedger(); std::optional rules; if (lastVal) + { rules = makeRulesGivenLedger(*lastVal, app_.config().features); + } else + { rules.emplace(app_.config().features); + } app_.openLedger().accept( app_, *rules, @@ -596,7 +639,8 @@ RCLConsensus::Adaptor::doAccept( for (auto const& [t, v] : rawCloseTimes.peers) { - JLOG(j_.info()) << std::to_string(v) << " time votes for " << std::to_string(t.time_since_epoch().count()); + JLOG(j_.info()) << std::to_string(v) << " time votes for " + << std::to_string(t.time_since_epoch().count()); closeCount += v; closeTotal += std::chrono::duration_cast(t.time_since_epoch()) * v; } @@ -608,28 +652,37 @@ RCLConsensus::Adaptor::doAccept( using duration = std::chrono::duration; using time_point = std::chrono::time_point; auto offset = time_point{closeTotal} - std::chrono::time_point_cast(closeTime); - JLOG(j_.info()) << "Our close offset is estimated at " << offset.count() << " (" << closeCount << ")"; + JLOG(j_.info()) << "Our close offset is estimated at " << offset.count() << " (" + << closeCount << ")"; app_.timeKeeper().adjustCloseTime(offset); } } void -RCLConsensus::Adaptor::notify(protocol::NodeEvent ne, RCLCxLedger const& ledger, bool haveCorrectLCL) +RCLConsensus::Adaptor::notify( + protocol::NodeEvent ne, + RCLCxLedger const& ledger, + bool haveCorrectLCL) { protocol::TMStatusChange s; if (!haveCorrectLCL) + { s.set_newevent(protocol::neLOST_SYNC); + } else + { s.set_newevent(ne); + } s.set_ledgerseq(ledger.seq()); s.set_networktime(app_.timeKeeper().now().time_since_epoch().count()); - s.set_ledgerhashprevious(ledger.parentID().begin(), std::decay_t::bytes); + s.set_ledgerhashprevious( + ledger.parentID().begin(), std::decay_t::bytes); s.set_ledgerhash(ledger.id().begin(), std::decay_t::bytes); - std::uint32_t uMin, uMax; + std::uint32_t uMin = 0, uMax = 0; if (!ledgerMaster_.getFullValidatedRange(uMin, uMax)) { uMin = 0; @@ -665,7 +718,14 @@ RCLConsensus::Adaptor::buildLCL( return buildLedger(*replayData, tapNONE, app_, j_); } return buildLedger( - previousLedger.ledger_, closeTime, closeTimeCorrect, closeResolution, app_, retriableTxs, failedTxs, j_); + previousLedger.ledger_, + closeTime, + closeTimeCorrect, + closeResolution, + app_, + retriableTxs, + failedTxs, + j_); }(); // Update fee computations based on accepted txs @@ -674,9 +734,13 @@ RCLConsensus::Adaptor::buildLCL( // And stash the ledger in the ledger master if (ledgerMaster_.storeLedger(built)) + { JLOG(j_.debug()) << "Consensus built ledger we already had"; + } else if (app_.getInboundLedgers().find(built->header().hash)) + { JLOG(j_.debug()) << "Consensus built ledger we were acquiring"; + } else JLOG(j_.debug()) << "Consensus built new ledger"; return RCLCxLedger{std::move(built)}; @@ -702,7 +766,11 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, RCLTxSet const& txns, auto const& keys = *validatorKeys_.keys; auto v = std::make_shared( - lastValidationTime_, keys.publicKey, keys.secretKey, validatorKeys_.nodeID, [&](STValidation& v) { + lastValidationTime_, + keys.publicKey, + keys.secretKey, + validatorKeys_.nodeID, + [&](STValidation& v) { v.setFieldH256(sfLedgerHash, ledger.id()); v.setFieldH256(sfConsensusHash, txns.id()); @@ -741,7 +809,8 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, RCLTxSet const& txns, // Amendments // FIXME: pass `v` and have the function insert the array // directly? - auto const amendments = app_.getAmendmentTable().doValidation(getEnabledAmendments(*ledger.ledger_)); + auto const amendments = + app_.getAmendmentTable().doValidation(getEnabledAmendments(*ledger.ledger_)); if (!amendments.empty()) v.setFieldV256(sfAmendments, STVector256(sfAmendments, amendments)); @@ -767,11 +836,13 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, RCLTxSet const& txns, void RCLConsensus::Adaptor::onModeChange(ConsensusMode before, ConsensusMode after) { - JLOG(j_.info()) << "Consensus mode change before=" << to_string(before) << ", after=" << to_string(after); + JLOG(j_.info()) << "Consensus mode change before=" << to_string(before) + << ", after=" << to_string(after); // If we were proposing but aren't any longer, we need to reset the // censorship tracking to avoid bogus warnings. - if ((before == ConsensusMode::proposing || before == ConsensusMode::observing) && before != after) + if ((before == ConsensusMode::proposing || before == ConsensusMode::observing) && + before != after) censorshipDetector_.reset(); mode_ = after; @@ -790,7 +861,9 @@ RCLConsensus::getJson(bool full) const } void -RCLConsensus::timerEntry(NetClock::time_point const& now, std::unique_ptr const& clog) +RCLConsensus::timerEntry( + NetClock::time_point const& now, + std::unique_ptr const& clog) { try { @@ -827,7 +900,9 @@ RCLConsensus::gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet) //! @see Consensus::simulate void -RCLConsensus::simulate(NetClock::time_point const& now, std::optional consensusDelay) +RCLConsensus::simulate( + NetClock::time_point const& now, + std::optional consensusDelay) { std::lock_guard _{mutex_}; consensus_.simulate(now, consensusDelay); @@ -845,7 +920,8 @@ RCLConsensus::Adaptor::preStartRound(RCLCxLedger const& prevLgr, hash_set= app_.getMaxDisallowedLedger() && !app_.getOPs().isBlocked(); + validating_ = validatorKeys_.keys && prevLgr.seq() >= app_.getMaxDisallowedLedger() && + !app_.getOPs().isBlocked(); // If we are not running in standalone mode and there's a configured UNL, // check to make sure that it's not expired. @@ -865,12 +941,14 @@ RCLConsensus::Adaptor::preStartRound(RCLCxLedger const& prevLgr, hash_set& trustedKeys) const +RCLConsensus::Adaptor::laggards( + Ledger_t::Seq const seq, + hash_set& trustedKeys) const { return app_.getValidations().laggards(seq, trustedKeys); } @@ -931,10 +1011,12 @@ RCLConsensus::startRound( std::unique_ptr const& clog) { std::lock_guard _{mutex_}; - consensus_.startRound(now, prevLgrId, prevLgr, nowUntrusted, adaptor_.preStartRound(prevLgr, nowTrusted), clog); + consensus_.startRound( + now, prevLgrId, prevLgr, nowUntrusted, adaptor_.preStartRound(prevLgr, nowTrusted), clog); } -RclConsensusLogger::RclConsensusLogger(char const* label, bool const validating, beast::Journal j) : j_(j) +RclConsensusLogger::RclConsensusLogger(char const* label, bool const validating, beast::Journal j) + : j_(j) { if (!validating && !j.info()) return; @@ -949,11 +1031,11 @@ RclConsensusLogger::~RclConsensusLogger() { if (!ss_) return; - auto const duration = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start_); + auto const duration = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_); std::stringstream outSs; - outSs << header_ << "duration " << (duration.count() / 1000) << '.' << std::setw(3) << std::setfill('0') - << (duration.count() % 1000) << "s. " << ss_->str(); + outSs << header_ << "duration " << (duration.count() / 1000) << '.' << std::setw(3) + << std::setfill('0') << (duration.count() % 1000) << "s. " << ss_->str(); j_.sink().writeAlways(beast::severities::kInfo, outSs.str()); } diff --git a/src/xrpld/app/consensus/RCLConsensus.h b/src/xrpld/app/consensus/RCLConsensus.h index 3e1b27cd9c..15d36a1aa6 100644 --- a/src/xrpld/app/consensus/RCLConsensus.h +++ b/src/xrpld/app/consensus/RCLConsensus.h @@ -275,7 +275,10 @@ class RCLConsensus @return Tentative consensus result */ Result - onClose(RCLCxLedger const& ledger, NetClock::time_point const& closeTime, ConsensusMode mode); + onClose( + RCLCxLedger const& ledger, + NetClock::time_point const& closeTime, + ConsensusMode mode); /** Process the accepted ledger. @@ -457,7 +460,9 @@ public: //! @see Consensus::timerEntry void - timerEntry(NetClock::time_point const& now, std::unique_ptr const& clog = {}); + timerEntry( + NetClock::time_point const& now, + std::unique_ptr const& clog = {}); //! @see Consensus::gotTxSet void @@ -473,7 +478,9 @@ public: //! @see Consensus::simulate void - simulate(NetClock::time_point const& now, std::optional consensusDelay); + simulate( + NetClock::time_point const& now, + std::optional consensusDelay); //! @see Consensus::proposal bool diff --git a/src/xrpld/app/consensus/RCLCxPeerPos.cpp b/src/xrpld/app/consensus/RCLCxPeerPos.cpp index 4128d65c49..cdb8350468 100644 --- a/src/xrpld/app/consensus/RCLCxPeerPos.cpp +++ b/src/xrpld/app/consensus/RCLCxPeerPos.cpp @@ -19,7 +19,7 @@ RCLCxPeerPos::RCLCxPeerPos( signature.size() != 0 && signature.size() <= signature_.capacity(), "xrpl::RCLCxPeerPos::RCLCxPeerPos : valid signature size"); - if (signature.size() != 0 && signature.size() <= signature_.capacity()) + if (!signature.empty() && signature.size() <= signature_.capacity()) signature_.assign(signature.begin(), signature.end()); } diff --git a/src/xrpld/app/consensus/RCLCxPeerPos.h b/src/xrpld/app/consensus/RCLCxPeerPos.h index b9ef492aae..5dad4a33eb 100644 --- a/src/xrpld/app/consensus/RCLCxPeerPos.h +++ b/src/xrpld/app/consensus/RCLCxPeerPos.h @@ -36,7 +36,11 @@ public: @param proposal The consensus proposal */ - RCLCxPeerPos(PublicKey const& publicKey, Slice const& signature, uint256 const& suppress, Proposal&& proposal); + RCLCxPeerPos( + PublicKey const& publicKey, + Slice const& signature, + uint256 const& suppress, + Proposal&& proposal); //! Verify the signing hash of the proposal bool diff --git a/src/xrpld/app/consensus/RCLCxTx.h b/src/xrpld/app/consensus/RCLCxTx.h index b7bde11a3b..0af43f7477 100644 --- a/src/xrpld/app/consensus/RCLCxTx.h +++ b/src/xrpld/app/consensus/RCLCxTx.h @@ -155,7 +155,8 @@ public: for (auto const& [k, v] : delta) { XRPL_ASSERT( - (v.first && !v.second) || (v.second && !v.first), "xrpl::RCLTxSet::compare : either side is set"); + (v.first && !v.second) || (v.second && !v.first), + "xrpl::RCLTxSet::compare : either side is set"); ret[k] = static_cast(v.first); } diff --git a/src/xrpld/app/consensus/RCLValidations.cpp b/src/xrpld/app/consensus/RCLValidations.cpp index 8c99ea9939..a968acb1a7 100644 --- a/src/xrpld/app/consensus/RCLValidations.cpp +++ b/src/xrpld/app/consensus/RCLValidations.cpp @@ -15,11 +15,14 @@ namespace xrpl { -RCLValidatedLedger::RCLValidatedLedger(MakeGenesis) : ledgerID_{0}, ledgerSeq_{0}, j_{beast::Journal::getNullSink()} +RCLValidatedLedger::RCLValidatedLedger(MakeGenesis) + : ledgerID_{0}, ledgerSeq_{0}, j_{beast::Journal::getNullSink()} { } -RCLValidatedLedger::RCLValidatedLedger(std::shared_ptr const& ledger, beast::Journal j) +RCLValidatedLedger::RCLValidatedLedger( + std::shared_ptr const& ledger, + beast::Journal j) : ledgerID_{ledger->header().hash}, ledgerSeq_{ledger->seq()}, j_{j} { auto const hashIndex = ledger->read(keylet::skip()); @@ -32,7 +35,8 @@ RCLValidatedLedger::RCLValidatedLedger(std::shared_ptr const& ledg ancestors_ = hashIndex->getFieldV256(sfHashes).value(); } else - JLOG(j_.warn()) << "Ledger " << ledgerSeq_ << ":" << ledgerID_ << " missing recent ancestor hashes"; + JLOG(j_.warn()) << "Ledger " << ledgerSeq_ << ":" << ledgerID_ + << " missing recent ancestor hashes"; } auto @@ -63,8 +67,9 @@ RCLValidatedLedger::operator[](Seq const& s) const -> ID return ancestors_[ancestors_.size() - diff]; } - JLOG(j_.warn()) << "Unable to determine hash of ancestor seq=" << s << " from ledger hash=" << ledgerID_ - << " seq=" << ledgerSeq_ << " (available: " << minSeq() << "-" << seq() << ")"; + JLOG(j_.warn()) << "Unable to determine hash of ancestor seq=" << s + << " from ledger hash=" << ledgerID_ << " seq=" << ledgerSeq_ + << " (available: " << minSeq() << "-" << seq() << ")"; // Default ID that is less than all others return ID{0}; } @@ -103,7 +108,10 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash) { using namespace std::chrono_literals; auto ledger = perf::measureDurationAndLog( - [&]() { return app_.getLedgerMaster().getLedgerByHash(hash); }, "getLedgerByHash", 10ms, j_); + [&]() { return app_.getLedgerMaster().getLedgerByHash(hash); }, + "getLedgerByHash", + 10ms, + j_); if (!ledger) { @@ -118,8 +126,11 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash) return std::nullopt; } - XRPL_ASSERT(!ledger->open() && ledger->isImmutable(), "xrpl::RCLValidationsAdaptor::acquire : valid ledger state"); - XRPL_ASSERT(ledger->header().hash == hash, "xrpl::RCLValidationsAdaptor::acquire : ledger hash match"); + XRPL_ASSERT( + !ledger->open() && ledger->isImmutable(), + "xrpl::RCLValidationsAdaptor::acquire : valid ledger state"); + XRPL_ASSERT( + ledger->header().hash == hash, "xrpl::RCLValidationsAdaptor::acquire : ledger hash match"); return RCLValidatedLedger(std::move(ledger), j_); } @@ -160,7 +171,8 @@ handleNewValidation( XRPL_ASSERT(j, "xrpl::handleNewValidation : journal is available"); if (j.has_value()) { - JLOG(j->trace()) << "Bypassing checkAccept for validation " << val->getLedgerHash(); + JLOG(j->trace()) + << "Bypassing checkAccept for validation " << val->getLedgerHash(); } } else @@ -179,8 +191,8 @@ handleNewValidation( // counterintuitively, we *especially* want to forward such validations, // so that our peers will also observe them and take independent notice of // such validators, informing their operators. - if (auto const ls = - val->isTrusted() ? validations.adaptor().journal().error() : validations.adaptor().journal().info(); + if (auto const ls = val->isTrusted() ? validations.adaptor().journal().error() + : validations.adaptor().journal().info(); ls.active()) { auto const id = [&masterKey, &signingKey]() { @@ -193,12 +205,18 @@ handleNewValidation( }(); if (outcome == ValStatus::conflicting) - ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") << id - << ": Conflicting validation for " << seq << "!\n[" << val->getSerializer().slice() << "]"; + { + ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") + << id << ": Conflicting validation for " << seq << "!\n[" + << val->getSerializer().slice() << "]"; + } if (outcome == ValStatus::multiple) - ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") << id - << ": Multiple validations for " << seq << "/" << hash << "!\n[" << val->getSerializer().slice() << "]"; + { + ls << "Byzantine Behavior Detector: " << (val->isTrusted() ? "trusted " : "untrusted ") + << id << ": Multiple validations for " << seq << "/" << hash << "!\n[" + << val->getSerializer().slice() << "]"; + } } } diff --git a/src/xrpld/app/ledger/AcceptedLedger.cpp b/src/xrpld/app/ledger/AcceptedLedger.cpp index 89806c2337..1da70702bf 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.cpp +++ b/src/xrpld/app/ledger/AcceptedLedger.cpp @@ -4,13 +4,16 @@ namespace xrpl { -AcceptedLedger::AcceptedLedger(std::shared_ptr const& ledger, Application& app) : mLedger(ledger) +AcceptedLedger::AcceptedLedger(std::shared_ptr const& ledger) : mLedger(ledger) { transactions_.reserve(256); auto insertAll = [&](auto const& txns) { for (auto const& item : txns) - transactions_.emplace_back(std::make_unique(ledger, item.first, item.second)); + { + transactions_.emplace_back( + std::make_unique(ledger, item.first, item.second)); + } }; transactions_.reserve(256); diff --git a/src/xrpld/app/ledger/AcceptedLedger.h b/src/xrpld/app/ledger/AcceptedLedger.h index c0f186c781..23cee6ce35 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.h +++ b/src/xrpld/app/ledger/AcceptedLedger.h @@ -1,6 +1,8 @@ #pragma once -#include +#include +#include +#include namespace xrpl { @@ -23,7 +25,7 @@ namespace xrpl { class AcceptedLedger : public CountedObject { public: - AcceptedLedger(std::shared_ptr const& ledger, Application& app); + AcceptedLedger(std::shared_ptr const& ledger); std::shared_ptr const& getLedger() const diff --git a/src/xrpld/app/ledger/AccountStateSF.cpp b/src/xrpld/app/ledger/AccountStateSF.cpp index 048a46e810..79c1f0eaf6 100644 --- a/src/xrpld/app/ledger/AccountStateSF.cpp +++ b/src/xrpld/app/ledger/AccountStateSF.cpp @@ -3,8 +3,12 @@ namespace xrpl { void -AccountStateSF::gotNode(bool, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, SHAMapNodeType) - const +AccountStateSF::gotNode( + bool, + SHAMapHash const& nodeHash, + std::uint32_t ledgerSeq, + Blob&& nodeData, + SHAMapNodeType) const { db_.store(hotACCOUNT_NODE, std::move(nodeData), nodeHash.as_uint256(), ledgerSeq); } diff --git a/src/xrpld/app/ledger/AccountStateSF.h b/src/xrpld/app/ledger/AccountStateSF.h index fb9da3a616..d17f3540ea 100644 --- a/src/xrpld/app/ledger/AccountStateSF.h +++ b/src/xrpld/app/ledger/AccountStateSF.h @@ -17,8 +17,12 @@ public: } void - gotNode(bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, SHAMapNodeType type) - const override; + gotNode( + bool fromFilter, + SHAMapHash const& nodeHash, + std::uint32_t ledgerSeq, + Blob&& nodeData, + SHAMapNodeType type) const override; std::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/xrpld/app/ledger/BuildLedger.h b/src/xrpld/app/ledger/BuildLedger.h index 7b4b85c4aa..32d45daea3 100644 --- a/src/xrpld/app/ledger/BuildLedger.h +++ b/src/xrpld/app/ledger/BuildLedger.h @@ -50,6 +50,10 @@ buildLedger( @return The newly built ledger */ std::shared_ptr -buildLedger(LedgerReplay const& replayData, ApplyFlags applyFlags, Application& app, beast::Journal j); +buildLedger( + LedgerReplay const& replayData, + ApplyFlags applyFlags, + Application& app, + beast::Journal j); } // namespace xrpl diff --git a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp index 6a8dbd3d7d..48080aaedb 100644 --- a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp +++ b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include namespace xrpl { @@ -21,7 +21,7 @@ ConsensusTransSetSF::gotNode( bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t, - Blob&& nodeData, + Blob&& nodeData, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) SHAMapNodeType type) const { if (fromFilter) @@ -50,7 +50,8 @@ ConsensusTransSetSF::gotNode( } catch (std::exception const& ex) { - JLOG(j_.warn()) << "Fetched invalid transaction in proposed set. Exception: " << ex.what(); + JLOG(j_.warn()) << "Fetched invalid transaction in proposed set. Exception: " + << ex.what(); } } } diff --git a/src/xrpld/app/ledger/ConsensusTransSetSF.h b/src/xrpld/app/ledger/ConsensusTransSetSF.h index 855577ecee..f439ef9cfa 100644 --- a/src/xrpld/app/ledger/ConsensusTransSetSF.h +++ b/src/xrpld/app/ledger/ConsensusTransSetSF.h @@ -21,8 +21,12 @@ public: // Note that the nodeData is overwritten by this call void - gotNode(bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, SHAMapNodeType type) - const override; + gotNode( + bool fromFilter, + SHAMapHash const& nodeHash, + std::uint32_t ledgerSeq, + Blob&& nodeData, + SHAMapNodeType type) const override; std::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index 46a8cf19b5..ef1362365d 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -166,7 +166,8 @@ private: // Data we have received from peers std::mutex mReceivedDataLock; - std::vector, std::shared_ptr>> mReceivedData; + std::vector, std::shared_ptr>> + mReceivedData; bool mReceiveDispatched; std::unique_ptr mPeerSet; }; diff --git a/src/xrpld/app/ledger/InboundLedgers.h b/src/xrpld/app/ledger/InboundLedgers.h index 30eb69ab7a..ecac6e07e4 100644 --- a/src/xrpld/app/ledger/InboundLedgers.h +++ b/src/xrpld/app/ledger/InboundLedgers.h @@ -34,7 +34,10 @@ public: // VFALCO TODO Remove the dependency on the Peer object. // virtual bool - gotLedgerData(LedgerHash const& ledgerHash, std::shared_ptr, std::shared_ptr) = 0; + gotLedgerData( + LedgerHash const& ledgerHash, + std::shared_ptr, + std::shared_ptr) = 0; virtual void gotStaleData(std::shared_ptr packet) = 0; diff --git a/src/xrpld/app/ledger/InboundTransactions.h b/src/xrpld/app/ledger/InboundTransactions.h index 7b85f56c5f..21c7ba0159 100644 --- a/src/xrpld/app/ledger/InboundTransactions.h +++ b/src/xrpld/app/ledger/InboundTransactions.h @@ -44,7 +44,10 @@ public: * @param message The LedgerData message. */ virtual void - gotData(uint256 const& setHash, std::shared_ptr peer, std::shared_ptr message) = 0; + gotData( + uint256 const& setHash, + std::shared_ptr peer, + std::shared_ptr message) = 0; /** Add a transaction set. * diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index e379c56d81..636410e57a 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -3,15 +3,13 @@ #include #include #include -#include -#include #include #include -#include #include #include #include +#include #include #include #include @@ -23,6 +21,7 @@ #include #include #include +#include #include #include @@ -109,7 +108,8 @@ public: txs_iter_impl(txs_iter_impl const&) = default; - txs_iter_impl(bool metadata, SHAMap::const_iterator iter) : metadata_(metadata), iter_(std::move(iter)) + txs_iter_impl(bool metadata, SHAMap::const_iterator iter) + : metadata_(metadata), iter_(std::move(iter)) { } @@ -145,7 +145,11 @@ public: //------------------------------------------------------------------------------ -Ledger::Ledger(create_genesis_t, Config const& config, std::vector const& amendments, Family& family) +Ledger::Ledger( + create_genesis_t, + Config const& config, + std::vector const& amendments, + Family& family) : mImmutable(false) , txMap_(SHAMapType::TRANSACTION, family) , stateMap_(SHAMapType::STATE, family) @@ -156,7 +160,8 @@ Ledger::Ledger(create_genesis_t, Config const& config, std::vector cons header_.drops = INITIAL_XRP; header_.closeTimeResolution = ledgerGenesisTimeResolution; - static auto const id = calcAccountID(generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")).first); + static auto const id = + calcAccountID(generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase")).first); { auto const sle = std::make_shared(keylet::account(id)); sle->setFieldU32(sfSequence, 1); @@ -226,7 +231,8 @@ Ledger::Ledger( JLOG(j.warn()) << "Don't have transaction root for ledger" << header_.seq; } - if (header_.accountHash.isNonZero() && !stateMap_.fetchRoot(SHAMapHash{header_.accountHash}, nullptr)) + if (header_.accountHash.isNonZero() && + !stateMap_.fetchRoot(SHAMapHash{header_.accountHash}, nullptr)) { loaded = false; JLOG(j.warn()) << "Don't have state data root for ledger" << header_.seq; @@ -286,7 +292,11 @@ Ledger::Ledger(LedgerHeader const& info, Config const& config, Family& family) header_.hash = calculateLedgerHash(header_); } -Ledger::Ledger(std::uint32_t ledgerSeq, NetClock::time_point closeTime, Config const& config, Family& family) +Ledger::Ledger( + std::uint32_t ledgerSeq, + NetClock::time_point closeTime, + Config const& config, + Family& family) : mImmutable(false) , txMap_(SHAMapType::TRANSACTION, family) , stateMap_(SHAMapType::STATE, family) @@ -321,7 +331,10 @@ Ledger::setImmutable(bool rehash) } void -Ledger::setAccepted(NetClock::time_point closeTime, NetClock::duration closeResolution, bool correctCloseTime) +Ledger::setAccepted( + NetClock::time_point closeTime, + NetClock::duration closeResolution, + bool correctCloseTime) { // Used when we witnessed the consensus. XRPL_ASSERT(!open(), "xrpl::Ledger::setAccepted : valid ledger state"); @@ -336,7 +349,8 @@ bool Ledger::addSLE(SLE const& sle) { auto const s = sle.getSerializer(); - return stateMap_.addItem(SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle.key(), s.slice())); + return stateMap_.addItem( + SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle.key(), s.slice())); } //------------------------------------------------------------------------------ @@ -493,7 +507,8 @@ Ledger::rawInsert(std::shared_ptr const& sle) { Serializer ss; sle->add(ss); - if (!stateMap_.addGiveItem(SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice()))) + if (!stateMap_.addGiveItem( + SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice()))) LogicError("Ledger::rawInsert: key already exists"); } @@ -502,7 +517,8 @@ Ledger::rawReplace(std::shared_ptr const& sle) { Serializer ss; sle->add(ss); - if (!stateMap_.updateGiveItem(SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice()))) + if (!stateMap_.updateGiveItem( + SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice()))) LogicError("Ledger::rawReplace: key not found"); } @@ -589,9 +605,13 @@ Ledger::setup() if (src) { if (src->native()) + { dest = src->xrp(); + } else + { ret = false; + } } }; assign(fees_.base, baseFeeXRP); @@ -616,15 +636,21 @@ Ledger::setup() extensionFees = extensionComputeLimit || extensionSizeLimit || gasPrice; } if (oldFees && newFees) + { // Should be all of one or the other, but not both ret = false; + } if (!rules_.enabled(featureXRPFees) && newFees) + { // Can't populate the new fees before the amendment is enabled ret = false; + } if (!rules_.enabled(featureSmartEscrow) && extensionFees) + { // Can't populate the extension fees before the amendment is // enabled ret = false; + } } } catch (SHAMapMissingNode const&) @@ -644,8 +670,9 @@ void Ledger::defaultFees(Config const& config) { XRPL_ASSERT( - fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 && fees_.extensionComputeLimit == 0 && - fees_.extensionSizeLimit == 0 && fees_.gasPrice == 0, + fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 && + fees_.extensionComputeLimit == 0 && fees_.extensionSizeLimit == 0 && + fees_.gasPrice == 0, "xrpl::Ledger::defaultFees : zero fees"); if (fees_.base == 0) fees_.base = config.FEES.reference_fee; @@ -790,9 +817,11 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const else { if (parallel) + { return stateMap_.walkMapParallel(missingNodes1, 32); - else - stateMap_.walkMap(missingNodes1, 32); + } + + stateMap_.walkMap(missingNodes1, 32); } if (!missingNodes1.empty()) @@ -829,7 +858,8 @@ bool Ledger::assertSensible(beast::Journal ledgerJ) const { if (header_.hash.isNonZero() && header_.accountHash.isNonZero() && - (header_.accountHash == stateMap_.getHash().as_uint256()) && (header_.txHash == txMap_.getHash().as_uint256())) + (header_.accountHash == stateMap_.getHash().as_uint256()) && + (header_.txHash == txMap_.getHash().as_uint256())) { return true; } @@ -865,7 +895,7 @@ Ledger::updateSkipList() auto sle = peek(k); std::vector hashes; - bool created; + bool created = false; if (!sle) { sle = std::make_shared(k); @@ -877,21 +907,26 @@ Ledger::updateSkipList() created = false; } - XRPL_ASSERT(hashes.size() <= 256, "xrpl::Ledger::updateSkipList : first maximum hashes size"); + XRPL_ASSERT( + hashes.size() <= 256, "xrpl::Ledger::updateSkipList : first maximum hashes size"); hashes.push_back(header_.parentHash); sle->setFieldV256(sfHashes, STVector256(hashes)); sle->setFieldU32(sfLastLedgerSequence, prevIndex); if (created) + { rawInsert(sle); + } else + { rawReplace(sle); + } } // update record of past 256 ledger auto const k = keylet::skip(); auto sle = peek(k); std::vector hashes; - bool created; + bool created = false; if (!sle) { sle = std::make_shared(k); @@ -909,26 +944,24 @@ Ledger::updateSkipList() sle->setFieldV256(sfHashes, STVector256(hashes)); sle->setFieldU32(sfLastLedgerSequence, prevIndex); if (created) + { rawInsert(sle); + } else + { rawReplace(sle); + } } bool Ledger::isFlagLedger() const { - return header_.seq % FLAG_LEDGER_INTERVAL == 0; + return ::xrpl::isFlagLedger(header_.seq); } bool Ledger::isVotingLedger() const { - return (header_.seq + 1) % FLAG_LEDGER_INTERVAL == 0; -} - -bool -isFlagLedger(LedgerIndex seq) -{ - return seq % FLAG_LEDGER_INTERVAL == 0; + return ::xrpl::isVotingLedger(header_.seq + 1); } static bool @@ -943,11 +976,9 @@ saveValidatedLedger(Application& app, std::shared_ptr const& ledge return true; } - auto const db = dynamic_cast(&app.getRelationalDatabase()); - if (!db) - Throw("Failed to get relational database"); + auto& db = app.getRelationalDatabase(); - auto const res = db->saveValidatedLedger(ledger, current); + auto const res = db.saveValidatedLedger(ledger, current); // Clients can now trust the database for // information about this ledger sequence. @@ -959,7 +990,11 @@ saveValidatedLedger(Application& app, std::shared_ptr const& ledge Returns false on error */ bool -pendSaveValidated(Application& app, std::shared_ptr const& ledger, bool isSynchronous, bool isCurrent) +pendSaveValidated( + Application& app, + std::shared_ptr const& ledger, + bool isSynchronous, + bool isCurrent) { if (!app.getHashRouter().setFlags(ledger->header().hash, HashRouterFlags::SAVED)) { @@ -988,9 +1023,9 @@ pendSaveValidated(Application& app, std::shared_ptr const& ledger, // See if we can use the JobQueue. if (!isSynchronous && app.getJobQueue().addJob( - isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER, std::to_string(ledger->seq()), [&app, ledger, isCurrent]() { - saveValidatedLedger(app, ledger, isCurrent); - })) + isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER, + std::to_string(ledger->seq()), + [&app, ledger, isCurrent]() { saveValidatedLedger(app, ledger, isCurrent); })) { return true; } @@ -1025,9 +1060,9 @@ Ledger::invariants() const std::shared_ptr loadLedgerHelper(LedgerHeader const& info, Application& app, bool acquire) { - bool loaded; - auto ledger = - std::make_shared(info, loaded, acquire, app.config(), app.getNodeFamily(), app.journal("Ledger")); + bool loaded = false; + auto ledger = std::make_shared( + info, loaded, acquire, app.config(), app.getNodeFamily(), app.journal("Ledger")); if (!loaded) ledger.reset(); @@ -1036,7 +1071,10 @@ loadLedgerHelper(LedgerHeader const& info, Application& app, bool acquire) } static void -finishLoadByIndexOrHash(std::shared_ptr const& ledger, Config const& config, beast::Journal j) +finishLoadByIndexOrHash( + std::shared_ptr const& ledger, + Config const& config, + beast::Journal j) { if (!ledger) return; @@ -1063,7 +1101,8 @@ getLatestLedger(Application& app) std::shared_ptr loadByIndex(std::uint32_t ledgerIndex, Application& app, bool acquire) { - if (std::optional info = app.getRelationalDatabase().getLedgerInfoByIndex(ledgerIndex)) + if (std::optional info = + app.getRelationalDatabase().getLedgerInfoByIndex(ledgerIndex)) { std::shared_ptr ledger = loadLedgerHelper(*info, app, acquire); finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger")); @@ -1075,11 +1114,14 @@ loadByIndex(std::uint32_t ledgerIndex, Application& app, bool acquire) std::shared_ptr loadByHash(uint256 const& ledgerHash, Application& app, bool acquire) { - if (std::optional info = app.getRelationalDatabase().getLedgerInfoByHash(ledgerHash)) + if (std::optional info = + app.getRelationalDatabase().getLedgerInfoByHash(ledgerHash)) { std::shared_ptr ledger = loadLedgerHelper(*info, app, acquire); finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger")); - XRPL_ASSERT(!ledger || ledger->header().hash == ledgerHash, "xrpl::loadByHash : ledger hash match if loaded"); + XRPL_ASSERT( + !ledger || ledger->header().hash == ledgerHash, + "xrpl::loadByHash : ledger hash match if loaded"); return ledger; } return {}; diff --git a/src/xrpld/app/ledger/Ledger.h b/src/xrpld/app/ledger/Ledger.h index cbfbc0030f..f040e93369 100644 --- a/src/xrpld/app/ledger/Ledger.h +++ b/src/xrpld/app/ledger/Ledger.h @@ -81,7 +81,11 @@ public: Amendments specified are enabled in the genesis ledger */ - Ledger(create_genesis_t, Config const& config, std::vector const& amendments, Family& family); + Ledger( + create_genesis_t, + Config const& config, + std::vector const& amendments, + Family& family); Ledger(LedgerHeader const& info, Config const& config, Family& family); @@ -106,7 +110,11 @@ public: Ledger(Ledger const& previous, NetClock::time_point closeTime); // used for database ledgers - Ledger(std::uint32_t ledgerSeq, NetClock::time_point closeTime, Config const& config, Family& family); + Ledger( + std::uint32_t ledgerSeq, + NetClock::time_point closeTime, + Config const& config, + Family& family); ~Ledger() = default; @@ -238,7 +246,10 @@ public: } void - setAccepted(NetClock::time_point closeTime, NetClock::duration closeResolution, bool correctCloseTime); + setAccepted( + NetClock::time_point closeTime, + NetClock::duration closeResolution, + bool correctCloseTime); void setImmutable(bool rehash = true); @@ -391,11 +402,6 @@ private: /** A ledger wrapped in a CachedView. */ using CachedLedger = CachedView; -std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 256; -/** Returns true if the given ledgerIndex is a flag ledgerIndex */ -bool -isFlagLedger(LedgerIndex seq); - //------------------------------------------------------------------------------ // // API @@ -403,7 +409,11 @@ isFlagLedger(LedgerIndex seq); //------------------------------------------------------------------------------ extern bool -pendSaveValidated(Application& app, std::shared_ptr const& ledger, bool isSynchronous, bool isCurrent); +pendSaveValidated( + Application& app, + std::shared_ptr const& ledger, + bool isSynchronous, + bool isCurrent); std::shared_ptr loadLedgerHelper(LedgerHeader const& sinfo, Application& app, bool acquire); diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index 50fe56fd90..5ff5555fff 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -20,7 +20,12 @@ LedgerHistory::LedgerHistory(beast::insight::Collector::ptr const& collector, Ap std::chrono::seconds{app_.config().getValueFor(SizedItem::ledgerAge)}, stopwatch(), app_.journal("TaggedCache")) - , m_consensus_validated("ConsensusValidated", 64, std::chrono::minutes{5}, stopwatch(), app_.journal("TaggedCache")) + , m_consensus_validated( + "ConsensusValidated", + 64, + std::chrono::minutes{5}, + stopwatch(), + app_.journal("TaggedCache")) , j_(app.journal("LedgerHistory")) { } @@ -31,11 +36,13 @@ LedgerHistory::insert(std::shared_ptr const& ledger, bool validate if (!ledger->isImmutable()) LogicError("mutable Ledger in insert"); - XRPL_ASSERT(ledger->stateMap().getHash().isNonZero(), "xrpl::LedgerHistory::insert : nonzero hash"); + XRPL_ASSERT( + ledger->stateMap().getHash().isNonZero(), "xrpl::LedgerHistory::insert : nonzero hash"); std::unique_lock sl(m_ledgers_by_hash.peekMutex()); - bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(ledger->header().hash, ledger); + bool const alreadyHad = + m_ledgers_by_hash.canonicalize_replace_cache(ledger->header().hash, ledger); if (validated) mLedgersByIndex[ledger->header().seq] = ledger->header().hash; @@ -71,13 +78,15 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) if (!ret) return ret; - XRPL_ASSERT(ret->header().seq == index, "xrpl::LedgerHistory::getLedgerBySeq : result sequence match"); + XRPL_ASSERT( + ret->header().seq == index, "xrpl::LedgerHistory::getLedgerBySeq : result sequence match"); { // Add this ledger to the local tracking by index std::unique_lock sl(m_ledgers_by_hash.peekMutex()); - XRPL_ASSERT(ret->isImmutable(), "xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger"); + XRPL_ASSERT( + ret->isImmutable(), "xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger"); m_ledgers_by_hash.canonicalize_replace_client(ret->header().hash, ret); mLedgersByIndex[ret->header().seq] = ret->header().hash; return (ret->header().seq == index) ? ret : nullptr; @@ -107,10 +116,14 @@ LedgerHistory::getLedgerByHash(LedgerHash const& hash) if (!ret) return ret; - XRPL_ASSERT(ret->isImmutable(), "xrpl::LedgerHistory::getLedgerByHash : immutable loaded ledger"); - XRPL_ASSERT(ret->header().hash == hash, "xrpl::LedgerHistory::getLedgerByHash : loaded ledger hash match"); + XRPL_ASSERT( + ret->isImmutable(), "xrpl::LedgerHistory::getLedgerByHash : immutable loaded ledger"); + XRPL_ASSERT( + ret->header().hash == hash, + "xrpl::LedgerHistory::getLedgerByHash : loaded ledger hash match"); m_ledgers_by_hash.canonicalize_replace_client(ret->header().hash, ret); - XRPL_ASSERT(ret->header().hash == hash, "xrpl::LedgerHistory::getLedgerByHash : result hash match"); + XRPL_ASSERT( + ret->header().hash == hash, "xrpl::LedgerHistory::getLedgerByHash : result hash match"); return ret; } @@ -122,17 +135,23 @@ log_one(ReadView const& ledger, uint256 const& tx, char const* msg, beast::Journ if (metaData != nullptr) { - JLOG(j.debug()) << "MISMATCH on TX " << tx << ": " << msg << " is missing this transaction:\n" + JLOG(j.debug()) << "MISMATCH on TX " << tx << ": " << msg + << " is missing this transaction:\n" << metaData->getJson(JsonOptions::none); } else { - JLOG(j.debug()) << "MISMATCH on TX " << tx << ": " << msg << " is missing this transaction."; + JLOG(j.debug()) << "MISMATCH on TX " << tx << ": " << msg + << " is missing this transaction."; } } static void -log_metadata_difference(ReadView const& builtLedger, ReadView const& validLedger, uint256 const& tx, beast::Journal j) +log_metadata_difference( + ReadView const& builtLedger, + ReadView const& validLedger, + uint256 const& tx, + beast::Journal j) { auto getMeta = [](ReadView const& ledger, uint256 const& txID) { std::optional ret; @@ -144,7 +163,8 @@ log_metadata_difference(ReadView const& builtLedger, ReadView const& validLedger auto validMetaData = getMeta(validLedger, tx); auto builtMetaData = getMeta(builtLedger, tx); - XRPL_ASSERT(validMetaData || builtMetaData, "xrpl::log_metadata_difference : some metadata present"); + XRPL_ASSERT( + validMetaData || builtMetaData, "xrpl::log_metadata_difference : some metadata present"); if (validMetaData && builtMetaData) { @@ -169,9 +189,11 @@ log_metadata_difference(ReadView const& builtLedger, ReadView const& validLedger { JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different result and index!"; JLOG(j.debug()) << " Built:" - << " Result: " << builtMetaData->getResult() << " Index: " << builtMetaData->getIndex(); + << " Result: " << builtMetaData->getResult() + << " Index: " << builtMetaData->getIndex(); JLOG(j.debug()) << " Valid:" - << " Result: " << validMetaData->getResult() << " Index: " << validMetaData->getIndex(); + << " Result: " << validMetaData->getResult() + << " Index: " << validMetaData->getIndex(); } else if (result_diff) { @@ -194,7 +216,8 @@ log_metadata_difference(ReadView const& builtLedger, ReadView const& validLedger { if (result_diff && index_diff) { - JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different result, index and nodes!"; + JLOG(j.debug()) << "MISMATCH on TX " << tx + << ": Different result, index and nodes!"; JLOG(j.debug()) << " Built:\n" << builtMetaData->getJson(JsonOptions::none); JLOG(j.debug()) << " Valid:\n" << validMetaData->getJson(JsonOptions::none); } @@ -255,7 +278,9 @@ leaves(SHAMap const& sm) std::vector v; for (auto const& item : sm) v.push_back(&item); - std::sort(v.begin(), v.end(), [](SHAMapItem const* lhs, SHAMapItem const* rhs) { return lhs->key() < rhs->key(); }); + std::sort(v.begin(), v.end(), [](SHAMapItem const* lhs, SHAMapItem const* rhs) { + return lhs->key() < rhs->key(); + }); return v; } @@ -282,7 +307,8 @@ LedgerHistory::handleMismatch( } XRPL_ASSERT( - builtLedger->header().seq == validLedger->header().seq, "xrpl::LedgerHistory::handleMismatch : sequence match"); + builtLedger->header().seq == validLedger->header().seq, + "xrpl::LedgerHistory::handleMismatch : sequence match"); if (auto stream = j_.debug()) { @@ -311,11 +337,14 @@ LedgerHistory::handleMismatch( if (builtConsensusHash && validatedConsensusHash) { if (builtConsensusHash != validatedConsensusHash) + { JLOG(j_.error()) << "MISMATCH on consensus transaction set " << " built: " << to_string(*builtConsensusHash) << " validated: " << to_string(*validatedConsensusHash); + } else - JLOG(j_.error()) << "MISMATCH with same consensus transaction set: " << to_string(*builtConsensusHash); + JLOG(j_.error()) << "MISMATCH with same consensus transaction set: " + << to_string(*builtConsensusHash); } // Find differences between built and valid ledgers @@ -323,7 +352,9 @@ LedgerHistory::handleMismatch( auto const validTx = leaves(validLedger->txMap()); if (builtTx == validTx) + { JLOG(j_.error()) << "MISMATCH with same " << builtTx.size() << " transactions"; + } else JLOG(j_.error()) << "MISMATCH with " << builtTx.size() << " built and " << validTx.size() << " valid transactions."; @@ -382,9 +413,14 @@ LedgerHistory::builtLedger( { if (entry->validated.value() != hash) { - JLOG(j_.error()) << "MISMATCH: seq=" << index << " validated:" << entry->validated.value() - << " then:" << hash; - handleMismatch(hash, entry->validated.value(), consensusHash, entry->validatedConsensusHash, consensus); + JLOG(j_.error()) << "MISMATCH: seq=" << index + << " validated:" << entry->validated.value() << " then:" << hash; + handleMismatch( + hash, + entry->validated.value(), + consensusHash, + entry->validatedConsensusHash, + consensus); } else { @@ -399,7 +435,9 @@ LedgerHistory::builtLedger( } void -LedgerHistory::validatedLedger(std::shared_ptr const& ledger, std::optional const& consensusHash) +LedgerHistory::validatedLedger( + std::shared_ptr const& ledger, + std::optional const& consensusHash) { LedgerIndex index = ledger->header().seq; LedgerHash hash = ledger->header().hash; @@ -414,9 +452,14 @@ LedgerHistory::validatedLedger(std::shared_ptr const& ledger, std: { if (entry->built.value() != hash) { - JLOG(j_.error()) << "MISMATCH: seq=" << index << " built:" << entry->built.value() << " then:" << hash; + JLOG(j_.error()) << "MISMATCH: seq=" << index << " built:" << entry->built.value() + << " then:" << hash; handleMismatch( - entry->built.value(), hash, entry->builtConsensusHash, consensusHash, entry->consensus.value()); + entry->built.value(), + hash, + entry->builtConsensusHash, + consensusHash, + entry->consensus.value()); } else { diff --git a/src/xrpld/app/ledger/LedgerHistory.h b/src/xrpld/app/ledger/LedgerHistory.h index e8784eb599..6ec279b28a 100644 --- a/src/xrpld/app/ledger/LedgerHistory.h +++ b/src/xrpld/app/ledger/LedgerHistory.h @@ -63,7 +63,9 @@ public: /** Report that we have validated a particular ledger */ void - validatedLedger(std::shared_ptr const&, std::optional const& consensusHash); + validatedLedger( + std::shared_ptr const&, + std::optional const& consensusHash); /** Repair a hash to index mapping @param ledgerIndex The index whose mapping is to be repaired diff --git a/src/xrpld/app/ledger/LedgerMaster.h b/src/xrpld/app/ledger/LedgerMaster.h index ceb9fa9045..c25e553455 100644 --- a/src/xrpld/app/ledger/LedgerMaster.h +++ b/src/xrpld/app/ledger/LedgerMaster.h @@ -185,7 +185,10 @@ public: void checkAccept(uint256 const& hash, std::uint32_t seq); void - consensusBuilt(std::shared_ptr const& ledger, uint256 const& consensusHash, Json::Value consensus); + consensusBuilt( + std::shared_ptr const& ledger, + uint256 const& consensusHash, + Json::Value consensus); void setBuildingLedger(LedgerIndex index); diff --git a/src/xrpld/app/ledger/LedgerReplayTask.h b/src/xrpld/app/ledger/LedgerReplayTask.h index 94eeee5f51..030121b240 100644 --- a/src/xrpld/app/ledger/LedgerReplayTask.h +++ b/src/xrpld/app/ledger/LedgerReplayTask.h @@ -42,7 +42,10 @@ public: * @param finishLedgerHash hash of the last ledger in the range * @param totalNumLedgers number of ledgers to download */ - TaskParameter(InboundLedger::Reason r, uint256 const& finishLedgerHash, std::uint32_t totalNumLedgers); + TaskParameter( + InboundLedger::Reason r, + uint256 const& finishLedgerHash, + std::uint32_t totalNumLedgers); /** * fill all the fields that was not filled during construction @@ -75,7 +78,7 @@ public: InboundLedgers& inboundLedgers, LedgerReplayer& replayer, std::shared_ptr& skipListAcquirer, - TaskParameter&& parameter); + TaskParameter const& parameter); ~LedgerReplayTask(); diff --git a/src/xrpld/app/ledger/LedgerReplayer.h b/src/xrpld/app/ledger/LedgerReplayer.h index 0d08e5a76f..218d22fb07 100644 --- a/src/xrpld/app/ledger/LedgerReplayer.h +++ b/src/xrpld/app/ledger/LedgerReplayer.h @@ -52,7 +52,10 @@ std::uint32_t constexpr MAX_QUEUED_TASKS = 100; class LedgerReplayer final { public: - LedgerReplayer(Application& app, InboundLedgers& inboundLedgers, std::unique_ptr peerSetBuilder); + LedgerReplayer( + Application& app, + InboundLedgers& inboundLedgers, + std::unique_ptr peerSetBuilder); ~LedgerReplayer(); @@ -86,7 +89,9 @@ public: * @note info and txns must have been verified against the ledger hash */ void - gotReplayDelta(LedgerHeader const& info, std::map>&& txns); + gotReplayDelta( + LedgerHeader const& info, + std::map>&& txns); /** Remove completed tasks */ void diff --git a/src/xrpld/app/ledger/LedgerToJson.h b/src/xrpld/app/ledger/LedgerToJson.h index 8cde134d23..9def1c3826 100644 --- a/src/xrpld/app/ledger/LedgerToJson.h +++ b/src/xrpld/app/ledger/LedgerToJson.h @@ -12,14 +12,26 @@ namespace xrpl { struct LedgerFill { - LedgerFill(ReadView const& l, RPC::Context const* ctx, int o = 0, std::vector q = {}) + LedgerFill( + ReadView const& l, + RPC::Context const* ctx, + int o = 0, + std::vector q = {}) : ledger(l), options(o), txQueue(std::move(q)), context(ctx) { if (context) closeTime = context->ledgerMaster.getCloseTimeBySeq(ledger.seq()); } - enum Options { dumpTxrp = 1, dumpState = 2, expand = 4, full = 8, binary = 16, ownerFunds = 32, dumpQueue = 64 }; + enum Options { + dumpTxrp = 1, + dumpState = 2, + expand = 4, + full = 8, + binary = 16, + ownerFunds = 32, + dumpQueue = 64 + }; ReadView const& ledger; int options; diff --git a/src/xrpld/app/ledger/OpenLedger.h b/src/xrpld/app/ledger/OpenLedger.h index 187086862c..76123bafae 100644 --- a/src/xrpld/app/ledger/OpenLedger.h +++ b/src/xrpld/app/ledger/OpenLedger.h @@ -60,7 +60,10 @@ public: @param ledger A closed ledger */ - explicit OpenLedger(std::shared_ptr const& ledger, CachedSLEs& cache, beast::Journal journal); + explicit OpenLedger( + std::shared_ptr const& ledger, + CachedSLEs& cache, + beast::Journal journal); /** Returns `true` if there are no transactions. diff --git a/src/xrpld/app/ledger/OrderBookDB.h b/src/xrpld/app/ledger/OrderBookDB.h deleted file mode 100644 index da604d7e22..0000000000 --- a/src/xrpld/app/ledger/OrderBookDB.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -namespace xrpl { - -class OrderBookDB -{ -public: - explicit OrderBookDB(Application& app); - - void - setup(std::shared_ptr const& ledger); - void - update(std::shared_ptr const& ledger); - - void - addOrderBook(Book const&); - - /** @return a list of all orderbooks that want this issuerID and currencyID. - */ - std::vector - getBooksByTakerPays(Issue const&, std::optional const& domain = std::nullopt); - - /** @return a count of all orderbooks that want this issuerID and - currencyID. */ - int - getBookSize(Issue const&, std::optional const& domain = std::nullopt); - - bool - isBookToXRP(Issue const&, std::optional domain = std::nullopt); - - BookListeners::pointer - getBookListeners(Book const&); - BookListeners::pointer - makeBookListeners(Book const&); - - // see if this txn effects any orderbook - void - processTxn(std::shared_ptr const& ledger, AcceptedLedgerTx const& alTx, MultiApiJson const& jvObj); - -private: - Application& app_; - - // Maps order books by "issue in" to "issue out": - hardened_hash_map> allBooks_; - - hardened_hash_map, hardened_hash_set> domainBooks_; - - // does an order book to XRP exist - hash_set xrpBooks_; - - // does an order book to XRP exist - hash_set> xrpDomainBooks_; - - std::recursive_mutex mLock; - - using BookToListenersMap = hash_map; - - BookToListenersMap mListeners; - - std::atomic seq_; - - beast::Journal const j_; -}; - -} // namespace xrpl diff --git a/src/xrpld/app/ledger/OrderBookDB.cpp b/src/xrpld/app/ledger/OrderBookDBImpl.cpp similarity index 78% rename from src/xrpld/app/ledger/OrderBookDB.cpp rename to src/xrpld/app/ledger/OrderBookDBImpl.cpp index 81a3bf5e4a..2691935911 100644 --- a/src/xrpld/app/ledger/OrderBookDB.cpp +++ b/src/xrpld/app/ledger/OrderBookDBImpl.cpp @@ -1,24 +1,33 @@ #include -#include -#include -#include -#include -#include +#include #include #include #include +#include +#include namespace xrpl { -OrderBookDB::OrderBookDB(Application& app) : app_(app), seq_(0), j_(app.journal("OrderBookDB")) +OrderBookDBImpl::OrderBookDBImpl(ServiceRegistry& registry, OrderBookDBConfig const& config) + : registry_(registry) + , pathSearchMax_(config.pathSearchMax) + , standalone_(config.standalone) + , seq_(0) + , j_(registry.journal("OrderBookDB")) { } -void -OrderBookDB::setup(std::shared_ptr const& ledger) +std::unique_ptr +make_OrderBookDB(ServiceRegistry& registry, OrderBookDBConfig const& config) { - if (!app_.config().standalone() && app_.getOPs().isNeedNetworkLedger()) + return std::make_unique(registry, config); +} + +void +OrderBookDBImpl::setup(std::shared_ptr const& ledger) +{ + if (!standalone_ && registry_.getOPs().isNeedNetworkLedger()) { JLOG(j_.warn()) << "Eliding full order book update: no ledger"; return; @@ -40,25 +49,33 @@ OrderBookDB::setup(std::shared_ptr const& ledger) JLOG(j_.debug()) << "Full order book update: " << seq << " to " << ledger->seq(); - if (app_.config().PATH_SEARCH_MAX != 0) + if (pathSearchMax_ != 0) { - if (app_.config().standalone()) + if (standalone_) + { update(ledger); + } else - app_.getJobQueue().addJob(jtUPDATE_PF, "OrderBookUpd", [this, ledger]() { update(ledger); }); + { + registry_.getJobQueue().addJob( + jtUPDATE_PF, "OrderBookUpd" + std::to_string(ledger->seq()), [this, ledger]() { + update(ledger); + }); + } } } void -OrderBookDB::update(std::shared_ptr const& ledger) +OrderBookDBImpl::update(std::shared_ptr const& ledger) { - if (app_.config().PATH_SEARCH_MAX == 0) + if (pathSearchMax_ == 0) return; // pathfinding has been disabled // A newer full update job is pending if (auto const seq = seq_.load(); seq > ledger->seq()) { - JLOG(j_.debug()) << "Eliding update for " << ledger->seq() << " because of pending update to later " << seq; + JLOG(j_.debug()) << "Eliding update for " << ledger->seq() + << " because of pending update to later " << seq; return; } @@ -79,7 +96,7 @@ OrderBookDB::update(std::shared_ptr const& ledger) { for (auto& sle : ledger->sles) { - if (app_.isStopping()) + if (registry_.isStopping()) { JLOG(j_.info()) << "Update halted because the process is stopping"; seq_.store(0); @@ -98,14 +115,22 @@ OrderBookDB::update(std::shared_ptr const& ledger) book.domain = (*sle)[~sfDomainID]; if (book.domain) + { domainBooks[{book.in, *book.domain}].insert(book.out); + } else + { allBooks[book.in].insert(book.out); + } if (book.domain && isXRP(book.out)) + { xrpDomainBooks.insert({book.in, *book.domain}); + } else if (isXRP(book.out)) + { xrpBooks.insert(book.in); + } ++cnt; } @@ -143,30 +168,38 @@ OrderBookDB::update(std::shared_ptr const& ledger) xrpDomainBooks_.swap(xrpDomainBooks); } - app_.getLedgerMaster().newOrderBookDB(); + registry_.getLedgerMaster().newOrderBookDB(); } void -OrderBookDB::addOrderBook(Book const& book) +OrderBookDBImpl::addOrderBook(Book const& book) { bool toXRP = isXRP(book.out); std::lock_guard sl(mLock); if (book.domain) + { domainBooks_[{book.in, *book.domain}].insert(book.out); + } else + { allBooks_[book.in].insert(book.out); + } if (book.domain && toXRP) + { xrpDomainBooks_.insert({book.in, *book.domain}); + } else if (toXRP) + { xrpBooks_.insert(book.in); + } } // return list of all orderbooks that want this issuerID and currencyID std::vector -OrderBookDB::getBooksByTakerPays(Issue const& issue, std::optional const& domain) +OrderBookDBImpl::getBooksByTakerPays(Issue const& issue, std::optional const& domain) { std::vector ret; @@ -185,16 +218,20 @@ OrderBookDB::getBooksByTakerPays(Issue const& issue, std::optional cons }; if (!domain) + { getBooks(allBooks_, issue); + } else + { getBooks(domainBooks_, std::make_pair(issue, *domain)); + } } return ret; } int -OrderBookDB::getBookSize(Issue const& issue, std::optional const& domain) +OrderBookDBImpl::getBookSize(Issue const& issue, std::optional const& domain) { std::lock_guard sl(mLock); @@ -213,7 +250,7 @@ OrderBookDB::getBookSize(Issue const& issue, std::optional const& domai } bool -OrderBookDB::isBookToXRP(Issue const& issue, std::optional domain) +OrderBookDBImpl::isBookToXRP(Issue const& issue, std::optional domain) { std::lock_guard sl(mLock); if (domain) @@ -222,7 +259,7 @@ OrderBookDB::isBookToXRP(Issue const& issue, std::optional domain) } BookListeners::pointer -OrderBookDB::makeBookListeners(Book const& book) +OrderBookDBImpl::makeBookListeners(Book const& book) { std::lock_guard sl(mLock); auto ret = getBookListeners(book); @@ -242,7 +279,7 @@ OrderBookDB::makeBookListeners(Book const& book) } BookListeners::pointer -OrderBookDB::getBookListeners(Book const& book) +OrderBookDBImpl::getBookListeners(Book const& book) { BookListeners::pointer ret; std::lock_guard sl(mLock); @@ -257,7 +294,7 @@ OrderBookDB::getBookListeners(Book const& book) // Based on the meta, send the meta to the streams that are listening. // We need to determine which streams a given meta effects. void -OrderBookDB::processTxn( +OrderBookDBImpl::processTxn( std::shared_ptr const& ledger, AcceptedLedgerTx const& alTx, MultiApiJson const& jvObj) @@ -278,8 +315,8 @@ OrderBookDB::processTxn( if (node.getFieldU16(sfLedgerEntryType) == ltOFFER) { auto process = [&, this](SField const& field) { - if (auto data = dynamic_cast(node.peekAtPField(field)); - data && data->isFieldPresent(sfTakerPays) && data->isFieldPresent(sfTakerGets)) + if (auto data = dynamic_cast(node.peekAtPField(field)); data && + data->isFieldPresent(sfTakerPays) && data->isFieldPresent(sfTakerGets)) { auto listeners = getBookListeners( {data->getFieldAmount(sfTakerGets).issue(), @@ -293,11 +330,17 @@ OrderBookDB::processTxn( // We need a field that contains the TakerGets and TakerPays // parameters. if (node.getFName() == sfModifiedNode) + { process(sfPreviousFields); + } else if (node.getFName() == sfCreatedNode) + { process(sfNewFields); + } else if (node.getFName() == sfDeletedNode) + { process(sfFinalFields); + } } } catch (std::exception const& ex) diff --git a/src/xrpld/app/ledger/OrderBookDBImpl.h b/src/xrpld/app/ledger/OrderBookDBImpl.h new file mode 100644 index 0000000000..d9043f942e --- /dev/null +++ b/src/xrpld/app/ledger/OrderBookDBImpl.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +/** Configuration for OrderBookDB */ +struct OrderBookDBConfig +{ + int pathSearchMax; + bool standalone; +}; + +/** Create an OrderBookDB instance. + + @param registry Service registry for accessing other services + @param config Configuration parameters + @return A new OrderBookDB instance +*/ +std::unique_ptr +make_OrderBookDB(ServiceRegistry& registry, OrderBookDBConfig const& config); + +class OrderBookDBImpl final : public OrderBookDB +{ +public: + OrderBookDBImpl(ServiceRegistry& registry, OrderBookDBConfig const& config); + + // OrderBookDB interface implementation + void + setup(std::shared_ptr const& ledger) override; + + void + addOrderBook(Book const& book) override; + + std::vector + getBooksByTakerPays(Issue const& issue, std::optional const& domain = std::nullopt) + override; + + int + getBookSize(Issue const& issue, std::optional const& domain = std::nullopt) override; + + bool + isBookToXRP(Issue const& issue, std::optional domain = std::nullopt) override; + + // OrderBookDBImpl-specific methods + void + update(std::shared_ptr const& ledger); + + // see if this txn effects any orderbook + void + processTxn( + std::shared_ptr const& ledger, + AcceptedLedgerTx const& alTx, + MultiApiJson const& jvObj) override; + + BookListeners::pointer + getBookListeners(Book const&) override; + BookListeners::pointer + makeBookListeners(Book const&) override; + +private: + ServiceRegistry& registry_; + int const pathSearchMax_; + bool const standalone_; + + // Maps order books by "issue in" to "issue out": + hardened_hash_map> allBooks_; + + hardened_hash_map, hardened_hash_set> domainBooks_; + + // does an order book to XRP exist + hash_set xrpBooks_; + + // does an order book to XRP exist + hash_set> xrpDomainBooks_; + + std::recursive_mutex mLock; + + using BookToListenersMap = hash_map; + + BookToListenersMap mListeners; + + std::atomic seq_; + + beast::Journal const j_; +}; + +} // namespace xrpl diff --git a/src/xrpld/app/ledger/TransactionMaster.h b/src/xrpld/app/ledger/TransactionMaster.h index 039ff5cb99..1c5894d3bb 100644 --- a/src/xrpld/app/ledger/TransactionMaster.h +++ b/src/xrpld/app/ledger/TransactionMaster.h @@ -42,11 +42,18 @@ public: fetch(uint256 const&, ClosedInterval const& range, error_code_i& ec); std::shared_ptr - fetch(boost::intrusive_ptr const& item, SHAMapNodeType type, std::uint32_t uCommitLedger); + fetch( + boost::intrusive_ptr const& item, + SHAMapNodeType type, + std::uint32_t uCommitLedger); // return value: true = we had the transaction already bool - inLedger(uint256 const& hash, std::uint32_t ledger, std::optional tseq, std::optional netID); + inLedger( + uint256 const& hash, + std::uint32_t ledger, + std::optional tseq, + std::optional netID); void canonicalize(std::shared_ptr* pTransaction); diff --git a/src/xrpld/app/ledger/TransactionStateSF.cpp b/src/xrpld/app/ledger/TransactionStateSF.cpp index ee2febbd83..11d3c83058 100644 --- a/src/xrpld/app/ledger/TransactionStateSF.cpp +++ b/src/xrpld/app/ledger/TransactionStateSF.cpp @@ -11,7 +11,9 @@ TransactionStateSF::gotNode( SHAMapNodeType type) const { - XRPL_ASSERT(type != SHAMapNodeType::tnTRANSACTION_NM, "xrpl::TransactionStateSF::gotNode : valid input"); + XRPL_ASSERT( + type != SHAMapNodeType::tnTRANSACTION_NM, + "xrpl::TransactionStateSF::gotNode : valid input"); db_.store(hotTRANSACTION_NODE, std::move(nodeData), nodeHash.as_uint256(), ledgerSeq); } diff --git a/src/xrpld/app/ledger/TransactionStateSF.h b/src/xrpld/app/ledger/TransactionStateSF.h index 2851884cfd..9ca84c2610 100644 --- a/src/xrpld/app/ledger/TransactionStateSF.h +++ b/src/xrpld/app/ledger/TransactionStateSF.h @@ -17,8 +17,12 @@ public: } void - gotNode(bool fromFilter, SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, SHAMapNodeType type) - const override; + gotNode( + bool fromFilter, + SHAMapHash const& nodeHash, + std::uint32_t ledgerSeq, + Blob&& nodeData, + SHAMapNodeType type) const override; std::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/xrpld/app/ledger/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp index 1414441b35..5ad72c7a69 100644 --- a/src/xrpld/app/ledger/detail/BuildLedger.cpp +++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp @@ -2,10 +2,11 @@ #include #include #include +#include #include -#include #include +#include namespace xrpl { @@ -88,8 +89,8 @@ applyTransactions( // Attempt to apply all of the retriable transactions for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass) { - JLOG(j.debug()) << (certainRetry ? "Pass: " : "Final pass: ") << pass << " begins (" << txns.size() - << " transactions)"; + JLOG(j.debug()) << (certainRetry ? "Pass: " : "Final pass: ") << pass << " begins (" + << txns.size() << " transactions)"; int changes = 0; auto it = txns.begin(); @@ -130,8 +131,8 @@ applyTransactions( } } - JLOG(j.debug()) << (certainRetry ? "Pass: " : "Final pass: ") << pass << " completed (" << changes - << " changes)"; + JLOG(j.debug()) << (certainRetry ? "Pass: " : "Final pass: ") << pass << " completed (" + << changes << " changes)"; // Accumulate changes. count += changes; @@ -163,7 +164,8 @@ buildLedger( std::set& failedTxns, beast::Journal j) { - JLOG(j.debug()) << "Report: Transaction Set = " << txns.key() << ", close " << closeTime.time_since_epoch().count() + JLOG(j.debug()) << "Report: Transaction Set = " << txns.key() << ", close " + << closeTime.time_since_epoch().count() << (closeTimeCorrect ? "" : " (incorrect)"); return buildLedgerImpl( @@ -179,18 +181,26 @@ buildLedger( auto const applied = applyTransactions(app, built, txns, failedTxns, accum, j); if (!txns.empty() || !failedTxns.empty()) - JLOG(j.debug()) << "Applied " << applied << " transactions; " << failedTxns.size() << " failed and " - << txns.size() << " will be retried. " - << "Total transactions in ledger (including Inner Batch): " << accum.txCount(); + { + JLOG(j.debug()) << "Applied " << applied << " transactions; " << failedTxns.size() + << " failed and " << txns.size() << " will be retried. " + << "Total transactions in ledger (including Inner Batch): " + << accum.txCount(); + } else JLOG(j.debug()) << "Applied " << applied << " transactions. " - << "Total transactions in ledger (including Inner Batch): " << accum.txCount(); + << "Total transactions in ledger (including Inner Batch): " + << accum.txCount(); }); } // Build a ledger by replaying std::shared_ptr -buildLedger(LedgerReplay const& replayData, ApplyFlags applyFlags, Application& app, beast::Journal j) +buildLedger( + LedgerReplay const& replayData, + ApplyFlags applyFlags, + Application& app, + beast::Journal j) { auto const& replayLedger = replayData.replay(); diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index 3c8f61a915..9f7312ea34 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -61,7 +61,12 @@ InboundLedger::InboundLedger( Reason reason, clock_type& clock, std::unique_ptr peerSet) - : TimeoutCounter(app, hash, ledgerAcquireTimeout, {jtLEDGER_DATA, "InboundLedger", 5}, app.journal("InboundLedger")) + : TimeoutCounter( + app, + hash, + ledgerAcquireTimeout, + {jtLEDGER_DATA, "InboundLedger", 5}, + app.journal("InboundLedger")) , m_clock(clock) , mHaveHeader(false) , mHaveState(false) @@ -115,8 +120,9 @@ std::size_t InboundLedger::getPeerCount() const { auto const& peerIds = mPeerSet->getPeerIds(); - return std::count_if( - peerIds.begin(), peerIds.end(), [this](auto id) { return (app_.overlay().findPeerByShortID(id) != nullptr); }); + return std::count_if(peerIds.begin(), peerIds.end(), [this](auto id) { + return (app_.overlay().findPeerByShortID(id) != nullptr); + }); } void @@ -139,9 +145,13 @@ InboundLedger::checkLocal() if (!isDone()) { if (mLedger) + { tryDB(mLedger->stateMap().family().db()); + } else + { tryDB(app_.getNodeFamily().db()); + } if (failed_ || complete_) { done(); @@ -164,7 +174,8 @@ InboundLedger::~InboundLedger() { JLOG(journal_.debug()) << "Acquire " << hash_ << " abort " << ((timeouts_ == 0) ? std::string() - : (std::string("timeouts:") + std::to_string(timeouts_) + " ")) + : (std::string("timeouts:") + + std::to_string(timeouts_) + " ")) << mStats.get(); } } @@ -177,7 +188,9 @@ neededHashes(uint256 const& root, SHAMap& map, int max, SHAMapSyncFilter* filter if (!root.isZero()) { if (map.getHash().isZero()) + { ret.push_back(root); + } else { auto mn = map.getMissingNodes(max, filter); @@ -216,7 +229,8 @@ InboundLedger::tryDB(NodeStore::Database& srcDB) if (mLedger->header().hash != hash_ || (mSeq != 0 && mSeq != mLedger->header().seq)) { // We know for a fact the ledger can never be acquired - JLOG(journal_.warn()) << "hash " << hash_ << " seq " << std::to_string(mSeq) << " cannot be a ledger"; + JLOG(journal_.warn()) + << "hash " << hash_ << " seq " << std::to_string(mSeq) << " cannot be a ledger"; mLedger.reset(); failed_ = true; } @@ -253,7 +267,8 @@ InboundLedger::tryDB(NodeStore::Database& srcDB) return; // Store the ledger header in the ledger's database - mLedger->stateMap().family().db().store(hotLEDGER, std::move(*data), hash_, mLedger->header().seq); + mLedger->stateMap().family().db().store( + hotLEDGER, std::move(*data), hash_, mLedger->header().seq); } if (mSeq == 0) @@ -394,8 +409,9 @@ InboundLedger::done() touch(); JLOG(journal_.debug()) << "Acquire " << hash_ << (failed_ ? " fail " : " ") - << ((timeouts_ == 0) ? std::string() - : (std::string("timeouts:") + std::to_string(timeouts_) + " ")) + << ((timeouts_ == 0) + ? std::string() + : (std::string("timeouts:") + std::to_string(timeouts_) + " ")) << mStats.get(); XRPL_ASSERT(complete_ || failed_, "xrpl::InboundLedger::done : complete or failed"); @@ -425,7 +441,9 @@ InboundLedger::done() self->app_.getLedgerMaster().tryAdvance(); } else + { self->app_.getInboundLedgers().logFailure(self->hash_, self->mSeq); + } }); } @@ -451,9 +469,13 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) ss << " from " << peer; if (complete_ || failed_) + { ss << " complete=" << complete_ << " failed=" << failed_; + } else + { ss << " header=" << mHaveHeader << " tx=" << mHaveTransactions << " as=" << mHaveState; + } stream << ss.str(); } @@ -532,7 +554,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) tmGL.set_itype(protocol::liBASE); if (mSeq != 0) tmGL.set_ledgerseq(mSeq); - JLOG(journal_.trace()) << "Sending header request to " << (peer ? "selected peer" : "all peers"); + JLOG(journal_.trace()) << "Sending header request to " + << (peer ? "selected peer" : "all peers"); mPeerSet->sendRequest(tmGL, peer); return; } @@ -551,7 +574,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) tmGL.set_querydepth(2); } else + { tmGL.set_querydepth(1); + } // Get the state data first because it's the most likely to be useful // if we wind up abandoning this fetch. @@ -571,7 +596,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) // we need the root node tmGL.set_itype(protocol::liAS_NODE); *tmGL.add_nodeids() = SHAMapNodeID().getRawString(); - JLOG(journal_.trace()) << "Sending AS root request to " << (peer ? "selected peer" : "all peers"); + JLOG(journal_.trace()) + << "Sending AS root request to " << (peer ? "selected peer" : "all peers"); mPeerSet->sendRequest(tmGL, peer); return; } @@ -590,7 +616,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->stateMap().isValid()) + { failed_ = true; + } else { mHaveState = true; @@ -611,15 +639,13 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) *(tmGL.add_nodeids()) = id.first.getRawString(); } - JLOG(journal_.trace()) << "Sending AS node request (" << nodes.size() << ") to " - << (peer ? "selected peer" : "all peers"); + JLOG(journal_.trace()) << "Sending AS node request (" << nodes.size() + << ") to " << (peer ? "selected peer" : "all peers"); mPeerSet->sendRequest(tmGL, peer); return; } - else - { - JLOG(journal_.trace()) << "All AS nodes filtered"; - } + + JLOG(journal_.trace()) << "All AS nodes filtered"; } } } @@ -641,7 +667,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) // we need the root node tmGL.set_itype(protocol::liTX_NODE); *(tmGL.add_nodeids()) = SHAMapNodeID().getRawString(); - JLOG(journal_.trace()) << "Sending TX root request to " << (peer ? "selected peer" : "all peers"); + JLOG(journal_.trace()) + << "Sending TX root request to " << (peer ? "selected peer" : "all peers"); mPeerSet->sendRequest(tmGL, peer); return; } @@ -654,7 +681,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->txMap().isValid()) + { failed_ = true; + } else { mHaveTransactions = true; @@ -679,30 +708,31 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) mPeerSet->sendRequest(tmGL, peer); return; } - else - { - JLOG(journal_.trace()) << "All TX nodes filtered"; - } + + JLOG(journal_.trace()) << "All TX nodes filtered"; } } } if (complete_ || failed_) { - JLOG(journal_.debug()) << "Done:" << (complete_ ? " complete" : "") << (failed_ ? " failed " : " ") - << mLedger->header().seq; + JLOG(journal_.debug()) << "Done:" << (complete_ ? " complete" : "") + << (failed_ ? " failed " : " ") << mLedger->header().seq; sl.unlock(); done(); } } void -InboundLedger::filterNodes(std::vector>& nodes, TriggerReason reason) +InboundLedger::filterNodes( + std::vector>& nodes, + TriggerReason reason) { // Sort nodes so that the ones we haven't recently // requested come before the ones we have. - auto dup = std::stable_partition( - nodes.begin(), nodes.end(), [this](auto const& item) { return mRecentNodes.count(item.second) == 0; }); + auto dup = std::stable_partition(nodes.begin(), nodes.end(), [this](auto const& item) { + return mRecentNodes.count(item.second) == 0; + }); // If everything is a duplicate we don't want to send // any query at all except on a timeout where we need @@ -750,7 +780,8 @@ InboundLedger::takeHeader(std::string const& data) mLedger = std::make_shared(deserializeHeader(makeSlice(data)), app_.config(), *f); if (mLedger->header().hash != hash_ || (mSeq != 0 && mSeq != mLedger->header().seq)) { - JLOG(journal_.warn()) << "Acquire hash mismatch: " << mLedger->header().hash << "!=" << hash_; + JLOG(journal_.warn()) << "Acquire hash mismatch: " << mLedger->header().hash + << "!=" << hash_; mLedger.reset(); return false; } @@ -803,16 +834,21 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) return; } - auto [map, rootHash, filter] = [&]() -> std::tuple> { + auto [map, rootHash, filter] = + [&]() -> std::tuple> { if (packet.type() == protocol::liTX_NODE) + { return { mLedger->txMap(), SHAMapHash{mLedger->header().txHash}, - std::make_unique(mLedger->txMap().family().db(), app_.getLedgerMaster())}; + std::make_unique( + mLedger->txMap().family().db(), app_.getLedgerMaster())}; + } return { mLedger->stateMap(), SHAMapHash{mLedger->header().accountHash}, - std::make_unique(mLedger->stateMap().family().db(), app_.getLedgerMaster())}; + std::make_unique( + mLedger->stateMap().family().db(), app_.getLedgerMaster())}; }(); try @@ -852,9 +888,13 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) if (!map.isSynching()) { if (packet.type() == protocol::liTX_NODE) + { mHaveTransactions = true; + } else + { mHaveState = true; + } if (mHaveTransactions && mHaveState) { @@ -885,7 +925,8 @@ InboundLedger::takeAsRootNode(Slice const& data, SHAMapAddNode& san) } AccountStateSF filter(mLedger->stateMap().family().db(), app_.getLedgerMaster()); - san += mLedger->stateMap().addRootNode(SHAMapHash{mLedger->header().accountHash}, data, &filter); + san += + mLedger->stateMap().addRootNode(SHAMapHash{mLedger->header().accountHash}, data, &filter); return san.isGood(); } @@ -950,7 +991,9 @@ InboundLedger::getNeededHashes() Returns 'true' if we need to dispatch */ bool -InboundLedger::gotData(std::weak_ptr peer, std::shared_ptr const& data) +InboundLedger::gotData( + std::weak_ptr peer, + std::shared_ptr const& data) { std::lock_guard sl(mReceivedDataLock); @@ -1033,8 +1076,6 @@ InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData& p if ((packet.type() == protocol::liTX_NODE) || (packet.type() == protocol::liAS_NODE)) { - std::string type = packet.type() == protocol::liTX_NODE ? "liTX_NODE: " : "liAS_NODE: "; - if (packet.nodes().empty()) { JLOG(journal_.info()) << peer->id() << ": response with no nodes"; @@ -1058,7 +1099,8 @@ InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData& p SHAMapAddNode san; receiveNode(packet, san); - JLOG(journal_.debug()) << "Ledger " << ((packet.type() == protocol::liTX_NODE) ? "TX" : "AS") + JLOG(journal_.debug()) << "Ledger " + << ((packet.type() == protocol::liTX_NODE) ? "TX" : "AS") << " node stats: " << san.get(); if (san.isUseful()) @@ -1107,9 +1149,13 @@ struct PeerDataCounts while (i != counts.end()) { if (i->second < thresh) + { i = counts.erase(i); + } else + { ++i; + } } } @@ -1133,7 +1179,8 @@ struct PeerDataCounts outFunc(v); } #else - std::sample(counts.begin(), counts.end(), boost::make_function_output_iterator(outFunc), n, rng); + std::sample( + counts.begin(), counts.end(), boost::make_function_output_iterator(outFunc), n, rng); #endif } }; @@ -1184,7 +1231,9 @@ InboundLedger::runData() // Select a random sample of the peers that gives us the most nodes that are // useful dataCounts.prune(); - dataCounts.sampleN(maxUsefulPeers, [&](std::shared_ptr const& peer) { trigger(peer, TriggerReason::reply); }); + dataCounts.sampleN(maxUsefulPeers, [&](std::shared_ptr const& peer) { + trigger(peer, TriggerReason::reply); + }); } Json::Value diff --git a/src/xrpld/app/ledger/detail/InboundLedgers.cpp b/src/xrpld/app/ledger/detail/InboundLedgers.cpp index 626f58b686..be7577a2c3 100644 --- a/src/xrpld/app/ledger/detail/InboundLedgers.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedgers.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -10,6 +9,7 @@ #include #include #include +#include #include #include @@ -51,7 +51,8 @@ public: acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override { auto doAcquire = [&, seq, reason]() -> std::shared_ptr { - XRPL_ASSERT(hash.isNonZero(), "xrpl::InboundLedgersImp::acquire::doAcquire : nonzero hash"); + XRPL_ASSERT( + hash.isNonZero(), "xrpl::InboundLedgersImp::acquire::doAcquire : nonzero hash"); // probably not the right rule if (app_.getOPs().isNeedNetworkLedger() && (reason != InboundLedger::Reason::GENERIC) && @@ -115,7 +116,8 @@ public: } catch (std::exception const& e) { - JLOG(j_.warn()) << "Exception thrown for acquiring new inbound ledger " << hash << ": " << e.what(); + JLOG(j_.warn()) << "Exception thrown for acquiring new inbound ledger " << hash << ": " + << e.what(); } catch (...) { @@ -159,17 +161,23 @@ public: /** We received a TMLedgerData from a peer. */ bool - gotLedgerData(LedgerHash const& hash, std::shared_ptr peer, std::shared_ptr packet) - override + gotLedgerData( + LedgerHash const& hash, + std::shared_ptr peer, + std::shared_ptr packet) override { if (auto ledger = find(hash)) { - JLOG(j_.trace()) << "Got data (" << packet->nodes().size() << ") for acquiring ledger: " << hash; + JLOG(j_.trace()) << "Got data (" << packet->nodes().size() + << ") for acquiring ledger: " << hash; // Stash the data for later processing and see if we need to // dispatch if (ledger->gotData(std::weak_ptr(peer), packet)) - app_.getJobQueue().addJob(jtLEDGER_DATA, "ProcessLData", [ledger]() { ledger->runData(); }); + { + app_.getJobQueue().addJob( + jtLEDGER_DATA, "ProcessLData", [ledger]() { ledger->runData(); }); + } return true; } @@ -180,7 +188,8 @@ public: // useful. if (packet->type() == protocol::liAS_NODE) { - app_.getJobQueue().addJob(jtLEDGER_DATA, "GotStaleData", [this, packet]() { gotStaleData(packet); }); + app_.getJobQueue().addJob( + jtLEDGER_DATA, "GotStaleData", [this, packet]() { gotStaleData(packet); }); } return false; @@ -234,7 +243,7 @@ public: newNode->getHash().as_uint256(), std::make_shared(s.begin(), s.end())); } } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } } @@ -283,9 +292,13 @@ public: for (auto const& it : mRecentFailures) { if (it.second > 1) + { ret[std::to_string(it.second)][jss::failed] = true; + } else + { ret[to_string(it.first)][jss::failed] = true; + } } } @@ -294,9 +307,13 @@ public: // getJson is expensive, so call without the lock std::uint32_t seq = it.second->getSeq(); if (seq > 1) + { ret[std::to_string(seq)] = it.second->getJson(0); + } else + { ret[to_string(it.first)] = it.second->getJson(0); + } } return ret; @@ -333,7 +350,7 @@ public: // Make a list of things to sweep, while holding the lock std::vector stuffToSweep; - std::size_t total; + std::size_t total = 0; { ScopedLockType sl(mLock); @@ -367,9 +384,11 @@ public: beast::expire(mRecentFailures, kReacquireInterval); } - JLOG(j_.debug()) << "Swept " << stuffToSweep.size() << " out of " << total << " inbound ledgers. Duration: " - << std::chrono::duration_cast(m_clock.now() - start).count() - << "ms"; + JLOG(j_.debug()) + << "Swept " << stuffToSweep.size() << " out of " << total + << " inbound ledgers. Duration: " + << std::chrono::duration_cast(m_clock.now() - start).count() + << "ms"; } void diff --git a/src/xrpld/app/ledger/detail/InboundTransactions.cpp b/src/xrpld/app/ledger/detail/InboundTransactions.cpp index 36ebb7bd9e..f27f2c33f0 100644 --- a/src/xrpld/app/ledger/detail/InboundTransactions.cpp +++ b/src/xrpld/app/ledger/detail/InboundTransactions.cpp @@ -2,13 +2,14 @@ #include #include #include -#include #include #include #include #include +#include +#include #include #include @@ -30,7 +31,8 @@ public: TransactionAcquire::pointer mAcquire; std::shared_ptr mSet; - InboundTransactionSet(std::uint32_t seq, std::shared_ptr const& set) : mSeq(seq), mSet(set) + InboundTransactionSet(std::uint32_t seq, std::shared_ptr const& set) + : mSeq(seq), mSet(set) { ; } @@ -49,13 +51,13 @@ public: std::function const&, bool)> gotSet, std::unique_ptr peerSetBuilder) : app_(app) - , m_seq(0) , m_zeroSet(m_map[uint256()]) , m_gotSet(std::move(gotSet)) , m_peerSetBuilder(std::move(peerSetBuilder)) , j_(app_.journal("InboundTransactions")) { - m_zeroSet.mSet = std::make_shared(SHAMapType::TRANSACTION, uint256(), app_.getNodeFamily()); + m_zeroSet.mSet = + std::make_shared(SHAMapType::TRANSACTION, uint256(), app_.getNodeFamily()); m_zeroSet.mSet->setUnbacked(); } @@ -112,12 +114,15 @@ public: /** We received a TMLedgerData from a peer. */ void - gotData(LedgerHash const& hash, std::shared_ptr peer, std::shared_ptr packet_ptr) - override + gotData( + LedgerHash const& hash, + std::shared_ptr peer, + std::shared_ptr packet_ptr) override { protocol::TMLedgerData& packet = *packet_ptr; - JLOG(j_.trace()) << "Got data (" << packet.nodes().size() << ") for acquiring ledger: " << hash; + JLOG(j_.trace()) << "Got data (" << packet.nodes().size() + << ") for acquiring ledger: " << hash; TransactionAcquire::pointer ta = getAcquire(hash); @@ -163,13 +168,16 @@ public: auto& inboundSet = m_map[hash]; - if (inboundSet.mSeq < m_seq) - inboundSet.mSeq = m_seq; + inboundSet.mSeq = std::max(inboundSet.mSeq, m_seq); if (inboundSet.mSet) + { isNew = false; + } else + { inboundSet.mSet = set; + } inboundSet.mAcquire.reset(); } @@ -198,9 +206,13 @@ public: while (it != m_map.end()) { if (it->second.mSeq < minSeq || it->second.mSeq > maxSeq) + { it = m_map.erase(it); + } else + { ++it; + } } } } @@ -222,7 +234,7 @@ private: bool stopping_{false}; MapType m_map; - std::uint32_t m_seq; + std::uint32_t m_seq{0}; // The empty transaction set whose hash is zero InboundTransactionSet& m_zeroSet; @@ -244,7 +256,8 @@ make_InboundTransactions( beast::insight::Collector::ptr const& collector, std::function const&, bool)> gotSet) { - return std::make_unique(app, collector, std::move(gotSet), make_PeerSetBuilder(app)); + return std::make_unique( + app, collector, std::move(gotSet), make_PeerSetBuilder(app)); } } // namespace xrpl diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp index f54c79f9c0..3052f787dc 100644 --- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp +++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp @@ -1,10 +1,10 @@ #include #include #include -#include #include #include +#include namespace xrpl { @@ -93,7 +93,9 @@ public: std::lock_guard lock(mutex_); if (maxRange_ == 0) + { map["status"] = "idle"; + } else { map["status"] = "running"; @@ -242,14 +244,20 @@ private: @return `true` if the ledger was cleaned. */ bool - doLedger(LedgerIndex const& ledgerIndex, LedgerHash const& ledgerHash, bool doNodes, bool doTxns) + doLedger( + LedgerIndex const& ledgerIndex, + LedgerHash const& ledgerHash, + bool doNodes, + bool doTxns) { - auto nodeLedger = app_.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); + auto nodeLedger = app_.getInboundLedgers().acquire( + ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); if (!nodeLedger) { JLOG(j_.debug()) << "Ledger " << ledgerIndex << " not available"; app_.getLedgerMaster().clearLedger(ledgerIndex); - app_.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); + app_.getInboundLedgers().acquire( + ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); return false; } @@ -272,7 +280,8 @@ private: { JLOG(j_.debug()) << "Ledger " << ledgerIndex << " is missing nodes"; app_.getLedgerMaster().clearLedger(ledgerIndex); - app_.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); + app_.getInboundLedgers().acquire( + ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); return false; } @@ -323,8 +332,8 @@ private: { // We found the hash and sequence of a better reference // ledger. - referenceLedger = - app_.getInboundLedgers().acquire(refHash, refIndex, InboundLedger::Reason::GENERIC); + referenceLedger = app_.getInboundLedgers().acquire( + refHash, refIndex, InboundLedger::Reason::GENERIC); if (referenceLedger) ledgerHash = getLedgerHash(referenceLedger, ledgerIndex); } @@ -349,10 +358,10 @@ private: while (!shouldExit()) { - LedgerIndex ledgerIndex; + LedgerIndex ledgerIndex = 0; LedgerHash ledgerHash; - bool doNodes; - bool doTxns; + bool doNodes = false; + bool doTxns = false; if (app_.getFeeTrack().isLoadedLocal()) { diff --git a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp index f06beda768..cb2e2e57ea 100644 --- a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp +++ b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp @@ -62,7 +62,8 @@ LedgerDeltaAcquire::trigger(std::size_t limit, ScopedLockType& sl) peerSet_->addPeers( limit, [this](auto peer) { - return peer->supportsFeature(ProtocolFeature::LedgerReplay) && peer->hasLedger(hash_, ledgerSeq_); + return peer->supportsFeature(ProtocolFeature::LedgerReplay) && + peer->hasLedger(hash_, ledgerSeq_); }, [this](auto peer) { if (peer->supportsFeature(ProtocolFeature::LedgerReplay)) @@ -144,7 +145,7 @@ LedgerDeltaAcquire::addDataCallback(InboundLedger::Reason reason, OnDeltaDataCB& { ScopedLockType sl(mtx_); dataReadyCallbacks_.emplace_back(std::move(cb)); - if (reasons_.count(reason) == 0) + if (!reasons_.contains(reason)) { reasons_.emplace(reason); if (fullLedger_) @@ -169,7 +170,9 @@ LedgerDeltaAcquire::tryBuild(std::shared_ptr const& parent) if (failed_ || !complete_ || !replayTemp_) return {}; - XRPL_ASSERT(parent->seq() + 1 == replayTemp_->seq(), "xrpl::LedgerDeltaAcquire::tryBuild : parent sequence match"); + XRPL_ASSERT( + parent->seq() + 1 == replayTemp_->seq(), + "xrpl::LedgerDeltaAcquire::tryBuild : parent sequence match"); XRPL_ASSERT( parent->header().hash == replayTemp_->header().parentHash, "xrpl::LedgerDeltaAcquire::tryBuild : parent hash match"); @@ -182,13 +185,12 @@ LedgerDeltaAcquire::tryBuild(std::shared_ptr const& parent) onLedgerBuilt(sl); return fullLedger_; } - else - { - failed_ = true; - complete_ = false; - JLOG(journal_.error()) << "tryBuild failed " << hash_ << " with parent " << parent->header().hash; - Throw("Cannot replay ledger"); - } + + failed_ = true; + complete_ = false; + JLOG(journal_.error()) << "tryBuild failed " << hash_ << " with parent " + << parent->header().hash; + Throw("Cannot replay ledger"); } void @@ -204,23 +206,24 @@ LedgerDeltaAcquire::onLedgerBuilt(ScopedLockType& sl, std::optionalfullLedger_, &app = this->app_]() { - for (auto reason : reasons) - { - switch (reason) + app_.getJobQueue().addJob( + jtREPLAY_TASK, "OnLedBuilt", [=, ledger = this->fullLedger_, &app = this->app_]() { + for (auto reason : reasons) { - case InboundLedger::Reason::GENERIC: - app.getLedgerMaster().storeLedger(ledger); - break; - default: - // TODO for other use cases - break; + switch (reason) + { + case InboundLedger::Reason::GENERIC: + app.getLedgerMaster().storeLedger(ledger); + break; + default: + // TODO for other use cases + break; + } } - } - if (firstTime) - app.getLedgerMaster().tryAdvance(); - }); + if (firstTime) + app.getLedgerMaster().tryAdvance(); + }); } void diff --git a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h index 66b8f27b85..2877d997e6 100644 --- a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h +++ b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.h @@ -65,7 +65,9 @@ public: * @note info and Txns must have been verified against the ledger hash */ void - processData(LedgerHeader const& info, std::map>&& orderedTxns); + processData( + LedgerHeader const& info, + std::map>&& orderedTxns); /** * Try to build the ledger if not already diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index c4299c1b63..42d7f2ec6e 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -3,18 +3,13 @@ #include #include #include -#include #include #include -#include -#include -#include #include #include #include #include #include -#include #include #include #include @@ -26,10 +21,15 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include +#include #include #include @@ -72,7 +72,8 @@ shouldAcquire( return minimumOnline.has_value() && candidateLedger >= *minimumOnline; }(); - JLOG(j.trace()) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT") << " be acquired"; + JLOG(j.trace()) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT") + << " be acquired"; return ret; } @@ -88,7 +89,12 @@ LedgerMaster::LedgerMaster( , fetch_depth_(app_.getSHAMapStore().clampFetchDepth(app_.config().FETCH_DEPTH)) , ledger_history_(app_.config().LEDGER_HISTORY) , ledger_fetch_size_(app_.config().getValueFor(SizedItem::ledgerFetch)) - , fetch_packs_("FetchPack", 65536, std::chrono::seconds{45}, stopwatch, app_.journal("TaggedCache")) + , fetch_packs_( + "FetchPack", + 65536, + std::chrono::seconds{45}, + stopwatch, + app_.journal("TaggedCache")) , m_stats(std::bind(&LedgerMaster::collect_metrics, this), collector) { } @@ -277,7 +283,9 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) app_.getOPs().setAmendmentWarned(); } else + { app_.getOPs().clearAmendmentWarned(); + } } } } @@ -311,8 +319,8 @@ LedgerMaster::canBeCurrent(std::shared_ptr const& ledger) auto validLedger = getValidatedLedger(); if (validLedger && (ledger->header().seq < validLedger->header().seq)) { - JLOG(m_journal.trace()) << "Candidate for current ledger has low seq " << ledger->header().seq << " < " - << validLedger->header().seq; + JLOG(m_journal.trace()) << "Candidate for current ledger has low seq " + << ledger->header().seq << " < " << validLedger->header().seq; return false; } @@ -328,8 +336,9 @@ LedgerMaster::canBeCurrent(std::shared_ptr const& ledger) if ((validLedger || (ledger->header().seq > 10)) && ((std::max(closeTime, ledgerClose) - std::min(closeTime, ledgerClose)) > 5min)) { - JLOG(m_journal.warn()) << "Candidate for current ledger has close time " << to_string(ledgerClose) - << " at network time " << to_string(closeTime) << " seq " << ledger->header().seq; + JLOG(m_journal.warn()) << "Candidate for current ledger has close time " + << to_string(ledgerClose) << " at network time " + << to_string(closeTime) << " seq " << ledger->header().seq; return false; } @@ -343,15 +352,17 @@ LedgerMaster::canBeCurrent(std::shared_ptr const& ledger) LedgerIndex maxSeq = validLedger->header().seq + 10; if (closeTime > validLedger->header().parentCloseTime) - maxSeq += - std::chrono::duration_cast(closeTime - validLedger->header().parentCloseTime) - .count() / + { + maxSeq += std::chrono::duration_cast( + closeTime - validLedger->header().parentCloseTime) + .count() / 2; + } if (ledger->header().seq > maxSeq) { - JLOG(m_journal.warn()) << "Candidate for current ledger has high seq " << ledger->header().seq << " > " - << maxSeq; + JLOG(m_journal.warn()) << "Candidate for current ledger has high seq " + << ledger->header().seq << " > " << maxSeq; return false; } @@ -513,9 +524,13 @@ LedgerMaster::getFullValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal } if (maybeMin == std::nullopt) + { minVal = maxVal; + } else + { minVal = 1 + *maybeMin; + } return true; } @@ -537,9 +552,9 @@ LedgerMaster::getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal) // Ensure we shrink the tips as much as possible. If we have 7-9 and // 8,9 are invalid, we don't want to see the 8 and shrink to just 9 // because then we'll have nothing when we could have 7. - while (pendingSaves.count(maxVal) > 0) + while (pendingSaves.contains(maxVal)) --maxVal; - while (pendingSaves.count(minVal) > 0) + while (pendingSaves.contains(minVal)) ++minVal; // Best effort for remaining exclusions @@ -548,9 +563,13 @@ LedgerMaster::getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal) if ((v.first >= minVal) && (v.first <= maxVal)) { if (v.first > ((minVal + maxVal) / 2)) + { maxVal = v.first - 1; + } else + { minVal = v.first + 1; + } } } @@ -570,9 +589,13 @@ LedgerMaster::getEarliestFetch() std::uint32_t e = getClosedLedger()->header().seq; if (e > fetch_depth_) + { e -= fetch_depth_; + } else + { e = 0; + } return e; } @@ -611,16 +634,19 @@ LedgerMaster::tryFill(std::shared_ptr ledger) mCompleteLedgers.insert(range(minHas, maxHas)); } maxHas = minHas; - ledgerHashes = app_.getRelationalDatabase().getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq); + ledgerHashes = + app_.getRelationalDatabase().getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq); it = ledgerHashes.find(seq); if (it == ledgerHashes.end()) break; - if (!nodeStore.fetchNodeObject(ledgerHashes.begin()->second.ledgerHash, ledgerHashes.begin()->first)) + if (!nodeStore.fetchNodeObject( + ledgerHashes.begin()->second.ledgerHash, ledgerHashes.begin()->first)) { // The ledger is not backed by the node store - JLOG(m_journal.warn()) << "SQL DB ledger sequence " << seq << " mismatches node store"; + JLOG(m_journal.warn()) + << "SQL DB ledger sequence " << seq << " mismatches node store"; break; } } @@ -707,7 +733,8 @@ LedgerMaster::fixMismatch(ReadView const& ledger) } catch (std::exception const& ex) { - JLOG(m_journal.warn()) << "fixMismatch encounters partial ledger. Exception: " << ex.what(); + JLOG(m_journal.warn()) + << "fixMismatch encounters partial ledger. Exception: " << ex.what(); clearLedger(lSeq); return; } @@ -722,8 +749,8 @@ LedgerMaster::fixMismatch(ReadView const& ledger) // we closed the seam if (invalidate != 0) { - JLOG(m_journal.warn()) - << "Match at " << lSeq << ", " << invalidate << " prior ledgers invalidated"; + JLOG(m_journal.warn()) << "Match at " << lSeq << ", " << invalidate + << " prior ledgers invalidated"; } return; @@ -743,12 +770,17 @@ LedgerMaster::fixMismatch(ReadView const& ledger) } void -LedgerMaster::setFullLedger(std::shared_ptr const& ledger, bool isSynchronous, bool isCurrent) +LedgerMaster::setFullLedger( + std::shared_ptr const& ledger, + bool isSynchronous, + bool isCurrent) { // A new ledger has been accepted as part of the trusted chain - JLOG(m_journal.debug()) << "Ledger " << ledger->header().seq << " accepted :" << ledger->header().hash; + JLOG(m_journal.debug()) << "Ledger " << ledger->header().seq + << " accepted :" << ledger->header().hash; XRPL_ASSERT( - ledger->stateMap().getHash().isNonZero(), "xrpl::LedgerMaster::setFullLedger : nonzero ledger state hash"); + ledger->stateMap().getHash().isNonZero(), + "xrpl::LedgerMaster::setFullLedger : nonzero ledger state hash"); ledger->setValidated(); ledger->setFull(); @@ -817,7 +849,8 @@ LedgerMaster::checkAccept(uint256 const& hash, std::uint32_t seq) if (seq < mValidLedgerSeq) return; - auto validations = app_.validators().negativeUNLFilter(app_.getValidations().getTrustedForLedger(hash, seq)); + auto validations = app_.validators().negativeUNLFilter( + app_.getValidations().getTrustedForLedger(hash, seq)); valCount = validations.size(); if (valCount >= app_.validators().quorum()) { @@ -890,8 +923,8 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) return; } - JLOG(m_journal.info()) << "Advancing accepted ledger to " << ledger->header().seq << " with >= " << minVal - << " validations"; + JLOG(m_journal.info()) << "Advancing accepted ledger to " << ledger->header().seq + << " with >= " << minVal << " validations"; ledger->setValidated(); ledger->setFull(); @@ -910,7 +943,7 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) fees.reserve(fees.size() + fees2.size()); std::copy(fees2.begin(), fees2.end(), std::back_inserter(fees)); } - std::uint32_t fee; + std::uint32_t fee = 0; if (!fees.empty()) { std::sort(fees.begin(), fees.end()); @@ -953,8 +986,8 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) if (upgradeWarningPrevTime_ == TimeKeeper::time_point()) { // Have not printed the warning before, check if need to print. - auto const vals = - app_.getValidations().getTrustedForLedger(ledger->header().parentHash, ledger->header().seq - 1); + auto const vals = app_.getValidations().getTrustedForLedger( + ledger->header().parentHash, ledger->header().seq - 1); std::size_t higherVersionCount = 0; std::size_t rippledCount = 0; for (auto const& v : vals) @@ -975,7 +1008,8 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) constexpr std::size_t reportingPercent = 90; constexpr std::size_t cutoffPercent = 60; auto const unlSize{app_.validators().getQuorumKeys().second.size()}; - needPrint = unlSize > 0 && calculatePercent(vals.size(), unlSize) >= reportingPercent && + needPrint = unlSize > 0 && + calculatePercent(vals.size(), unlSize) >= reportingPercent && calculatePercent(higherVersionCount, rippledCount) >= cutoffPercent; } } @@ -1021,7 +1055,8 @@ LedgerMaster::consensusBuilt( if (ledger->header().seq <= mValidLedgerSeq) { auto stream = app_.journal("LedgerConsensus").info(); - JLOG(stream) << "Consensus built old ledger: " << ledger->header().seq << " <= " << mValidLedgerSeq; + JLOG(stream) << "Consensus built old ledger: " << ledger->header().seq + << " <= " << mValidLedgerSeq; return; } @@ -1044,10 +1079,7 @@ LedgerMaster::consensusBuilt( class valSeq { public: - valSeq() : valCount_(0), ledgerSeq_(0) - { - ; - } + valSeq() = default; void mergeValidation(LedgerIndex seq) @@ -1059,8 +1091,8 @@ LedgerMaster::consensusBuilt( ledgerSeq_ = seq; } - std::size_t valCount_; - LedgerIndex ledgerSeq_; + std::size_t valCount_{0}; + LedgerIndex ledgerSeq_{0}; }; // Count the number of current, trusted validations @@ -1078,6 +1110,7 @@ LedgerMaster::consensusBuilt( // Of the ledgers with sufficient validations, // find the one with the highest sequence for (auto& v : count) + { if (v.second.valCount_ > neededValidations) { // If we still don't know the sequence, get it @@ -1093,6 +1126,7 @@ LedgerMaster::consensusBuilt( maxLedger = v.first; } } + } if (maxSeq > mValidLedgerSeq) { @@ -1144,7 +1178,8 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl if (mValidLedgerSeq > (mPubLedgerSeq + MAX_LEDGER_GAP)) { - JLOG(m_journal.warn()) << "Gap in validated ledger stream " << mPubLedgerSeq << " - " << mValidLedgerSeq - 1; + JLOG(m_journal.warn()) << "Gap in validated ledger stream " << mPubLedgerSeq << " - " + << mValidLedgerSeq - 1; auto valLedger = mValidLedger.get(); ret.push_back(valLedger); @@ -1188,7 +1223,8 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl else if (hash->isZero()) { // LCOV_EXCL_START - JLOG(m_journal.fatal()) << "Ledger: " << valSeq << " does not have hash for " << seq; + JLOG(m_journal.fatal()) + << "Ledger: " << valSeq << " does not have hash for " << seq; UNREACHABLE( "xrpl::LedgerMaster::findNewLedgersToPublish : ledger " "not found"); @@ -1203,7 +1239,10 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl { // Can we try to acquire the ledger we need? if (!ledger && (++acqCount < ledger_fetch_size_)) - ledger = app_.getInboundLedgers().acquire(*hash, seq, InboundLedger::Reason::GENERIC); + { + ledger = app_.getInboundLedgers().acquire( + *hash, seq, InboundLedger::Reason::GENERIC); + } } // Did we acquire the next ledger we need to publish? @@ -1219,7 +1258,8 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl } catch (std::exception const& ex) { - JLOG(m_journal.error()) << "Exception while trying to find ledgers to publish: " << ex.what(); + JLOG(m_journal.error()) << "Exception while trying to find ledgers to publish: " + << ex.what(); } if (app_.config().LEDGER_REPLAY) @@ -1234,17 +1274,20 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl auto finishLedger = valLedger; while (startLedger->seq() + 1 < finishLedger->seq()) { - if (auto const parent = mLedgerHistory.getLedgerByHash(finishLedger->header().parentHash); parent) + if (auto const parent = + mLedgerHistory.getLedgerByHash(finishLedger->header().parentHash); + parent) { finishLedger = parent; } else { auto numberLedgers = finishLedger->seq() - startLedger->seq() + 1; - JLOG(m_journal.debug()) << "Publish LedgerReplays " << numberLedgers - << " ledgers, from seq=" << startLedger->header().seq << ", " - << startLedger->header().hash << " to seq=" << finishLedger->header().seq - << ", " << finishLedger->header().hash; + JLOG(m_journal.debug()) + << "Publish LedgerReplays " << numberLedgers + << " ledgers, from seq=" << startLedger->header().seq << ", " + << startLedger->header().hash << " to seq=" << finishLedger->header().seq + << ", " << finishLedger->header().hash; app_.getLedgerReplayer().replay( InboundLedger::Reason::GENERIC, finishLedger->header().hash, numberLedgers); break; @@ -1268,7 +1311,9 @@ LedgerMaster::tryAdvance() app_.getJobQueue().addJob(jtADVANCE, "AdvanceLedger", [this]() { std::unique_lock sl(m_mutex); - XRPL_ASSERT(!mValidLedger.empty() && mAdvanceThread, "xrpl::LedgerMaster::tryAdvance : has valid ledger"); + XRPL_ASSERT( + !mValidLedger.empty() && mAdvanceThread, + "xrpl::LedgerMaster::tryAdvance : has valid ledger"); JLOG(m_journal.trace()) << "advanceThread<"; @@ -1308,7 +1353,8 @@ LedgerMaster::updatePaths() { std::lock_guard ml(m_mutex); - if (!mValidLedger.empty() && (!mPathLedger || (mPathLedger->header().seq != mValidLedgerSeq))) + if (!mValidLedger.empty() && + (!mPathLedger || (mPathLedger->header().seq != mValidLedgerSeq))) { // We have a new valid ledger since the last full pathfinding mPathLedger = mValidLedger.get(); lastLedger = mPathLedger; @@ -1329,7 +1375,8 @@ LedgerMaster::updatePaths() if (!standalone_) { // don't pathfind with a ledger that's more than 60 seconds old using namespace std::chrono; - auto age = time_point_cast(app_.timeKeeper().closeTime()) - lastLedger->header().closeTime; + auto age = time_point_cast(app_.timeKeeper().closeTime()) - + lastLedger->header().closeTime; if (age > 1min) { JLOG(m_journal.debug()) << "Published ledger too old for updating paths"; @@ -1375,13 +1422,17 @@ LedgerMaster::updatePaths() { // our parent is the problem app_.getInboundLedgers().acquire( - lastLedger->header().parentHash, lastLedger->header().seq - 1, InboundLedger::Reason::GENERIC); + lastLedger->header().parentHash, + lastLedger->header().seq - 1, + InboundLedger::Reason::GENERIC); } else { // this ledger is the problem app_.getInboundLedgers().acquire( - lastLedger->header().hash, lastLedger->header().seq, InboundLedger::Reason::GENERIC); + lastLedger->header().hash, + lastLedger->header().seq, + InboundLedger::Reason::GENERIC); } } } @@ -1422,7 +1473,8 @@ LedgerMaster::newPFWork(char const* name, std::unique_lock { if (!app_.isStopping() && mPathFindThread < 2 && app_.getPathRequests().requestsPending()) { - JLOG(m_journal.debug()) << "newPFWork: Creating job. path find threads: " << mPathFindThread; + JLOG(m_journal.debug()) << "newPFWork: Creating job. path find threads: " + << mPathFindThread; if (app_.getJobQueue().addJob(jtUPDATE_PF, name, [this]() { updatePaths(); })) { ++mPathFindThread; @@ -1602,7 +1654,7 @@ LedgerMaster::getLedgerBySeq(std::uint32_t index) if (hash) return mLedgerHistory.getLedgerByHash(*hash); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { // Missing nodes are already handled } @@ -1696,7 +1748,8 @@ LedgerMaster::fetchForHistory( if (!app_.getInboundLedgers().isFailure(*hash)) { ledger = app_.getInboundLedgers().acquire(*hash, missing, reason); - if (!ledger && missing != fetch_seq_ && missing > app_.getNodeStore().earliestLedgerSeq()) + if (!ledger && missing != fetch_seq_ && + missing > app_.getNodeStore().earliestLedgerSeq()) { JLOG(m_journal.trace()) << "fetchForHistory want fetch pack " << missing; fetch_seq_ = missing; @@ -1714,7 +1767,7 @@ LedgerMaster::fetchForHistory( XRPL_ASSERT(seq == missing, "xrpl::LedgerMaster::fetchForHistory : sequence match"); JLOG(m_journal.trace()) << "fetchForHistory acquired " << seq; setFullLedger(ledger, false, false); - int fillInProgress; + int fillInProgress = 0; { std::lock_guard lock(m_mutex); mHistLedger = ledger; @@ -1728,17 +1781,19 @@ LedgerMaster::fetchForHistory( std::lock_guard lock(m_mutex); mFillInProgress = seq; } - app_.getJobQueue().addJob(jtADVANCE, "TryFill", [this, ledger]() { tryFill(ledger); }); + app_.getJobQueue().addJob( + jtADVANCE, "TryFill", [this, ledger]() { tryFill(ledger); }); } progress = true; } else { - std::uint32_t fetchSz; + std::uint32_t fetchSz = 0; // Do not fetch ledger sequences lower // than the earliest ledger sequence fetchSz = app_.getNodeStore().earliestLedgerSeq(); - fetchSz = missing >= fetchSz ? std::min(ledger_fetch_size_, (missing - fetchSz) + 1) : 0; + fetchSz = + missing >= fetchSz ? std::min(ledger_fetch_size_, (missing - fetchSz) + 1) : 0; try { for (std::uint32_t i = 0; i < fetchSz; ++i) @@ -1766,7 +1821,8 @@ LedgerMaster::fetchForHistory( JLOG(m_journal.fatal()) << "Pub:" << mPubLedgerSeq << " Val:" << mValidLedgerSeq; JLOG(m_journal.fatal()) << "Ledgers: " << app_.getLedgerMaster().getCompleteLedgers(); JLOG(m_journal.fatal()) << "Acquire reason: " - << (reason == InboundLedger::Reason::HISTORY ? "HISTORY" : "NOT HISTORY"); + << (reason == InboundLedger::Reason::HISTORY ? "HISTORY" + : "NOT HISTORY"); clearLedger(missing + 1); progress = true; } @@ -1785,7 +1841,8 @@ LedgerMaster::doAdvance(std::unique_lock& sl) if (pubLedgers.empty()) { if (!standalone_ && !app_.getFeeTrack().isLoadedLocal() && - (app_.getJobQueue().getJobCount(jtPUBOLDLEDGER) < 10) && (mValidLedgerSeq == mPubLedgerSeq) && + (app_.getJobQueue().getJobCount(jtPUBOLDLEDGER) < 10) && + (mValidLedgerSeq == mPubLedgerSeq) && (getValidatedLedgerAge() < MAX_LEDGER_AGE_ACQUIRE) && (app_.getNodeStore().getWriteLoad() < MAX_WRITE_LOAD_ACQUIRE)) { @@ -1795,7 +1852,9 @@ LedgerMaster::doAdvance(std::unique_lock& sl) { std::lock_guard sll(mCompleteLock); missing = prevMissing( - mCompleteLedgers, mPubLedger->header().seq, app_.getNodeStore().earliestLedgerSeq()); + mCompleteLedgers, + mPubLedger->header().seq, + app_.getNodeStore().earliestLedgerSeq()); } if (missing) { @@ -1811,7 +1870,9 @@ LedgerMaster::doAdvance(std::unique_lock& sl) JLOG(m_journal.trace()) << "advanceThread should acquire"; } else + { missing = std::nullopt; + } } if (missing) { @@ -1831,7 +1892,8 @@ LedgerMaster::doAdvance(std::unique_lock& sl) } else { - JLOG(m_journal.trace()) << "tryAdvance found " << pubLedgers.size() << " ledgers to publish"; + JLOG(m_journal.trace()) + << "tryAdvance found " << pubLedgers.size() << " ledgers to publish"; for (auto const& ledger : pubLedgers) { { @@ -2093,9 +2155,11 @@ LedgerMaster::txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex) return {}; for (auto it = lgr->txs.begin(); it != lgr->txs.end(); ++it) + { if (it->first && it->second && it->second->isFieldPresent(sfTransactionIndex) && it->second->getFieldU32(sfTransactionIndex) == txnIndex) return it->first->getTransactionID(); + } return {}; } diff --git a/src/xrpld/app/ledger/detail/LedgerReplay.cpp b/src/xrpld/app/ledger/detail/LedgerReplay.cpp index e77adc9700..d115402115 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplay.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplay.cpp @@ -3,7 +3,9 @@ namespace xrpl { -LedgerReplay::LedgerReplay(std::shared_ptr parent, std::shared_ptr replay) +LedgerReplay::LedgerReplay( + std::shared_ptr parent, + std::shared_ptr replay) : parent_{std::move(parent)}, replay_{std::move(replay)} { for (auto const& item : replay_->txMap()) diff --git a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp index 4fcb924c27..93cf0e5cce 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -14,7 +15,8 @@ LedgerReplayMsgHandler::LedgerReplayMsgHandler(Application& app, LedgerReplayer& } protocol::TMProofPathResponse -LedgerReplayMsgHandler::processProofPathRequest(std::shared_ptr const& msg) +LedgerReplayMsgHandler::processProofPathRequest( + std::shared_ptr const& msg) { protocol::TMProofPathRequest& packet = *msg; protocol::TMProofPathResponse reply; @@ -57,7 +59,8 @@ LedgerReplayMsgHandler::processProofPathRequest(std::shared_ptrsize(); + JLOG(journal_.debug()) << "getProofPath for the node " << key << " of ledger " << ledgerHash + << " path length " << path->size(); return reply; } bool -LedgerReplayMsgHandler::processProofPathResponse(std::shared_ptr const& msg) +LedgerReplayMsgHandler::processProofPathResponse( + std::shared_ptr const& msg) { protocol::TMProofPathResponse& reply = *msg; if (reply.has_error() || !reply.has_key() || !reply.has_ledgerhash() || !reply.has_type() || @@ -133,7 +137,7 @@ LedgerReplayMsgHandler::processProofPathResponse(std::shared_ptr(node.get())->peekItem()) + if (auto item = safe_downcast(node.get())->peekItem()) { replayer_.gotSkipList(info, item); return true; @@ -144,7 +148,8 @@ LedgerReplayMsgHandler::processProofPathResponse(std::shared_ptr const& msg) +LedgerReplayMsgHandler::processReplayDeltaRequest( + std::shared_ptr const& msg) { protocol::TMReplayDeltaRequest& packet = *msg; protocol::TMReplayDeltaResponse reply; @@ -182,7 +187,8 @@ LedgerReplayMsgHandler::processReplayDeltaRequest(std::shared_ptr const& msg) +LedgerReplayMsgHandler::processReplayDeltaResponse( + std::shared_ptr const& msg) { protocol::TMReplayDeltaResponse& reply = *msg; if (reply.has_error() || !reply.has_ledgerheader()) @@ -227,7 +233,8 @@ LedgerReplayMsgHandler::processReplayDeltaResponse(std::shared_ptr const& sList) +LedgerReplayTask::TaskParameter::update( + uint256 const& hash, + std::uint32_t seq, + std::vector const& sList) { if (finishHash_ != hash || sList.size() + 1 < totalLedgers_ || full_) return false; @@ -28,7 +31,9 @@ LedgerReplayTask::TaskParameter::update(uint256 const& hash, std::uint32_t seq, skipList_ = sList; skipList_.emplace_back(finishHash_); startHash_ = skipList_[skipList_.size() - totalLedgers_]; - XRPL_ASSERT(startHash_.isNonZero(), "xrpl::LedgerReplayTask::TaskParameter::update : nonzero start hash"); + XRPL_ASSERT( + startHash_.isNonZero(), + "xrpl::LedgerReplayTask::TaskParameter::update : nonzero start hash"); startSeq_ = finishSeq_ - totalLedgers_ + 1; full_ = true; return true; @@ -62,7 +67,7 @@ LedgerReplayTask::LedgerReplayTask( InboundLedgers& inboundLedgers, LedgerReplayer& replayer, std::shared_ptr& skipListAcquirer, - TaskParameter&& parameter) + TaskParameter const& parameter) : TimeoutCounter( app, parameter.finishHash_, @@ -72,9 +77,10 @@ LedgerReplayTask::LedgerReplayTask( , inboundLedgers_(inboundLedgers) , replayer_(replayer) , parameter_(parameter) - , maxTimeouts_(std::max( - LedgerReplayParameters::TASK_MAX_TIMEOUTS_MINIMUM, - parameter.totalLedgers_ * LedgerReplayParameters::TASK_MAX_TIMEOUTS_MULTIPLIER)) + , maxTimeouts_( + std::max( + LedgerReplayParameters::TASK_MAX_TIMEOUTS_MINIMUM, + parameter.totalLedgers_ * LedgerReplayParameters::TASK_MAX_TIMEOUTS_MULTIPLIER)) , skipListAcquirer_(skipListAcquirer) { JLOG(journal_.trace()) << "Create " << hash_; @@ -126,12 +132,13 @@ LedgerReplayTask::trigger(ScopedLockType& sl) parent_ = app_.getLedgerMaster().getLedgerByHash(parameter_.startHash_); if (!parent_) { - parent_ = - inboundLedgers_.acquire(parameter_.startHash_, parameter_.startSeq_, InboundLedger::Reason::GENERIC); + parent_ = inboundLedgers_.acquire( + parameter_.startHash_, parameter_.startSeq_, InboundLedger::Reason::GENERIC); } if (parent_) { - JLOG(journal_.trace()) << "Got start ledger " << parameter_.startHash_ << " for task " << hash_; + JLOG(journal_.trace()) + << "Got start ledger " << parameter_.startHash_ << " for task " << hash_; } } @@ -151,9 +158,10 @@ void LedgerReplayTask::tryAdvance(ScopedLockType& sl) { JLOG(journal_.trace()) << "tryAdvance task " << hash_ - << (parameter_.full_ ? ", full parameter" : ", waiting to fill parameter") - << ", deltaIndex=" << deltaToBuild_ << ", totalDeltas=" << deltas_.size() << ", parent " - << (parent_ ? parent_->header().hash : uint256()); + << (parameter_.full_ ? ", full parameter" + : ", waiting to fill parameter") + << ", deltaIndex=" << deltaToBuild_ << ", totalDeltas=" << deltas_.size() + << ", parent " << (parent_ ? parent_->header().hash : uint256()); bool shouldTry = parent_ && parameter_.full_ && parameter_.totalLedgers_ - 1 == deltas_.size(); if (!shouldTry) @@ -165,15 +173,19 @@ LedgerReplayTask::tryAdvance(ScopedLockType& sl) { auto& delta = deltas_[deltaToBuild_]; XRPL_ASSERT( - parent_->seq() + 1 == delta->ledgerSeq_, "xrpl::LedgerReplayTask::tryAdvance : consecutive sequence"); + parent_->seq() + 1 == delta->ledgerSeq_, + "xrpl::LedgerReplayTask::tryAdvance : consecutive sequence"); if (auto l = delta->tryBuild(parent_); l) { - JLOG(journal_.debug()) << "Task " << hash_ << " got ledger " << l->header().hash - << " deltaIndex=" << deltaToBuild_ << " totalDeltas=" << deltas_.size(); + JLOG(journal_.debug()) + << "Task " << hash_ << " got ledger " << l->header().hash + << " deltaIndex=" << deltaToBuild_ << " totalDeltas=" << deltas_.size(); parent_ = l; } else + { return; + } } complete_ = true; @@ -186,7 +198,10 @@ LedgerReplayTask::tryAdvance(ScopedLockType& sl) } void -LedgerReplayTask::updateSkipList(uint256 const& hash, std::uint32_t seq, std::vector const& sList) +LedgerReplayTask::updateSkipList( + uint256 const& hash, + std::uint32_t seq, + std::vector const& sList) { { ScopedLockType sl(mtx_); @@ -235,9 +250,13 @@ LedgerReplayTask::addDelta(std::shared_ptr const& delta) if (auto sptr = wptr.lock(); sptr) { if (!good) + { sptr->cancel(); + } else + { sptr->deltaReady(hash); + } } }); diff --git a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp index b52abc5624..553ff91174 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp @@ -22,10 +22,14 @@ LedgerReplayer::~LedgerReplayer() } void -LedgerReplayer::replay(InboundLedger::Reason r, uint256 const& finishLedgerHash, std::uint32_t totalNumLedgers) +LedgerReplayer::replay( + InboundLedger::Reason r, + uint256 const& finishLedgerHash, + std::uint32_t totalNumLedgers) { XRPL_ASSERT( - finishLedgerHash.isNonZero() && totalNumLedgers > 0 && totalNumLedgers <= LedgerReplayParameters::MAX_TASK_SIZE, + finishLedgerHash.isNonZero() && totalNumLedgers > 0 && + totalNumLedgers <= LedgerReplayParameters::MAX_TASK_SIZE, "xrpl::LedgerReplayer::replay : valid inputs"); LedgerReplayTask::TaskParameter parameter(r, finishLedgerHash, totalNumLedgers); @@ -52,7 +56,8 @@ LedgerReplayer::replay(InboundLedger::Reason r, uint256 const& finishLedgerHash, return; } } - JLOG(j_.info()) << "Replay " << totalNumLedgers << " ledgers. Finish ledger hash " << parameter.finishHash_; + JLOG(j_.info()) << "Replay " << totalNumLedgers << " ledgers. Finish ledger hash " + << parameter.finishHash_; auto i = skipLists_.find(parameter.finishHash_); if (i != skipLists_.end()) @@ -66,7 +71,8 @@ LedgerReplayer::replay(InboundLedger::Reason r, uint256 const& finishLedgerHash, newSkipList = true; } - task = std::make_shared(app_, inboundLedgers_, *this, skipList, std::move(parameter)); + task = std::make_shared( + app_, inboundLedgers_, *this, skipList, std::move(parameter)); tasks_.push_back(task); } @@ -90,10 +96,17 @@ LedgerReplayer::createDeltas(std::shared_ptr task) JLOG(j_.trace()) << "Creating " << parameter.totalLedgers_ - 1 << " deltas"; if (parameter.totalLedgers_ > 1) { - auto skipListItem = std::find(parameter.skipList_.begin(), parameter.skipList_.end(), parameter.startHash_); - if (skipListItem == parameter.skipList_.end() || ++skipListItem == parameter.skipList_.end()) + auto skipListItem = + std::find(parameter.skipList_.begin(), parameter.skipList_.end(), parameter.startHash_); + auto const wasLast = skipListItem == parameter.skipList_.end(); + if (not wasLast) + ++skipListItem; + auto const isLast = skipListItem == parameter.skipList_.end(); + + if (wasLast || isLast) { - JLOG(j_.error()) << "Task parameter error when creating deltas " << parameter.finishHash_; + JLOG(j_.error()) << "Task parameter error when creating deltas " + << parameter.finishHash_; return; } @@ -128,7 +141,9 @@ LedgerReplayer::createDeltas(std::shared_ptr task) } void -LedgerReplayer::gotSkipList(LedgerHeader const& info, boost::intrusive_ptr const& item) +LedgerReplayer::gotSkipList( + LedgerHeader const& info, + boost::intrusive_ptr const& item) { std::shared_ptr skipList = {}; { @@ -149,7 +164,9 @@ LedgerReplayer::gotSkipList(LedgerHeader const& info, boost::intrusive_ptr>&& txns) +LedgerReplayer::gotReplayDelta( + LedgerHeader const& info, + std::map>&& txns) { std::shared_ptr delta = {}; { @@ -175,8 +192,8 @@ LedgerReplayer::sweep() auto const start = std::chrono::steady_clock::now(); { std::lock_guard lock(mtx_); - JLOG(j_.debug()) << "Sweeping, LedgerReplayer has " << tasks_.size() << " tasks, " << skipLists_.size() - << " skipLists, and " << deltas_.size() << " deltas."; + JLOG(j_.debug()) << "Sweeping, LedgerReplayer has " << tasks_.size() << " tasks, " + << skipLists_.size() << " skipLists, and " << deltas_.size() << " deltas."; tasks_.erase( std::remove_if( @@ -200,16 +217,19 @@ LedgerReplayer::sweep() it = subTasks.erase(it); } else + { ++it; + } } }; removeCannotLocked(skipLists_); removeCannotLocked(deltas_); } - JLOG(j_.debug()) - << " LedgerReplayer sweep lock duration " - << std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count() - << "ms"; + JLOG(j_.debug()) << " LedgerReplayer sweep lock duration " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count() + << "ms"; } void diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index a675550537..ff8e7e55e9 100644 --- a/src/xrpld/app/ledger/detail/LedgerToJson.cpp +++ b/src/xrpld/app/ledger/detail/LedgerToJson.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -36,7 +37,8 @@ void fillJson(Json::Value& json, bool closed, LedgerHeader const& info, bool bFull, unsigned apiVersion) { json[jss::parent_hash] = to_string(info.parentHash); - json[jss::ledger_index] = (apiVersion > 1) ? Json::Value(info.seq) : Json::Value(std::to_string(info.seq)); + json[jss::ledger_index] = + (apiVersion > 1) ? Json::Value(info.seq) : Json::Value(std::to_string(info.seq)); if (closed) { @@ -73,7 +75,9 @@ void fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info) { if (!closed) + { json[jss::closed] = false; + } else { json[jss::closed] = true; @@ -119,11 +123,17 @@ fillJsonTx( // If applicable, insert delivered amount if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) + { RPC::insertDeliveredAmount( - txJson[jss::meta], fill.ledger, txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + txJson[jss::meta], + fill.ledger, + txn, + {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + } // If applicable, insert mpt issuance id - RPC::insertMPTokenIssuanceID(txJson[jss::meta], txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + RPC::insertMPTokenIssuanceID( + txJson[jss::meta], txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); } if (!fill.ledger.open()) @@ -149,8 +159,13 @@ fillJsonTx( // If applicable, insert delivered amount if (txnType == ttPAYMENT || txnType == ttCHECK_CASH) + { RPC::insertDeliveredAmount( - txJson[jss::metaData], fill.ledger, txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + txJson[jss::metaData], + fill.ledger, + txn, + {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); + } // If applicable, insert mpt issuance id RPC::insertMPTokenIssuanceID( @@ -168,7 +183,11 @@ fillJsonTx( if (account != amount.getIssuer()) { auto const ownerFunds = accountFunds( - fill.ledger, account, amount, fhIGNORE_FREEZE, beast::Journal{beast::Journal::getNullSink()}); + fill.ledger, + account, + amount, + fhIGNORE_FREEZE, + beast::Journal{beast::Journal::getNullSink()}); txJson[jss::owner_funds] = ownerFunds.getText(); } } @@ -221,9 +240,13 @@ fillJsonState(Json::Value& json, LedgerFill const& fill) obj[jss::tx_blob] = serializeHex(*sle); } else if (expanded) + { array.append(sle->getJson(JsonOptions::none)); + } else + { array.append(to_string(sle->key())); + } } } @@ -254,9 +277,13 @@ fillJsonQueue(Json::Value& json, LedgerFill const& fill) auto&& temp = fillJsonTx(fill, bBinary, bExpanded, tx.txn, nullptr); if (fill.context->apiVersion > 1) + { copyFrom(txJson, temp); + } else + { copyFrom(txJson[jss::tx], temp); + } } } @@ -267,14 +294,18 @@ fillJson(Json::Value& json, LedgerFill const& fill) // Is there a way to report this back? auto bFull = isFull(fill); if (isBinary(fill)) + { fillJsonBinary(json, !fill.ledger.open(), fill.ledger.header()); + } else + { fillJson( json, !fill.ledger.open(), fill.ledger.header(), bFull, (fill.context ? fill.context->apiVersion : RPC::apiMaximumSupportedVersion)); + } if (bFull || fill.options & LedgerFill::dumpTxrp) fillJsonTx(json, fill); @@ -306,8 +337,10 @@ getJson(LedgerFill const& fill) void copyFrom(Json::Value& to, Json::Value const& from) { - if (!to) // Short circuit this very common case. + if (!to) + { // Short circuit this very common case. to = from; + } else { // TODO: figure out if there is a way to remove this clause diff --git a/src/xrpld/app/ledger/detail/LocalTxs.cpp b/src/xrpld/app/ledger/detail/LocalTxs.cpp index 48bd80fbf2..8eb821a6c9 100644 --- a/src/xrpld/app/ledger/detail/LocalTxs.cpp +++ b/src/xrpld/app/ledger/detail/LocalTxs.cpp @@ -142,10 +142,12 @@ public: return acctSeq > seqProx; // Remove tefPAST_SEQ if (seqProx.isTicket() && acctSeq.value() <= seqProx.value()) + { // Keep ticket from the future. Note, however, that the // transaction will not be held indefinitely since LocalTxs // will only hold a transaction for a maximum of 5 ledgers. return false; + } // Ticket should have been created by now. Remove if ticket // does not exist. diff --git a/src/xrpld/app/ledger/detail/OpenLedger.cpp b/src/xrpld/app/ledger/detail/OpenLedger.cpp index 698862d4da..a26751bc64 100644 --- a/src/xrpld/app/ledger/detail/OpenLedger.cpp +++ b/src/xrpld/app/ledger/detail/OpenLedger.cpp @@ -1,19 +1,22 @@ #include #include -#include #include -#include #include #include +#include #include #include +#include #include namespace xrpl { -OpenLedger::OpenLedger(std::shared_ptr const& ledger, CachedSLEs& cache, beast::Journal journal) +OpenLedger::OpenLedger( + std::shared_ptr const& ledger, + CachedSLEs& cache, + beast::Journal journal) : j_(journal), cache_(cache), current_(create(ledger->rules(), ledger)) { } @@ -79,9 +82,8 @@ OpenLedger::accept( *ledger, boost::adaptors::transform( current_->txs, - [](std::pair, std::shared_ptr> const& p) { - return p.first; - }), + [](std::pair, std::shared_ptr> const& + p) { return p.first; }), retries, flags, j_); @@ -137,7 +139,8 @@ OpenLedger::accept( std::shared_ptr OpenLedger::create(Rules const& rules, std::shared_ptr const& ledger) { - return std::make_shared(open_ledger, rules, std::make_shared(ledger, cache_)); + return std::make_shared( + open_ledger, rules, std::make_shared(ledger, cache_)); } auto diff --git a/src/xrpld/app/ledger/detail/SkipListAcquire.cpp b/src/xrpld/app/ledger/detail/SkipListAcquire.cpp index 353b4fdbc8..2191ef965a 100644 --- a/src/xrpld/app/ledger/detail/SkipListAcquire.cpp +++ b/src/xrpld/app/ledger/detail/SkipListAcquire.cpp @@ -54,7 +54,8 @@ SkipListAcquire::trigger(std::size_t limit, ScopedLockType& sl) peerSet_->addPeers( limit, [this](auto peer) { - return peer->supportsFeature(ProtocolFeature::LedgerReplay) && peer->hasLedger(hash_, 0); + return peer->supportsFeature(ProtocolFeature::LedgerReplay) && + peer->hasLedger(hash_, 0); }, [this](auto peer) { if (peer->supportsFeature(ProtocolFeature::LedgerReplay)) @@ -68,7 +69,8 @@ SkipListAcquire::trigger(std::size_t limit, ScopedLockType& sl) } else { - JLOG(journal_.trace()) << "Add a no feature peer " << peer->id() << " for " << hash_; + JLOG(journal_.trace()) + << "Add a no feature peer " << peer->id() << " for " << hash_; if (++noFeaturePeerCount_ >= LedgerReplayParameters::MAX_NO_FEATURE_PEER_COUNT) { JLOG(journal_.debug()) << "Fall back for " << hash_; @@ -106,7 +108,9 @@ SkipListAcquire::pmDowncast() } void -SkipListAcquire::processData(std::uint32_t ledgerSeq, boost::intrusive_ptr const& item) +SkipListAcquire::processData( + std::uint32_t ledgerSeq, + boost::intrusive_ptr const& item) { XRPL_ASSERT(ledgerSeq != 0 && item, "xrpl::SkipListAcquire::processData : valid inputs"); ScopedLockType sl(mtx_); @@ -123,7 +127,7 @@ SkipListAcquire::processData(std::uint32_t ledgerSeq, boost::intrusive_ptr const& ledger, ScopedLockType& sl) { - if (auto const hashIndex = ledger->read(keylet::skip()); hashIndex && hashIndex->isFieldPresent(sfHashes)) + if (auto const hashIndex = ledger->read(keylet::skip()); + hashIndex && hashIndex->isFieldPresent(sfHashes)) { auto const& slist = hashIndex->getFieldV256(sfHashes).value(); if (!slist.empty()) @@ -170,7 +175,10 @@ SkipListAcquire::retrieveSkipList(std::shared_ptr const& ledger, S } void -SkipListAcquire::onSkipListAcquired(std::vector const& skipList, std::uint32_t ledgerSeq, ScopedLockType& sl) +SkipListAcquire::onSkipListAcquired( + std::vector const& skipList, + std::uint32_t ledgerSeq, + ScopedLockType& sl) { complete_ = true; data_ = std::make_shared(ledgerSeq, skipList); diff --git a/src/xrpld/app/ledger/detail/SkipListAcquire.h b/src/xrpld/app/ledger/detail/SkipListAcquire.h index f3bfb60bee..5a79e8ae5c 100644 --- a/src/xrpld/app/ledger/detail/SkipListAcquire.h +++ b/src/xrpld/app/ledger/detail/SkipListAcquire.h @@ -113,7 +113,10 @@ private: * @param sl lock. this function must be called with the lock */ void - onSkipListAcquired(std::vector const& skipList, std::uint32_t ledgerSeq, ScopedLockType& sl); + onSkipListAcquired( + std::vector const& skipList, + std::uint32_t ledgerSeq, + ScopedLockType& sl); /** * Call the OnSkipListDataCB callbacks diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp index 0ce6de0d34..e2d4409717 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp @@ -52,17 +52,20 @@ TimeoutCounter::queueJob(ScopedLockType& sl) if (isDone()) return; if (queueJobParameter_.jobLimit && - app_.getJobQueue().getJobCountTotal(queueJobParameter_.jobType) >= queueJobParameter_.jobLimit) + app_.getJobQueue().getJobCountTotal(queueJobParameter_.jobType) >= + queueJobParameter_.jobLimit) { - JLOG(journal_.debug()) << "Deferring " << queueJobParameter_.jobName << " timer due to load"; + JLOG(journal_.debug()) << "Deferring " << queueJobParameter_.jobName + << " timer due to load"; setTimer(sl); return; } - app_.getJobQueue().addJob(queueJobParameter_.jobType, queueJobParameter_.jobName, [wptr = pmDowncast()]() { - if (auto sptr = wptr.lock(); sptr) - sptr->invokeOnTimer(); - }); + app_.getJobQueue().addJob( + queueJobParameter_.jobType, queueJobParameter_.jobName, [wptr = pmDowncast()]() { + if (auto sptr = wptr.lock(); sptr) + sptr->invokeOnTimer(); + }); } void diff --git a/src/xrpld/app/ledger/detail/TransactionAcquire.cpp b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp index f838a912eb..18c5066064 100644 --- a/src/xrpld/app/ledger/detail/TransactionAcquire.cpp +++ b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp @@ -3,8 +3,10 @@ #include #include #include -#include +#include + +#include #include namespace xrpl { @@ -19,8 +21,16 @@ enum { MAX_TIMEOUTS = 20, }; -TransactionAcquire::TransactionAcquire(Application& app, uint256 const& hash, std::unique_ptr peerSet) - : TimeoutCounter(app, hash, TX_ACQUIRE_TIMEOUT, {jtTXN_DATA, "TxAcq", {}}, app.journal("TransactionAcquire")) +TransactionAcquire::TransactionAcquire( + Application& app, + uint256 const& hash, + std::unique_ptr peerSet) + : TimeoutCounter( + app, + hash, + TX_ACQUIRE_TIMEOUT, + {jtTXN_DATA, "TxAcq", {}}, + app.journal("TransactionAcquire")) , mHaveRoot(false) , mPeerSet(std::move(peerSet)) { @@ -50,8 +60,9 @@ TransactionAcquire::done() // not be called. That's fine. According to David the giveSet() call // just updates the consensus and related structures when we acquire // a transaction set. No need to update them if we're shutting down. - app_.getJobQueue().addJob( - jtTXN_DATA, "ComplAcquire", [pap, hash, map]() { pap->getInboundTransactions().giveSet(hash, map, true); }); + app_.getJobQueue().addJob(jtTXN_DATA, "ComplAcquire", [pap, hash, map]() { + pap->getInboundTransactions().giveSet(hash, map, true); + }); } } @@ -93,7 +104,8 @@ TransactionAcquire::trigger(std::shared_ptr const& peer) if (!mHaveRoot) { - JLOG(journal_.trace()) << "TransactionAcquire::trigger " << (peer ? "havePeer" : "noPeer") << " no root"; + JLOG(journal_.trace()) << "TransactionAcquire::trigger " << (peer ? "havePeer" : "noPeer") + << " no root"; protocol::TMGetLedger tmGL; tmGL.set_ledgerhash(hash_.begin(), hash_.size()); tmGL.set_itype(protocol::liTS_CANDIDATE); @@ -118,9 +130,13 @@ TransactionAcquire::trigger(std::shared_ptr const& peer) if (nodes.empty()) { if (mMap->isValid()) + { complete_ = true; + } else + { failed_ = true; + } done(); return; @@ -172,13 +188,17 @@ TransactionAcquire::takeNodes( if (d.first.isRoot()) { if (mHaveRoot) + { JLOG(journal_.debug()) << "Got root TXS node, already have it"; + } else if (!mMap->addRootNode(SHAMapHash{hash_}, d.second, nullptr).isGood()) { JLOG(journal_.warn()) << "TX acquire got bad root node"; } else + { mHaveRoot = true; + } } else if (!mMap->addKnownNode(d.first, d.second, &sf).isGood()) { @@ -193,7 +213,8 @@ TransactionAcquire::takeNodes( } catch (std::exception const& ex) { - JLOG(journal_.error()) << "Peer " << peer->id() << " sent us junky transaction node data: " << ex.what(); + JLOG(journal_.error()) << "Peer " << peer->id() + << " sent us junky transaction node data: " << ex.what(); return SHAMapAddNode::invalid(); } } @@ -202,7 +223,9 @@ void TransactionAcquire::addPeers(std::size_t limit) { mPeerSet->addPeers( - limit, [this](auto peer) { return peer->hasTxSet(hash_); }, [this](auto peer) { trigger(peer); }); + limit, + [this](auto peer) { return peer->hasTxSet(hash_); }, + [this](auto peer) { trigger(peer); }); } void @@ -220,8 +243,7 @@ TransactionAcquire::stillNeed() { ScopedLockType sl(mtx_); - if (timeouts_ > NORM_TIMEOUTS) - timeouts_ = NORM_TIMEOUTS; + timeouts_ = std::min(timeouts_, NORM_TIMEOUTS); failed_ = false; } diff --git a/src/xrpld/app/ledger/detail/TransactionAcquire.h b/src/xrpld/app/ledger/detail/TransactionAcquire.h index 4345c74fc6..a39f6a2f35 100644 --- a/src/xrpld/app/ledger/detail/TransactionAcquire.h +++ b/src/xrpld/app/ledger/detail/TransactionAcquire.h @@ -19,7 +19,9 @@ public: ~TransactionAcquire() = default; SHAMapAddNode - takeNodes(std::vector> const& data, std::shared_ptr const&); + takeNodes( + std::vector> const& data, + std::shared_ptr const&); void init(int startPeers); diff --git a/src/xrpld/app/ledger/detail/TransactionMaster.cpp b/src/xrpld/app/ledger/detail/TransactionMaster.cpp index 834234dd4e..b45dbd5ba9 100644 --- a/src/xrpld/app/ledger/detail/TransactionMaster.cpp +++ b/src/xrpld/app/ledger/detail/TransactionMaster.cpp @@ -9,7 +9,13 @@ namespace xrpl { TransactionMaster::TransactionMaster(Application& app) - : mApp(app), mCache("TransactionCache", 65536, std::chrono::minutes{30}, stopwatch(), mApp.journal("TaggedCache")) + : mApp(app) + , mCache( + "TransactionCache", + 65536, + std::chrono::minutes{30}, + stopwatch(), + mApp.journal("TaggedCache")) { } @@ -57,7 +63,10 @@ TransactionMaster::fetch(uint256 const& txnID, error_code_i& ec) } std::variant, std::shared_ptr>, TxSearched> -TransactionMaster::fetch(uint256 const& txnID, ClosedInterval const& range, error_code_i& ec) +TransactionMaster::fetch( + uint256 const& txnID, + ClosedInterval const& range, + error_code_i& ec) { using TxPair = std::pair, std::shared_ptr>; @@ -78,7 +87,10 @@ TransactionMaster::fetch(uint256 const& txnID, ClosedInterval const& r } std::shared_ptr -TransactionMaster::fetch(boost::intrusive_ptr const& item, SHAMapNodeType type, std::uint32_t uCommitLedger) +TransactionMaster::fetch( + boost::intrusive_ptr const& item, + SHAMapNodeType type, + std::uint32_t uCommitLedger) { std::shared_ptr txn; auto iTx = fetch_from_cache(item->key()); diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 2c0d3c2b82..b922c2dc2b 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -6,50 +6,53 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #include #include #include -#include -#include -#include -#include #include #include #include #include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include #include -#include #include #include #include #include +#include #include #include #include #include +#include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include +#include +#include +#include #include #include @@ -89,7 +92,7 @@ private: beast::Journal journal, std::chrono::milliseconds interval, boost::asio::io_context& ios) - : m_event(ev), m_journal(journal), m_probe(interval, ios), lastSample_{} + : m_event(ev), m_journal(journal), m_probe(interval, ios) { } @@ -158,6 +161,7 @@ public: NodeCache m_tempNodeCache; CachedSLEs cachedSLEs_; + std::unique_ptr networkIDService_; std::optional> nodeIdentity_; ValidatorKeys const validatorKeys_; @@ -165,8 +169,7 @@ public: std::unique_ptr m_nodeStore; NodeFamily nodeFamily_; - // VFALCO TODO Make OrderBookDB abstract - OrderBookDB m_orderBookDB; + std::unique_ptr m_orderBookDB; std::unique_ptr m_pathRequests; std::unique_ptr m_ledgerMaster; std::unique_ptr ledgerCleaner_; @@ -192,7 +195,7 @@ public: boost::asio::steady_timer sweepTimer_; boost::asio::steady_timer entropyTimer_; - std::unique_ptr mRelationalDatabase; + std::optional relationalDatabase_; std::unique_ptr mWalletDB; std::unique_ptr overlay_; std::optional trapTxID_; @@ -235,78 +238,110 @@ public: //-------------------------------------------------------------------------- - ApplicationImp(std::unique_ptr config, std::unique_ptr logs, std::unique_ptr timeKeeper) + ApplicationImp( + std::unique_ptr config, + std::unique_ptr logs, + std::unique_ptr timeKeeper) : BasicApp(numberOfThreads(*config)) , config_(std::move(config)) , logs_(std::move(logs)) , timeKeeper_(std::move(timeKeeper)) - , instanceCookie_(1 + rand_int(crypto_prng(), std::numeric_limits::max() - 1)) + , instanceCookie_( + 1 + rand_int(crypto_prng(), std::numeric_limits::max() - 1)) , m_journal(logs_->journal("Application")) // PerfLog must be started before any other threads are launched. - , perfLog_(perf::make_PerfLog( - perf::setup_PerfLog(config_->section("perf"), config_->CONFIG_DIR), - *this, - logs_->journal("PerfLog"), - [this] { signalStop("PerfLog"); })) + , perfLog_( + perf::make_PerfLog( + perf::setup_PerfLog(config_->section("perf"), config_->CONFIG_DIR), + *this, + logs_->journal("PerfLog"), + [this] { signalStop("PerfLog"); })) , m_txMaster(*this) - , m_collectorManager(make_CollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector"))) + , m_collectorManager( + make_CollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector"))) - , m_jobQueue(std::make_unique( - [](std::unique_ptr const& config) { - if (config->standalone() && !config->FORCE_MULTI_THREAD) - return 1; + , m_jobQueue( + std::make_unique( + [](std::unique_ptr const& config) { + if (config->standalone() && !config->FORCE_MULTI_THREAD) + return 1; - if (config->WORKERS) - return config->WORKERS; + if (config->WORKERS) + return config->WORKERS; - auto count = static_cast(std::thread::hardware_concurrency()); + auto count = static_cast(std::thread::hardware_concurrency()); - // Be more aggressive about the number of threads to use - // for the job queue if the server is configured as - // "large" or "huge" if there are enough cores. - if (config->NODE_SIZE >= 4 && count >= 16) - count = 6 + std::min(count, 8); - else if (config->NODE_SIZE >= 3 && count >= 8) - count = 4 + std::min(count, 6); - else - count = 2 + std::min(count, 4); + // Be more aggressive about the number of threads to use + // for the job queue if the server is configured as + // "large" or "huge" if there are enough cores. + if (config->NODE_SIZE >= 4 && count >= 16) + { + count = 6 + std::min(count, 8); + } + else if (config->NODE_SIZE >= 3 && count >= 8) + { + count = 4 + std::min(count, 6); + } + else + { + count = 2 + std::min(count, 4); + } - return count; - }(config_), - m_collectorManager->group("jobq"), - logs_->journal("JobQueue"), - *logs_, - *perfLog_)) + return count; + }(config_), + m_collectorManager->group("jobq"), + logs_->journal("JobQueue"), + *logs_, + *perfLog_)) , m_nodeStoreScheduler(*m_jobQueue) - , m_shaMapStore(make_SHAMapStore(*this, m_nodeStoreScheduler, logs_->journal("SHAMapStore"))) + , m_shaMapStore( + make_SHAMapStore(*this, m_nodeStoreScheduler, logs_->journal("SHAMapStore"))) - , m_tempNodeCache("NodeCache", 16384, std::chrono::seconds{90}, stopwatch(), logs_->journal("TaggedCache")) + , m_tempNodeCache( + "NodeCache", + 16384, + std::chrono::seconds{90}, + stopwatch(), + logs_->journal("TaggedCache")) - , cachedSLEs_("Cached SLEs", 0, std::chrono::minutes(1), stopwatch(), logs_->journal("CachedSLEs")) + , cachedSLEs_( + "Cached SLEs", + 0, + std::chrono::minutes(1), + stopwatch(), + logs_->journal("CachedSLEs")) + + , networkIDService_(std::make_unique(config_->NETWORK_ID)) , validatorKeys_(*config_, m_journal) - , m_resourceManager(Resource::make_Manager(m_collectorManager->collector(), logs_->journal("Resource"))) + , m_resourceManager( + Resource::make_Manager(m_collectorManager->collector(), logs_->journal("Resource"))) - , m_nodeStore(m_shaMapStore->makeNodeStore(config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4)) + , m_nodeStore(m_shaMapStore->makeNodeStore( + config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4)) , nodeFamily_(*this, *m_collectorManager) - , m_orderBookDB(*this) + , m_orderBookDB(make_OrderBookDB(*this, {config_->PATH_SEARCH_MAX, config_->standalone()})) , m_pathRequests( - std::make_unique(*this, logs_->journal("PathRequest"), m_collectorManager->collector())) + std::make_unique( + *this, + logs_->journal("PathRequest"), + m_collectorManager->collector())) - , m_ledgerMaster(std::make_unique( - *this, - stopwatch(), - m_collectorManager->collector(), - logs_->journal("LedgerMaster"))) + , m_ledgerMaster( + std::make_unique( + *this, + stopwatch(), + m_collectorManager->collector(), + logs_->journal("LedgerMaster"))) , ledgerCleaner_(make_LedgerCleaner(*this, logs_->journal("LedgerCleaner"))) @@ -318,9 +353,15 @@ public: , m_inboundTransactions(make_InboundTransactions( *this, m_collectorManager->collector(), - [this](std::shared_ptr const& set, bool fromAcquire) { gotTXSet(set, fromAcquire); })) + [this](std::shared_ptr const& set, bool fromAcquire) { + gotTXSet(set, fromAcquire); + })) - , m_ledgerReplayer(std::make_unique(*this, *m_inboundLedgers, make_PeerSetBuilder(*this))) + , m_ledgerReplayer( + std::make_unique( + *this, + *m_inboundLedgers, + make_PeerSetBuilder(*this))) , m_acceptedLedgerCache( "AcceptedLedger", @@ -344,19 +385,21 @@ public: , cluster_(std::make_unique(logs_->journal("Overlay"))) - , peerReservations_(std::make_unique(logs_->journal("PeerReservationTable"))) + , peerReservations_( + std::make_unique(logs_->journal("PeerReservationTable"))) , validatorManifests_(std::make_unique(logs_->journal("ManifestCache"))) , publisherManifests_(std::make_unique(logs_->journal("ManifestCache"))) - , validators_(std::make_unique( - *validatorManifests_, - *publisherManifests_, - *timeKeeper_, - config_->legacy("database_path"), - logs_->journal("ValidatorList"), - config_->VALIDATION_QUORUM)) + , validators_( + std::make_unique( + *validatorManifests_, + *publisherManifests_, + *timeKeeper_, + config_->legacy("database_path"), + logs_->journal("ValidatorList"), + config_->VALIDATION_QUORUM)) , validatorSites_(std::make_unique(*this)) @@ -563,7 +606,7 @@ public: } void - gotTXSet(std::shared_ptr const& set, bool fromAcquire) + gotTXSet(std::shared_ptr const& set, bool fromAcquire) const { if (set) m_networkOPs->mapComplete(set, fromAcquire); @@ -614,7 +657,7 @@ public: OrderBookDB& getOrderBookDB() override { - return m_orderBookDB; + return *m_orderBookDB; } PathRequests& @@ -629,6 +672,12 @@ public: return cachedSLEs_; } + NetworkIDService& + getNetworkIDService() override + { + return *networkIDService_; + } + AmendmentTable& getAmendmentTable() override { @@ -731,10 +780,10 @@ public: getRelationalDatabase() override { XRPL_ASSERT( - mRelationalDatabase, + relationalDatabase_, "xrpl::ApplicationImp::getRelationalDatabase : non-null " "relational database"); - return *mRelationalDatabase; + return *relationalDatabase_; } DatabaseCon& @@ -744,6 +793,24 @@ public: return *mWalletDB; } + virtual Fees + getFees() const override + { + XRPL_ASSERT(config_, "xrpl::ApplicationImp::getFees : non-null config"); + + auto const& f1(config_->FEES); + + Fees f2; + f2.base = f1.reference_fee; + f2.reserve = f1.account_reserve; + f2.increment = f1.owner_reserve; + f2.extensionComputeLimit = f1.extension_compute_limit; + f2.extensionSizeLimit = f1.extension_size_limit; + f2.gasPrice = f1.gas_price; + + return f2; + } + bool serverOkay(std::string& reason) override; @@ -762,7 +829,7 @@ public: try { - mRelationalDatabase = RelationalDatabase::init(*this, *config_, *m_jobQueue); + relationalDatabase_.emplace(setup_RelationalDatabase(*this, *config_, *m_jobQueue)); // wallet database auto setup = setup_DatabaseCon(*config_, m_journal); @@ -780,21 +847,22 @@ public: } bool - initNodeStore() + initNodeStore() const { if (config_->doImport) { auto j = logs_->journal("NodeObject"); NodeStore::DummyScheduler dummyScheduler; - std::unique_ptr source = NodeStore::Manager::instance().make_Database( - megabytes(config_->getValueFor(SizedItem::burstSize, std::nullopt)), - dummyScheduler, - 0, - config_->section(ConfigSection::importNodeDatabase()), - j); + std::unique_ptr source = + NodeStore::Manager::instance().make_Database( + megabytes(config_->getValueFor(SizedItem::burstSize, std::nullopt)), + dummyScheduler, + 0, + config_->section(ConfigSection::importNodeDatabase()), + j); - JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" << m_nodeStore->getName() - << "'."; + JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" + << m_nodeStore->getName() << "'."; using namespace std::chrono; auto const start = steady_clock::now(); @@ -802,7 +870,8 @@ public: m_nodeStore->importDatabase(*source); auto const elapsed = duration_cast(steady_clock::now() - start); - JLOG(j.warn()) << "Node import from '" << source->getName() << "' took " << elapsed.count() << " seconds."; + JLOG(j.warn()) << "Node import from '" << source->getName() << "' took " + << elapsed.count() << " seconds."; } return true; @@ -824,23 +893,27 @@ public: setSweepTimer() { // Only start the timer if waitHandlerCounter_ is not yet joined. - if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) { - if (e.value() == boost::system::errc::success) - { - m_jobQueue->addJob(jtSWEEP, "sweep", [this]() { doSweep(); }); - } - // Recover as best we can if an unexpected error occurs. - if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted) - { - // Try again later and hope for the best. - JLOG(m_journal.error()) << "Sweep timer got error '" << e.message() << "'. Restarting timer."; - setSweepTimer(); - } - })) + if (auto optionalCountedHandler = + waitHandlerCounter_.wrap([this](boost::system::error_code const& e) { + if (e.value() == boost::system::errc::success) + { + m_jobQueue->addJob(jtSWEEP, "sweep", [this]() { doSweep(); }); + } + // Recover as best we can if an unexpected error occurs. + if (e.value() != boost::system::errc::success && + e.value() != boost::asio::error::operation_aborted) + { + // Try again later and hope for the best. + JLOG(m_journal.error()) + << "Sweep timer got error '" << e.message() << "'. Restarting timer."; + setSweepTimer(); + } + })) { using namespace std::chrono; sweepTimer_.expires_after( - seconds{config_->SWEEP_INTERVAL.value_or(config_->getValueFor(SizedItem::sweepInterval))}); + seconds{config_->SWEEP_INTERVAL.value_or( + config_->getValueFor(SizedItem::sweepInterval))}); sweepTimer_.async_wait(std::move(*optionalCountedHandler)); } } @@ -849,20 +922,23 @@ public: setEntropyTimer() { // Only start the timer if waitHandlerCounter_ is not yet joined. - if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) { - if (e.value() == boost::system::errc::success) - { - crypto_prng().mix_entropy(); - setEntropyTimer(); - } - // Recover as best we can if an unexpected error occurs. - if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted) - { - // Try again later and hope for the best. - JLOG(m_journal.error()) << "Entropy timer got error '" << e.message() << "'. Restarting timer."; - setEntropyTimer(); - } - })) + if (auto optionalCountedHandler = + waitHandlerCounter_.wrap([this](boost::system::error_code const& e) { + if (e.value() == boost::system::errc::success) + { + crypto_prng().mix_entropy(); + setEntropyTimer(); + } + // Recover as best we can if an unexpected error occurs. + if (e.value() != boost::system::errc::success && + e.value() != boost::asio::error::operation_aborted) + { + // Try again later and hope for the best. + JLOG(m_journal.error()) << "Entropy timer got error '" << e.message() + << "'. Restarting timer."; + setEntropyTimer(); + } + })) { using namespace std::chrono_literals; entropyTimer_.expires_after(5min); @@ -873,7 +949,9 @@ public: void doSweep() { - if (!config_->standalone() && !getRelationalDatabase().transactionDbHasSpace(*config_)) + XRPL_ASSERT( + relationalDatabase_, "xrpl::ApplicationImp::doSweep : non-null relational database"); + if (!config_->standalone() && !relationalDatabase_->transactionDbHasSpace(*config_)) { signalStop("Out of transaction DB space"); } @@ -883,23 +961,28 @@ public: // have listeners register for "onSweep ()" notification. { - std::shared_ptr const fullBelowCache = nodeFamily_.getFullBelowCache(); + std::shared_ptr const fullBelowCache = + nodeFamily_.getFullBelowCache(); - std::shared_ptr const treeNodeCache = nodeFamily_.getTreeNodeCache(); + std::shared_ptr const treeNodeCache = + nodeFamily_.getTreeNodeCache(); std::size_t const oldFullBelowSize = fullBelowCache->size(); std::size_t const oldTreeNodeSize = treeNodeCache->size(); nodeFamily_.sweep(); - JLOG(m_journal.debug()) << "NodeFamily::FullBelowCache sweep. Size before: " << oldFullBelowSize - << "; size after: " << fullBelowCache->size(); + JLOG(m_journal.debug()) + << "NodeFamily::FullBelowCache sweep. Size before: " << oldFullBelowSize + << "; size after: " << fullBelowCache->size(); - JLOG(m_journal.debug()) << "NodeFamily::TreeNodeCache sweep. Size before: " << oldTreeNodeSize - << "; size after: " << treeNodeCache->size(); + JLOG(m_journal.debug()) + << "NodeFamily::TreeNodeCache sweep. Size before: " << oldTreeNodeSize + << "; size after: " << treeNodeCache->size(); } { - TaggedCache const& masterTxCache = getMasterTransaction().getCache(); + TaggedCache const& masterTxCache = + getMasterTransaction().getCache(); std::size_t const oldMasterTxSize = masterTxCache.size(); @@ -913,8 +996,9 @@ public: getLedgerMaster().sweep(); - JLOG(m_journal.debug()) << "LedgerMaster sweep. Size before: " << oldLedgerMasterCacheSize - << "; size after: " << getLedgerMaster().getFetchPackCacheSize(); + JLOG(m_journal.debug()) + << "LedgerMaster sweep. Size before: " << oldLedgerMasterCacheSize + << "; size after: " << getLedgerMaster().getFetchPackCacheSize(); } { // NodeCache == TaggedCache @@ -933,25 +1017,30 @@ public: getValidations().expire(m_journal); - JLOG(m_journal.debug()) << "Validations Current expire. Size before: " << oldCurrentCacheSize - << "; size after: " << getValidations().sizeOfCurrentCache(); + JLOG(m_journal.debug()) + << "Validations Current expire. Size before: " << oldCurrentCacheSize + << "; size after: " << getValidations().sizeOfCurrentCache(); - JLOG(m_journal.debug()) << "Validations SeqEnforcer expire. Size before: " << oldSizeSeqEnforcesSize - << "; size after: " << getValidations().sizeOfSeqEnforcersCache(); + JLOG(m_journal.debug()) + << "Validations SeqEnforcer expire. Size before: " << oldSizeSeqEnforcesSize + << "; size after: " << getValidations().sizeOfSeqEnforcersCache(); - JLOG(m_journal.debug()) << "Validations ByLedger expire. Size before: " << oldByLedgerSize - << "; size after: " << getValidations().sizeOfByLedgerCache(); + JLOG(m_journal.debug()) + << "Validations ByLedger expire. Size before: " << oldByLedgerSize + << "; size after: " << getValidations().sizeOfByLedgerCache(); - JLOG(m_journal.debug()) << "Validations BySequence expire. Size before: " << oldBySequenceSize - << "; size after: " << getValidations().sizeOfBySequenceCache(); + JLOG(m_journal.debug()) + << "Validations BySequence expire. Size before: " << oldBySequenceSize + << "; size after: " << getValidations().sizeOfBySequenceCache(); } { std::size_t const oldInboundLedgersSize = getInboundLedgers().cacheSize(); getInboundLedgers().sweep(); - JLOG(m_journal.debug()) << "InboundLedgers sweep. Size before: " << oldInboundLedgersSize - << "; size after: " << getInboundLedgers().cacheSize(); + JLOG(m_journal.debug()) + << "InboundLedgers sweep. Size before: " << oldInboundLedgersSize + << "; size after: " << getInboundLedgers().cacheSize(); } { size_t const oldTasksSize = getLedgerReplayer().tasksSize(); @@ -963,19 +1052,22 @@ public: JLOG(m_journal.debug()) << "LedgerReplayer tasks sweep. Size before: " << oldTasksSize << "; size after: " << getLedgerReplayer().tasksSize(); - JLOG(m_journal.debug()) << "LedgerReplayer deltas sweep. Size before: " << oldDeltasSize - << "; size after: " << getLedgerReplayer().deltasSize(); + JLOG(m_journal.debug()) + << "LedgerReplayer deltas sweep. Size before: " << oldDeltasSize + << "; size after: " << getLedgerReplayer().deltasSize(); - JLOG(m_journal.debug()) << "LedgerReplayer skipLists sweep. Size before: " << oldSkipListsSize - << "; size after: " << getLedgerReplayer().skipListsSize(); + JLOG(m_journal.debug()) + << "LedgerReplayer skipLists sweep. Size before: " << oldSkipListsSize + << "; size after: " << getLedgerReplayer().skipListsSize(); } { std::size_t const oldAcceptedLedgerSize = m_acceptedLedgerCache.size(); m_acceptedLedgerCache.sweep(); - JLOG(m_journal.debug()) << "AcceptedLedgerCache sweep. Size before: " << oldAcceptedLedgerSize - << "; size after: " << m_acceptedLedgerCache.size(); + JLOG(m_journal.debug()) + << "AcceptedLedgerCache sweep. Size before: " << oldAcceptedLedgerSize + << "; size after: " << m_acceptedLedgerCache.size(); } { std::size_t const oldCachedSLEsSize = cachedSLEs_.size(); @@ -986,6 +1078,8 @@ public: << "; size after: " << cachedSLEs_.size(); } + mallocTrim("doSweep", m_journal); + // Set timer to do another sweep later. setSweepTimer(); } @@ -1002,6 +1096,12 @@ public: return trapTxID_; } + size_t + getNumberOfThreads() const override + { + return get_number_of_threads(); + } + private: // For a newly-started validator, this is the greatest persisted ledger // and new validations must be greater than this. @@ -1017,10 +1117,20 @@ private: loadLedgerFromFile(std::string const& ledgerID); bool - loadOldLedger(std::string const& ledgerID, bool replay, bool isFilename, std::optional trapTxID); + loadOldLedger( + std::string const& ledgerID, + bool replay, + bool isFilename, + std::optional trapTxID); void setMaxDisallowedLedger(); + + Application& + app() override + { + return *this; + } }; //------------------------------------------------------------------------------ @@ -1109,25 +1219,35 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) Section const& upVoted = config_->section(SECTION_AMENDMENTS); m_amendmentTable = make_AmendmentTable( - *this, config().AMENDMENT_MAJORITY_TIME, supported, upVoted, downVoted, logs_->journal("Amendments")); + *this, + config().AMENDMENT_MAJORITY_TIME, + supported, + upVoted, + downVoted, + logs_->journal("Amendments")); } Pathfinder::initPathTable(); auto const startUp = config_->START_UP; JLOG(m_journal.debug()) << "startUp: " << startUp; - if (startUp == Config::FRESH) + if (startUp == StartUpType::FRESH) { JLOG(m_journal.info()) << "Starting new Ledger"; startGenesisLedger(); } - else if (startUp == Config::LOAD || startUp == Config::LOAD_FILE || startUp == Config::REPLAY) + else if ( + startUp == StartUpType::LOAD || startUp == StartUpType::LOAD_FILE || + startUp == StartUpType::REPLAY) { JLOG(m_journal.info()) << "Loading specified Ledger"; if (!loadOldLedger( - config_->START_LEDGER, startUp == Config::REPLAY, startUp == Config::LOAD_FILE, config_->TRAP_TX_HASH)) + config_->START_LEDGER, + startUp == StartUpType::REPLAY, + startUp == StartUpType::LOAD_FILE, + config_->TRAP_TX_HASH)) { JLOG(m_journal.error()) << "The specified ledger could not be loaded."; if (config_->FAST_LOAD) @@ -1142,7 +1262,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) } } } - else if (startUp == Config::NETWORK) + else if (startUp == StartUpType::NETWORK) { // This should probably become the default once we have a stable // network. @@ -1161,7 +1281,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) m_ledgerMaster->setLedgerRangePresent(forcedRange->first, forcedRange->second); } - m_orderBookDB.setup(getLedgerMaster().getCurrentLedger()); + m_orderBookDB->setup(getLedgerMaster().getCurrentLedger()); nodeIdentity_ = getNodeIdentity(*this, cmdline); @@ -1250,7 +1370,8 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) { try { - auto setup = setup_ServerHandler(*config_, beast::logstream{m_journal.error()}); + beast::logstream logStream{m_journal.error()}; + auto setup = setup_ServerHandler(*config_, logStream); setup.makeContexts(); serverHandler_->setup(setup, m_journal); fixConfigPorts(*config_, serverHandler_->endpoints()); @@ -1315,7 +1436,8 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) if (!jrReader.parse(cmd, jvCommand)) { - JLOG(m_journal.fatal()) << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd; + JLOG(m_journal.fatal()) + << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd; } if (!config_->quiet()) @@ -1441,8 +1563,9 @@ ApplicationImp::run() validatorSites_->stop(); // TODO Store manifests in manifests.sqlite instead of wallet.db - validatorManifests_->save( - getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { return validators().listed(pubKey); }); + validatorManifests_->save(getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { + return validators().listed(pubKey); + }); publisherManifests_->save(getWalletDB(), "PublisherManifests", [this](PublicKey const& pubKey) { return validators().trustedPublisher(pubKey); @@ -1474,7 +1597,9 @@ ApplicationImp::signalStop(std::string msg) if (!isTimeToStop.test_and_set(std::memory_order_acquire)) { if (msg.empty()) + { JLOG(m_journal.warn()) << "Server stopping"; + } else JLOG(m_journal.warn()) << "Server stopping: " << msg; @@ -1528,8 +1653,9 @@ ApplicationImp::fdRequired() const void ApplicationImp::startGenesisLedger() { - std::vector const initialAmendments = - (config_->START_UP == Config::FRESH) ? m_amendmentTable->getDesired() : std::vector{}; + std::vector const initialAmendments = (config_->START_UP == StartUpType::FRESH) + ? m_amendmentTable->getDesired() + : std::vector{}; std::shared_ptr const genesis = std::make_shared(create_genesis, *config_, initialAmendments, nodeFamily_); @@ -1651,7 +1777,8 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) } if (ledger.get().isMember("total_coins")) { - totalDrops = beast::lexicalCastThrow(ledger.get()["total_coins"].asString()); + totalDrops = + beast::lexicalCastThrow(ledger.get()["total_coins"].asString()); } ledger = ledger.get()["accountState"]; @@ -1749,7 +1876,12 @@ ApplicationImp::loadOldLedger( { // Try to build the ledger from the back end auto il = std::make_shared( - *this, hash, 0, InboundLedger::Reason::GENERIC, stopwatch(), make_DummyPeerSet(*this)); + *this, + hash, + 0, + InboundLedger::Reason::GENERIC, + stopwatch(), + make_DummyPeerSet(*this)); if (il->checkLocal()) loadLedger = il->getLedger(); } @@ -1762,7 +1894,7 @@ ApplicationImp::loadOldLedger( else { // assume by sequence - std::uint32_t index; + std::uint32_t index = 0; if (beast::lexicalCastChecked(index, ledgerID)) loadLedger = loadByIndex(index, *this); @@ -1826,7 +1958,8 @@ ApplicationImp::loadOldLedger( "get the older rules.\n*** CONTINUING ***\n"; } - JLOG(m_journal.info()) << "Loading ledger " << loadLedger->header().hash << " seq:" << loadLedger->header().seq; + JLOG(m_journal.info()) << "Loading ledger " << loadLedger->header().hash + << " seq:" << loadLedger->header().seq; if (loadLedger->header().accountHash.isZero()) { @@ -1990,9 +2123,13 @@ Application::Application() : beast::PropertyStream::Source("app") //------------------------------------------------------------------------------ std::unique_ptr -make_Application(std::unique_ptr config, std::unique_ptr logs, std::unique_ptr timeKeeper) +make_Application( + std::unique_ptr config, + std::unique_ptr logs, + std::unique_ptr timeKeeper) { - return std::make_unique(std::move(config), std::move(logs), std::move(timeKeeper)); + return std::make_unique( + std::move(config), std::move(logs), std::move(timeKeeper)); } void diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 53cc264ad4..0000ae010b 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -1,10 +1,10 @@ #pragma once #include -#include #include #include +#include #include #include #include @@ -112,8 +112,6 @@ public: public: Application(); - virtual ~Application() = default; - virtual bool setup(boost::program_options::variables_map const& options) = 0; @@ -127,8 +125,6 @@ public: checkSigs() const = 0; virtual void checkSigs(bool) = 0; - virtual bool - isStopping() const = 0; // // --- @@ -138,14 +134,9 @@ public: virtual std::uint64_t instanceID() const = 0; - virtual Logs& - logs() = 0; virtual Config& config() = 0; - virtual boost::asio::io_context& - getIOContext() = 0; - virtual std::pair const& nodeIdentity() = 0; @@ -158,27 +149,24 @@ public: virtual bool serverOkay(std::string& reason) = 0; - virtual beast::Journal - journal(std::string const& name) = 0; - /* Returns the number of file descriptors the application needs */ virtual int fdRequired() const = 0; - /** Retrieve the "wallet database" */ - virtual DatabaseCon& - getWalletDB() = 0; - /** Ensure that a newly-started validator does not sign proposals older * than the last ledger it persisted. */ virtual LedgerIndex getMaxDisallowedLedger() = 0; - virtual std::optional const& - trapTxID() const = 0; + /** Returns the number of io_context (I/O worker) threads used by the application. */ + virtual size_t + getNumberOfThreads() const = 0; }; std::unique_ptr -make_Application(std::unique_ptr config, std::unique_ptr logs, std::unique_ptr timeKeeper); +make_Application( + std::unique_ptr config, + std::unique_ptr logs, + std::unique_ptr timeKeeper); } // namespace xrpl diff --git a/src/xrpld/app/main/BasicApp.h b/src/xrpld/app/main/BasicApp.h index 278c255af3..19f07d1e5b 100644 --- a/src/xrpld/app/main/BasicApp.h +++ b/src/xrpld/app/main/BasicApp.h @@ -23,4 +23,10 @@ public: { return io_context_; } + + size_t + get_number_of_threads() const + { + return threads_.size(); + } }; diff --git a/src/xrpld/app/main/CollectorManager.cpp b/src/xrpld/app/main/CollectorManager.cpp index 7a3306b60b..353a49de91 100644 --- a/src/xrpld/app/main/CollectorManager.cpp +++ b/src/xrpld/app/main/CollectorManager.cpp @@ -17,7 +17,8 @@ public: if (server == "statsd") { - beast::IP::Endpoint const address(beast::IP::Endpoint::from_string(get(params, "address"))); + beast::IP::Endpoint const address( + beast::IP::Endpoint::from_string(get(params, "address"))); std::string const& prefix(get(params, "prefix")); m_collector = beast::insight::StatsDCollector::New(address, prefix, journal); diff --git a/src/xrpld/app/main/GRPCServer.cpp b/src/xrpld/app/main/GRPCServer.cpp index 9b07728eb8..a9810ae22b 100644 --- a/src/xrpld/app/main/GRPCServer.cpp +++ b/src/xrpld/app/main/GRPCServer.cpp @@ -24,11 +24,12 @@ getEndpoint(std::string const& peer) peerClean = peer.substr(first + 1); } - std::optional endpoint = beast::IP::Endpoint::from_string_checked(peerClean); + std::optional endpoint = + beast::IP::Endpoint::from_string_checked(peerClean); if (endpoint) return beast::IP::to_asio_endpoint(endpoint.value()); } - catch (std::exception const&) + catch (std::exception const&) // NOLINT(bugprone-empty-catch) { } return {}; @@ -69,7 +70,15 @@ std::shared_ptr GRPCServerImpl::CallData::clone() { return std::make_shared>( - service_, cq_, app_, bindListener_, handler_, forward_, requiredCondition_, loadType_, secureGatewayIPs_); + service_, + cq_, + app_, + bindListener_, + handler_, + forward_, + requiredCondition_, + loadType_, + secureGatewayIPs_); } template @@ -90,8 +99,8 @@ GRPCServerImpl::CallData::process() // ensures that finished is always true when this CallData object // is returned as a tag in handleRpcs(), after sending the response finished_ = true; - auto coro = - app_.getJobQueue().postCoro(JobType::jtRPC, "gRPC-Client", [thisShared](std::shared_ptr coro) { + auto coro = app_.getJobQueue().postCoro( + JobType::jtRPC, "gRPC-Client", [thisShared](std::shared_ptr coro) { thisShared->process(coro); }); @@ -113,7 +122,8 @@ GRPCServerImpl::CallData::process(std::shared_ptr::process(std::shared_ptr::getRole(bool isUnlimited) { if (isUnlimited) + { return Role::IDENTIFIED; - else - return Role::USER; + } + + return Role::USER; } template @@ -288,7 +301,8 @@ GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app), journal_(app_.jour return; try { - boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(*optIp), std::stoi(*optPort)); + boost::asio::ip::tcp::endpoint endpoint( + boost::asio::ip::make_address(*optIp), std::stoi(*optPort)); std::stringstream ss; ss << endpoint; @@ -362,15 +376,17 @@ GRPCServerImpl::handleRpcs() std::vector> requests = setupListeners(); auto erase = [&requests](Processor* ptr) { - auto it = std::find_if( - requests.begin(), requests.end(), [ptr](std::shared_ptr& sPtr) { return sPtr.get() == ptr; }); + auto it = + std::find_if(requests.begin(), requests.end(), [ptr](std::shared_ptr& sPtr) { + return sPtr.get() == ptr; + }); BOOST_ASSERT(it != requests.end()); it->swap(requests.back()); requests.pop_back(); }; - void* tag; // uniquely identifies a request. - bool ok; + void* tag = nullptr; // uniquely identifies a request. + bool ok = false; // Block waiting to read the next event from the completion queue. The // event is uniquely identified by its tag, which in this case is the // memory address of a CallData instance. @@ -428,60 +444,71 @@ GRPCServerImpl::setupListeners() auto addToRequests = [&requests](auto callData) { requests.push_back(std::move(callData)); }; { - using cd = CallData; + using cd = + CallData; - addToRequests(std::make_shared( - service_, - *cq_, - app_, - &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedger, - doLedgerGrpc, - &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedger, - RPC::NO_CONDITION, - Resource::feeMediumBurdenRPC, - secureGatewayIPs_)); + addToRequests( + std::make_shared( + service_, + *cq_, + app_, + &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedger, + doLedgerGrpc, + &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedger, + RPC::NO_CONDITION, + Resource::feeMediumBurdenRPC, + secureGatewayIPs_)); } { - using cd = CallData; + using cd = CallData< + org::xrpl::rpc::v1::GetLedgerDataRequest, + org::xrpl::rpc::v1::GetLedgerDataResponse>; - addToRequests(std::make_shared( - service_, - *cq_, - app_, - &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerData, - doLedgerDataGrpc, - &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerData, - RPC::NO_CONDITION, - Resource::feeMediumBurdenRPC, - secureGatewayIPs_)); + addToRequests( + std::make_shared( + service_, + *cq_, + app_, + &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerData, + doLedgerDataGrpc, + &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerData, + RPC::NO_CONDITION, + Resource::feeMediumBurdenRPC, + secureGatewayIPs_)); } { - using cd = CallData; + using cd = CallData< + org::xrpl::rpc::v1::GetLedgerDiffRequest, + org::xrpl::rpc::v1::GetLedgerDiffResponse>; - addToRequests(std::make_shared( - service_, - *cq_, - app_, - &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerDiff, - doLedgerDiffGrpc, - &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerDiff, - RPC::NO_CONDITION, - Resource::feeMediumBurdenRPC, - secureGatewayIPs_)); + addToRequests( + std::make_shared( + service_, + *cq_, + app_, + &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerDiff, + doLedgerDiffGrpc, + &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerDiff, + RPC::NO_CONDITION, + Resource::feeMediumBurdenRPC, + secureGatewayIPs_)); } { - using cd = CallData; + using cd = CallData< + org::xrpl::rpc::v1::GetLedgerEntryRequest, + org::xrpl::rpc::v1::GetLedgerEntryResponse>; - addToRequests(std::make_shared( - service_, - *cq_, - app_, - &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerEntry, - doLedgerEntryGrpc, - &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerEntry, - RPC::NO_CONDITION, - Resource::feeMediumBurdenRPC, - secureGatewayIPs_)); + addToRequests( + std::make_shared( + service_, + *cq_, + app_, + &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerEntry, + doLedgerEntryGrpc, + &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerEntry, + RPC::NO_CONDITION, + Resource::feeMediumBurdenRPC, + secureGatewayIPs_)); } return requests; } diff --git a/src/xrpld/app/main/GRPCServer.h b/src/xrpld/app/main/GRPCServer.h index fdac2b6484..037c91df93 100644 --- a/src/xrpld/app/main/GRPCServer.h +++ b/src/xrpld/app/main/GRPCServer.h @@ -3,13 +3,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include @@ -89,8 +89,11 @@ private: static unsigned constexpr apiVersion = 1; template - using Forward = std::function< - grpc::Status(org::xrpl::rpc::v1::XRPLedgerAPIService::Stub*, grpc::ClientContext*, Request, Response*)>; + using Forward = std::function; public: explicit GRPCServerImpl(Application& app); @@ -123,7 +126,8 @@ public: private: // Class encompassing the state and logic needed to serve a request. template - class CallData : public Processor, public std::enable_shared_from_this> + class CallData : public Processor, + public std::enable_shared_from_this> { private: // The means of communication with the gRPC runtime for an asynchronous diff --git a/src/xrpld/app/main/LoadManager.cpp b/src/xrpld/app/main/LoadManager.cpp index d1336b20d9..f0fc4085ac 100644 --- a/src/xrpld/app/main/LoadManager.cpp +++ b/src/xrpld/app/main/LoadManager.cpp @@ -1,10 +1,10 @@ #include #include -#include -#include #include #include +#include +#include #include #include @@ -13,7 +13,7 @@ namespace xrpl { LoadManager::LoadManager(Application& app, beast::Journal journal) - : app_(app), journal_(journal), lastHeartbeat_(), armed_(false) + : app_(app), journal_(journal), armed_(false) { } @@ -115,7 +115,8 @@ LoadManager::run() { if (timeSpentStalled < stallFatalLogMessageTimeLimit) { - JLOG(journal_.warn()) << "Server stalled for " << timeSpentStalled.count() << " seconds."; + JLOG(journal_.warn()) + << "Server stalled for " << timeSpentStalled.count() << " seconds."; if (app_.getJobQueue().isOverloaded()) { @@ -124,7 +125,8 @@ LoadManager::run() } else { - JLOG(journal_.fatal()) << "Server stalled for " << timeSpentStalled.count() << " seconds."; + JLOG(journal_.fatal()) + << "Server stalled for " << timeSpentStalled.count() << " seconds."; JLOG(journal_.fatal()) << "JobQueue: " << app_.getJobQueue().getJson(0); } } @@ -145,7 +147,8 @@ LoadManager::run() bool change = false; if (app_.getJobQueue().isOverloaded()) { - JLOG(journal_.info()) << "Raising local fee (JQ overload): " << app_.getJobQueue().getJson(0); + JLOG(journal_.info()) << "Raising local fee (JQ overload): " + << app_.getJobQueue().getJson(0); change = app_.getFeeTrack().raiseLocalFee(); } else diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index aaf7af95ab..03f8076ed1 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -7,7 +6,9 @@ #include #include +#include #include +#include #include #include @@ -60,7 +61,7 @@ adjustDescriptorLimit(int needed, beast::Journal j) { #ifdef RLIMIT_NOFILE // Get the current limit, then adjust it to what we need. - struct rlimit rl; + struct rlimit rl{}; int available = 0; @@ -68,9 +69,13 @@ adjustDescriptorLimit(int needed, beast::Journal j) { // If the limit is infinite, then we are good. if (rl.rlim_cur == RLIM_INFINITY) + { available = needed; + } else + { available = rl.rlim_cur; + } if (available < needed) { @@ -86,11 +91,11 @@ adjustDescriptorLimit(int needed, beast::Journal j) if (needed > available) { - j.fatal() << "Insufficient number of file descriptors: " << needed << " are needed, but only " << available - << " are available."; + j.fatal() << "Insufficient number of file descriptors: " << needed + << " are needed, but only " << available << " are available."; - std::cerr << "Insufficient number of file descriptors: " << needed << " are needed, but only " << available - << " are available.\n"; + std::cerr << "Insufficient number of file descriptors: " << needed + << " are needed, but only " << available << " are available.\n"; return false; } @@ -102,7 +107,7 @@ adjustDescriptorLimit(int needed, beast::Journal j) void printHelp(po::options_description const& desc) { - std::cerr << systemName() << "d [options] \n" + std::cerr << systemName() << " [options] \n" << desc << std::endl << "Commands: \n" " account_currencies []\n" @@ -194,8 +199,10 @@ public: operator()(beast::unit_test::suite_info const& s) { for (auto& sel : selectors_) + { if (sel(s)) return true; + } return false; } @@ -224,7 +231,8 @@ anyMissing(Runner& runner, multi_selector const& pred) { auto const missing = pred.size() - runner.suites(); runner.add_failures(missing); - std::cout << "Failed: " << missing << " filters did not match any existing test suites" << std::endl; + std::cout << "Failed: " << missing << " filters did not match any existing test suites" + << std::endl; return true; } return false; @@ -275,7 +283,10 @@ runUnitTests( } for (std::size_t i = 0; i < num_jobs; ++i) - children.emplace_back(boost::process::v1::exe = exe_name, boost::process::v1::args = args); + { + children.emplace_back( + boost::process::v1::exe = exe_name, boost::process::v1::args = args); + } int bad_child_exits = 0; int terminated_child_exits = 0; @@ -302,17 +313,15 @@ runUnitTests( return EXIT_FAILURE; return EXIT_SUCCESS; } - else - { - // child - multi_runner_child runner{num_jobs, quiet, log}; - runner.arg(argument); - auto const anyFailed = runner.run_multi(multi_selector(pattern)); - if (anyFailed) - return EXIT_FAILURE; - return EXIT_SUCCESS; - } + // child + multi_runner_child runner{num_jobs, quiet, log}; + runner.arg(argument); + auto const anyFailed = runner.run_multi(multi_selector(pattern)); + + if (anyFailed) + return EXIT_FAILURE; + return EXIT_SUCCESS; } #endif // ENABLE_TESTS @@ -355,10 +364,12 @@ run(int argc, char** argv) po::options_description data("Ledger/Data Options"); data.add_options()("import", importText.c_str())( - "ledger", po::value(), "Load the specified ledger and start from the value given.")( + "ledger", + po::value(), + "Load the specified ledger and start from the value given.")( "ledgerfile", po::value(), "Load the specified ledger file.")( - "load", "Load the current ledger from the local DB.")("net", "Get the initial ledger from the network.")( - "replay", "Replay a ledger close.")( + "load", "Load the current ledger from the local DB.")( + "net", "Get the initial ledger from the network.")("replay", "Replay a ledger close.")( "trap_tx_hash", po::value(), "Trap a specific transaction during replay.")( "start", "Start from a fresh Ledger.")("vacuum", "VACUUM the transaction db.")( "valid", "Consider the initial ledger a valid network ledger."); @@ -395,12 +406,15 @@ run(int argc, char** argv) "is made available to each suite that runs. Interpretation of the " "argument is handled individually by any suite that accesses it -- " "as such, it typically only make sense to provide this when running " - "a single suite.")("unittest-ipv6", "Use IPv6 localhost when running unittests (default is IPv4).")( + "a single suite.")( + "unittest-ipv6", "Use IPv6 localhost when running unittests (default is IPv4).")( "unittest-log", "Force unit test log message output. Only useful in combination with " "--quiet, in which case log messages will print but suite/case names " "will not.")( - "unittest-jobs", po::value(), "Number of unittest jobs to run in parallel (child processes)."); + "unittest-jobs", + po::value(), + "Number of unittest jobs to run in parallel (child processes)."); #endif // ENABLE_TESTS // These are hidden options, not intended to be shown in the usage/help @@ -460,21 +474,17 @@ run(int argc, char** argv) return 1; } - if (vm.count("help")) + if (vm.contains("help")) { printHelp(desc); return 0; } - if (vm.count("version")) + if (vm.contains("version")) { std::cout << "rippled version " << BuildInfo::getVersionString() << std::endl; -#ifdef GIT_COMMIT_HASH - std::cout << "Git commit hash: " << GIT_COMMIT_HASH << std::endl; -#endif -#ifdef GIT_BRANCH - std::cout << "Git build branch: " << GIT_BRANCH << std::endl; -#endif + std::cout << "Git commit hash: " << xrpl::git::getCommitHash() << std::endl; + std::cout << "Git build branch: " << xrpl::git::getBuildBranch() << std::endl; return 0; } @@ -489,53 +499,53 @@ run(int argc, char** argv) // Run the unit tests if requested. // The unit tests will exit the application with an appropriate return code. // - if (vm.count("unittest")) + if (vm.contains("unittest")) { std::string argument; - if (vm.count("unittest-arg")) + if (vm.contains("unittest-arg")) argument = vm["unittest-arg"].as(); std::size_t numJobs = 1; bool unittestChild = false; - if (vm.count("unittest-jobs")) + if (vm.contains("unittest-jobs")) numJobs = std::max(numJobs, vm["unittest-jobs"].as()); - unittestChild = bool(vm.count("unittest-child")); + unittestChild = vm.contains("unittest-child"); return runUnitTests( vm["unittest"].as(), argument, - bool(vm.count("quiet")), - bool(vm.count("unittest-log")), + vm.contains("quiet"), + vm.contains("unittest-log"), unittestChild, - bool(vm.count("unittest-ipv6")), + vm.contains("unittest-ipv6"), numJobs, argc, argv); } // LCOV_EXCL_START - else + + if (vm.contains("unittest-jobs")) { - if (vm.count("unittest-jobs")) - { - // unittest jobs only makes sense with `unittest` - std::cerr << "rippled: '--unittest-jobs' specified without " - "'--unittest'.\n"; - std::cerr << "To run the unit tests the '--unittest' option must " - "be present.\n"; - return 1; - } + // unittest jobs only makes sense with `unittest` + std::cerr << "rippled: '--unittest-jobs' specified without " + "'--unittest'.\n"; + std::cerr << "To run the unit tests the '--unittest' option must " + "be present.\n"; + return 1; } + #endif // ENABLE_TESTS auto config = std::make_unique(); - auto configFile = vm.count("conf") ? vm["conf"].as() : std::string(); + auto configFile = vm.contains("conf") ? vm["conf"].as() : std::string(); // config file, quiet flag. - config->setup(configFile, bool(vm.count("quiet")), bool(vm.count("silent")), bool(vm.count("standalone"))); + config->setup( + configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone")); - if (vm.count("vacuum")) + if (vm.contains("vacuum")) { if (config->standalone()) { @@ -565,7 +575,9 @@ run(int argc, char** argv) auto const r = [&vm]() -> std::vector { std::vector strVec; boost::split( - strVec, vm["force_ledger_present_range"].as(), boost::algorithm::is_any_of(",")); + strVec, + vm["force_ledger_present_range"].as(), + boost::algorithm::is_any_of(",")); std::vector result; for (auto& s : strVec) { @@ -599,21 +611,21 @@ run(int argc, char** argv) } } - if (vm.count("start")) + if (vm.contains("start")) { - config->START_UP = Config::FRESH; + config->START_UP = StartUpType::FRESH; } - if (vm.count("import")) + if (vm.contains("import")) config->doImport = true; - if (vm.count("ledger")) + if (vm.contains("ledger")) { config->START_LEDGER = vm["ledger"].as(); - if (vm.count("replay")) + if (vm.contains("replay")) { - config->START_UP = Config::REPLAY; - if (vm.count("trap_tx_hash")) + config->START_UP = StartUpType::REPLAY; + if (vm.contains("trap_tx_hash")) { uint256 tmp = {}; auto hash = vm["trap_tx_hash"].as(); @@ -631,43 +643,45 @@ run(int argc, char** argv) } } else - config->START_UP = Config::LOAD; + { + config->START_UP = StartUpType::LOAD; + } } - else if (vm.count("ledgerfile")) + else if (vm.contains("ledgerfile")) { config->START_LEDGER = vm["ledgerfile"].as(); - config->START_UP = Config::LOAD_FILE; + config->START_UP = StartUpType::LOAD_FILE; } - else if (vm.count("load") || config->FAST_LOAD) + else if (vm.contains("load") || config->FAST_LOAD) { - config->START_UP = Config::LOAD; + config->START_UP = StartUpType::LOAD; } - if (vm.count("trap_tx_hash") && vm.count("replay") == 0) + if (vm.contains("trap_tx_hash") && !vm.contains("replay")) { std::cerr << "Cannot use trap option without replay option" << std::endl; return -1; } - if (vm.count("net") && !config->FAST_LOAD) + if (vm.contains("net") && !config->FAST_LOAD) { - if ((config->START_UP == Config::LOAD) || (config->START_UP == Config::REPLAY)) + if ((config->START_UP == StartUpType::LOAD) || (config->START_UP == StartUpType::REPLAY)) { std::cerr << "Net and load/replay options are incompatible" << std::endl; return -1; } - config->START_UP = Config::NETWORK; + config->START_UP = StartUpType::NETWORK; } - if (vm.count("valid")) + if (vm.contains("valid")) { config->START_VALID = true; } // Override the RPC destination IP address. This must // happen after the config file is loaded. - if (vm.count("rpc_ip")) + if (vm.contains("rpc_ip")) { auto endpoint = beast::IP::Endpoint::from_string_checked(vm["rpc_ip"].as()); if (!endpoint) @@ -679,7 +693,7 @@ run(int argc, char** argv) if (endpoint->port() == 0) { std::cerr << "No port specified in rpc_ip.\n"; - if (vm.count("rpc_port")) + if (vm.contains("rpc_port")) { std::cerr << "WARNING: using deprecated rpc_port param.\n"; try @@ -695,13 +709,15 @@ run(int argc, char** argv) } } else + { return -1; + } } config->rpc_ip = std::move(*endpoint); } - if (vm.count("quorum")) + if (vm.contains("quorum")) { try { @@ -722,26 +738,31 @@ run(int argc, char** argv) using namespace beast::severities; Severity thresh = kInfo; - if (vm.count("quiet")) + if (vm.contains("quiet")) + { thresh = kFatal; - else if (vm.count("verbose")) + } + else if (vm.contains("verbose")) + { thresh = kTrace; + } auto logs = std::make_unique(thresh); // No arguments. Run server. - if (!vm.count("parameters")) + if (!vm.contains("parameters")) { // TODO: this comment can be removed in a future release - // say 1.7 or higher if (config->had_trailing_comments()) { - JLOG(logs->journal("Application").warn()) << "Trailing comments were seen in your config file. " - << "The treatment of inline/trailing comments has changed " - "recently. " - << "Any `#` characters NOT intended to delimit comments should " - "be " - << "preceded by a \\"; + JLOG(logs->journal("Application").warn()) + << "Trailing comments were seen in your config file. " + << "The treatment of inline/trailing comments has changed " + "recently. " + << "Any `#` characters NOT intended to delimit comments should " + "be " + << "preceded by a \\"; } // We want at least 1024 file descriptors. We'll @@ -749,10 +770,11 @@ run(int argc, char** argv) if (!adjustDescriptorLimit(1024, logs->journal("Application"))) return -1; - if (vm.count("debug")) + if (vm.contains("debug")) setDebugLogSink(logs->makeSink("Debug", beast::severities::kTrace)); - auto app = make_Application(std::move(config), std::move(logs), std::make_unique()); + auto app = + make_Application(std::move(config), std::move(logs), std::make_unique()); if (!app->setup(vm)) return -1; @@ -773,7 +795,8 @@ run(int argc, char** argv) // We have an RPC command to process: beast::setCurrentThreadName("rippled: rpc"); - return RPCCall::fromCommandLine(*config, vm["parameters"].as>(), *logs); + return RPCCall::fromCommandLine( + *config, vm["parameters"].as>(), *logs); // LCOV_EXCL_STOP } diff --git a/src/xrpld/app/main/NodeIdentity.cpp b/src/xrpld/app/main/NodeIdentity.cpp index b585b80b5b..bc2c943b10 100644 --- a/src/xrpld/app/main/NodeIdentity.cpp +++ b/src/xrpld/app/main/NodeIdentity.cpp @@ -1,9 +1,10 @@ #include #include -#include #include #include +#include + namespace xrpl { std::pair @@ -11,7 +12,7 @@ getNodeIdentity(Application& app, boost::program_options::variables_map const& c { std::optional seed; - if (cmdline.count("nodeid")) + if (cmdline.contains("nodeid")) { seed = parseGenericSeed(cmdline["nodeid"].as(), false); @@ -36,7 +37,7 @@ getNodeIdentity(Application& app, boost::program_options::variables_map const& c auto db = app.getWalletDB().checkoutDb(); - if (cmdline.count("newnodeid") != 0) + if (cmdline.contains("newnodeid")) clearNodeIdentity(*db); return getNodeIdentity(*db); diff --git a/src/xrpld/app/main/NodeStoreScheduler.cpp b/src/xrpld/app/main/NodeStoreScheduler.cpp index 1b3ff4ab03..1ca8e80523 100644 --- a/src/xrpld/app/main/NodeStoreScheduler.cpp +++ b/src/xrpld/app/main/NodeStoreScheduler.cpp @@ -27,7 +27,9 @@ NodeStoreScheduler::onFetch(NodeStore::FetchReport const& report) return; jobQueue_.addLoadEvents( - report.fetchType == NodeStore::FetchType::async ? jtNS_ASYNC_READ : jtNS_SYNC_READ, 1, report.elapsed); + report.fetchType == NodeStore::FetchType::async ? jtNS_ASYNC_READ : jtNS_SYNC_READ, + 1, + report.elapsed); } void diff --git a/src/xrpld/app/misc/AmendmentTableImpl.h b/src/xrpld/app/misc/AmendmentTableImpl.h new file mode 100644 index 0000000000..fe7c067d5a --- /dev/null +++ b/src/xrpld/app/misc/AmendmentTableImpl.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace xrpl { + +std::unique_ptr +make_AmendmentTable( + ServiceRegistry& registry, + std::chrono::seconds majorityTime, + std::vector const& supported, + Section const& enabled, + Section const& vetoed, + beast::Journal journal); + +} // namespace xrpl diff --git a/src/xrpld/app/misc/CanonicalTXSet.cpp b/src/xrpld/app/misc/CanonicalTXSet.cpp index 6e6102afe7..f971c100eb 100644 --- a/src/xrpld/app/misc/CanonicalTXSet.cpp +++ b/src/xrpld/app/misc/CanonicalTXSet.cpp @@ -32,8 +32,12 @@ CanonicalTXSet::accountKey(AccountID const& account) void CanonicalTXSet::insert(std::shared_ptr const& txn) { - map_.insert(std::make_pair( - Key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID()), txn)); + map_.insert( + std::make_pair( + Key(accountKey(txn->getAccountID(sfAccount)), + txn->getSeqProxy(), + txn->getTransactionID()), + txn)); } std::shared_ptr @@ -57,7 +61,8 @@ CanonicalTXSet::popAcctTransaction(std::shared_ptr const& tx) Key const after(effectiveAccount, seqProxy, beast::zero); auto const itrNext{map_.lower_bound(after)}; if (itrNext != map_.end() && itrNext->first.getAccount() == effectiveAccount && - (!itrNext->second->getSeqProxy().isSeq() || itrNext->second->getSeqProxy().value() == seqProxy.value() + 1)) + (!itrNext->second->getSeqProxy().isSeq() || + itrNext->second->getSeqProxy().value() == seqProxy.value() + 1)) { result = std::move(itrNext->second); map_.erase(itrNext); diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 41b70cfcfc..eb0ce5617b 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -55,7 +55,8 @@ VotableValue::getVotes() const for (auto const& [key, val] : voteMap_) { // Take most voted value between current and target, inclusive - if ((key <= std::max(target_, current_)) && (key >= std::min(target_, current_)) && (val > weight)) + if ((key <= std::max(target_, current_)) && (key >= std::min(target_, current_)) && + (val > weight)) { ourVote = key; weight = val; @@ -90,7 +91,8 @@ public: //-------------------------------------------------------------------------- -FeeVoteImpl::FeeVoteImpl(FeeSetup const& setup, beast::Journal journal) : target_(setup), journal_(journal) +FeeVoteImpl::FeeVoteImpl(FeeSetup const& setup, beast::Journal journal) + : target_(setup), journal_(journal) { } @@ -112,7 +114,11 @@ FeeVoteImpl::doValidation(Fees const& lastFees, Rules const& rules, STValidation { vote(lastFees.base, target_.reference_fee, "base fee", sfBaseFeeDrops); vote(lastFees.reserve, target_.account_reserve, "base reserve", sfReserveBaseDrops); - vote(lastFees.increment, target_.owner_reserve, "reserve increment", sfReserveIncrementDrops); + vote( + lastFees.increment, + target_.owner_reserve, + "reserve increment", + sfReserveIncrementDrops); } else { @@ -134,8 +140,14 @@ FeeVoteImpl::doValidation(Fees const& lastFees, Rules const& rules, STValidation }; voteAndConvert(lastFees.base, target_.reference_fee, to64, "base fee", sfBaseFee); - voteAndConvert(lastFees.reserve, target_.account_reserve, to32, "base reserve", sfReserveBase); - voteAndConvert(lastFees.increment, target_.owner_reserve, to32, "reserve increment", sfReserveIncrement); + voteAndConvert( + lastFees.reserve, target_.account_reserve, to32, "base reserve", sfReserveBase); + voteAndConvert( + lastFees.increment, + target_.owner_reserve, + to32, + "reserve increment", + sfReserveIncrement); } if (rules.enabled(featureSmartEscrow)) { @@ -144,7 +156,11 @@ FeeVoteImpl::doValidation(Fees const& lastFees, Rules const& rules, STValidation target_.extension_compute_limit, "extension compute limit", sfExtensionComputeLimit); - vote(lastFees.extensionSizeLimit, target_.extension_size_limit, "extension size limit", sfExtensionSizeLimit); + vote( + lastFees.extensionSizeLimit, + target_.extension_size_limit, + "extension size limit", + sfExtensionSizeLimit); vote(lastFees.gasPrice, target_.gas_price, "gas price", sfGasPrice); } } @@ -157,7 +173,8 @@ FeeVoteImpl::doVoting( { // LCL must be flag ledger XRPL_ASSERT( - lastClosedLedger && isFlagLedger(lastClosedLedger->seq()), "xrpl::FeeVoteImpl::doVoting : has a flag ledger"); + lastClosedLedger && isFlagLedger(lastClosedLedger->seq()), + "xrpl::FeeVoteImpl::doVoting : has a flag ledger"); detail::VotableValue baseFeeVote(lastClosedLedger->fees().base, target_.reference_fee); @@ -168,7 +185,8 @@ FeeVoteImpl::doVoting( detail::VotableValue extensionComputeVote( lastClosedLedger->fees().extensionComputeLimit, target_.extension_compute_limit); - detail::VotableValue extensionSizeVote(lastClosedLedger->fees().extensionSizeLimit, target_.extension_size_limit); + detail::VotableValue extensionSizeVote( + lastClosedLedger->fees().extensionSizeLimit, target_.extension_size_limit); detail::VotableValue gasPriceVote(lastClosedLedger->fees().gasPrice, target_.gas_price); @@ -182,9 +200,13 @@ FeeVoteImpl::doVoting( { auto const vote = field->xrp(); if (isLegalAmountSigned(vote)) + { value.addVote(vote); + } else + { value.noVote(); + } } else { @@ -212,12 +234,16 @@ FeeVoteImpl::doVoting( auto const vote = *field; if (vote <= std::numeric_limits::max() && isLegalAmountSigned(XRPAmount{unsafe_cast(vote)})) + { value.addVote(XRPAmount{unsafe_cast(vote)}); + } else + { // Invalid amounts will be treated as if they're // not provided. Don't throw because this value is // provided by an external entity. value.noVote(); + } } else { @@ -273,11 +299,11 @@ FeeVoteImpl::doVoting( auto const seq = lastClosedLedger->header().seq + 1; // add transactions to our position - if (baseFee.second || baseReserve.second || incReserve.second || extensionCompute.second || extensionSize.second || - gasPrice.second) + if (baseFee.second || baseReserve.second || incReserve.second || extensionCompute.second || + extensionSize.second || gasPrice.second) { - JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" << baseReserve.first << "/" - << incReserve.first; + JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" + << baseReserve.first << "/" << incReserve.first; STTx feeTx(ttFEE, [=, &rules](auto& obj) { obj[sfAccount] = AccountID(); @@ -293,8 +319,10 @@ FeeVoteImpl::doVoting( // Without the featureXRPFees amendment, these fields are // required. obj[sfBaseFee] = baseFee.first.dropsAs(baseFeeVote.current()); - obj[sfReserveBase] = baseReserve.first.dropsAs(baseReserveVote.current()); - obj[sfReserveIncrement] = incReserve.first.dropsAs(incReserveVote.current()); + obj[sfReserveBase] = + baseReserve.first.dropsAs(baseReserveVote.current()); + obj[sfReserveIncrement] = + incReserve.first.dropsAs(incReserveVote.current()); obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED; } if (rules.enabled(featureSmartEscrow)) @@ -312,7 +340,8 @@ FeeVoteImpl::doVoting( Serializer s; feeTx.add(s); - if (!initialPosition->addGiveItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(txID, s.slice()))) + if (!initialPosition->addGiveItem( + SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(txID, s.slice()))) { JLOG(journal_.warn()) << "Ledger already had fee change"; } diff --git a/src/xrpld/app/misc/NegativeUNLVote.cpp b/src/xrpld/app/misc/NegativeUNLVote.cpp index d4a64a34dd..8f0ba5255a 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.cpp +++ b/src/xrpld/app/misc/NegativeUNLVote.cpp @@ -52,7 +52,7 @@ NegativeUNLVote::doVoting( { auto nid = calcNodeID(k); negUnlNodeIDs.emplace(nid); - if (!nidToKeyMap.count(nid)) + if (!nidToKeyMap.contains(nid)) { nidToKeyMap.emplace(nid, k); } @@ -68,14 +68,16 @@ NegativeUNLVote::doVoting( if (!candidates.toDisableCandidates.empty()) { auto n = choose(prevLedger->header().hash, candidates.toDisableCandidates); - XRPL_ASSERT(nidToKeyMap.contains(n), "xrpl::NegativeUNLVote::doVoting : found node to disable"); + XRPL_ASSERT( + nidToKeyMap.contains(n), "xrpl::NegativeUNLVote::doVoting : found node to disable"); addTx(seq, nidToKeyMap.at(n), ToDisable, initialSet); } if (!candidates.toReEnableCandidates.empty()) { auto n = choose(prevLedger->header().hash, candidates.toReEnableCandidates); - XRPL_ASSERT(nidToKeyMap.contains(n), "xrpl::NegativeUNLVote::doVoting : found node to enable"); + XRPL_ASSERT( + nidToKeyMap.contains(n), "xrpl::NegativeUNLVote::doVoting : found node to enable"); addTx(seq, nidToKeyMap.at(n), ToReEnable, initialSet); } } @@ -97,7 +99,8 @@ NegativeUNLVote::addTx( Serializer s; negUnlTx.add(s); if (!initialSet->addGiveItem( - SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(negUnlTx.getTransactionID(), s.slice()))) + SHAMapNodeType::tnTRANSACTION_NM, + make_shamapitem(negUnlTx.getTransactionID(), s.slice()))) { JLOG(j_.warn()) << "N-UNL: ledger seq=" << seq << ", add ttUNL_MODIFY tx failed"; } @@ -105,7 +108,8 @@ NegativeUNLVote::addTx( { JLOG(j_.debug()) << "N-UNL: ledger seq=" << seq << ", add a ttUNL_MODIFY Tx with txID: " << negUnlTx.getTransactionID() - << ", the validator to " << (modify == ToDisable ? "disable: " : "re-enable: ") << vp; + << ", the validator to " + << (modify == ToDisable ? "disable: " : "re-enable: ") << vp; } } @@ -152,8 +156,8 @@ NegativeUNLVote::buildScoreTable( auto const numAncestors = ledgerAncestors.size(); if (numAncestors < FLAG_LEDGER_INTERVAL) { - JLOG(j_.debug()) << "N-UNL: ledger " << seq << " not enough history. Can trace back only " << numAncestors - << " ledgers."; + JLOG(j_.debug()) << "N-UNL: ledger " << seq << " not enough history. Can trace back only " + << numAncestors << " ledgers."; return {}; } @@ -168,9 +172,10 @@ NegativeUNLVote::buildScoreTable( // the score table. for (int i = 0; i < FLAG_LEDGER_INTERVAL; ++i) { - for (auto const& v : validations.getTrustedForLedger(ledgerAncestors[numAncestors - 1 - i], seq - 2 - i)) + for (auto const& v : + validations.getTrustedForLedger(ledgerAncestors[numAncestors - 1 - i], seq - 2 - i)) { - if (scoreTable.count(v->getNodeID())) + if (scoreTable.contains(v->getNodeID())) ++scoreTable[v->getNodeID()]; } } @@ -184,26 +189,26 @@ NegativeUNLVote::buildScoreTable( }(); if (myValidationCount < negativeUNLMinLocalValsToVote) { - JLOG(j_.debug()) << "N-UNL: ledger " << seq << ". Local node only issued " << myValidationCount - << " validations in last " << FLAG_LEDGER_INTERVAL << " ledgers." + JLOG(j_.debug()) << "N-UNL: ledger " << seq << ". Local node only issued " + << myValidationCount << " validations in last " << FLAG_LEDGER_INTERVAL + << " ledgers." << " The reliability measurement could be wrong."; return {}; } - else if (myValidationCount > negativeUNLMinLocalValsToVote && myValidationCount <= FLAG_LEDGER_INTERVAL) + if (myValidationCount > negativeUNLMinLocalValsToVote && + myValidationCount <= FLAG_LEDGER_INTERVAL) { return scoreTable; } - else - { - // cannot happen because validations.getTrustedForLedger does not - // return multiple validations of the same ledger from a validator. - JLOG(j_.error()) << "N-UNL: ledger " << seq << ". Local node issued " << myValidationCount - << " validations in last " << FLAG_LEDGER_INTERVAL << " ledgers. Too many!"; - return {}; - } + + // cannot happen because validations.getTrustedForLedger does not + // return multiple validations of the same ledger from a validator. + JLOG(j_.error()) << "N-UNL: ledger " << seq << ". Local node issued " << myValidationCount + << " validations in last " << FLAG_LEDGER_INTERVAL << " ledgers. Too many!"; + return {}; } -NegativeUNLVote::Candidates const +NegativeUNLVote::Candidates NegativeUNLVote::findAllCandidates( hash_set const& unl, hash_set const& negUnl, @@ -211,17 +216,19 @@ NegativeUNLVote::findAllCandidates( { // Compute if need to find more validators to disable auto const canAdd = [&]() -> bool { - auto const maxNegativeListed = static_cast(std::ceil(unl.size() * negativeUNLMaxListed)); + auto const maxNegativeListed = + static_cast(std::ceil(unl.size() * negativeUNLMaxListed)); std::size_t negativeListed = 0; for (auto const& n : unl) { - if (negUnl.count(n)) + if (negUnl.contains(n)) ++negativeListed; } bool const result = negativeListed < maxNegativeListed; JLOG(j_.trace()) << "N-UNL: nodeId " << myId_ << " lowWaterMark " << negativeUNLLowWaterMark - << " highWaterMark " << negativeUNLHighWaterMark << " canAdd " << result << " negativeListed " - << negativeListed << " maxNegativeListed " << maxNegativeListed; + << " highWaterMark " << negativeUNLHighWaterMark << " canAdd " << result + << " negativeListed " << negativeListed << " maxNegativeListed " + << maxNegativeListed; return result; }(); @@ -235,7 +242,8 @@ NegativeUNLVote::findAllCandidates( // (2) has less than negativeUNLLowWaterMark validations, // (3) is not in negUnl, and // (4) is not a new validator. - if (canAdd && score < negativeUNLLowWaterMark && !negUnl.count(nodeId) && !newValidators_.count(nodeId)) + if (canAdd && score < negativeUNLLowWaterMark && !negUnl.contains(nodeId) && + !newValidators_.contains(nodeId)) { JLOG(j_.trace()) << "N-UNL: toDisable candidate " << nodeId; candidates.toDisableCandidates.push_back(nodeId); @@ -244,7 +252,7 @@ NegativeUNLVote::findAllCandidates( // Find toReEnable Candidates: check if // (1) has more than negativeUNLHighWaterMark validations, // (2) is in negUnl - if (score > negativeUNLHighWaterMark && negUnl.count(nodeId)) + if (score > negativeUNLHighWaterMark && negUnl.contains(nodeId)) { JLOG(j_.trace()) << "N-UNL: toReEnable candidate " << nodeId; candidates.toReEnableCandidates.push_back(nodeId); @@ -265,7 +273,7 @@ NegativeUNLVote::findAllCandidates( { for (auto const& n : negUnl) { - if (!unl.count(n)) + if (!unl.contains(n)) { candidates.toReEnableCandidates.push_back(n); } @@ -280,7 +288,7 @@ NegativeUNLVote::newValidators(LedgerIndex seq, hash_set const& nowTrust std::lock_guard lock(mutex_); for (auto const& n : nowTrusted) { - if (newValidators_.find(n) == newValidators_.end()) + if (!newValidators_.contains(n)) { JLOG(j_.trace()) << "N-UNL: add a new validator " << n << " at ledger seq=" << seq; newValidators_[n] = seq; diff --git a/src/xrpld/app/misc/NegativeUNLVote.h b/src/xrpld/app/misc/NegativeUNLVote.h index 4019d32f13..601ac7d060 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.h +++ b/src/xrpld/app/misc/NegativeUNLVote.h @@ -125,7 +125,11 @@ private: * @param initialSet the transaction set */ void - addTx(LedgerIndex seq, PublicKey const& vp, NegativeUNLModify modify, std::shared_ptr const& initialSet); + addTx( + LedgerIndex seq, + PublicKey const& vp, + NegativeUNLModify modify, + std::shared_ptr const& initialSet); /** * Pick one candidate from a vector of candidates. @@ -168,7 +172,7 @@ private: * @param scoreTable the score table * @return the candidates to disable and the candidates to re-enable */ - Candidates const + Candidates findAllCandidates( hash_set const& unl, hash_set const& negUnl, diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index b725879f96..29be555568 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,24 +7,20 @@ #include #include #include -#include #include #include #include -#include #include -#include -#include -#include #include #include #include #include #include +#include #include -#include #include #include +#include #include #include #include @@ -38,18 +35,29 @@ #include #include #include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include #include +#include +#include #include #include @@ -161,9 +169,9 @@ class NetworkOPsImp final : public NetworkOPs struct CounterData { decltype(counters_) counters; - decltype(mode_) mode; + decltype(mode_) mode = OperatingMode::DISCONNECTED; decltype(start_) start; - decltype(initialSyncUs_) initialSyncUs; + decltype(initialSyncUs_) initialSyncUs{}; }; CounterData @@ -179,7 +187,10 @@ class NetworkOPsImp final : public NetworkOPs { ServerFeeSummary() = default; - ServerFeeSummary(XRPAmount fee, TxQ::Metrics&& escalationMetrics, LoadFeeTrack const& loadFeeTrack); + ServerFeeSummary( + XRPAmount fee, + TxQ::Metrics&& escalationMetrics, + LoadFeeTrack const& loadFeeTrack); bool operator!=(ServerFeeSummary const& b) const; @@ -197,7 +208,7 @@ class NetworkOPsImp final : public NetworkOPs public: NetworkOPsImp( - Application& app, + ServiceRegistry& registry, NetworkOPs::clock_type& clock, bool standalone, std::size_t minPeerCount, @@ -208,7 +219,7 @@ public: boost::asio::io_context& io_svc, beast::Journal journal, beast::insight::Collector::ptr const& collector) - : app_(app) + : registry_(registry) , m_journal(journal) , m_localTX(make_LocalTxs()) , mMode(start_valid ? OperatingMode::FULL : OperatingMode::DISCONNECTED) @@ -216,16 +227,21 @@ public: , clusterTimer_(io_svc) , accountHistoryTxTimer_(io_svc) , mConsensus( - app, - make_FeeVote(setup_FeeVote(app_.config().section("voting")), app_.logs().journal("FeeVote")), + registry_.app(), + make_FeeVote( + setup_FeeVote(registry_.app().config().section("voting")), + registry_.logs().journal("FeeVote")), ledgerMaster, *m_localTX, - app.getInboundTransactions(), + registry.getInboundTransactions(), beast::get_abstract_clock(), validatorKeys, - app_.logs().journal("LedgerConsensus")) - , validatorPK_(validatorKeys.keys ? validatorKeys.keys->publicKey : decltype(validatorPK_){}) - , validatorMasterPK_(validatorKeys.keys ? validatorKeys.keys->masterPublicKey : decltype(validatorMasterPK_){}) + registry_.logs().journal("LedgerConsensus")) + , validatorPK_( + validatorKeys.keys ? validatorKeys.keys->publicKey : decltype(validatorPK_){}) + , validatorMasterPK_( + validatorKeys.keys ? validatorKeys.keys->masterPublicKey + : decltype(validatorMasterPK_){}) , m_ledgerMaster(ledgerMaster) , m_job_queue(job_queue) , m_standalone(standalone) @@ -261,8 +277,11 @@ public: submitTransaction(std::shared_ptr const&) override; void - processTransaction(std::shared_ptr& transaction, bool bUnlimited, bool bLocal, FailHard failType) - override; + processTransaction( + std::shared_ptr& transaction, + bool bUnlimited, + bool bLocal, + FailHard failType) override; void processTransactionSet(CanonicalTXSet const& set) override; @@ -288,7 +307,10 @@ public: * @param failType fail_hard setting from transaction submission. */ void - doTransactionAsync(std::shared_ptr transaction, bool bUnlimited, FailHard failtype); + doTransactionAsync( + std::shared_ptr transaction, + bool bUnlimited, + FailHard failtype); private: bool @@ -356,7 +378,8 @@ private: public: bool - beginConsensus(uint256 const& networkClosed, std::unique_ptr const& clog) override; + beginConsensus(uint256 const& networkClosed, std::unique_ptr const& clog) + override; void endConsensus(std::unique_ptr const& clog) override; void @@ -439,22 +462,27 @@ public: // InfoSub::Source. // void - subAccount(InfoSub::ref ispListener, hash_set const& vnaAccountIDs, bool rt) override; + subAccount(InfoSub::ref ispListener, hash_set const& vnaAccountIDs, bool rt) + override; void - unsubAccount(InfoSub::ref ispListener, hash_set const& vnaAccountIDs, bool rt) override; + unsubAccount(InfoSub::ref ispListener, hash_set const& vnaAccountIDs, bool rt) + override; // Just remove the subscription from the tracking // not from the InfoSub. Needed for InfoSub destruction void - unsubAccountInternal(std::uint64_t seq, hash_set const& vnaAccountIDs, bool rt) override; + unsubAccountInternal(std::uint64_t seq, hash_set const& vnaAccountIDs, bool rt) + override; error_code_i subAccountHistory(InfoSub::ref ispListener, AccountID const& account) override; void - unsubAccountHistory(InfoSub::ref ispListener, AccountID const& account, bool historyOnly) override; + unsubAccountHistory(InfoSub::ref ispListener, AccountID const& account, bool historyOnly) + override; void - unsubAccountHistoryInternal(std::uint64_t seq, AccountID const& account, bool historyOnly) override; + unsubAccountHistoryInternal(std::uint64_t seq, AccountID const& account, bool historyOnly) + override; bool subLedger(InfoSub::ref ispListener, Json::Value& jvResult) override; @@ -545,7 +573,8 @@ public: } catch (boost::system::system_error const& e) { - JLOG(m_journal.error()) << "NetworkOPs: accountHistoryTxTimer cancel error: " << e.what(); + JLOG(m_journal.error()) + << "NetworkOPs: accountHistoryTxTimer cancel error: " << e.what(); } } // Make sure that any waitHandlers pending in our timers are done. @@ -623,23 +652,16 @@ private: { AccountID const accountId_; // forward - std::uint32_t forwardTxIndex_; + std::uint32_t forwardTxIndex_{0}; // separate backward and forward - std::uint32_t separationLedgerSeq_; + std::uint32_t separationLedgerSeq_{0}; // history, backward - std::uint32_t historyLastLedgerSeq_; - std::int32_t historyTxIndex_; - bool haveHistorical_; - std::atomic stopHistorical_; + std::uint32_t historyLastLedgerSeq_{0}; + std::int32_t historyTxIndex_{-1}; + bool haveHistorical_{false}; + std::atomic stopHistorical_{false}; - SubAccountHistoryIndex(AccountID const& accountId) - : accountId_(accountId) - , forwardTxIndex_(0) - , separationLedgerSeq_(0) - , historyLastLedgerSeq_(0) - , historyTxIndex_(-1) - , haveHistorical_(false) - , stopHistorical_(false) + SubAccountHistoryIndex(AccountID const& accountId) : accountId_(accountId) { } }; @@ -653,19 +675,22 @@ private: InfoSub::wptr sinkWptr_; std::shared_ptr index_; }; - using SubAccountHistoryMapType = hash_map>; + using SubAccountHistoryMapType = + hash_map>; /** * @note called while holding mSubLock */ void - subAccountHistoryStart(std::shared_ptr const& ledger, SubAccountHistoryInfoWeak& subInfo); + subAccountHistoryStart( + std::shared_ptr const& ledger, + SubAccountHistoryInfoWeak& subInfo); void addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo); void setAccountHistoryJobTimer(SubAccountHistoryInfoWeak subInfo); - Application& app_; + ServiceRegistry& registry_; beast::Journal m_journal; std::unique_ptr m_localTX; @@ -689,7 +714,7 @@ private: std::optional const validatorPK_; std::optional const validatorMasterPK_; - ConsensusPhase mLastConsensusPhase; + ConsensusPhase mLastConsensusPhase{ConsensusPhase::open}; LedgerMaster& m_ledgerMaster; @@ -731,7 +756,7 @@ private: DispatchState mDispatchState = DispatchState::none; std::vector mTransactions; - StateAccounting accounting_{}; + StateAccounting accounting_; std::set pendingValidations_; std::mutex validationsMutex_; @@ -742,15 +767,19 @@ private: template Stats(Handler const& handler, beast::insight::Collector::ptr const& collector) : hook(collector->make_hook(handler)) - , disconnected_duration(collector->make_gauge("State_Accounting", "Disconnected_duration")) + , disconnected_duration( + collector->make_gauge("State_Accounting", "Disconnected_duration")) , connected_duration(collector->make_gauge("State_Accounting", "Connected_duration")) , syncing_duration(collector->make_gauge("State_Accounting", "Syncing_duration")) , tracking_duration(collector->make_gauge("State_Accounting", "Tracking_duration")) , full_duration(collector->make_gauge("State_Accounting", "Full_duration")) - , disconnected_transitions(collector->make_gauge("State_Accounting", "Disconnected_transitions")) - , connected_transitions(collector->make_gauge("State_Accounting", "Connected_transitions")) + , disconnected_transitions( + collector->make_gauge("State_Accounting", "Disconnected_transitions")) + , connected_transitions( + collector->make_gauge("State_Accounting", "Connected_transitions")) , syncing_transitions(collector->make_gauge("State_Accounting", "Syncing_transitions")) - , tracking_transitions(collector->make_gauge("State_Accounting", "Tracking_transitions")) + , tracking_transitions( + collector->make_gauge("State_Accounting", "Tracking_transitions")) , full_transitions(collector->make_gauge("State_Accounting", "Full_transitions")) { } @@ -779,7 +808,8 @@ private: //------------------------------------------------------------------------------ -static std::array const stateNames{{"disconnected", "connected", "syncing", "tracking", "full"}}; +static std::array const stateNames{ + {"disconnected", "connected", "syncing", "tracking", "full"}}; std::array const NetworkOPsImp::states_ = stateNames; @@ -847,7 +877,7 @@ NetworkOPsImp::getHostId(bool forAdmin) // For non-admin uses hash the node public key into a // single RFC1751 word: static std::string const shroudedHostId = [this]() { - auto const& id = app_.nodeIdentity(); + auto const& id = registry_.app().nodeIdentity(); return RFC1751::getWordFromBlob(id.first.data(), id.first.size()); }(); @@ -861,7 +891,7 @@ NetworkOPsImp::setStateTimer() setHeartbeatTimer(); // Only do this work if a cluster is configured - if (app_.cluster().size() != 0) + if (registry_.cluster().size() != 0) setClusterTimer(); } @@ -880,10 +910,12 @@ NetworkOPsImp::setTimer( onExpire(); } // Recover as best we can if an unexpected error occurs. - if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted) + if (e.value() != boost::system::errc::success && + e.value() != boost::asio::error::operation_aborted) { // Try again later and hope for the best. - JLOG(m_journal.error()) << "Timer got error '" << e.message() << "'. Restarting timer."; + JLOG(m_journal.error()) + << "Timer got error '" << e.message() << "'. Restarting timer."; onError(); } })) @@ -899,7 +931,9 @@ NetworkOPsImp::setHeartbeatTimer() setTimer( heartbeatTimer_, mConsensus.parms().ledgerGRANULARITY, - [this]() { m_job_queue.addJob(jtNETOP_TIMER, "NetHeart", [this]() { processHeartbeatTimer(); }); }, + [this]() { + m_job_queue.addJob(jtNETOP_TIMER, "NetHeart", [this]() { processHeartbeatTimer(); }); + }, [this]() { setHeartbeatTimer(); }); } @@ -911,14 +945,17 @@ NetworkOPsImp::setClusterTimer() setTimer( clusterTimer_, 10s, - [this]() { m_job_queue.addJob(jtNETOP_CLUSTER, "NetCluster", [this]() { processClusterTimer(); }); }, + [this]() { + m_job_queue.addJob(jtNETOP_CLUSTER, "NetCluster", [this]() { processClusterTimer(); }); + }, [this]() { setClusterTimer(); }); } void NetworkOPsImp::setAccountHistoryJobTimer(SubAccountHistoryInfoWeak subInfo) { - JLOG(m_journal.debug()) << "Scheduling AccountHistory job for account " << toBase58(subInfo.index_->accountId_); + JLOG(m_journal.debug()) << "Scheduling AccountHistory job for account " + << toBase58(subInfo.index_->accountId_); using namespace std::chrono_literals; setTimer( accountHistoryTxTimer_, @@ -932,13 +969,13 @@ NetworkOPsImp::processHeartbeatTimer() { RclConsensusLogger clog("Heartbeat Timer", mConsensus.validating(), m_journal); { - std::unique_lock lock{app_.getMasterMutex()}; + std::unique_lock lock{registry_.app().getMasterMutex()}; // VFALCO NOTE This is for diagnosing a crash on exit - LoadManager& mgr(app_.getLoadManager()); + LoadManager& mgr(registry_.getLoadManager()); mgr.heartbeat(); - std::size_t const numPeers = app_.overlay().size(); + std::size_t const numPeers = registry_.overlay().size(); // do we have sufficient peers? If not, we are disconnected. if (numPeers < minPeerCount_) @@ -954,8 +991,8 @@ NetworkOPsImp::processHeartbeatTimer() } else { - CLOG(clog.ss()) << "already DISCONNECTED. too few peers (" << numPeers << "), need at least " - << minPeerCount_; + CLOG(clog.ss()) << "already DISCONNECTED. too few peers (" << numPeers + << "), need at least " << minPeerCount_; } // MasterMutex lock need not be held to call setHeartbeatTimer() @@ -979,9 +1016,13 @@ NetworkOPsImp::processHeartbeatTimer() auto origMode = mMode.load(); CLOG(clog.ss()) << "mode: " << strOperatingMode(origMode, true); if (mMode == OperatingMode::SYNCING) + { setMode(OperatingMode::SYNCING); + } else if (mMode == OperatingMode::CONNECTED) + { setMode(OperatingMode::CONNECTED); + } auto newMode = mMode.load(); if (origMode != newMode) { @@ -990,7 +1031,7 @@ NetworkOPsImp::processHeartbeatTimer() CLOG(clog.ss()) << ". "; } - mConsensus.timerEntry(app_.timeKeeper().closeTime(), clog.ss()); + mConsensus.timerEntry(registry_.timeKeeper().closeTime(), clog.ss()); CLOG(clog.ss()) << "consensus phase " << to_string(mLastConsensusPhase); ConsensusPhase const currPhase = mConsensus.phase(); @@ -1008,16 +1049,17 @@ NetworkOPsImp::processHeartbeatTimer() void NetworkOPsImp::processClusterTimer() { - if (app_.cluster().size() == 0) + if (registry_.cluster().size() == 0) return; using namespace std::chrono_literals; - bool const update = app_.cluster().update( - app_.nodeIdentity().first, + bool const update = registry_.cluster().update( + registry_.app().nodeIdentity().first, "", - (m_ledgerMaster.getValidatedLedgerAge() <= 4min) ? app_.getFeeTrack().getLocalFee() : 0, - app_.timeKeeper().now()); + (m_ledgerMaster.getValidatedLedgerAge() <= 4min) ? registry_.getFeeTrack().getLocalFee() + : 0, + registry_.timeKeeper().now()); if (!update) { @@ -1027,7 +1069,7 @@ NetworkOPsImp::processClusterTimer() } protocol::TMCluster cluster; - app_.cluster().for_each([&cluster](ClusterNode const& node) { + registry_.cluster().for_each([&cluster](ClusterNode const& node) { protocol::TMClusterNode& n = *cluster.add_clusternodes(); n.set_publickey(toBase58(TokenType::NodePublic, node.identity())); n.set_reporttime(node.getReportTime().time_since_epoch().count()); @@ -1036,14 +1078,15 @@ NetworkOPsImp::processClusterTimer() n.set_nodename(node.name()); }); - Resource::Gossip gossip = app_.getResourceManager().exportConsumers(); + Resource::Gossip gossip = registry_.getResourceManager().exportConsumers(); for (auto& item : gossip.items) { protocol::TMLoadSource& node = *cluster.add_loadsources(); node.set_name(to_string(item.address)); node.set_cost(item.balance); } - app_.overlay().foreach(send_if(std::make_shared(cluster, protocol::mtCLUSTER), peer_in_cluster())); + registry_.overlay().foreach( + send_if(std::make_shared(cluster, protocol::mtCLUSTER), peer_in_cluster())); setClusterTimer(); } @@ -1088,7 +1131,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) auto const trans = sterilize(*iTrans); auto const txid = trans->getTransactionID(); - auto const flags = app_.getHashRouter().getFlags(txid); + auto const flags = registry_.getHashRouter().getFlags(txid); if ((flags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) { @@ -1099,7 +1142,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) try { auto const [validity, reason] = - checkValidity(app_.getHashRouter(), *trans, m_ledgerMaster.getValidatedRules(), app_.config()); + checkValidity(registry_.getHashRouter(), *trans, m_ledgerMaster.getValidatedRules()); if (validity != Validity::Valid) { @@ -1116,7 +1159,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) std::string reason; - auto tx = std::make_shared(trans, reason, app_); + auto tx = std::make_shared(trans, reason, registry_.app()); m_job_queue.addJob(jtTRANSACTION, "SubmitTxn", [this, tx]() { auto t = tx; @@ -1127,7 +1170,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) bool NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) { - auto const newFlags = app_.getHashRouter().getFlags(transaction->getID()); + auto const newFlags = registry_.getHashRouter().getFlags(transaction->getID()); if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) { @@ -1148,15 +1191,16 @@ NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) { transaction->setStatus(INVALID); transaction->setResult(temINVALID_FLAG); - app_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); + registry_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); return false; } // NOTE ximinez - I think this check is redundant, // but I'm not 100% sure yet. // If so, only cost is looking up HashRouter flags. - auto const [validity, reason] = checkValidity(app_.getHashRouter(), sttx, view->rules(), app_.config()); - XRPL_ASSERT(validity == Validity::Valid, "xrpl::NetworkOPsImp::processTransaction : valid validity"); + auto const [validity, reason] = checkValidity(registry_.getHashRouter(), sttx, view->rules()); + XRPL_ASSERT( + validity == Validity::Valid, "xrpl::NetworkOPsImp::processTransaction : valid validity"); // Not concerned with local checks at this point. if (validity == Validity::SigBad) @@ -1164,12 +1208,12 @@ NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) JLOG(m_journal.info()) << "Transaction has bad signature: " << reason; transaction->setStatus(INVALID); transaction->setResult(temBAD_SIGNATURE); - app_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); + registry_.getHashRouter().setFlags(transaction->getID(), HashRouterFlags::BAD); return false; } // canonicalize can change our pointer - app_.getMasterTransaction().canonicalize(&transaction); + registry_.getMasterTransaction().canonicalize(&transaction); return true; } @@ -1188,13 +1232,20 @@ NetworkOPsImp::processTransaction( return; if (bLocal) + { doTransactionSync(transaction, bUnlimited, failType); + } else + { doTransactionAsync(transaction, bUnlimited, failType); + } } void -NetworkOPsImp::doTransactionAsync(std::shared_ptr transaction, bool bUnlimited, FailHard failType) +NetworkOPsImp::doTransactionAsync( + std::shared_ptr transaction, + bool bUnlimited, + FailHard failType) { std::lock_guard lock(mMutex); @@ -1214,7 +1265,10 @@ NetworkOPsImp::doTransactionAsync(std::shared_ptr transaction, bool } void -NetworkOPsImp::doTransactionSync(std::shared_ptr transaction, bool bUnlimited, FailHard failType) +NetworkOPsImp::doTransactionSync( + std::shared_ptr transaction, + bool bUnlimited, + FailHard failType) { std::unique_lock lock(mMutex); @@ -1224,8 +1278,9 @@ NetworkOPsImp::doTransactionSync(std::shared_ptr transaction, bool transaction->setApplying(); } - doTransactionSyncBatch( - lock, [&transaction](std::unique_lock const&) { return transaction->getApplying(); }); + doTransactionSyncBatch(lock, [&transaction](std::unique_lock const&) { + return transaction->getApplying(); + }); } void @@ -1244,7 +1299,7 @@ NetworkOPsImp::doTransactionSyncBatch( { apply(lock); - if (mTransactions.size()) + if (!mTransactions.empty()) { // More transactions need to be applied, but by another job. if (m_job_queue.addJob(jtBATCH, "TxBatchSync", [this]() { transactionBatch(); })) @@ -1265,7 +1320,7 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) for (auto const& [_, tx] : set) { std::string reason; - auto transaction = std::make_shared(tx, reason, app_); + auto transaction = std::make_shared(tx, reason, registry_.app()); if (transaction->getStatus() == INVALID) { @@ -1273,7 +1328,7 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) { JLOG(m_journal.trace()) << "Exception checking transaction: " << reason; } - app_.getHashRouter().setFlags(tx->getTransactionID(), HashRouterFlags::BAD); + registry_.getHashRouter().setFlags(tx->getTransactionID(), HashRouterFlags::BAD); continue; } @@ -1299,7 +1354,9 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) } if (mTransactions.empty()) + { mTransactions.swap(transactions); + } else { mTransactions.reserve(mTransactions.size() + transactions.size()); @@ -1314,8 +1371,9 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) doTransactionSyncBatch(lock, [&](std::unique_lock const&) { XRPL_ASSERT(lock.owns_lock(), "xrpl::NetworkOPsImp::processTransactionSet has lock"); - return std::any_of( - mTransactions.begin(), mTransactions.end(), [](auto const& t) { return t.transaction->getApplying(); }); + return std::any_of(mTransactions.begin(), mTransactions.end(), [](auto const& t) { + return t.transaction->getApplying(); + }); }); } @@ -1327,7 +1385,7 @@ NetworkOPsImp::transactionBatch() if (mDispatchState == DispatchState::running) return; - while (mTransactions.size()) + while (!mTransactions.empty()) { apply(lock); } @@ -1340,20 +1398,21 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) std::vector transactions; mTransactions.swap(transactions); XRPL_ASSERT(!transactions.empty(), "xrpl::NetworkOPsImp::apply : non-empty transactions"); - XRPL_ASSERT(mDispatchState != DispatchState::running, "xrpl::NetworkOPsImp::apply : is not running"); + XRPL_ASSERT( + mDispatchState != DispatchState::running, "xrpl::NetworkOPsImp::apply : is not running"); mDispatchState = DispatchState::running; batchLock.unlock(); { - std::unique_lock masterLock{app_.getMasterMutex(), std::defer_lock}; + std::unique_lock masterLock{registry_.app().getMasterMutex(), std::defer_lock}; bool changed = false; { std::unique_lock ledgerLock{m_ledgerMaster.peekMutex(), std::defer_lock}; std::lock(masterLock, ledgerLock); - app_.openLedger().modify([&](OpenView& view, beast::Journal j) { + registry_.openLedger().modify([&](OpenView& view, beast::Journal j) { for (TransactionStatus& e : transactions) { // we check before adding to the batch @@ -1364,7 +1423,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (e.failType == FailHard::yes) flags |= tapFAIL_HARD; - auto const result = app_.getTxQ().apply(app_, view, e.transaction->getSTransaction(), flags, j); + auto const result = registry_.getTxQ().apply( + registry_.app(), view, e.transaction->getSTransaction(), flags, j); e.result = result.ter; e.applied = result.applied; changed = changed || result.applied; @@ -1379,7 +1439,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (auto const l = m_ledgerMaster.getValidatedLedger()) validatedLedgerIndex = l->header().seq; - auto newOL = app_.openLedger().current(); + auto newOL = registry_.openLedger().current(); for (TransactionStatus& e : transactions) { e.transaction->clearSubmitResult(); @@ -1393,10 +1453,10 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) e.transaction->setResult(e.result); if (isTemMalformed(e.result)) - app_.getHashRouter().setFlags(e.transaction->getID(), HashRouterFlags::BAD); + registry_.getHashRouter().setFlags(e.transaction->getID(), HashRouterFlags::BAD); #ifdef DEBUG - if (e.result != tesSUCCESS) + if (!isTesSuccess(e.result)) { std::string token, human; @@ -1409,7 +1469,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) bool addLocal = e.local; - if (e.result == tesSUCCESS) + if (isTesSuccess(e.result)) { JLOG(m_journal.debug()) << "Transaction is now included in open ledger"; e.transaction->setStatus(INCLUDED); @@ -1420,14 +1480,15 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) auto const& txCur = e.transaction->getSTransaction(); std::size_t count = 0; - for (auto txNext = m_ledgerMaster.popAcctTransaction(txCur); txNext && count < maxPoppedTransactions; + for (auto txNext = m_ledgerMaster.popAcctTransaction(txCur); + txNext && count < maxPoppedTransactions; txNext = m_ledgerMaster.popAcctTransaction(txCur), ++count) { if (!batchLock.owns_lock()) batchLock.lock(); std::string reason; auto const trans = sterilize(*txNext); - auto t = std::make_shared(trans, reason, app_); + auto t = std::make_shared(trans, reason, registry_.app()); if (t->getApplying()) break; submit_held.emplace_back(t, false, false, FailHard::no); @@ -1459,9 +1520,11 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) { if (e.failType != FailHard::yes) { - auto const lastLedgerSeq = e.transaction->getSTransaction()->at(~sfLastLedgerSequence); - auto const ledgersLeft = lastLedgerSeq ? *lastLedgerSeq - m_ledgerMaster.getCurrentLedgerIndex() - : std::optional{}; + auto const lastLedgerSeq = + e.transaction->getSTransaction()->at(~sfLastLedgerSequence); + auto const ledgersLeft = lastLedgerSeq + ? *lastLedgerSeq - m_ledgerMaster.getCurrentLedgerIndex() + : std::optional{}; // If any of these conditions are met, the transaction can // be held: // 1. It was submitted locally. (Note that this flag is only @@ -1479,7 +1542,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) // up!) // if (e.local || (ledgersLeft && ledgersLeft <= LocalTxs::holdLedgers) || - app_.getHashRouter().setFlags(e.transaction->getID(), HashRouterFlags::HELD)) + registry_.getHashRouter().setFlags( + e.transaction->getID(), HashRouterFlags::HELD)) { // transaction should be held JLOG(m_journal.debug()) << "Transaction should be held: " << e.result; @@ -1491,8 +1555,8 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) JLOG(m_journal.debug()) << "Not holding transaction " << e.transaction->getID() << ": " << (e.local ? "local" : "network") << ", " - << "result: " << e.result - << " ledgers left: " << (ledgersLeft ? to_string(*ledgersLeft) : "unspecified"); + << "result: " << e.result << " ledgers left: " + << (ledgersLeft ? to_string(*ledgersLeft) : "unspecified"); } } else @@ -1505,15 +1569,17 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (addLocal && !enforceFailHard) { - m_localTX->push_back(m_ledgerMaster.getCurrentLedgerIndex(), e.transaction->getSTransaction()); + m_localTX->push_back( + m_ledgerMaster.getCurrentLedgerIndex(), e.transaction->getSTransaction()); e.transaction->setKept(); } - if ((e.applied || ((mMode != OperatingMode::FULL) && (e.failType != FailHard::yes) && e.local) || + if ((e.applied || + ((mMode != OperatingMode::FULL) && (e.failType != FailHard::yes) && e.local) || (e.result == terQUEUED)) && !enforceFailHard) { - auto const toSkip = app_.getHashRouter().shouldRelay(e.transaction->getID()); + auto const toSkip = registry_.getHashRouter().shouldRelay(e.transaction->getID()); if (auto const sttx = *(e.transaction->getSTransaction()); toSkip && // Skip relaying if it's an inner batch txn. The flag should // only be set if the Batch feature is enabled. If Batch is @@ -1527,19 +1593,21 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) sttx.add(s); tx.set_rawtransaction(s.data(), s.size()); tx.set_status(protocol::tsCURRENT); - tx.set_receivetimestamp(app_.timeKeeper().now().time_since_epoch().count()); + tx.set_receivetimestamp( + registry_.timeKeeper().now().time_since_epoch().count()); tx.set_deferred(e.result == terQUEUED); // FIXME: This should be when we received it - app_.overlay().relay(e.transaction->getID(), tx, *toSkip); + registry_.overlay().relay(e.transaction->getID(), tx, *toSkip); e.transaction->setBroadcast(); } } if (validatedLedgerIndex) { - auto [fee, accountSeq, availableSeq] = - app_.getTxQ().getTxRequiredFeeAndSeq(*newOL, e.transaction->getSTransaction()); - e.transaction->setCurrentLedgerState(*validatedLedgerIndex, fee, accountSeq, availableSeq); + auto [fee, accountSeq, availableSeq] = registry_.getTxQ().getTxRequiredFeeAndSeq( + *newOL, e.transaction->getSTransaction()); + e.transaction->setCurrentLedgerState( + *validatedLedgerIndex, fee, accountSeq, availableSeq); } } } @@ -1552,7 +1620,9 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (!submit_held.empty()) { if (mTransactions.empty()) + { mTransactions.swap(submit_held); + } else { mTransactions.reserve(mTransactions.size() + submit_held.size()); @@ -1578,7 +1648,7 @@ NetworkOPsImp::getOwnerInfo(std::shared_ptr lpLedger, AccountID auto sleNode = lpLedger->read(keylet::page(root)); if (sleNode) { - std::uint64_t uNodeDir; + std::uint64_t uNodeDir = 0; do { @@ -1713,7 +1783,7 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint //------------------------------------------------------------------------- // Determine preferred last closed ledger - auto& validations = app_.getValidations(); + auto& validations = registry_.getValidations(); JLOG(m_journal.debug()) << "ValidationTrie " << Json::Compact(validations.getJsonTrie()); // Will rely on peer LCL if no trusted validations exist @@ -1750,7 +1820,9 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint switchLedgers = false; } else + { networkClosed = closedLedger; + } if (!switchLedgers) return false; @@ -1758,7 +1830,10 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint auto consensus = m_ledgerMaster.getLedgerByHash(closedLedger); if (!consensus) - consensus = app_.getInboundLedgers().acquire(closedLedger, 0, InboundLedger::Reason::CONSENSUS); + { + consensus = registry_.getInboundLedgers().acquire( + closedLedger, 0, InboundLedger::Reason::CONSENSUS); + } if (consensus && (!m_ledgerMaster.canBeCurrent(consensus) || @@ -1799,7 +1874,7 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC clearNeedNetworkLedger(); // Update fee computations. - app_.getTxQ().processClosedLedger(app_, *newLCL, true); + registry_.getTxQ().processClosedLedger(registry_.app(), *newLCL, true); // Caller must own master lock { @@ -1807,14 +1882,18 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC // open ledger. Then apply local tx. auto retries = m_localTX->getTxSet(); - auto const lastVal = app_.getLedgerMaster().getValidatedLedger(); + auto const lastVal = registry_.getLedgerMaster().getValidatedLedger(); std::optional rules; if (lastVal) - rules = makeRulesGivenLedger(*lastVal, app_.config().features); + { + rules = makeRulesGivenLedger(*lastVal, registry_.app().config().features); + } else - rules.emplace(app_.config().features); - app_.openLedger().accept( - app_, + { + rules.emplace(registry_.app().config().features); + } + registry_.openLedger().accept( + registry_.app(), *rules, newLCL, OrderedTxs({}), @@ -1824,7 +1903,7 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC "jump", [&](OpenView& view, beast::Journal j) { // Stuff the ledger with transactions from the queue. - return app_.getTxQ().accept(app_, view); + return registry_.getTxQ().accept(registry_.app(), view); }); } @@ -1833,21 +1912,26 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC protocol::TMStatusChange s; s.set_newevent(protocol::neSWITCHED_LEDGER); s.set_ledgerseq(newLCL->header().seq); - s.set_networktime(app_.timeKeeper().now().time_since_epoch().count()); - s.set_ledgerhashprevious(newLCL->header().parentHash.begin(), newLCL->header().parentHash.size()); + s.set_networktime(registry_.timeKeeper().now().time_since_epoch().count()); + s.set_ledgerhashprevious( + newLCL->header().parentHash.begin(), newLCL->header().parentHash.size()); s.set_ledgerhash(newLCL->header().hash.begin(), newLCL->header().hash.size()); - app_.overlay().foreach(send_always(std::make_shared(s, protocol::mtSTATUS_CHANGE))); + registry_.overlay().foreach( + send_always(std::make_shared(s, protocol::mtSTATUS_CHANGE))); } bool -NetworkOPsImp::beginConsensus(uint256 const& networkClosed, std::unique_ptr const& clog) +NetworkOPsImp::beginConsensus( + uint256 const& networkClosed, + std::unique_ptr const& clog) { XRPL_ASSERT(networkClosed.isNonZero(), "xrpl::NetworkOPsImp::beginConsensus : nonzero input"); auto closingInfo = m_ledgerMaster.getCurrentLedger()->header(); - JLOG(m_journal.info()) << "Consensus time for #" << closingInfo.seq << " with LCL " << closingInfo.parentHash; + JLOG(m_journal.info()) << "Consensus time for #" << closingInfo.seq << " with LCL " + << closingInfo.parentHash; auto prevLedger = m_ledgerMaster.getLedgerByHash(closingInfo.parentHash); @@ -1874,23 +1958,28 @@ NetworkOPsImp::beginConsensus(uint256 const& networkClosed, std::unique_ptrnegativeUNL()); - TrustChanges const changes = app_.validators().updateTrusted( - app_.getValidations().getCurrentNodeIDs(), + registry_.validators().setNegativeUNL(prevLedger->negativeUNL()); + TrustChanges const changes = registry_.validators().updateTrusted( + registry_.getValidations().getCurrentNodeIDs(), closingInfo.parentCloseTime, *this, - app_.overlay(), - app_.getHashRouter()); + registry_.overlay(), + registry_.getHashRouter()); if (!changes.added.empty() || !changes.removed.empty()) { - app_.getValidations().trustChanged(changes.added, changes.removed); + registry_.getValidations().trustChanged(changes.added, changes.removed); // Update the AmendmentTable so it tracks the current validators. - app_.getAmendmentTable().trustChanged(app_.validators().getQuorumKeys().second); + registry_.getAmendmentTable().trustChanged(registry_.validators().getQuorumKeys().second); } mConsensus.startRound( - app_.timeKeeper().closeTime(), networkClosed, prevLedger, changes.removed, changes.added, clog); + registry_.timeKeeper().closeTime(), + networkClosed, + prevLedger, + changes.removed, + changes.added, + clog); ConsensusPhase const currPhase = mConsensus.phase(); if (mLastConsensusPhase != currPhase) @@ -1925,7 +2014,7 @@ NetworkOPsImp::processTrustedProposal(RCLCxPeerPos peerPos) return false; } - return mConsensus.peerProposal(app_.timeKeeper().closeTime(), peerPos); + return mConsensus.peerProposal(registry_.timeKeeper().closeTime(), peerPos); } void @@ -1939,11 +2028,11 @@ NetworkOPsImp::mapComplete(std::shared_ptr const& map, bool fromAcquire) protocol::TMHaveTransactionSet msg; msg.set_hash(map->getHash().as_uint256().begin(), 256 / 8); msg.set_status(protocol::tsHAVE); - app_.overlay().foreach(send_always(std::make_shared(msg, protocol::mtHAVE_SET))); + registry_.overlay().foreach(send_always(std::make_shared(msg, protocol::mtHAVE_SET))); // We acquired it because consensus asked us to if (fromAcquire) - mConsensus.gotTxSet(app_.timeKeeper().closeTime(), RCLTxSet{map}); + mConsensus.gotTxSet(registry_.timeKeeper().closeTime(), RCLTxSet{map}); } void @@ -1951,7 +2040,7 @@ NetworkOPsImp::endConsensus(std::unique_ptr const& clog) { uint256 deadLedger = m_ledgerMaster.getClosedLedger()->header().parentHash; - for (auto const& it : app_.overlay().getActivePeers()) + for (auto const& it : registry_.overlay().getActivePeers()) { if (it && (it->getClosedLedgerHash() == deadLedger)) { @@ -1961,7 +2050,7 @@ NetworkOPsImp::endConsensus(std::unique_ptr const& clog) } uint256 networkClosed; - bool ledgerChange = checkLastClosedLedger(app_.overlay().getActivePeers(), networkClosed); + bool ledgerChange = checkLastClosedLedger(registry_.overlay().getActivePeers(), networkClosed); if (networkClosed.isZero()) { @@ -1984,13 +2073,15 @@ NetworkOPsImp::endConsensus(std::unique_ptr const& clog) setMode(OperatingMode::TRACKING); } - if (((mMode == OperatingMode::CONNECTED) || (mMode == OperatingMode::TRACKING)) && !ledgerChange) + if (((mMode == OperatingMode::CONNECTED) || (mMode == OperatingMode::TRACKING)) && + !ledgerChange) { // check if the ledger is good enough to go to FULL // Note: Do not go to FULL if we don't have the previous ledger // check if the ledger is bad enough to go to CONNECTED -- TODO auto current = m_ledgerMaster.getCurrentLedger(); - if (app_.timeKeeper().now() < (current->header().parentCloseTime + 2 * current->header().closeTimeResolution)) + if (registry_.timeKeeper().now() < + (current->header().parentCloseTime + 2 * current->header().closeTimeResolution)) { setMode(OperatingMode::FULL); } @@ -2059,15 +2150,16 @@ NetworkOPsImp::ServerFeeSummary::ServerFeeSummary( bool NetworkOPsImp::ServerFeeSummary::operator!=(NetworkOPsImp::ServerFeeSummary const& b) const { - if (loadFactorServer != b.loadFactorServer || loadBaseServer != b.loadBaseServer || baseFee != b.baseFee || - em.has_value() != b.em.has_value()) + if (loadFactorServer != b.loadFactorServer || loadBaseServer != b.loadBaseServer || + baseFee != b.baseFee || em.has_value() != b.em.has_value()) return true; if (em && b.em) { return ( em->minProcessingFeeLevel != b.em->minProcessingFeeLevel || - em->openLedgerFeeLevel != b.em->openLedgerFeeLevel || em->referenceFeeLevel != b.em->referenceFeeLevel); + em->openLedgerFeeLevel != b.em->openLedgerFeeLevel || + em->referenceFeeLevel != b.em->referenceFeeLevel); } return false; @@ -2096,9 +2188,9 @@ NetworkOPsImp::pubServer() Json::Value jvObj(Json::objectValue); ServerFeeSummary f{ - app_.openLedger().current()->fees().base, - app_.getTxQ().getMetrics(*app_.openLedger().current()), - app_.getFeeTrack()}; + registry_.openLedger().current()->fees().base, + registry_.getTxQ().getMetrics(*registry_.openLedger().current()), + registry_.getFeeTrack()}; jvObj[jss::type] = "serverStatus"; jvObj[jss::server_status] = strOperatingMode(); @@ -2110,7 +2202,8 @@ NetworkOPsImp::pubServer() { auto const loadFactor = std::max( safe_cast(f.loadFactorServer), - mulDiv(f.em->openLedgerFeeLevel, f.loadBaseServer, f.em->referenceFeeLevel).value_or(xrpl::muldiv_max)); + mulDiv(f.em->openLedgerFeeLevel, f.loadBaseServer, f.em->referenceFeeLevel) + .value_or(xrpl::muldiv_max)); jvObj[jss::load_factor] = trunc32(loadFactor); jvObj[jss::load_factor_fee_escalation] = f.em->openLedgerFeeLevel.jsonClipped(); @@ -2118,7 +2211,9 @@ NetworkOPsImp::pubServer() jvObj[jss::load_factor_fee_reference] = f.em->referenceFeeLevel.jsonClipped(); } else + { jvObj[jss::load_factor] = f.loadFactorServer; + } mLastFeeSummary = f; @@ -2189,7 +2284,7 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) jvObj[jss::flags] = val->getFlags(); jvObj[jss::signing_time] = *(*val)[~sfSigningTime]; jvObj[jss::data] = strHex(val->getSerializer().slice()); - jvObj[jss::network_id] = app_.config().NETWORK_ID; + jvObj[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); if (auto version = (*val)[~sfServerVersion]) jvObj[jss::server_version] = std::to_string(*version); @@ -2200,7 +2295,7 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) if (auto hash = (*val)[~sfValidatedHash]) jvObj[jss::validated_hash] = strHex(*hash); - auto const masterKey = app_.validatorManifests().getMasterKey(signerPublic); + auto const masterKey = registry_.validatorManifests().getMasterKey(signerPublic); if (masterKey != signerPublic) jvObj[jss::master_key] = toBase58(TokenType::NodePublic, masterKey); @@ -2237,13 +2332,16 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) if (auto const baseFeeXRP = ~val->at(~sfBaseFeeDrops); baseFeeXRP && baseFeeXRP->native()) jvObj[jss::base_fee] = baseFeeXRP->xrp().jsonClipped(); - if (auto const reserveBaseXRP = ~val->at(~sfReserveBaseDrops); reserveBaseXRP && reserveBaseXRP->native()) + if (auto const reserveBaseXRP = ~val->at(~sfReserveBaseDrops); + reserveBaseXRP && reserveBaseXRP->native()) jvObj[jss::reserve_base] = reserveBaseXRP->xrp().jsonClipped(); - if (auto const reserveIncXRP = ~val->at(~sfReserveIncrementDrops); reserveIncXRP && reserveIncXRP->native()) + if (auto const reserveIncXRP = ~val->at(~sfReserveIncrementDrops); + reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); - if (auto const extensionComputeLimit = ~val->at(~sfExtensionComputeLimit); extensionComputeLimit) + if (auto const extensionComputeLimit = ~val->at(~sfExtensionComputeLimit); + extensionComputeLimit) jvObj[jss::extension_compute] = *extensionComputeLimit; if (auto const extensionSizeLimit = ~val->at(~sfExtensionSizeLimit); extensionSizeLimit) @@ -2316,12 +2414,12 @@ NetworkOPsImp::setMode(OperatingMode om) using namespace std::chrono_literals; if (om == OperatingMode::CONNECTED) { - if (app_.getLedgerMaster().getValidatedLedgerAge() < 1min) + if (registry_.getLedgerMaster().getValidatedLedgerAge() < 1min) om = OperatingMode::SYNCING; } else if (om == OperatingMode::SYNCING) { - if (app_.getLedgerMaster().getValidatedLedgerAge() >= 1min) + if (registry_.getLedgerMaster().getValidatedLedgerAge() >= 1min) om = OperatingMode::CONNECTED; } @@ -2349,20 +2447,25 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str try { if (pendingValidations_.contains(val->getLedgerHash())) + { bypassAccept = BypassAccept::yes; + } else + { pendingValidations_.insert(val->getLedgerHash()); + } scope_unlock unlock(lock); - handleNewValidation(app_, val, source, bypassAccept, m_journal); + handleNewValidation(registry_.app(), val, source, bypassAccept, m_journal); } catch (std::exception const& e) { - JLOG(m_journal.warn()) << "Exception thrown for handling new validation " << val->getLedgerHash() << ": " - << e.what(); + JLOG(m_journal.warn()) << "Exception thrown for handling new validation " + << val->getLedgerHash() << ": " << e.what(); } catch (...) { - JLOG(m_journal.warn()) << "Unknown exception thrown for handling new validation " << val->getLedgerHash(); + JLOG(m_journal.warn()) << "Unknown exception thrown for handling new validation " + << val->getLedgerHash(); } if (bypassAccept == BypassAccept::no) { @@ -2375,7 +2478,7 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str JLOG(m_journal.debug()) << [this, &val]() -> auto { std::stringstream ss; ss << "VALIDATION: " << val->render() << " master_key: "; - auto master = app_.validators().getTrustedKey(val->getSignerPublic()); + auto master = registry_.validators().getTrustedKey(val->getSignerPublic()); if (master) { ss << toBase58(TokenType::NodePublic, *master); @@ -2389,7 +2492,7 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str // We will always relay trusted validations; if configured, we will // also relay all untrusted validations. - return app_.config().RELAY_UNTRUSTED_VALIDATIONS == 1 || val->isTrusted(); + return registry_.app().config().RELAY_UNTRUSTED_VALIDATIONS == 1 || val->isTrusted(); } Json::Value @@ -2431,7 +2534,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) "One or more unsupported amendments have reached majority. " "Upgrade to the latest version before they are activated " "to avoid being amendment blocked."; - if (auto const expected = app_.getAmendmentTable().firstUnsupportedExpected()) + if (auto const expected = registry_.getAmendmentTable().firstUnsupportedExpected()) { auto& d = w[jss::details] = Json::objectValue; d[jss::expected_date] = expected->time_since_epoch().count(); @@ -2448,23 +2551,27 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) info[jss::hostid] = getHostId(admin); // domain: if configured with a domain, report it: - if (!app_.config().SERVER_DOMAIN.empty()) - info[jss::server_domain] = app_.config().SERVER_DOMAIN; + if (!registry_.app().config().SERVER_DOMAIN.empty()) + info[jss::server_domain] = registry_.app().config().SERVER_DOMAIN; info[jss::build_version] = BuildInfo::getVersionString(); info[jss::server_state] = strOperatingMode(admin); - info[jss::time] = to_string(std::chrono::floor(std::chrono::system_clock::now())); + info[jss::time] = + to_string(std::chrono::floor(std::chrono::system_clock::now())); if (needNetworkLedger_) info[jss::network_ledger] = "waiting"; - info[jss::validation_quorum] = static_cast(app_.validators().quorum()); + info[jss::validation_quorum] = static_cast(registry_.validators().quorum()); if (admin) { - switch (app_.config().NODE_SIZE) + // Note: By default the node size is "tiny". When parsing it's an error if the final + // NODE_SIZE is over 4 so below code should be safe. + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) + switch (registry_.app().config().NODE_SIZE) { case 0: info[jss::node_size] = "tiny"; @@ -2483,20 +2590,25 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) break; } - auto when = app_.validators().expires(); + auto when = registry_.validators().expires(); if (!human) { if (when) - info[jss::validator_list_expires] = safe_cast(when->time_since_epoch().count()); + { + info[jss::validator_list_expires] = + safe_cast(when->time_since_epoch().count()); + } else + { info[jss::validator_list_expires] = 0; + } } else { auto& x = (info[jss::validator_list] = Json::objectValue); - x[jss::count] = static_cast(app_.validators().count()); + x[jss::count] = static_cast(registry_.validators().count()); if (when) { @@ -2509,10 +2621,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) { x[jss::expiration] = to_string(*when); - if (*when > app_.timeKeeper().now()) + if (*when > registry_.timeKeeper().now()) + { x[jss::status] = "active"; + } else + { x[jss::status] = "expired"; + } } } else @@ -2522,23 +2638,21 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } } -#if defined(GIT_COMMIT_HASH) || defined(GIT_BRANCH) + if (!xrpl::git::getCommitHash().empty() || !xrpl::git::getBuildBranch().empty()) { auto& x = (info[jss::git] = Json::objectValue); -#ifdef GIT_COMMIT_HASH - x[jss::hash] = GIT_COMMIT_HASH; -#endif -#ifdef GIT_BRANCH - x[jss::branch] = GIT_BRANCH; -#endif + if (!xrpl::git::getCommitHash().empty()) + x[jss::hash] = xrpl::git::getCommitHash(); + if (!xrpl::git::getBuildBranch().empty()) + x[jss::branch] = xrpl::git::getBuildBranch(); } -#endif } - info[jss::io_latency_ms] = static_cast(app_.getIOLatency().count()); + info[jss::io_latency_ms] = static_cast(registry_.app().getIOLatency().count()); if (admin) { - if (auto const localPubKey = app_.validators().localPublicKey(); localPubKey && app_.getValidationPublicKey()) + if (auto const localPubKey = registry_.validators().localPublicKey(); + localPubKey && registry_.app().getValidationPublicKey()) { info[jss::pubkey_validator] = toBase58(TokenType::NodePublic, localPubKey.value()); } @@ -2550,17 +2664,17 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (counters) { - info[jss::counters] = app_.getPerfLog().countersJson(); + info[jss::counters] = registry_.getPerfLog().countersJson(); Json::Value nodestore(Json::objectValue); - app_.getNodeStore().getCountsJson(nodestore); + registry_.getNodeStore().getCountsJson(nodestore); info[jss::counters][jss::nodestore] = nodestore; - info[jss::current_activities] = app_.getPerfLog().currentJson(); + info[jss::current_activities] = registry_.getPerfLog().currentJson(); } - info[jss::pubkey_node] = toBase58(TokenType::NodePublic, app_.nodeIdentity().first); + info[jss::pubkey_node] = toBase58(TokenType::NodePublic, registry_.app().nodeIdentity().first); - info[jss::complete_ledgers] = app_.getLedgerMaster().getCompleteLedgers(); + info[jss::complete_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); if (amendmentBlocked_) info[jss::amendment_blocked] = true; @@ -2570,14 +2684,15 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (fp != 0) info[jss::fetch_pack] = Json::UInt(fp); - info[jss::peers] = Json::UInt(app_.overlay().size()); + info[jss::peers] = Json::UInt(registry_.overlay().size()); Json::Value lastClose = Json::objectValue; lastClose[jss::proposers] = Json::UInt(mConsensus.prevProposers()); if (human) { - lastClose[jss::converge_time_s] = std::chrono::duration{mConsensus.prevRoundTime()}.count(); + lastClose[jss::converge_time_s] = + std::chrono::duration{mConsensus.prevRoundTime()}.count(); } else { @@ -2591,21 +2706,24 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (admin) info[jss::load] = m_job_queue.getJson(); - if (auto const netid = app_.overlay().networkID()) + if (auto const netid = registry_.overlay().networkID()) info[jss::network_id] = static_cast(*netid); - auto const escalationMetrics = app_.getTxQ().getMetrics(*app_.openLedger().current()); + auto const escalationMetrics = registry_.getTxQ().getMetrics(*registry_.openLedger().current()); - auto const loadFactorServer = app_.getFeeTrack().getLoadFactor(); - auto const loadBaseServer = app_.getFeeTrack().getLoadBase(); + auto const loadFactorServer = registry_.getFeeTrack().getLoadFactor(); + auto const loadBaseServer = registry_.getFeeTrack().getLoadBase(); /* Scale the escalated fee level to unitless "load factor". In practice, this just strips the units, but it will continue to work correctly if either base value ever changes. */ - auto const loadFactorFeeEscalation = - mulDiv(escalationMetrics.openLedgerFeeLevel, loadBaseServer, escalationMetrics.referenceFeeLevel) - .value_or(xrpl::muldiv_max); + auto const loadFactorFeeEscalation = mulDiv( + escalationMetrics.openLedgerFeeLevel, + loadBaseServer, + escalationMetrics.referenceFeeLevel) + .value_or(xrpl::muldiv_max); - auto const loadFactor = std::max(safe_cast(loadFactorServer), loadFactorFeeEscalation); + auto const loadFactor = + std::max(safe_cast(loadFactorServer), loadFactorFeeEscalation); if (!human) { @@ -2631,32 +2749,42 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (admin) { - std::uint32_t fee = app_.getFeeTrack().getLocalFee(); + std::uint32_t fee = registry_.getFeeTrack().getLocalFee(); if (fee != loadBaseServer) info[jss::load_factor_local] = static_cast(fee) / loadBaseServer; - fee = app_.getFeeTrack().getRemoteFee(); + fee = registry_.getFeeTrack().getRemoteFee(); if (fee != loadBaseServer) info[jss::load_factor_net] = static_cast(fee) / loadBaseServer; - fee = app_.getFeeTrack().getClusterFee(); + fee = registry_.getFeeTrack().getClusterFee(); if (fee != loadBaseServer) info[jss::load_factor_cluster] = static_cast(fee) / loadBaseServer; } if (escalationMetrics.openLedgerFeeLevel != escalationMetrics.referenceFeeLevel && (admin || loadFactorFeeEscalation != loadFactor)) + { info[jss::load_factor_fee_escalation] = - escalationMetrics.openLedgerFeeLevel.decimalFromReference(escalationMetrics.referenceFeeLevel); + escalationMetrics.openLedgerFeeLevel.decimalFromReference( + escalationMetrics.referenceFeeLevel); + } if (escalationMetrics.minProcessingFeeLevel != escalationMetrics.referenceFeeLevel) + { info[jss::load_factor_fee_queue] = - escalationMetrics.minProcessingFeeLevel.decimalFromReference(escalationMetrics.referenceFeeLevel); + escalationMetrics.minProcessingFeeLevel.decimalFromReference( + escalationMetrics.referenceFeeLevel); + } } bool valid = false; auto lpClosed = m_ledgerMaster.getValidatedLedger(); if (lpClosed) + { valid = true; + } else + { lpClosed = m_ledgerMaster.getClosedLedger(); + } if (lpClosed) { @@ -2677,7 +2805,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::extension_size] = lpClosed->fees().extensionSizeLimit; l[jss::gas_price] = lpClosed->fees().gasPrice; } - l[jss::close_time] = Json::Value::UInt(lpClosed->header().closeTime.time_since_epoch().count()); + l[jss::close_time] = + Json::Value::UInt(lpClosed->header().closeTime.time_since_epoch().count()); } else { @@ -2691,7 +2820,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::gas_price] = lpClosed->fees().gasPrice; } - if (auto const closeOffset = app_.timeKeeper().closeOffset(); std::abs(closeOffset.count()) >= 60) + if (auto const closeOffset = registry_.timeKeeper().closeOffset(); + std::abs(closeOffset.count()) >= 60) l[jss::close_time_offset] = static_cast(closeOffset.count()); constexpr std::chrono::seconds highAgeThreshold{1000000}; @@ -2703,7 +2833,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) else { auto lCloseTime = lpClosed->header().closeTime; - auto closeTime = app_.timeKeeper().closeTime(); + auto closeTime = registry_.timeKeeper().closeTime(); if (lCloseTime <= closeTime) { using namespace std::chrono_literals; @@ -2714,34 +2844,44 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } if (valid) + { info[jss::validated_ledger] = l; + } else + { info[jss::closed_ledger] = l; + } auto lpPublished = m_ledgerMaster.getPublishedLedger(); if (!lpPublished) + { info[jss::published_ledger] = "none"; + } else if (lpPublished->header().seq != lpClosed->header().seq) + { info[jss::published_ledger] = lpPublished->header().seq; + } } accounting_.json(info); info[jss::uptime] = UptimeClock::now().time_since_epoch().count(); - info[jss::jq_trans_overflow] = std::to_string(app_.overlay().getJqTransOverflow()); - info[jss::peer_disconnects] = std::to_string(app_.overlay().getPeerDisconnect()); - info[jss::peer_disconnects_resources] = std::to_string(app_.overlay().getPeerDisconnectCharges()); + info[jss::jq_trans_overflow] = std::to_string(registry_.overlay().getJqTransOverflow()); + info[jss::peer_disconnects] = std::to_string(registry_.overlay().getPeerDisconnect()); + info[jss::peer_disconnects_resources] = + std::to_string(registry_.overlay().getPeerDisconnectCharges()); // This array must be sorted in increasing order. - static constexpr std::array protocols{"http", "https", "peer", "ws", "ws2", "wss", "wss2"}; + static constexpr std::array protocols{ + "http", "https", "peer", "ws", "ws2", "wss", "wss2"}; static_assert(std::is_sorted(std::begin(protocols), std::end(protocols))); { Json::Value ports{Json::arrayValue}; - for (auto const& port : app_.getServerHandler().setup().ports) + for (auto const& port : registry_.getServerHandler().setup().ports) { // Don't publish admin ports for non-admin users if (!admin && - !(port.admin_nets_v4.empty() && port.admin_nets_v6.empty() && port.admin_user.empty() && - port.admin_password.empty())) + !(port.admin_nets_v4.empty() && port.admin_nets_v6.empty() && + port.admin_user.empty() && port.admin_password.empty())) continue; std::vector proto; std::set_intersection( @@ -2760,9 +2900,9 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } } - if (app_.config().exists(SECTION_PORT_GRPC)) + if (registry_.app().config().exists(SECTION_PORT_GRPC)) { - auto const& grpcSection = app_.config().section(SECTION_PORT_GRPC); + auto const& grpcSection = registry_.app().config().section(SECTION_PORT_GRPC); auto const optPort = grpcSection.get("port"); if (optPort && grpcSection.get("ip")) { @@ -2781,13 +2921,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) void NetworkOPsImp::clearLedgerFetch() { - app_.getInboundLedgers().clearFailures(); + registry_.getInboundLedgers().clearFailures(); } Json::Value NetworkOPsImp::getLedgerFetchInfo() { - return app_.getInboundLedgers().getInfo(); + return registry_.getInboundLedgers().getInfo(); } void @@ -2836,17 +2976,22 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) // Ledgers are published only when they acquire sufficient validations // Holes are filled across connection loss or other catastrophe - std::shared_ptr alpAccepted = app_.getAcceptedLedgerCache().fetch(lpAccepted->header().hash); + std::shared_ptr alpAccepted = + registry_.getAcceptedLedgerCache().fetch(lpAccepted->header().hash); if (!alpAccepted) { - alpAccepted = std::make_shared(lpAccepted, app_); - app_.getAcceptedLedgerCache().canonicalize_replace_client(lpAccepted->header().hash, alpAccepted); + alpAccepted = std::make_shared(lpAccepted); + registry_.getAcceptedLedgerCache().canonicalize_replace_client( + lpAccepted->header().hash, alpAccepted); } - XRPL_ASSERT(alpAccepted->getLedger().get() == lpAccepted.get(), "xrpl::NetworkOPsImp::pubLedger : accepted input"); + XRPL_ASSERT( + alpAccepted->getLedger().get() == lpAccepted.get(), + "xrpl::NetworkOPsImp::pubLedger : accepted input"); { - JLOG(m_journal.debug()) << "Publishing ledger " << lpAccepted->header().seq << " " << lpAccepted->header().hash; + JLOG(m_journal.debug()) << "Publishing ledger " << lpAccepted->header().seq << " " + << lpAccepted->header().hash; std::lock_guard sl(mSubLock); @@ -2857,9 +3002,10 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) jvObj[jss::type] = "ledgerClosed"; jvObj[jss::ledger_index] = lpAccepted->header().seq; jvObj[jss::ledger_hash] = to_string(lpAccepted->header().hash); - jvObj[jss::ledger_time] = Json::Value::UInt(lpAccepted->header().closeTime.time_since_epoch().count()); + jvObj[jss::ledger_time] = + Json::Value::UInt(lpAccepted->header().closeTime.time_since_epoch().count()); - jvObj[jss::network_id] = app_.config().NETWORK_ID; + jvObj[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); if (!lpAccepted->rules().enabled(featureXRPFees)) jvObj[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; @@ -2877,7 +3023,7 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) if (mMode >= OperatingMode::SYNCING) { - jvObj[jss::validated_ledgers] = app_.getLedgerMaster().getCompleteLedgers(); + jvObj[jss::validated_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); } auto it = mStreamMaps[sLedger].begin(); @@ -2890,7 +3036,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) ++it; } else + { it = mStreamMaps[sLedger].erase(it); + } } } @@ -2908,7 +3056,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) ++it; } else + { it = mStreamMaps[sBookChanges].erase(it); + } } } @@ -2945,9 +3095,9 @@ void NetworkOPsImp::reportFeeChange() { ServerFeeSummary f{ - app_.openLedger().current()->fees().base, - app_.getTxQ().getMetrics(*app_.openLedger().current()), - app_.getFeeTrack()}; + registry_.openLedger().current()->fees().base, + registry_.getTxQ().getMetrics(*registry_.openLedger().current()), + registry_.getFeeTrack()}; // only schedule the job if something has changed if (f != mLastFeeSummary) @@ -3008,11 +3158,12 @@ NetworkOPsImp::transJson( lookup.second && lookup.second->isFieldPresent(sfTransactionIndex)) { uint32_t const txnSeq = lookup.second->getFieldU32(sfTransactionIndex); - uint32_t netID = app_.config().NETWORK_ID; + uint32_t netID = registry_.getNetworkIDService().getNetworkID(); if (transaction->isFieldPresent(sfNetworkID)) netID = transaction->getFieldU32(sfNetworkID); - if (std::optional ctid = RPC::encodeCTID(ledger->header().seq, txnSeq, netID); ctid) + if (std::optional ctid = RPC::encodeCTID(ledger->header().seq, txnSeq, netID); + ctid) jvObj[jss::ctid] = *ctid; } if (!ledger->open()) @@ -3046,7 +3197,8 @@ NetworkOPsImp::transJson( // If the offer create is not self funded then add the owner balance if (account != amount.issue().account) { - auto const ownerFunds = accountFunds(*ledger, account, amount, fhIGNORE_FREEZE, app_.journal("View")); + auto const ownerFunds = + accountFunds(*ledger, account, amount, fhIGNORE_FREEZE, registry_.journal("View")); jvObj[jss::transaction][jss::owner_funds] = ownerFunds.getText(); } } @@ -3101,7 +3253,9 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sTransactions].erase(it); + } } it = mStreamMaps[sRTTransactions].begin(); @@ -3118,12 +3272,14 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sRTTransactions].erase(it); + } } } if (transaction.getResult() == tesSUCCESS) - app_.getOrderBookDB().processTxn(ledger, transaction, jvObj); + registry_.getOrderBookDB().processTxn(ledger, transaction, jvObj); pubAccountTransaction(ledger, transaction, last); } @@ -3147,7 +3303,8 @@ NetworkOPsImp::pubAccountTransaction( { for (auto const& affectedAccount : transaction.getAffected()) { - if (auto simiIt = mSubRTAccount.find(affectedAccount); simiIt != mSubRTAccount.end()) + if (auto simiIt = mSubRTAccount.find(affectedAccount); + simiIt != mSubRTAccount.end()) { auto it = simiIt->second.begin(); @@ -3162,7 +3319,9 @@ NetworkOPsImp::pubAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } @@ -3180,11 +3339,14 @@ NetworkOPsImp::pubAccountTransaction( ++iAccepted; } else + { it = simiIt->second.erase(it); + } } } - if (auto historyIt = mSubAccountHistory.find(affectedAccount); historyIt != mSubAccountHistory.end()) + if (auto historyIt = mSubAccountHistory.find(affectedAccount); + historyIt != mSubAccountHistory.end()) { auto& subs = historyIt->second; auto it = subs.begin(); @@ -3199,7 +3361,8 @@ NetworkOPsImp::pubAccountTransaction( if (auto isSptr = info.sinkWptr_.lock(); isSptr) { - accountHistoryNotify.emplace_back(SubAccountHistoryInfo{isSptr, info.index_}); + accountHistoryNotify.emplace_back( + SubAccountHistoryInfo{isSptr, info.index_}); ++it; } else @@ -3214,7 +3377,8 @@ NetworkOPsImp::pubAccountTransaction( } } - JLOG(m_journal.trace()) << "pubAccountTransaction: " << "proposed=" << iProposed << ", accepted=" << iAccepted; + JLOG(m_journal.trace()) << "pubAccountTransaction: " << "proposed=" << iProposed + << ", accepted=" << iAccepted; if (!notify.empty() || !accountHistoryNotify.empty()) { @@ -3275,7 +3439,8 @@ NetworkOPsImp::pubProposedAccountTransaction( { for (auto const& affectedAccount : tx->getMentionedAccounts()) { - if (auto simiIt = mSubRTAccount.find(affectedAccount); simiIt != mSubRTAccount.end()) + if (auto simiIt = mSubRTAccount.find(affectedAccount); + simiIt != mSubRTAccount.end()) { auto it = simiIt->second.begin(); @@ -3290,7 +3455,9 @@ NetworkOPsImp::pubProposedAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } } @@ -3305,9 +3472,11 @@ NetworkOPsImp::pubProposedAccountTransaction( MultiApiJson jvObj = transJson(tx, result, false, ledger, std::nullopt); for (InfoSub::ref isrListener : notify) + { jvObj.visit( isrListener->getApiVersion(), // [&](Json::Value const& jv) { isrListener->send(jv, true); }); + } XRPL_ASSERT( jvObj.isMember(jss::account_history_tx_stream) == MultiApiJson::none, @@ -3331,7 +3500,10 @@ NetworkOPsImp::pubProposedAccountTransaction( // void -NetworkOPsImp::subAccount(InfoSub::ref isrListener, hash_set const& vnaAccountIDs, bool rt) +NetworkOPsImp::subAccount( + InfoSub::ref isrListener, + hash_set const& vnaAccountIDs, + bool rt) { SubInfoMapType& subMap = rt ? mSubRTAccount : mSubAccount; @@ -3364,7 +3536,10 @@ NetworkOPsImp::subAccount(InfoSub::ref isrListener, hash_set const& v } void -NetworkOPsImp::unsubAccount(InfoSub::ref isrListener, hash_set const& vnaAccountIDs, bool rt) +NetworkOPsImp::unsubAccount( + InfoSub::ref isrListener, + hash_set const& vnaAccountIDs, + bool rt) { for (auto const& naAccountID : vnaAccountIDs) { @@ -3377,7 +3552,10 @@ NetworkOPsImp::unsubAccount(InfoSub::ref isrListener, hash_set const& } void -NetworkOPsImp::unsubAccountInternal(std::uint64_t uSeq, hash_set const& vnaAccountIDs, bool rt) +NetworkOPsImp::unsubAccountInternal( + std::uint64_t uSeq, + hash_set const& vnaAccountIDs, + bool rt) { std::lock_guard sl(mSubLock); @@ -3408,7 +3586,7 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) static auto const databaseType = [&]() -> DatabaseType { // Use a dynamic_cast to return DatabaseType::None // on failure. - if (dynamic_cast(&app_.getRelationalDatabase())) + if (dynamic_cast(®istry_.getRelationalDatabase())) { return DatabaseType::Sqlite; } @@ -3419,8 +3597,8 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) { // LCOV_EXCL_START UNREACHABLE("xrpl::NetworkOPsImp::addAccountHistoryJob : no database"); - JLOG(m_journal.error()) << "AccountHistory job for account " << toBase58(subInfo.index_->accountId_) - << " no database"; + JLOG(m_journal.error()) << "AccountHistory job for account " + << toBase58(subInfo.index_->accountId_) << " no database"; if (auto sptr = subInfo.sinkWptr_.lock(); sptr) { sptr->send(rpcError(rpcINTERNAL), true); @@ -3430,244 +3608,261 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) // LCOV_EXCL_STOP } - app_.getJobQueue().addJob(jtCLIENT_ACCT_HIST, "HistTxStream", [this, dbType = databaseType, subInfo]() { - auto const& accountId = subInfo.index_->accountId_; - auto& lastLedgerSeq = subInfo.index_->historyLastLedgerSeq_; - auto& txHistoryIndex = subInfo.index_->historyTxIndex_; + registry_.getJobQueue().addJob( + jtCLIENT_ACCT_HIST, "HistTxStream", [this, dbType = databaseType, subInfo]() { + auto const& accountId = subInfo.index_->accountId_; + auto& lastLedgerSeq = subInfo.index_->historyLastLedgerSeq_; + auto& txHistoryIndex = subInfo.index_->historyTxIndex_; - JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) - << " started. lastLedgerSeq=" << lastLedgerSeq; + JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) + << " started. lastLedgerSeq=" << lastLedgerSeq; - auto isFirstTx = [&](std::shared_ptr const& tx, std::shared_ptr const& meta) -> bool { - /* - * genesis account: first tx is the one with seq 1 - * other account: first tx is the one created the account - */ - if (accountId == genesisAccountId) - { - auto stx = tx->getSTransaction(); - if (stx->getAccountID(sfAccount) == accountId && stx->getSeqValue() == 1) - return true; - } - - for (auto& node : meta->getNodes()) - { - if (node.getFieldU16(sfLedgerEntryType) != ltACCOUNT_ROOT) - continue; - - if (node.isFieldPresent(sfNewFields)) + auto isFirstTx = [&](std::shared_ptr const& tx, + std::shared_ptr const& meta) -> bool { + /* + * genesis account: first tx is the one with seq 1 + * other account: first tx is the one created the account + */ + if (accountId == genesisAccountId) { - if (auto inner = dynamic_cast(node.peekAtPField(sfNewFields)); inner) + auto stx = tx->getSTransaction(); + if (stx->getAccountID(sfAccount) == accountId && stx->getSeqValue() == 1) + return true; + } + + for (auto& node : meta->getNodes()) + { + if (node.getFieldU16(sfLedgerEntryType) != ltACCOUNT_ROOT) + continue; + + if (node.isFieldPresent(sfNewFields)) { - if (inner->isFieldPresent(sfAccount) && inner->getAccountID(sfAccount) == accountId) + if (auto inner = + dynamic_cast(node.peekAtPField(sfNewFields)); + inner) { - return true; + if (inner->isFieldPresent(sfAccount) && + inner->getAccountID(sfAccount) == accountId) + { + return true; + } } } } - } - return false; - }; + return false; + }; - auto send = [&](Json::Value const& jvObj, bool unsubscribe) -> bool { - if (auto sptr = subInfo.sinkWptr_.lock()) - { - sptr->send(jvObj, true); - if (unsubscribe) - unsubAccountHistory(sptr, accountId, false); - return true; - } - - return false; - }; - - auto sendMultiApiJson = [&](MultiApiJson const& jvObj, bool unsubscribe) -> bool { - if (auto sptr = subInfo.sinkWptr_.lock()) - { - jvObj.visit( - sptr->getApiVersion(), // - [&](Json::Value const& jv) { sptr->send(jv, true); }); - - if (unsubscribe) - unsubAccountHistory(sptr, accountId, false); - return true; - } - - return false; - }; - - auto getMoreTxns = [&](std::uint32_t minLedger, - std::uint32_t maxLedger, - std::optional marker) - -> std::optional< - std::pair>> { - switch (dbType) - { - case Sqlite: { - auto db = static_cast(&app_.getRelationalDatabase()); - RelationalDatabase::AccountTxPageOptions options{accountId, minLedger, maxLedger, marker, 0, true}; - return db->newestAccountTxPage(options); - } - // LCOV_EXCL_START - default: { - UNREACHABLE( - "xrpl::NetworkOPsImp::addAccountHistoryJob : " - "getMoreTxns : invalid database type"); - return {}; - } - // LCOV_EXCL_STOP - } - }; - - /* - * search backward until the genesis ledger or asked to stop - */ - while (lastLedgerSeq >= 2 && !subInfo.index_->stopHistorical_) - { - int feeChargeCount = 0; - if (auto sptr = subInfo.sinkWptr_.lock(); sptr) - { - sptr->getConsumer().charge(Resource::feeMediumBurdenRPC); - ++feeChargeCount; - } - else - { - JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) - << " no InfoSub. Fee charged " << feeChargeCount << " times."; - return; - } - - // try to search in 1024 ledgers till reaching genesis ledgers - auto startLedgerSeq = (lastLedgerSeq > 1024 + 2 ? lastLedgerSeq - 1024 : 2); - JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) - << ", working on ledger range [" << startLedgerSeq << "," << lastLedgerSeq << "]"; - - auto haveRange = [&]() -> bool { - std::uint32_t validatedMin = UINT_MAX; - std::uint32_t validatedMax = 0; - auto haveSomeValidatedLedgers = app_.getLedgerMaster().getValidatedRange(validatedMin, validatedMax); - - return haveSomeValidatedLedgers && validatedMin <= startLedgerSeq && lastLedgerSeq <= validatedMax; - }(); - - if (!haveRange) - { - JLOG(m_journal.debug()) << "AccountHistory reschedule job for account " << toBase58(accountId) - << ", incomplete ledger range [" << startLedgerSeq << "," << lastLedgerSeq - << "]"; - setAccountHistoryJobTimer(subInfo); - return; - } - - std::optional marker{}; - while (!subInfo.index_->stopHistorical_) - { - auto dbResult = getMoreTxns(startLedgerSeq, lastLedgerSeq, marker); - if (!dbResult) + auto send = [&](Json::Value const& jvObj, bool unsubscribe) -> bool { + if (auto sptr = subInfo.sinkWptr_.lock()) { - // LCOV_EXCL_START - UNREACHABLE( - "xrpl::NetworkOPsImp::addAccountHistoryJob : " - "getMoreTxns failed"); - JLOG(m_journal.debug()) - << "AccountHistory job for account " << toBase58(accountId) << " getMoreTxns failed."; - send(rpcError(rpcINTERNAL), true); - return; - // LCOV_EXCL_STOP + sptr->send(jvObj, true); + if (unsubscribe) + unsubAccountHistory(sptr, accountId, false); + return true; } - auto const& txns = dbResult->first; - marker = dbResult->second; - size_t num_txns = txns.size(); - for (size_t i = 0; i < num_txns; ++i) - { - auto const& [tx, meta] = txns[i]; + return false; + }; - if (!tx || !meta) - { - JLOG(m_journal.debug()) - << "AccountHistory job for account " << toBase58(accountId) << " empty tx or meta."; - send(rpcError(rpcINTERNAL), true); - return; + auto sendMultiApiJson = [&](MultiApiJson const& jvObj, bool unsubscribe) -> bool { + if (auto sptr = subInfo.sinkWptr_.lock()) + { + jvObj.visit( + sptr->getApiVersion(), // + [&](Json::Value const& jv) { sptr->send(jv, true); }); + + if (unsubscribe) + unsubAccountHistory(sptr, accountId, false); + return true; + } + + return false; + }; + + auto getMoreTxns = [&](std::uint32_t minLedger, + std::uint32_t maxLedger, + std::optional marker) + -> std::optional>> { + switch (dbType) + { + case Sqlite: { + auto db = + safe_downcast(®istry_.getRelationalDatabase()); + RelationalDatabase::AccountTxPageOptions options{ + accountId, minLedger, maxLedger, marker, 0, true}; + return db->newestAccountTxPage(options); } - auto curTxLedger = app_.getLedgerMaster().getLedgerBySeq(tx->getLedger()); - if (!curTxLedger) + // LCOV_EXCL_START + default: { + UNREACHABLE( + "xrpl::NetworkOPsImp::addAccountHistoryJob : " + "getMoreTxns : invalid database type"); + return {}; + } + // LCOV_EXCL_STOP + } + }; + + /* + * search backward until the genesis ledger or asked to stop + */ + while (lastLedgerSeq >= 2 && !subInfo.index_->stopHistorical_) + { + int feeChargeCount = 0; + if (auto sptr = subInfo.sinkWptr_.lock(); sptr) + { + sptr->getConsumer().charge(Resource::feeMediumBurdenRPC); + ++feeChargeCount; + } + else + { + JLOG(m_journal.trace()) + << "AccountHistory job for account " << toBase58(accountId) + << " no InfoSub. Fee charged " << feeChargeCount << " times."; + return; + } + + // try to search in 1024 ledgers till reaching genesis ledgers + auto startLedgerSeq = (lastLedgerSeq > 1024 + 2 ? lastLedgerSeq - 1024 : 2); + JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) + << ", working on ledger range [" << startLedgerSeq << "," + << lastLedgerSeq << "]"; + + auto haveRange = [&]() -> bool { + std::uint32_t validatedMin = UINT_MAX; + std::uint32_t validatedMax = 0; + auto haveSomeValidatedLedgers = + registry_.getLedgerMaster().getValidatedRange(validatedMin, validatedMax); + + return haveSomeValidatedLedgers && validatedMin <= startLedgerSeq && + lastLedgerSeq <= validatedMax; + }(); + + if (!haveRange) + { + JLOG(m_journal.debug()) << "AccountHistory reschedule job for account " + << toBase58(accountId) << ", incomplete ledger range [" + << startLedgerSeq << "," << lastLedgerSeq << "]"; + setAccountHistoryJobTimer(subInfo); + return; + } + + std::optional marker{}; + while (!subInfo.index_->stopHistorical_) + { + auto dbResult = getMoreTxns(startLedgerSeq, lastLedgerSeq, marker); + if (!dbResult) { // LCOV_EXCL_START UNREACHABLE( "xrpl::NetworkOPsImp::addAccountHistoryJob : " - "getLedgerBySeq failed"); - JLOG(m_journal.debug()) - << "AccountHistory job for account " << toBase58(accountId) << " no ledger."; - send(rpcError(rpcINTERNAL), true); - return; - // LCOV_EXCL_STOP - } - std::shared_ptr stTxn = tx->getSTransaction(); - if (!stTxn) - { - // LCOV_EXCL_START - UNREACHABLE( - "NetworkOPsImp::addAccountHistoryJob : " - "getSTransaction failed"); - JLOG(m_journal.debug()) - << "AccountHistory job for account " << toBase58(accountId) << " getSTransaction failed."; + "getMoreTxns failed"); + JLOG(m_journal.debug()) << "AccountHistory job for account " + << toBase58(accountId) << " getMoreTxns failed."; send(rpcError(rpcINTERNAL), true); return; // LCOV_EXCL_STOP } - auto const mRef = std::ref(*meta); - auto const trR = meta->getResultTER(); - MultiApiJson jvTx = transJson(stTxn, trR, true, curTxLedger, mRef); - - jvTx.set(jss::account_history_tx_index, txHistoryIndex--); - if (i + 1 == num_txns || txns[i + 1].first->getLedger() != tx->getLedger()) - jvTx.set(jss::account_history_boundary, true); - - if (isFirstTx(tx, meta)) + auto const& txns = dbResult->first; + marker = dbResult->second; + size_t num_txns = txns.size(); + for (size_t i = 0; i < num_txns; ++i) { - jvTx.set(jss::account_history_tx_first, true); + auto const& [tx, meta] = txns[i]; + + if (!tx || !meta) + { + JLOG(m_journal.debug()) << "AccountHistory job for account " + << toBase58(accountId) << " empty tx or meta."; + send(rpcError(rpcINTERNAL), true); + return; + } + auto curTxLedger = + registry_.getLedgerMaster().getLedgerBySeq(tx->getLedger()); + if (!curTxLedger) + { + // LCOV_EXCL_START + UNREACHABLE( + "xrpl::NetworkOPsImp::addAccountHistoryJob : " + "getLedgerBySeq failed"); + JLOG(m_journal.debug()) << "AccountHistory job for account " + << toBase58(accountId) << " no ledger."; + send(rpcError(rpcINTERNAL), true); + return; + // LCOV_EXCL_STOP + } + std::shared_ptr stTxn = tx->getSTransaction(); + if (!stTxn) + { + // LCOV_EXCL_START + UNREACHABLE( + "NetworkOPsImp::addAccountHistoryJob : " + "getSTransaction failed"); + JLOG(m_journal.debug()) + << "AccountHistory job for account " << toBase58(accountId) + << " getSTransaction failed."; + send(rpcError(rpcINTERNAL), true); + return; + // LCOV_EXCL_STOP + } + + auto const mRef = std::ref(*meta); + auto const trR = meta->getResultTER(); + MultiApiJson jvTx = transJson(stTxn, trR, true, curTxLedger, mRef); + + jvTx.set(jss::account_history_tx_index, txHistoryIndex--); + if (i + 1 == num_txns || txns[i + 1].first->getLedger() != tx->getLedger()) + jvTx.set(jss::account_history_boundary, true); + + if (isFirstTx(tx, meta)) + { + jvTx.set(jss::account_history_tx_first, true); + sendMultiApiJson(jvTx, false); + + JLOG(m_journal.trace()) + << "AccountHistory job for account " << toBase58(accountId) + << " done, found last tx."; + return; + } + sendMultiApiJson(jvTx, false); + } + if (marker) + { JLOG(m_journal.trace()) - << "AccountHistory job for account " << toBase58(accountId) << " done, found last tx."; - return; + << "AccountHistory job for account " << toBase58(accountId) + << " paging, marker=" << marker->ledgerSeq << ":" << marker->txnSeq; } else { - sendMultiApiJson(jvTx, false); + break; } } - if (marker) + if (!subInfo.index_->stopHistorical_) { - JLOG(m_journal.trace()) << "AccountHistory job for account " << toBase58(accountId) - << " paging, marker=" << marker->ledgerSeq << ":" << marker->txnSeq; - } - else - { - break; + lastLedgerSeq = startLedgerSeq - 1; + if (lastLedgerSeq <= 1) + { + JLOG(m_journal.trace()) + << "AccountHistory job for account " << toBase58(accountId) + << " done, reached genesis ledger."; + return; + } } } - - if (!subInfo.index_->stopHistorical_) - { - lastLedgerSeq = startLedgerSeq - 1; - if (lastLedgerSeq <= 1) - { - JLOG(m_journal.trace()) - << "AccountHistory job for account " << toBase58(accountId) << " done, reached genesis ledger."; - return; - } - } - } - }); + }); } void -NetworkOPsImp::subAccountHistoryStart(std::shared_ptr const& ledger, SubAccountHistoryInfoWeak& subInfo) +NetworkOPsImp::subAccountHistoryStart( + std::shared_ptr const& ledger, + SubAccountHistoryInfoWeak& subInfo) { subInfo.index_->separationLedgerSeq_ = ledger->seq(); auto const& accountId = subInfo.index_->accountId_; @@ -3684,8 +3879,9 @@ NetworkOPsImp::subAccountHistoryStart(std::shared_ptr const& led { if (sleAcct->getFieldU32(sfSequence) == 1) { - JLOG(m_journal.debug()) << "subAccountHistoryStart, genesis account " << toBase58(accountId) - << " does not have tx, no need to add AccountHistory job."; + JLOG(m_journal.debug()) + << "subAccountHistoryStart, genesis account " << toBase58(accountId) + << " does not have tx, no need to add AccountHistory job."; return; } } @@ -3702,8 +3898,8 @@ NetworkOPsImp::subAccountHistoryStart(std::shared_ptr const& led subInfo.index_->historyLastLedgerSeq_ = ledger->seq(); subInfo.index_->haveHistorical_ = true; - JLOG(m_journal.debug()) << "subAccountHistoryStart, add AccountHistory job: accountId=" << toBase58(accountId) - << ", currentLedgerSeq=" << ledger->seq(); + JLOG(m_journal.debug()) << "subAccountHistoryStart, add AccountHistory job: accountId=" + << toBase58(accountId) << ", currentLedgerSeq=" << ledger->seq(); addAccountHistoryJob(subInfo); } @@ -3713,7 +3909,8 @@ NetworkOPsImp::subAccountHistory(InfoSub::ref isrListener, AccountID const& acco { if (!isrListener->insertSubAccountHistory(accountId)) { - JLOG(m_journal.debug()) << "subAccountHistory, already subscribed to account " << toBase58(accountId); + JLOG(m_journal.debug()) << "subAccountHistory, already subscribed to account " + << toBase58(accountId); return rpcINVALID_PARAMS; } @@ -3731,7 +3928,7 @@ NetworkOPsImp::subAccountHistory(InfoSub::ref isrListener, AccountID const& acco simIterator->second.emplace(isrListener->getSeq(), ahi); } - auto const ledger = app_.getLedgerMaster().getValidatedLedger(); + auto const ledger = registry_.getLedgerMaster().getValidatedLedger(); if (ledger) { subAccountHistoryStart(ledger, ahi); @@ -3748,7 +3945,10 @@ NetworkOPsImp::subAccountHistory(InfoSub::ref isrListener, AccountID const& acco } void -NetworkOPsImp::unsubAccountHistory(InfoSub::ref isrListener, AccountID const& account, bool historyOnly) +NetworkOPsImp::unsubAccountHistory( + InfoSub::ref isrListener, + AccountID const& account, + bool historyOnly) { if (!historyOnly) isrListener->deleteSubAccountHistory(account); @@ -3756,7 +3956,10 @@ NetworkOPsImp::unsubAccountHistory(InfoSub::ref isrListener, AccountID const& ac } void -NetworkOPsImp::unsubAccountHistoryInternal(std::uint64_t seq, AccountID const& account, bool historyOnly) +NetworkOPsImp::unsubAccountHistoryInternal( + std::uint64_t seq, + AccountID const& account, + bool historyOnly) { std::lock_guard sl(mSubLock); auto simIterator = mSubAccountHistory.find(account); @@ -3785,8 +3988,10 @@ NetworkOPsImp::unsubAccountHistoryInternal(std::uint64_t seq, AccountID const& a bool NetworkOPsImp::subBook(InfoSub::ref isrListener, Book const& book) { - if (auto listeners = app_.getOrderBookDB().makeBookListeners(book)) + if (auto listeners = registry_.getOrderBookDB().makeBookListeners(book)) + { listeners->addSubscriber(isrListener); + } else { // LCOV_EXCL_START @@ -3799,7 +4004,7 @@ NetworkOPsImp::subBook(InfoSub::ref isrListener, Book const& book) bool NetworkOPsImp::unsubBook(std::uint64_t uSeq, Book const& book) { - if (auto listeners = app_.getOrderBookDB().getBookListeners(book)) + if (auto listeners = registry_.getOrderBookDB().getBookListeners(book)) listeners->removeSubscriber(uSeq); return true; @@ -3818,7 +4023,7 @@ NetworkOPsImp::acceptLedger(std::optional consensusDe // FIXME Could we improve on this and remove the need for a specialized // API in Consensus? beginConsensus(m_ledgerMaster.getClosedLedger()->header().hash, {}); - mConsensus.simulate(app_.timeKeeper().closeTime(), consensusDelay); + mConsensus.simulate(registry_.timeKeeper().closeTime(), consensusDelay); return m_ledgerMaster.getCurrentLedger()->header().seq; } @@ -3830,13 +4035,14 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) { jvResult[jss::ledger_index] = lpClosed->header().seq; jvResult[jss::ledger_hash] = to_string(lpClosed->header().hash); - jvResult[jss::ledger_time] = Json::Value::UInt(lpClosed->header().closeTime.time_since_epoch().count()); + jvResult[jss::ledger_time] = + Json::Value::UInt(lpClosed->header().closeTime.time_since_epoch().count()); if (!lpClosed->rules().enabled(featureXRPFees)) jvResult[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; jvResult[jss::fee_base] = lpClosed->fees().base.jsonClipped(); jvResult[jss::reserve_base] = lpClosed->fees().reserve.jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); - jvResult[jss::network_id] = app_.config().NETWORK_ID; + jvResult[jss::network_id] = registry_.getNetworkIDService().getNetworkID(); if (lpClosed->rules().enabled(featureSmartEscrow)) { jvResult[jss::extension_compute] = lpClosed->fees().extensionComputeLimit; @@ -3847,7 +4053,7 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger()) { - jvResult[jss::validated_ledgers] = app_.getLedgerMaster().getCompleteLedgers(); + jvResult[jss::validated_ledgers] = registry_.getLedgerMaster().getCompleteLedgers(); } std::lock_guard sl(mSubLock); @@ -3906,13 +4112,14 @@ NetworkOPsImp::subServer(InfoSub::ref isrListener, Json::Value& jvResult, bool a // CHECKME: is it necessary to provide a random number here? beast::rngfill(uRandom.begin(), uRandom.size(), crypto_prng()); - auto const& feeTrack = app_.getFeeTrack(); + auto const& feeTrack = registry_.getFeeTrack(); jvResult[jss::random] = to_string(uRandom); jvResult[jss::server_status] = strOperatingMode(admin); jvResult[jss::load_base] = feeTrack.getLoadBase(); jvResult[jss::load_factor] = feeTrack.getLoadFactor(); jvResult[jss::hostid] = getHostId(admin); - jvResult[jss::pubkey_node] = toBase58(TokenType::NodePublic, app_.nodeIdentity().first); + jvResult[jss::pubkey_node] = + toBase58(TokenType::NodePublic, registry_.app().nodeIdentity().first); std::lock_guard sl(mSubLock); return mStreamMaps[sServer].emplace(isrListener->getSeq(), isrListener).second; @@ -4048,7 +4255,7 @@ NetworkOPsImp::tryRemoveRpcSub(std::string const& strUrl) // this entry before removing for (SubMapType const& map : mStreamMaps) { - if (map.find(pInfo->getSeq()) != map.end()) + if (map.contains(pInfo->getSeq())) return false; } mRpcSubMap.erase(strUrl); @@ -4087,18 +4294,19 @@ NetworkOPsImp::getBookPage( ReadView const& view = *lpLedger; - bool const bGlobalFreeze = isGlobalFrozen(view, book.out.account) || isGlobalFrozen(view, book.in.account); + bool const bGlobalFreeze = + isGlobalFrozen(view, book.out.account) || isGlobalFrozen(view, book.in.account); bool bDone = false; bool bDirectAdvance = true; std::shared_ptr sleOfferDir; uint256 offerIndex; - unsigned int uBookEntry; + unsigned int uBookEntry = 0; STAmount saDirRate; auto const rate = transferRate(view, book.out.account); - auto viewJ = app_.journal("View"); + auto viewJ = registry_.journal("View"); while (!bDone && iLimit-- > 0) { @@ -4110,9 +4318,13 @@ NetworkOPsImp::getBookPage( auto const ledgerIndex = view.succ(uTipIndex, uBookEnd); if (ledgerIndex) + { sleOfferDir = view.read(keylet::page(*ledgerIndex)); + } else + { sleOfferDir.reset(); + } if (!sleOfferDir) { @@ -4170,7 +4382,12 @@ NetworkOPsImp::getBookPage( // Did not find balance in table. saOwnerFunds = accountHolds( - view, uOfferOwnerID, book.out.currency, book.out.account, fhZERO_IF_FROZEN, viewJ); + view, + uOfferOwnerID, + book.out.currency, + book.out.account, + fhZERO_IF_FROZEN, + viewJ); if (saOwnerFunds < beast::zero) { @@ -4211,7 +4428,8 @@ NetworkOPsImp::getBookPage( saTakerGetsFunded = saOwnerFundsLimit; saTakerGetsFunded.setJson(jvOffer[jss::taker_gets_funded]); - std::min(saTakerPays, multiply(saTakerGetsFunded, saDirRate, saTakerPays.issue())) + std::min( + saTakerPays, multiply(saTakerGetsFunded, saDirRate, saTakerPays.issue())) .setJson(jvOffer[jss::taker_pays_funded]); } @@ -4272,7 +4490,8 @@ NetworkOPsImp::getBookPage( auto const rate = transferRate(lesActive, book.out.account); - bool const bGlobalFreeze = lesActive.isGlobalFrozen(book.out.account) || lesActive.isGlobalFrozen(book.in.account); + bool const bGlobalFreeze = + lesActive.isGlobalFrozen(book.out.account) || lesActive.isGlobalFrozen(book.in.account); while (iLimit-- > 0 && obIterator.nextOffer()) { @@ -4310,8 +4529,8 @@ NetworkOPsImp::getBookPage( { // Did not find balance in table. - saOwnerFunds = - lesActive.accountHolds(uOfferOwnerID, book.out.currency, book.out.account, fhZERO_IF_FROZEN); + saOwnerFunds = lesActive.accountHolds( + uOfferOwnerID, book.out.currency, book.out.account, fhZERO_IF_FROZEN); if (saOwnerFunds.isNegative()) { @@ -4383,22 +4602,31 @@ inline void NetworkOPsImp::collect_metrics() { auto [counters, mode, start, initialSync] = accounting_.getCounterData(); - auto const current = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + auto const current = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start); counters[static_cast(mode)].dur += current; std::lock_guard lock(m_statsMutex); - m_stats.disconnected_duration.set(counters[static_cast(OperatingMode::DISCONNECTED)].dur.count()); - m_stats.connected_duration.set(counters[static_cast(OperatingMode::CONNECTED)].dur.count()); - m_stats.syncing_duration.set(counters[static_cast(OperatingMode::SYNCING)].dur.count()); - m_stats.tracking_duration.set(counters[static_cast(OperatingMode::TRACKING)].dur.count()); + m_stats.disconnected_duration.set( + counters[static_cast(OperatingMode::DISCONNECTED)].dur.count()); + m_stats.connected_duration.set( + counters[static_cast(OperatingMode::CONNECTED)].dur.count()); + m_stats.syncing_duration.set( + counters[static_cast(OperatingMode::SYNCING)].dur.count()); + m_stats.tracking_duration.set( + counters[static_cast(OperatingMode::TRACKING)].dur.count()); m_stats.full_duration.set(counters[static_cast(OperatingMode::FULL)].dur.count()); - m_stats.disconnected_transitions.set(counters[static_cast(OperatingMode::DISCONNECTED)].transitions); - m_stats.connected_transitions.set(counters[static_cast(OperatingMode::CONNECTED)].transitions); - m_stats.syncing_transitions.set(counters[static_cast(OperatingMode::SYNCING)].transitions); - m_stats.tracking_transitions.set(counters[static_cast(OperatingMode::TRACKING)].transitions); - m_stats.full_transitions.set(counters[static_cast(OperatingMode::FULL)].transitions); + m_stats.disconnected_transitions.set( + counters[static_cast(OperatingMode::DISCONNECTED)].transitions); + m_stats.connected_transitions.set( + counters[static_cast(OperatingMode::CONNECTED)].transitions); + m_stats.syncing_transitions.set( + counters[static_cast(OperatingMode::SYNCING)].transitions); + m_stats.tracking_transitions.set( + counters[static_cast(OperatingMode::TRACKING)].transitions); + m_stats.full_transitions.set( + counters[static_cast(OperatingMode::FULL)].transitions); } void @@ -4410,7 +4638,8 @@ NetworkOPsImp::StateAccounting::mode(OperatingMode om) ++counters_[static_cast(om)].transitions; if (om == OperatingMode::FULL && counters_[static_cast(om)].transitions == 1) { - initialSyncUs_ = std::chrono::duration_cast(now - processStart_).count(); + initialSyncUs_ = + std::chrono::duration_cast(now - processStart_).count(); } counters_[static_cast(mode_)].dur += std::chrono::duration_cast(now - start_); @@ -4423,8 +4652,8 @@ void NetworkOPsImp::StateAccounting::json(Json::Value& obj) const { auto [counters, mode, start, initialSync] = getCounterData(); - auto const current = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + auto const current = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start); counters[static_cast(mode)].dur += current; obj[jss::state_accounting] = Json::objectValue; @@ -4446,7 +4675,7 @@ NetworkOPsImp::StateAccounting::json(Json::Value& obj) const std::unique_ptr make_NetworkOPs( - Application& app, + ServiceRegistry& registry, NetworkOPs::clock_type& clock, bool standalone, std::size_t minPeerCount, @@ -4459,7 +4688,7 @@ make_NetworkOPs( beast::insight::Collector::ptr const& collector) { return std::make_unique( - app, + registry, clock, standalone, minPeerCount, diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index dbdd682ef8..13746369f5 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -1,13 +1,13 @@ #include -#include #include -#include #include #include #include #include #include +#include +#include #include #include @@ -60,7 +60,10 @@ SHAMapStoreImp::SavedStateDB::setLastRotated(LedgerIndex seq) //------------------------------------------------------------------------------ -SHAMapStoreImp::SHAMapStoreImp(Application& app, NodeStore::Scheduler& scheduler, beast::Journal journal) +SHAMapStoreImp::SHAMapStoreImp( + Application& app, + NodeStore::Scheduler& scheduler, + beast::Journal journal) : app_(app) , scheduler_(scheduler) , journal_(journal) @@ -72,7 +75,8 @@ SHAMapStoreImp::SHAMapStoreImp(Application& app, NodeStore::Scheduler& scheduler Section& section{config.section(ConfigSection::nodeDatabase())}; if (section.empty()) { - Throw("Missing [" + ConfigSection::nodeDatabase() + "] entry in configuration file"); + Throw( + "Missing [" + ConfigSection::nodeDatabase() + "] entry in configuration file"); } // RocksDB only. Use sensible defaults if no values specified. @@ -93,7 +97,7 @@ SHAMapStoreImp::SHAMapStoreImp(Application& app, NodeStore::Scheduler& scheduler { // Configuration that affects the behavior of online delete get_if_exists(section, "delete_batch", deleteBatch_); - std::uint32_t temp; + std::uint32_t temp = 0; if (get_if_exists(section, "back_off_milliseconds", temp) || // Included for backward compatibility with an undocumented setting get_if_exists(section, "backOff", temp)) @@ -107,10 +111,12 @@ SHAMapStoreImp::SHAMapStoreImp(Application& app, NodeStore::Scheduler& scheduler get_if_exists(section, "advisory_delete", advisoryDelete_); - auto const minInterval = config.standalone() ? minimumDeletionIntervalSA_ : minimumDeletionInterval_; + auto const minInterval = + config.standalone() ? minimumDeletionIntervalSA_ : minimumDeletionInterval_; if (deleteInterval_ < minInterval) { - Throw("online_delete must be at least " + std::to_string(minInterval)); + Throw( + "online_delete must be at least " + std::to_string(minInterval)); } if (config.LEDGER_HISTORY > deleteInterval_) @@ -137,7 +143,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads) SavedState state = state_db_.getState(); auto writableBackend = makeBackendRotating(state.writableDb); auto archiveBackend = makeBackendRotating(state.archiveDb); - if (!state.writableDb.size()) + if (state.writableDb.empty()) { state.writableDb = writableBackend->getName(); state.archiveDb = archiveBackend->getName(); @@ -201,7 +207,8 @@ bool SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node) { // Copy a single record from node to dbRotating_ - dbRotating_->fetchNodeObject(node.getHash().as_uint256(), 0, NodeStore::FetchType::synchronous, true); + dbRotating_->fetchNodeObject( + node.getHash().as_uint256(), 0, NodeStore::FetchType::synchronous, true); if (!(++nodeCount % checkHealthInterval_)) { if (healthWait() == stopping) @@ -241,7 +248,9 @@ SHAMapStoreImp::run() validatedLedger = std::move(newLedger_); } else + { continue; + } } LedgerIndex const validatedSeq = validatedLedger->header().seq; @@ -251,14 +260,15 @@ SHAMapStoreImp::run() state_db_.setLastRotated(lastRotated); } - bool const readyToRotate = - validatedSeq >= lastRotated + deleteInterval_ && canDelete_ >= lastRotated - 1 && healthWait() == keepGoing; + bool const readyToRotate = validatedSeq >= lastRotated + deleteInterval_ && + canDelete_ >= lastRotated - 1 && healthWait() == keepGoing; // will delete up to (not including) lastRotated if (readyToRotate) { - JLOG(journal_.warn()) << "rotating validatedSeq " << validatedSeq << " lastRotated " << lastRotated - << " deleteInterval " << deleteInterval_ << " canDelete_ " << canDelete_ << " state " + JLOG(journal_.warn()) << "rotating validatedSeq " << validatedSeq << " lastRotated " + << lastRotated << " deleteInterval " << deleteInterval_ + << " canDelete_ " << canDelete_ << " state " << app_.getOPs().strOperatingMode(false) << " age " << ledgerMaster_->getValidatedLedgerAge().count() << 's'; @@ -272,18 +282,24 @@ SHAMapStoreImp::run() try { validatedLedger->stateMap().snapShot(false)->visitNodes( - std::bind(&SHAMapStoreImp::copyNode, this, std::ref(nodeCount), std::placeholders::_1)); + std::bind( + &SHAMapStoreImp::copyNode, + this, + std::ref(nodeCount), + std::placeholders::_1)); } catch (SHAMapMissingNode const& e) { - JLOG(journal_.error()) << "Missing node while copying ledger before rotate: " << e.what(); + JLOG(journal_.error()) + << "Missing node while copying ledger before rotate: " << e.what(); continue; } if (healthWait() == stopping) return; // Only log if we completed without a "health" abort - JLOG(journal_.debug()) << "copied ledger " << validatedSeq << " nodecount " << nodeCount; + JLOG(journal_.debug()) + << "copied ledger " << validatedSeq << " nodecount " << nodeCount; JLOG(journal_.debug()) << "freshening caches"; freshenCaches(); @@ -303,7 +319,8 @@ SHAMapStoreImp::run() lastRotated = validatedSeq; dbRotating_->rotate( - std::move(newBackend), [&](std::string const& writableName, std::string const& archiveName) { + std::move(newBackend), + [&](std::string const& writableName, std::string const& archiveName) { SavedState savedState; savedState.writableDb = writableName; savedState.archiveDb = archiveName; @@ -365,29 +382,41 @@ SHAMapStoreImp::dbPaths() bool archiveDbExists = false; std::vector pathsToDelete; - for (boost::filesystem::directory_iterator it(dbPath); it != boost::filesystem::directory_iterator(); ++it) + for (boost::filesystem::directory_iterator it(dbPath); + it != boost::filesystem::directory_iterator(); + ++it) { if (!state.writableDb.compare(it->path().string())) + { writableDbExists = true; + } else if (!state.archiveDb.compare(it->path().string())) + { archiveDbExists = true; + } else if (!dbPrefix_.compare(it->path().stem().string())) + { pathsToDelete.push_back(it->path()); + } } - if ((!writableDbExists && state.writableDb.size()) || (!archiveDbExists && state.archiveDb.size()) || - (writableDbExists != archiveDbExists) || state.writableDb.empty() != state.archiveDb.empty()) + if ((!writableDbExists && !state.writableDb.empty()) || + (!archiveDbExists && !state.archiveDb.empty()) || (writableDbExists != archiveDbExists) || + state.writableDb.empty() != state.archiveDb.empty()) { boost::filesystem::path stateDbPathName = app_.config().legacy("database_path"); stateDbPathName /= dbName_; stateDbPathName += "*"; journal_.error() << "state db error:\n" - << " writableDbExists " << writableDbExists << " archiveDbExists " << archiveDbExists << '\n' - << " writableDb '" << state.writableDb << "' archiveDb '" << state.archiveDb << "\n\n" + << " writableDbExists " << writableDbExists << " archiveDbExists " + << archiveDbExists << '\n' + << " writableDb '" << state.writableDb << "' archiveDb '" + << state.archiveDb << "\n\n" << "The existing data is in a corrupted state.\n" - << "To resume operation, remove the files matching " << stateDbPathName.string() - << " and contents of the directory " << get(section, "path") << '\n' + << "To resume operation, remove the files matching " + << stateDbPathName.string() << " and contents of the directory " + << get(section, "path") << '\n' << "Optionally, you can move those files to another\n" << "location if you wish to analyze or back up the data.\n" << "However, there is no guarantee that the data in its\n" @@ -407,7 +436,7 @@ SHAMapStoreImp::makeBackendRotating(std::string path) Section section{app_.config().section(ConfigSection::nodeDatabase())}; boost::filesystem::path newPath; - if (path.size()) + if (!path.empty()) { newPath = path; } @@ -457,15 +486,16 @@ SHAMapStoreImp::clearSql( return; } - JLOG(journal_.debug()) << "start deleting in: " << TableName << " from " << min << " to " << lastRotated; + JLOG(journal_.debug()) << "start deleting in: " << TableName << " from " << min << " to " + << lastRotated; while (min < lastRotated) { min = std::min(lastRotated, min + deleteBatch_); - JLOG(journal_.trace()) << "Begin: Delete up to " << deleteBatch_ << " rows with LedgerSeq < " << min - << " from: " << TableName; + JLOG(journal_.trace()) << "Begin: Delete up to " << deleteBatch_ + << " rows with LedgerSeq < " << min << " from: " << TableName; deleteBeforeSeq(min); - JLOG(journal_.trace()) << "End: Delete up to " << deleteBatch_ << " rows with LedgerSeq < " << min - << " from: " << TableName; + JLOG(journal_.trace()) << "End: Delete up to " << deleteBatch_ << " rows with LedgerSeq < " + << min << " from: " << TableName; if (healthWait() == stopping) return; if (min < lastRotated) @@ -507,16 +537,13 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated) if (healthWait() == stopping) return; - SQLiteDatabase* const db = dynamic_cast(&app_.getRelationalDatabase()); - - if (!db) - Throw("Failed to get relational database"); + auto& db = app_.getRelationalDatabase(); clearSql( lastRotated, "Ledgers", - [db]() -> std::optional { return db->getMinLedgerSeq(); }, - [db](LedgerIndex min) -> void { db->deleteBeforeLedgerSeq(min); }); + [&db]() -> std::optional { return db.getMinLedgerSeq(); }, + [&db](LedgerIndex min) -> void { db.deleteBeforeLedgerSeq(min); }); if (healthWait() == stopping) return; @@ -526,16 +553,16 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated) clearSql( lastRotated, "Transactions", - [&db]() -> std::optional { return db->getTransactionsMinLedgerSeq(); }, - [&db](LedgerIndex min) -> void { db->deleteTransactionsBeforeLedgerSeq(min); }); + [&db]() -> std::optional { return db.getTransactionsMinLedgerSeq(); }, + [&db](LedgerIndex min) -> void { db.deleteTransactionsBeforeLedgerSeq(min); }); if (healthWait() == stopping) return; clearSql( lastRotated, "AccountTransactions", - [&db]() -> std::optional { return db->getAccountTransactionsMinLedgerSeq(); }, - [&db](LedgerIndex min) -> void { db->deleteAccountTransactionsBeforeLedgerSeq(min); }); + [&db]() -> std::optional { return db.getAccountTransactionsMinLedgerSeq(); }, + [&db](LedgerIndex min) -> void { db.deleteAccountTransactionsBeforeLedgerSeq(min); }); if (healthWait() == stopping) return; } @@ -550,8 +577,9 @@ SHAMapStoreImp::healthWait() { lock.unlock(); JLOG(journal_.warn()) << "Waiting " << recoveryWaitTime_.count() - << "s for node to stabilize. state: " << app_.getOPs().strOperatingMode(mode, false) - << ". age " << age.count() << 's'; + << "s for node to stabilize. state: " + << app_.getOPs().strOperatingMode(mode, false) << ". age " + << age.count() << 's'; std::this_thread::sleep_for(recoveryWaitTime_); age = ledgerMaster_->getValidatedLedgerAge(); mode = netOPs_->getOperatingMode(); diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index b046a78979..df3c16b24f 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -2,11 +2,11 @@ #include #include -#include -#include #include #include +#include +#include #include #include diff --git a/src/xrpld/app/misc/Transaction.h b/src/xrpld/app/misc/Transaction.h index 22f3e9d1fd..fa126e6bb5 100644 --- a/src/xrpld/app/misc/Transaction.h +++ b/src/xrpld/app/misc/Transaction.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,11 +36,10 @@ enum TransStatus { INCOMPLETE = 8 // needs more signatures }; -enum class TxSearched { all, some, unknown }; - // This class is for constructing and examining transactions. // Transactions are static so manipulation functions are unnecessary. -class Transaction : public std::enable_shared_from_this, public CountedObject +class Transaction : public std::enable_shared_from_this, + public CountedObject { public: using pointer = std::shared_ptr; @@ -246,8 +246,15 @@ public: { CurrentLedgerState() = delete; - CurrentLedgerState(LedgerIndex li, XRPAmount fee, std::uint32_t accSeqNext, std::uint32_t accSeqAvail) - : validatedLedger{li}, minFeeRequired{fee}, accountSeqNext{accSeqNext}, accountSeqAvail{accSeqAvail} + CurrentLedgerState( + LedgerIndex li, + XRPAmount fee, + std::uint32_t accSeqNext, + std::uint32_t accSeqAvail) + : validatedLedger{li} + , minFeeRequired{fee} + , accountSeqNext{accSeqNext} + , accountSeqAvail{accSeqAvail} { } @@ -337,15 +344,26 @@ public: static Locator locate(uint256 const& id, Application& app); - static std::variant, std::shared_ptr>, TxSearched> - load(uint256 const& id, Application& app, error_code_i& ec); + static std:: + variant, std::shared_ptr>, TxSearched> + load(uint256 const& id, Application& app, error_code_i& ec); - static std::variant, std::shared_ptr>, TxSearched> - load(uint256 const& id, Application& app, ClosedInterval const& range, error_code_i& ec); + static std:: + variant, std::shared_ptr>, TxSearched> + load( + uint256 const& id, + Application& app, + ClosedInterval const& range, + error_code_i& ec); private: - static std::variant, std::shared_ptr>, TxSearched> - load(uint256 const& id, Application& app, std::optional> const& range, error_code_i& ec); + static std:: + variant, std::shared_ptr>, TxSearched> + load( + uint256 const& id, + Application& app, + std::optional> const& range, + error_code_i& ec); uint256 mTransactionID; diff --git a/src/xrpld/app/misc/TxQ.h b/src/xrpld/app/misc/TxQ.h index b50ff1a0b5..4f02d6d617 100644 --- a/src/xrpld/app/misc/TxQ.h +++ b/src/xrpld/app/misc/TxQ.h @@ -1,13 +1,12 @@ #pragma once -#include - #include #include #include #include #include #include +#include #include #include @@ -251,7 +250,12 @@ public: will return `{ terQUEUED, false }`. */ ApplyResult - apply(Application& app, OpenView& view, std::shared_ptr const& tx, ApplyFlags flags, beast::Journal j); + apply( + Application& app, + OpenView& view, + std::shared_ptr const& tx, + ApplyFlags flags, + beast::Journal j); /** Fill the new open ledger with transactions from the queue. @@ -338,7 +342,9 @@ public: private: // Implementation for nextQueuableSeq(). The passed lock must be held. SeqProxy - nextQueuableSeqImpl(std::shared_ptr const& sleAccount, std::lock_guard const&) const; + nextQueuableSeqImpl( + std::shared_ptr const& sleAccount, + std::lock_guard const&) const; /** Track and use the fee escalation metrics of the @@ -371,12 +377,16 @@ private: public: /// Constructor FeeMetrics(Setup const& setup, beast::Journal j) - : minimumTxnCount_(setup.standAlone ? setup.minimumTxnInLedgerSA : setup.minimumTxnInLedger) - , targetTxnCount_(setup.targetTxnInLedger < minimumTxnCount_ ? minimumTxnCount_ : setup.targetTxnInLedger) + : minimumTxnCount_( + setup.standAlone ? setup.minimumTxnInLedgerSA : setup.minimumTxnInLedger) + , targetTxnCount_( + setup.targetTxnInLedger < minimumTxnCount_ ? minimumTxnCount_ + : setup.targetTxnInLedger) , maximumTxnCount_( - setup.maximumTxnInLedger - ? *setup.maximumTxnInLedger < targetTxnCount_ ? targetTxnCount_ : *setup.maximumTxnInLedger - : std::optional(std::nullopt)) + setup.maximumTxnInLedger ? *setup.maximumTxnInLedger < targetTxnCount_ + ? targetTxnCount_ + : *setup.maximumTxnInLedger + : std::optional(std::nullopt)) , txnsExpected_(minimumTxnCount_) , recentTxnCounts_(setup.ledgersInQueue) , escalationMultiplier_(setup.minimumEscalationMultiplier) @@ -706,10 +716,11 @@ private: std::optional const& replacedTxIter, std::shared_ptr const& tx); - using FeeHook = - boost::intrusive::member_hook, &MaybeTx::byFeeListHook>; + using FeeHook = boost::intrusive:: + member_hook, &MaybeTx::byFeeListHook>; - using FeeMultiSet = boost::intrusive::multiset>; + using FeeMultiSet = + boost::intrusive::multiset>; using AccountMap = std::map; @@ -782,7 +793,10 @@ private: FeeMultiSet::iterator_type eraseAndAdvance(FeeMultiSet::const_iterator_type); /// Erase a range of items, based on TxQAccount::TxMap iterators TxQAccount::TxMap::iterator - erase(TxQAccount& txQAccount, TxQAccount::TxMap::const_iterator begin, TxQAccount::TxMap::const_iterator end); + erase( + TxQAccount& txQAccount, + TxQAccount::TxMap::const_iterator begin, + TxQAccount::TxMap::const_iterator end); /** All-or-nothing attempt to try to apply the queued txs for @@ -820,7 +834,8 @@ toDrops(FeeLevel const& level, XRPAmount baseFee) inline FeeLevel64 toFeeLevel(XRPAmount const& drops, XRPAmount const& baseFee) { - return mulDiv(drops, TxQ::baseLevel, baseFee).value_or(FeeLevel64(std::numeric_limits::max())); + return mulDiv(drops, TxQ::baseLevel, baseFee) + .value_or(FeeLevel64(std::numeric_limits::max())); } } // namespace xrpl diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index 4fd610be04..e774da4713 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include @@ -292,7 +292,10 @@ public: struct MessageWithHash { explicit MessageWithHash() = default; - explicit MessageWithHash(std::shared_ptr const& message_, uint256 hash_, std::size_t num_); + explicit MessageWithHash( + std::shared_ptr const& message_, + uint256 hash_, + std::size_t num_); std::shared_ptr message; uint256 hash; std::size_t numVLs = 0; @@ -596,13 +599,14 @@ public: May be called concurrently */ void - for_each_available(std::function const& blobInfos, - PublicKey const& pubKey, - std::size_t maxSequence, - uint256 const& hash)> func) const; + for_each_available( + std::function const& blobInfos, + PublicKey const& pubKey, + std::size_t maxSequence, + uint256 const& hash)> func) const; /** Returns the current valid list for the given publisher key, if available, as a Json object. @@ -767,7 +771,9 @@ private: lock_guard const&); static void - buildBlobInfos(std::map& blobInfos, PublisherListCollection const& lists); + buildBlobInfos( + std::map& blobInfos, + PublisherListCollection const& lists); static std::map buildBlobInfos(PublisherListCollection const& lists); @@ -804,7 +810,10 @@ private: writing to a cache file, or serving to a /vl/ query */ static Json::Value - buildFileData(std::string const& pubKey, PublisherListCollection const& pubCollection, beast::Journal j); + buildFileData( + std::string const& pubKey, + PublisherListCollection const& pubCollection, + beast::Journal j); /** Build a Json representation of the collection, suitable for writing to a cache file, or serving to a /vl/ query diff --git a/src/xrpld/app/misc/ValidatorSite.h b/src/xrpld/app/misc/ValidatorSite.h index dc44e28bcb..9d9031b9a2 100644 --- a/src/xrpld/app/misc/ValidatorSite.h +++ b/src/xrpld/app/misc/ValidatorSite.h @@ -197,7 +197,7 @@ private: onSiteFetch( boost::system::error_code const& ec, endpoint_type const& endpoint, - detail::response_type&& res, + detail::response_type const& res, std::size_t siteIdx); /// Store latest list fetched from anywhere @@ -207,17 +207,26 @@ private: /// Initiate request to given resource. /// lock over sites_mutex_ required void - makeRequest(std::shared_ptr resource, std::size_t siteIdx, std::lock_guard const&); + makeRequest( + std::shared_ptr resource, + std::size_t siteIdx, + std::lock_guard const&); /// Parse json response from validator list site. /// lock over sites_mutex_ required void - parseJsonResponse(std::string const& res, std::size_t siteIdx, std::lock_guard const&); + parseJsonResponse( + std::string const& res, + std::size_t siteIdx, + std::lock_guard const&); /// Interpret a redirect response. /// lock over sites_mutex_ required std::shared_ptr - processRedirect(detail::response_type& res, std::size_t siteIdx, std::lock_guard const&); + processRedirect( + detail::response_type const& res, + std::size_t siteIdx, + std::lock_guard const&); /// If no sites are provided, or a site fails to load, /// get a list of local cache files from the ValidatorList. diff --git a/src/xrpld/app/misc/detail/AccountTxPaging.cpp b/src/xrpld/app/misc/detail/AccountTxPaging.cpp index e3fc2de4f8..4e79b53ed0 100644 --- a/src/xrpld/app/misc/detail/AccountTxPaging.cpp +++ b/src/xrpld/app/misc/detail/AccountTxPaging.cpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { @@ -26,13 +27,17 @@ convertBlobsToTxResult( // if properly formed meta is available we can use it to generate ctid if (metaset->getAsObject().isFieldPresent(sfTransactionIndex)) + { tr->setStatus( Transaction::sqlTransactionStatus(status), ledger_index, metaset->getAsObject().getFieldU32(sfTransactionIndex), - app.config().NETWORK_ID); + app.getNetworkIDService().getNetworkID()); + } else + { tr->setStatus(Transaction::sqlTransactionStatus(status), ledger_index); + } to.emplace_back(std::move(tr), metaset); }; diff --git a/src/xrpld/app/misc/detail/AccountTxPaging.h b/src/xrpld/app/misc/detail/AccountTxPaging.h index 6dff5c481b..209ca67ab6 100644 --- a/src/xrpld/app/misc/detail/AccountTxPaging.h +++ b/src/xrpld/app/misc/detail/AccountTxPaging.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 2942c8bde6..2b7005f4cc 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -1,12 +1,10 @@ -#include -#include -#include -#include - +#include +#include #include #include #include #include +#include #include #include @@ -42,7 +40,10 @@ parseSection(Section const& section) uint256 id; if (!id.parseHex(match[1])) - Throw("Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]"); + { + Throw( + "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]"); + } names.push_back(std::make_pair(id, match[2])); } @@ -155,17 +156,19 @@ public: { auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic()); // If this validation comes from one of our trusted validators... - if (auto const iter = recordedVotes_.find(val->getSignerPublic()); iter != recordedVotes_.end()) + if (auto const iter = recordedVotes_.find(val->getSignerPublic()); + iter != recordedVotes_.end()) { iter->second.timeout = newTimeout; if (val->isFieldPresent(sfAmendments)) { auto const& choices = val->getFieldV256(sfAmendments); iter->second.upVotes.assign(choices.begin(), choices.end()); - JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has " << choices.size() - << " amendment votes: " + JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has " + << choices.size() << " amendment votes: " << boost::algorithm::join( - iter->second.upVotes | boost::adaptors::transformed(to_string<256, void>), + iter->second.upVotes | + boost::adaptors::transformed(to_string<256, void>), ", "); // TODO: Maybe transform using to_short_string once #5126 is // merged @@ -177,7 +180,8 @@ public: { // This validator does not upVote any amendments right now. iter->second.upVotes.clear(); - JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has no amendment votes."; + JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman + << " has no amendment votes."; } } else @@ -216,7 +220,8 @@ public: "expired"); using namespace std::chrono; auto const age = duration_cast(newTimeout - *votes.second.timeout); - JLOG(j.debug()) << "recordVotes: Using " << age.count() << "min old cached votes from " << pkHuman; + JLOG(j.debug()) << "recordVotes: Using " << age.count() + << "min old cached votes from " << pkHuman; } }); } @@ -282,7 +287,10 @@ private: int threshold_ = 0; public: - AmendmentSet(Rules const& rules, TrustedVotes const& trustedVotes, std::lock_guard const& lock) + AmendmentSet( + Rules const& rules, + TrustedVotes const& trustedVotes, + std::lock_guard const& lock) { // process validations for ledger before flag ledger. auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock); @@ -293,7 +301,8 @@ public: threshold_ = std::max( 1L, static_cast( - (trustedValidations_ * amendmentMajorityCalcThreshold.num) / amendmentMajorityCalcThreshold.den)); + (trustedValidations_ * amendmentMajorityCalcThreshold.num) / + amendmentMajorityCalcThreshold.den)); } bool @@ -350,7 +359,7 @@ private: mutable std::mutex mutex_; hash_map amendmentMap_; - std::uint32_t lastUpdateSeq_; + std::uint32_t lastUpdateSeq_{0}; // Record of the last votes seen from trusted validators. TrustedVotes previousTrustedVotes_; @@ -363,7 +372,7 @@ private: std::unique_ptr lastVote_; // True if an unsupported amendment is enabled - bool unsupportedEnabled_; + bool unsupportedEnabled_{false}; // Unset if no unsupported amendments reach majority, // else set to the earliest time an unsupported amendment @@ -400,7 +409,7 @@ private: public: AmendmentTableImpl( - Application& app, + ServiceRegistry& registry, std::chrono::seconds majorityTime, std::vector const& supported, Section const& enabled, @@ -438,7 +447,10 @@ public: needValidatedLedger(LedgerIndex seq) const override; void - doValidatedLedger(LedgerIndex seq, std::set const& enabled, majorityAmendments_t const& majority) override; + doValidatedLedger( + LedgerIndex seq, + std::set const& enabled, + majorityAmendments_t const& majority) override; void trustChanged(hash_set const& allTrusted) override; @@ -461,13 +473,13 @@ public: //------------------------------------------------------------------------------ AmendmentTableImpl::AmendmentTableImpl( - Application& app, + ServiceRegistry& registry, std::chrono::seconds majorityTime, std::vector const& supported, Section const& enabled, Section const& vetoed, beast::Journal journal) - : lastUpdateSeq_(0), majorityTime_(majorityTime), unsupportedEnabled_(false), j_(journal), db_(app.getWalletDB()) + : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB()) { std::lock_guard lock(mutex_); @@ -499,7 +511,8 @@ AmendmentTableImpl::AmendmentTableImpl( break; } - JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name << ") is supported and will be " + JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name + << ") is supported and will be " << (s.vote == AmendmentVote::up ? "up" : "down") << " voted by default if not enabled on the ledger."; } @@ -514,11 +527,10 @@ AmendmentTableImpl::AmendmentTableImpl( " in favor of data in db/wallet.db."; break; } - else - { // Otherwise transfer config data into the table - detect_conflict.insert(a.first); - persistVote(a.first, a.second, AmendmentVote::up); - } + + // Otherwise transfer config data into the table + detect_conflict.insert(a.first); + persistVote(a.first, a.second, AmendmentVote::up); } // Parse vetoed amendments from config @@ -530,17 +542,17 @@ AmendmentTableImpl::AmendmentTableImpl( " in favor of data in db/wallet.db."; break; } + + // Otherwise transfer config data into the table + if (!detect_conflict.contains(a.first)) + { + persistVote(a.first, a.second, AmendmentVote::down); + } else - { // Otherwise transfer config data into the table - if (detect_conflict.count(a.first) == 0) - { - persistVote(a.first, a.second, AmendmentVote::down); - } - else - { - JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '(' << a.first << ", " - << a.second << ") both [veto_amendments] and [amendments]."; - } + { + JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '(' + << a.first << ", " << a.second + << ") both [veto_amendments] and [amendments]."; } } @@ -559,14 +571,16 @@ AmendmentTableImpl::AmendmentTableImpl( } if (!amend_hash.parseHex(*amendment_hash)) { - Throw("Invalid amendment ID '" + *amendment_hash + " in wallet.db"); + Throw( + "Invalid amendment ID '" + *amendment_hash + " in wallet.db"); } if (*vote == AmendmentVote::down) { // Unknown amendments are effectively vetoed already if (auto s = get(amend_hash, lock)) { - JLOG(j_.info()) << "Amendment {" << *amendment_name << ", " << amend_hash << "} is downvoted."; + JLOG(j_.info()) << "Amendment {" << *amendment_name << ", " << amend_hash + << "} is downvoted."; if (!amendment_name->empty()) s->name = *amendment_name; // An obsolete amendment's vote can never be changed @@ -578,7 +592,8 @@ AmendmentTableImpl::AmendmentTableImpl( { AmendmentState& s = add(amend_hash, lock); - JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", " << amend_hash << "} is upvoted."; + JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", " << amend_hash + << "} is upvoted."; if (!amendment_name->empty()) s.name = *amendment_name; // An obsolete amendment's vote can never be changed @@ -629,9 +644,14 @@ AmendmentTableImpl::find(std::string const& name) const } void -AmendmentTableImpl::persistVote(uint256 const& amendment, std::string const& name, AmendmentVote vote) const +AmendmentTableImpl::persistVote( + uint256 const& amendment, + std::string const& name, + AmendmentVote vote) const { - XRPL_ASSERT(vote != AmendmentVote::obsolete, "xrpl::AmendmentTableImpl::persistVote : valid vote input"); + XRPL_ASSERT( + vote != AmendmentVote::obsolete, + "xrpl::AmendmentTableImpl::persistVote : valid vote input"); auto db = db_.checkoutDb(); voteAmendment(*db, amendment, name, vote); } @@ -724,7 +744,8 @@ AmendmentTableImpl::doValidation(std::set const& enabled) const amendments.reserve(amendmentMap_.size()); for (auto const& e : amendmentMap_) { - if (e.second.supported && e.second.vote == AmendmentVote::up && (enabled.count(e.first) == 0)) + if (e.second.supported && e.second.vote == AmendmentVote::up && + (!enabled.contains(e.first))) { amendments.push_back(e.first); JLOG(j_.info()) << "Voting for amendment " << e.second.name; @@ -753,8 +774,9 @@ AmendmentTableImpl::doVoting( majorityAmendments_t const& majorityAmendments, std::vector> const& valSet) { - JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": " << enabledAmendments.size() << ", " - << majorityAmendments.size() << ", " << valSet.size(); + JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": " + << enabledAmendments.size() << ", " << majorityAmendments.size() << ", " + << valSet.size(); std::lock_guard lock(mutex_); @@ -793,7 +815,8 @@ AmendmentTableImpl::doVoting( auto const logStr = [&entry, &vote]() { std::stringstream ss; - ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first) << " votes"; + ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first) + << " votes"; return ss.str(); }(); @@ -872,7 +895,8 @@ AmendmentTableImpl::doValidatedLedger( if (!s.supported) { - JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at " << to_string(time); + JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at " + << to_string(time); if (!firstUnsupportedExpected_ || firstUnsupportedExpected_ > time) firstUnsupportedExpected_ = time; } @@ -903,9 +927,13 @@ AmendmentTableImpl::injectJson( if (!fs.enabled && isAdmin) { if (fs.vote == AmendmentVote::obsolete) + { v[jss::vetoed] = "Obsolete"; + } else + { v[jss::vetoed] = fs.vote == AmendmentVote::down; + } } v[jss::enabled] = fs.enabled; @@ -931,7 +959,8 @@ AmendmentTableImpl::getJson(bool isAdmin) const std::lock_guard lock(mutex_); for (auto const& e : amendmentMap_) { - injectJson(ret[to_string(e.first)] = Json::objectValue, e.first, e.second, isAdmin, lock); + injectJson( + ret[to_string(e.first)] = Json::objectValue, e.first, e.second, isAdmin, lock); } } return ret; @@ -957,14 +986,15 @@ AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const std::unique_ptr make_AmendmentTable( - Application& app, + ServiceRegistry& registry, std::chrono::seconds majorityTime, std::vector const& supported, Section const& enabled, Section const& vetoed, beast::Journal journal) { - return std::make_unique(app, majorityTime, supported, enabled, vetoed, journal); + return std::make_unique( + registry, majorityTime, supported, enabled, vetoed, journal); } } // namespace xrpl diff --git a/src/xrpld/app/misc/detail/Manifest.cpp b/src/xrpld/app/misc/detail/Manifest.cpp index 952814656b..4526bf6c16 100644 --- a/src/xrpld/app/misc/detail/Manifest.cpp +++ b/src/xrpld/app/misc/detail/Manifest.cpp @@ -1,13 +1,12 @@ -#include -#include -#include - #include #include #include #include #include #include +#include +#include +#include #include @@ -146,13 +145,19 @@ template Stream& logMftAct(Stream& s, std::string const& action, PublicKey const& pk, std::uint32_t seq) { - s << "Manifest: " << action << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq << ";"; + s << "Manifest: " << action << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq + << ";"; return s; } template Stream& -logMftAct(Stream& s, std::string const& action, PublicKey const& pk, std::uint32_t seq, std::uint32_t oldSeq) +logMftAct( + Stream& s, + std::string const& action, + PublicKey const& pk, + std::uint32_t seq, + std::uint32_t oldSeq) { s << "Manifest: " << action << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq << ";OldSeq: " << oldSeq << ";"; @@ -234,9 +239,11 @@ loadValidatorToken(std::vector const& blob, beast::Journal journal) std::string tokenStr; tokenStr.reserve( - std::accumulate(blob.cbegin(), blob.cend(), std::size_t(0), [](std::size_t init, std::string const& s) { - return init + s.size(); - })); + std::accumulate( + blob.cbegin(), + blob.cend(), + std::size_t(0), + [](std::size_t init, std::string const& s) { return init + s.size(); })); for (auto const& line : blob) tokenStr += boost::algorithm::trim_copy(line); @@ -349,8 +356,8 @@ ManifestCache::applyManifest(Manifest m) // signature should be checked. Since `prewriteCheck` is run twice (see // comment below), `checkSignature` only needs to be set to true on the // first run. - auto prewriteCheck = - [this, &m](auto const& iter, bool checkSignature, auto const& lock) -> std::optional { + auto prewriteCheck = [this, &m](auto const& iter, bool checkSignature, auto const& lock) + -> std::optional { XRPL_ASSERT(lock.owns_lock(), "xrpl::ManifestCache::applyManifest::prewriteCheck : locked"); (void)lock; // not used. parameter is present to ensure the mutex is // locked when the lambda is called. @@ -407,9 +414,11 @@ ManifestCache::applyManifest(Manifest m) // Sanity check: the ephemeral key of this manifest should not be // used as the master or ephemeral key of another manifest: - if (auto const x = signingToMasterKeys_.find(*m.signingKey); x != signingToMasterKeys_.end()) + if (auto const x = signingToMasterKeys_.find(*m.signingKey); + x != signingToMasterKeys_.end()) { - JLOG(j_.warn()) << to_string(m) << ": Ephemeral key already used as ephemeral key for " + JLOG(j_.warn()) << to_string(m) + << ": Ephemeral key already used as ephemeral key for " << toBase58(TokenType::NodePublic, x->second); return ManifestDisposition::badEphemeralKey; @@ -417,7 +426,8 @@ ManifestCache::applyManifest(Manifest m) if (auto const x = map_.find(*m.signingKey); x != map_.end()) { - JLOG(j_.warn()) << to_string(m) << ": Ephemeral key used as master key for " << to_string(x->second); + JLOG(j_.warn()) << to_string(m) << ": Ephemeral key used as master key for " + << to_string(x->second); return ManifestDisposition::badEphemeralKey; } @@ -524,11 +534,12 @@ ManifestCache::load( if (!configRevocation.empty()) { std::string revocationStr; - revocationStr.reserve(std::accumulate( - configRevocation.cbegin(), - configRevocation.cend(), - std::size_t(0), - [](std::size_t init, std::string const& s) { return init + s.size(); })); + revocationStr.reserve( + std::accumulate( + configRevocation.cbegin(), + configRevocation.cend(), + std::size_t(0), + [](std::size_t init, std::string const& s) { return init + s.size(); })); for (auto const& line : configRevocation) revocationStr += boost::algorithm::trim_copy(line); diff --git a/src/xrpld/app/misc/detail/Transaction.cpp b/src/xrpld/app/misc/detail/Transaction.cpp index 61a2a36695..35b49504d5 100644 --- a/src/xrpld/app/misc/detail/Transaction.cpp +++ b/src/xrpld/app/misc/detail/Transaction.cpp @@ -1,18 +1,21 @@ #include #include -#include #include -#include -#include #include #include +#include #include #include +#include +#include namespace xrpl { -Transaction::Transaction(std::shared_ptr const& stx, std::string& reason, Application& app) noexcept +Transaction::Transaction( + std::shared_ptr const& stx, + std::string& reason, + Application& app) noexcept : mTransaction(stx), mApp(app), j_(app.journal("Ledger")) { try @@ -50,26 +53,26 @@ Transaction::setStatus( TransStatus Transaction::sqlTransactionStatus(boost::optional const& status) { - char const c = (status) ? (*status)[0] : safe_cast(txnSqlUnknown); + auto const c = (status) ? safe_cast((*status)[0]) : TxnSql::txnSqlUnknown; - switch (c) + switch (static_cast(c)) { - case txnSqlNew: + case TxnSql::txnSqlNew: return NEW; - case txnSqlConflict: + case TxnSql::txnSqlConflict: return CONFLICTED; - case txnSqlHeld: + case TxnSql::txnSqlHeld: return HELD; - case txnSqlValidated: + case TxnSql::txnSqlValidated: return COMMITTED; - case txnSqlIncluded: + case TxnSql::txnSqlIncluded: return INCLUDED; + default: + XRPL_ASSERT( + c == TxnSql::txnSqlUnknown, + "xrpl::Transaction::sqlTransactionStatus : unknown transaction status"); } - XRPL_ASSERT( - c == txnSqlUnknown, - "xrpl::Transaction::sqlTransactionStatus : unknown transaction " - "status"); return INVALID; } @@ -99,7 +102,11 @@ Transaction::load(uint256 const& id, Application& app, error_code_i& ec) } std::variant, std::shared_ptr>, TxSearched> -Transaction::load(uint256 const& id, Application& app, ClosedInterval const& range, error_code_i& ec) +Transaction::load( + uint256 const& id, + Application& app, + ClosedInterval const& range, + error_code_i& ec) { using op = std::optional>; @@ -113,14 +120,9 @@ Transaction::load( std::optional> const& range, error_code_i& ec) { - auto const db = dynamic_cast(&app.getRelationalDatabase()); + auto& db = app.getRelationalDatabase(); - if (!db) - { - Throw("Failed to get relational database"); - } - - return db->getTransaction(id, range, ec); + return db.getTransaction(id, range, ec); } // options 1 to include the date of the transaction diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 61741b73f0..020fa501d5 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -1,12 +1,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -55,27 +55,35 @@ getLastLedgerSequence(STTx const& tx) static FeeLevel64 increase(FeeLevel64 level, std::uint32_t increasePercent) { - return mulDiv(level, 100 + increasePercent, 100).value_or(static_cast(xrpl::muldiv_max)); + return mulDiv(level, 100 + increasePercent, 100) + .value_or(static_cast(xrpl::muldiv_max)); } ////////////////////////////////////////////////////////////////////////// std::size_t -TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, TxQ::Setup const& setup) +TxQ::FeeMetrics::update( + Application& app, + ReadView const& view, + bool timeLeap, + TxQ::Setup const& setup) { std::vector feeLevels; auto const txBegin = view.txs.begin(); auto const txEnd = view.txs.end(); auto const size = std::distance(txBegin, txEnd); feeLevels.reserve(size); - std::for_each(txBegin, txEnd, [&](auto const& tx) { feeLevels.push_back(getFeeLevelPaid(view, *tx.first)); }); + std::for_each(txBegin, txEnd, [&](auto const& tx) { + feeLevels.push_back(getFeeLevelPaid(view, *tx.first)); + }); std::sort(feeLevels.begin(), feeLevels.end()); XRPL_ASSERT(size == feeLevels.size(), "xrpl::TxQ::FeeMetrics::update : fee levels size"); JLOG((timeLeap ? j_.warn() : j_.debug())) << "Ledger " << view.header().seq << " has " << size << " transactions. " - << "Ledgers are processing " << (timeLeap ? "slowly" : "as expected") << ". Expected transactions is currently " - << txnsExpected_ << " and multiplier is " << escalationMultiplier_; + << "Ledgers are processing " << (timeLeap ? "slowly" : "as expected") + << ". Expected transactions is currently " << txnsExpected_ << " and multiplier is " + << escalationMultiplier_; if (timeLeap) { @@ -84,16 +92,16 @@ TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, T auto const cutPct = 100 - setup.slowConsensusDecreasePercent; // upperLimit must be >= minimumTxnCount_ or std::clamp can give // unexpected results - auto const upperLimit = - std::max(mulDiv(txnsExpected_, cutPct, 100).value_or(xrpl::muldiv_max), minimumTxnCount_); + auto const upperLimit = std::max( + mulDiv(txnsExpected_, cutPct, 100).value_or(xrpl::muldiv_max), minimumTxnCount_); txnsExpected_ = std::clamp( mulDiv(size, cutPct, 100).value_or(xrpl::muldiv_max), minimumTxnCount_, upperLimit); recentTxnCounts_.clear(); } else if (size > txnsExpected_ || size > targetTxnCount_) { - recentTxnCounts_.push_back( - mulDiv(size, 100 + setup.normalConsensusIncreasePercent, 100).value_or(xrpl::muldiv_max)); + recentTxnCounts_.push_back(mulDiv(size, 100 + setup.normalConsensusIncreasePercent, 100) + .value_or(xrpl::muldiv_max)); auto const iter = std::max_element(recentTxnCounts_.begin(), recentTxnCounts_.end()); BOOST_ASSERT(iter != recentTxnCounts_.end()); auto const next = [&] { @@ -123,11 +131,12 @@ TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, T // evaluates to the middle element; for an even // number of elements, it will add the two elements // on either side of the "middle" and average them. - escalationMultiplier_ = (feeLevels[size / 2] + feeLevels[(size - 1) / 2] + FeeLevel64{1}) / 2; + escalationMultiplier_ = + (feeLevels[size / 2] + feeLevels[(size - 1) / 2] + FeeLevel64{1}) / 2; escalationMultiplier_ = std::max(escalationMultiplier_, setup.minimumEscalationMultiplier); } - JLOG(j_.debug()) << "Expected transactions updated to " << txnsExpected_ << " and multiplier updated to " - << escalationMultiplier_; + JLOG(j_.debug()) << "Expected transactions updated to " << txnsExpected_ + << " and multiplier updated to " << escalationMultiplier_; return size; } @@ -228,7 +237,8 @@ TxQ::FeeMetrics::escalatedSeriesFeeLevel( // are nearly nil. if (!sumNlast.first) return {sumNlast.first, FeeLevel64{sumNlast.second}}; - auto const totalFeeLevel = mulDiv(multiplier, sumNlast.second - sumNcurrent.second, target * target); + auto const totalFeeLevel = + mulDiv(multiplier, sumNlast.second - sumNcurrent.second, target * target); return {totalFeeLevel.has_value(), *totalFeeLevel}; } @@ -262,8 +272,9 @@ TxQ::MaybeTx::apply(Application& app, OpenView& view, beast::Journal j) if (pfResult->rules != view.rules() || pfResult->flags != flags) { - JLOG(j.debug()) << "Queued transaction " << txID << " rules or flags have changed. Flags from " - << pfResult->flags << " to " << flags; + JLOG(j.debug()) << "Queued transaction " << txID + << " rules or flags have changed. Flags from " << pfResult->flags << " to " + << flags; pfResult.emplace(preflight(app, view.rules(), pfResult->tx, flags, pfResult->j)); } @@ -273,7 +284,8 @@ TxQ::MaybeTx::apply(Application& app, OpenView& view, beast::Journal j) return doApply(pcresult, app, view); } -TxQ::TxQAccount::TxQAccount(std::shared_ptr const& txn) : TxQAccount(txn->getAccountID(sfAccount)) +TxQ::TxQAccount::TxQAccount(std::shared_ptr const& txn) + : TxQAccount(txn->getAccountID(sfAccount)) { } @@ -312,7 +324,8 @@ TxQ::TxQAccount::remove(SeqProxy seqProx) ////////////////////////////////////////////////////////////////////////// -TxQ::TxQ(Setup const& setup, beast::Journal j) : setup_(setup), j_(j), feeMetrics_(setup, j), maxSize_(std::nullopt) +TxQ::TxQ(Setup const& setup, beast::Journal j) + : setup_(setup), j_(j), feeMetrics_(setup, j), maxSize_(std::nullopt) { } @@ -343,7 +356,8 @@ TxQ::canBeHeld( // AccountTxnID is not supported by the transaction // queue yet, but should be added in the future. // tapFAIL_HARD transactions are never held - if (tx.isFieldPresent(sfPreviousTxnID) || tx.isFieldPresent(sfAccountTxnID) || (flags & tapFAIL_HARD)) + if (tx.isFieldPresent(sfPreviousTxnID) || tx.isFieldPresent(sfAccountTxnID) || + (flags & tapFAIL_HARD)) return telCAN_NOT_QUEUE; { @@ -372,23 +386,29 @@ TxQ::canBeHeld( // transaction fills the _first_ sequence hole for the account. auto const txSeqProx = tx.getSeqProxy(); if (txSeqProx.isTicket()) + { // Tickets always follow sequence-based transactions, so a ticket // cannot unblock a sequence-based transaction. return telCAN_NOT_QUEUE_FULL; + } // This is the next queuable sequence-based SeqProxy for the account. SeqProxy const nextQueuable = nextQueuableSeqImpl(sleAccount, lock); if (txSeqProx != nextQueuable) + { // The provided transaction does not fill the next open sequence gap. return telCAN_NOT_QUEUE_FULL; + } // Make sure they are not just topping off the account's queued // sequence-based transactions. if (auto const nextTxIter = txQAcct.transactions.upper_bound(nextQueuable); nextTxIter != txQAcct.transactions.end() && nextTxIter->first.isSeq()) + { // There is a next transaction and it is sequence based. They are // filling a real gap. Allow it. return tesSUCCESS; + } return telCAN_NOT_QUEUE_FULL; } @@ -409,11 +429,13 @@ TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter) -> FeeMultiSet:: } auto -TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter) -> FeeMultiSet::iterator_type +TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter) + -> FeeMultiSet::iterator_type { auto& txQAccount = byAccount_.at(candidateIter->account); auto const accountIter = txQAccount.transactions.find(candidateIter->seqProxy); - XRPL_ASSERT(accountIter != txQAccount.transactions.end(), "xrpl::TxQ::eraseAndAdvance : account found"); + XRPL_ASSERT( + accountIter != txQAccount.transactions.end(), "xrpl::TxQ::eraseAndAdvance : account found"); // Note that sequence-based transactions must be applied in sequence order // from smallest to largest. But ticket-based transactions can be @@ -422,7 +444,8 @@ TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter) -> Fee candidateIter->seqProxy.isTicket() || accountIter == txQAccount.transactions.begin(), "xrpl::TxQ::eraseAndAdvance : ticket or sequence"); XRPL_ASSERT( - byFee_.iterator_to(accountIter->second) == candidateIter, "xrpl::TxQ::eraseAndAdvance : found in byFee"); + byFee_.iterator_to(accountIter->second) == candidateIter, + "xrpl::TxQ::eraseAndAdvance : found in byFee"); auto const accountNextIter = std::next(accountIter); // Check if the next transaction for this account is earlier in the queue, @@ -483,8 +506,8 @@ TxQ::tryClearAccountQueueUpThruTx( if (!requiredTotalFeeLevel.first) return {telINSUF_FEE_P, false}; - auto const totalFeeLevelPaid = - std::accumulate(beginTxIter, endTxIter, feeLevelPaid, [](auto const& total, auto const& txn) { + auto const totalFeeLevelPaid = std::accumulate( + beginTxIter, endTxIter, feeLevelPaid, [](auto const& total, auto const& txn) { return total + txn.second.feeLevel; }); @@ -660,7 +683,12 @@ TxQ::tryClearAccountQueueUpThruTx( // beyond that limit are rejected. // ApplyResult -TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& tx, ApplyFlags flags, beast::Journal j) +TxQ::apply( + Application& app, + OpenView& view, + std::shared_ptr const& tx, + ApplyFlags flags, + beast::Journal j) { NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; @@ -668,7 +696,7 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // etc. before doing potentially expensive queue // replace and multi-transaction operations. auto const pfResult = preflight(app, view.rules(), *tx, flags, j); - if (pfResult.ter != tesSUCCESS) + if (!isTesSuccess(pfResult.ter)) return {pfResult.ter, false}; // See if the transaction paid a high enough fee that it can go straight @@ -696,9 +724,11 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& if (txSeqProx.isTicket() && !view.exists(keylet::ticket(account, txSeqProx))) { if (txSeqProx.value() < acctSeqProx.value()) + { // The ticket number is low enough that it should already be // in the ledger if it were ever going to exist. return {tefNO_TICKET, false}; + } // We don't queue transactions that use Tickets unless // we can find the Ticket in the ledger. @@ -721,7 +751,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // to individually check each queued ticket against the ledger. struct TxIter { - TxIter(TxQAccount::TxMap::iterator first_, TxQAccount::TxMap::iterator end_) : first(first_), end(end_) + TxIter(TxQAccount::TxMap::iterator first_, TxQAccount::TxMap::iterator end_) + : first(first_), end(end_) { } @@ -729,7 +760,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& TxQAccount::TxMap::iterator end; }; - std::optional const txIter = [accountIter, accountIsInQueue, acctSeqProx]() -> std::optional { + std::optional const txIter = + [accountIter, accountIsInQueue, acctSeqProx]() -> std::optional { if (!accountIsInQueue) return {}; @@ -738,9 +770,11 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& TxQAccount::TxMap::iterator const firstIter = acctTxs.lower_bound(acctSeqProx); if (firstIter == acctTxs.end()) + { // Even though there may be transactions in the queue, there are // none that we should pay attention to. return {}; + } return {TxIter{firstIter, acctTxs.end()}}; }(); @@ -773,7 +807,9 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // If the transaction is intending to replace a transaction in the queue // identify the one that might be replaced. - auto replacedTxIter = [accountIsInQueue, &accountIter, txSeqProx]() -> std::optional { + auto replacedTxIter = [accountIsInQueue, + &accountIter, + txSeqProx]() -> std::optional { if (accountIsInQueue) { TxQAccount& txQAcct = accountIter->second; @@ -799,7 +835,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // // We only need to check if txIter->first is a blocker because we // require that a blocker be alone in the account's queue. - if (acctTxCount == 1 && txIter->first->second.consequences().isBlocker() && (txIter->first->first != txSeqProx)) + if (acctTxCount == 1 && txIter->first->second.consequences().isBlocker() && + (txIter->first->first != txSeqProx)) { return {telCAN_NOT_QUEUE_BLOCKED, false}; } @@ -814,23 +851,25 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // Is the current transaction's fee higher than // the queued transaction's fee + a percentage TxQAccount::TxMap::iterator const& existingIter = *replacedTxIter; - auto requiredRetryLevel = increase(existingIter->second.feeLevel, setup_.retrySequencePercent); - JLOG(j_.trace()) << "Found transaction in queue for account " << account << " with " << txSeqProx - << " new txn fee level is " << feeLevelPaid << ", old txn fee level is " - << existingIter->second.feeLevel << ", new txn needs fee level of " << requiredRetryLevel; + auto requiredRetryLevel = + increase(existingIter->second.feeLevel, setup_.retrySequencePercent); + JLOG(j_.trace()) << "Found transaction in queue for account " << account << " with " + << txSeqProx << " new txn fee level is " << feeLevelPaid + << ", old txn fee level is " << existingIter->second.feeLevel + << ", new txn needs fee level of " << requiredRetryLevel; if (feeLevelPaid > requiredRetryLevel) { // Continue, leaving the queued transaction marked for removal. // DO NOT REMOVE if the new tx fails, because there may // be other txs dependent on it in the queue. - JLOG(j_.trace()) << "Removing transaction from queue " << existingIter->second.txID << " in favor of " - << transactionID; + JLOG(j_.trace()) << "Removing transaction from queue " << existingIter->second.txID + << " in favor of " << transactionID; } else { // Drop the current transaction - JLOG(j_.trace()) << "Ignoring transaction " << transactionID << " in favor of queued " - << existingIter->second.txID; + JLOG(j_.trace()) << "Ignoring transaction " << transactionID + << " in favor of queued " << existingIter->second.txID; return {telCAN_NOT_QUEUE_FEE, false}; } } @@ -882,7 +921,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& { // If the transaction is queueable, create the multiTxn // object to hold the info we need to adjust for prior txns. - TER const ter{canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)}; + TER const ter{ + canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)}; if (!isTesSuccess(ter)) return {ter, false}; @@ -898,7 +938,7 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // // o Additional transactions with Sequences should // follow preceding sequence-based transactions with no - // gaps (except for those required by CreateTicket + // gaps (except for those required by TicketCreate // transactions). // Find the entry in the queue that precedes the new @@ -919,9 +959,13 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& if (txSeqProx.isSeq()) { if (txSeqProx < acctSeqProx) + { return {tefPAST_SEQ, false}; - else if (txSeqProx > acctSeqProx) + } + if (txSeqProx > acctSeqProx) + { return {terPRE_SEQ, false}; + } } } else if (!replacedTxIter) @@ -1012,7 +1056,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& if (totalFee >= balance || (reserve > 10 * base && totalFee >= reserve)) { // Drop the current transaction - JLOG(j_.trace()) << "Ignoring transaction " << transactionID << ". Total fees in flight too high."; + JLOG(j_.trace()) << "Ignoring transaction " << transactionID + << ". Total fees in flight too high."; return {telCAN_NOT_QUEUE_BALANCE, false}; } @@ -1026,7 +1071,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // Subtract the fees and XRP spend from all of the other // transactions in the queue. That prevents a transaction // inserted in the middle from fouling up later transactions. - auto const potentialTotalSpend = totalFee + std::min(balance - std::min(balance, reserve), potentialSpend); + auto const potentialTotalSpend = + totalFee + std::min(balance - std::min(balance, reserve), potentialSpend); XRPL_ASSERT( potentialTotalSpend > XRPAmount{0} || (potentialTotalSpend == XRPAmount{0} && multiTxn->applyView.fees().base == 0), @@ -1037,8 +1083,9 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // sequence, set the account to match it. If it has a ticket, use // the next queueable sequence, which is the closest approximation // to the most successful case. - sleBump->at(sfSequence) = - txSeqProx.isSeq() ? txSeqProx.value() : nextQueuableSeqImpl(sleAccount, lock).value(); + sleBump->at(sfSequence) = txSeqProx.isSeq() + ? txSeqProx.value() + : nextQueuableSeqImpl(sleAccount, lock).value(); } } @@ -1060,9 +1107,10 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // Too low of a fee should get caught by preclaim XRPL_ASSERT(feeLevelPaid >= baseLevel, "xrpl::TxQ::apply : minimum fee"); - JLOG(j_.trace()) << "Transaction " << transactionID << " from account " << account << " has fee level of " - << feeLevelPaid << " needs at least " << requiredFeeLevel - << " to get in the open ledger, which has " << view.txCount() << " entries."; + JLOG(j_.trace()) << "Transaction " << transactionID << " from account " << account + << " has fee level of " << feeLevelPaid << " needs at least " + << requiredFeeLevel << " to get in the open ledger, which has " + << view.txCount() << " entries."; /* Quick heuristic check to see if it's worth checking that this tx has a high enough fee to clear all the txs in front of it in the queue. @@ -1082,8 +1130,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& conditions change, but don't waste the effort to clear). */ if (txSeqProx.isSeq() && txIter && multiTxn.has_value() && - txIter->first->second.retriesRemaining == MaybeTx::retriesAllowed && feeLevelPaid > requiredFeeLevel && - requiredFeeLevel > baseLevel) + txIter->first->second.retriesRemaining == MaybeTx::retriesAllowed && + feeLevelPaid > requiredFeeLevel && requiredFeeLevel > baseLevel) { OpenView sandbox(open_ledger, &view, view.rules()); @@ -1140,7 +1188,8 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // queued. However, it can occur if settings are changed, // and there is unit test coverage. JLOG(j_.info()) << "Queue is full, and transaction " << transactionID - << " would kick a transaction from the same account (" << account << ") out of the queue."; + << " would kick a transaction from the same account (" << account + << ") out of the queue."; return {telCAN_NOT_QUEUE_FULL, false}; } auto const& endAccount = byAccount_.at(lastRIter->account); @@ -1173,10 +1222,11 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // valuable, so kick out the cheapest transaction. auto dropRIter = endAccount.transactions.rbegin(); XRPL_ASSERT( - dropRIter->second.account == lastRIter->account, "xrpl::TxQ::apply : cheapest transaction found"); + dropRIter->second.account == lastRIter->account, + "xrpl::TxQ::apply : cheapest transaction found"); JLOG(j_.info()) << "Removing last item of account " << lastRIter->account - << " from queue with average fee of " << endEffectiveFeeLevel << " in favor of " - << transactionID << " with fee of " << feeLevelPaid; + << " from queue with average fee of " << endEffectiveFeeLevel + << " in favor of " << transactionID << " with fee of " << feeLevelPaid; erase(byFee_.iterator_to(dropRIter->second)); } else @@ -1212,8 +1262,9 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr const& // Then index it into the byFee lookup. byFee_.insert(candidate); - JLOG(j_.debug()) << "Added transaction " << candidate.txID << " with result " << transToken(pfResult.ter) - << " from " << (accountIsInQueue ? "existing" : "new") << " account " << candidate.account + JLOG(j_.debug()) << "Added transaction " << candidate.txID << " with result " + << transToken(pfResult.ter) << " from " + << (accountIsInQueue ? "existing" : "new") << " account " << candidate.account << " to queue." << " Flags: " << flags; @@ -1264,9 +1315,13 @@ TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap) for (auto txQAccountIter = byAccount_.begin(); txQAccountIter != byAccount_.end();) { if (txQAccountIter->second.empty()) + { txQAccountIter = byAccount_.erase(txQAccountIter); + } else + { ++txQAccountIter; + } } } @@ -1323,36 +1378,46 @@ TxQ::accept(Application& app, OpenView& view) // There is a sequence transaction at the front of the queue and // candidate has a later sequence, so skip this candidate. We // need to process sequence-based transactions in sequence order. - JLOG(j_.trace()) << "Skipping queued transaction " << candidateIter->txID << " from account " - << candidateIter->account << " as it is not the first."; + JLOG(j_.trace()) << "Skipping queued transaction " << candidateIter->txID + << " from account " << candidateIter->account + << " as it is not the first."; candidateIter++; continue; } auto const requiredFeeLevel = getRequiredFeeLevel(view, tapNONE, metricsSnapshot, lock); auto const feeLevelPaid = candidateIter->feeLevel; - JLOG(j_.trace()) << "Queued transaction " << candidateIter->txID << " from account " << candidateIter->account - << " has fee level of " << feeLevelPaid << " needs at least " << requiredFeeLevel; + JLOG(j_.trace()) << "Queued transaction " << candidateIter->txID << " from account " + << candidateIter->account << " has fee level of " << feeLevelPaid + << " needs at least " << requiredFeeLevel; if (feeLevelPaid >= requiredFeeLevel) { - JLOG(j_.trace()) << "Applying queued transaction " << candidateIter->txID << " to open ledger."; + JLOG(j_.trace()) << "Applying queued transaction " << candidateIter->txID + << " to open ledger."; auto const [txnResult, didApply, _metadata] = candidateIter->apply(app, view, j_); if (didApply) { // Remove the candidate from the queue - JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID << " applied successfully with " - << transToken(txnResult) << ". Remove from queue."; + JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID + << " applied successfully with " << transToken(txnResult) + << ". Remove from queue."; candidateIter = eraseAndAdvance(candidateIter); ledgerChanged = true; } - else if (isTefFailure(txnResult) || isTemMalformed(txnResult) || candidateIter->retriesRemaining <= 0) + else if ( + isTefFailure(txnResult) || isTemMalformed(txnResult) || + candidateIter->retriesRemaining <= 0) { if (candidateIter->retriesRemaining <= 0) + { account.retryPenalty = true; + } else + { account.dropPenalty = true; + } JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID << " failed with " << transToken(txnResult) << ". Remove from queue."; candidateIter = eraseAndAdvance(candidateIter); @@ -1363,9 +1428,13 @@ TxQ::accept(Application& app, OpenView& view) << transToken(txnResult) << ". Leave in queue." << " Applied: " << didApply << ". Flags: " << candidateIter->flags; if (account.retryPenalty && candidateIter->retriesRemaining > 2) + { candidateIter->retriesRemaining = 1; + } else + { --candidateIter->retriesRemaining; + } candidateIter->lastResult = txnResult; if (account.dropPenalty && account.transactions.size() > 1 && isFull<95>()) { @@ -1376,9 +1445,10 @@ TxQ::accept(Application& app, OpenView& view) { // Since the failed transaction has a ticket, order // doesn't matter. Drop this one. - JLOG(j_.info()) << "Queue is nearly full, and transaction " << candidateIter->txID - << " failed with " << transToken(txnResult) - << ". Removing ticketed tx from account " << account.account; + JLOG(j_.info()) + << "Queue is nearly full, and transaction " << candidateIter->txID + << " failed with " << transToken(txnResult) + << ". Removing ticketed tx from account " << account.account; candidateIter = eraseAndAdvance(candidateIter); } else @@ -1389,11 +1459,13 @@ TxQ::accept(Application& app, OpenView& view) // this account. auto dropRIter = account.transactions.rbegin(); XRPL_ASSERT( - dropRIter->second.account == candidateIter->account, "xrpl::TxQ::accept : account check"); + dropRIter->second.account == candidateIter->account, + "xrpl::TxQ::accept : account check"); - JLOG(j_.info()) << "Queue is nearly full, and transaction " << candidateIter->txID - << " failed with " << transToken(txnResult) - << ". Removing last item from account " << account.account; + JLOG(j_.info()) + << "Queue is nearly full, and transaction " << candidateIter->txID + << " failed with " << transToken(txnResult) + << ". Removing last item from account " << account.account; auto endIter = byFee_.iterator_to(dropRIter->second); if (endIter != candidateIter) erase(endIter); @@ -1401,7 +1473,9 @@ TxQ::accept(Application& app, OpenView& view) } } else + { ++candidateIter; + } } } else @@ -1416,9 +1490,13 @@ TxQ::accept(Application& app, OpenView& view) // reordered. LedgerHash const& parentHash = view.header().parentHash; if (parentHash == parentHash_) + { JLOG(j_.warn()) << "Parent ledger hash unchanged from " << parentHash; + } else + { parentHash_ = parentHash; + } [[maybe_unused]] auto const startingSize = byFee_.size(); // byFee_ doesn't "own" the candidate objects inside it, so it's @@ -1462,7 +1540,9 @@ TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const // sequence number, that is not used by a transaction in the queue, must // be found and returned. SeqProxy -TxQ::nextQueuableSeqImpl(std::shared_ptr const& sleAccount, std::lock_guard const&) const +TxQ::nextQueuableSeqImpl( + std::shared_ptr const& sleAccount, + std::lock_guard const&) const { // If the account is not in the ledger or a non-account was passed // then return zero. We have no idea. @@ -1484,12 +1564,14 @@ TxQ::nextQueuableSeqImpl(std::shared_ptr const& sleAccount, std::lock TxQAccount::TxMap::const_iterator txIter = acctTxs.lower_bound(acctSeqProx); if (txIter == acctTxs.end() || !txIter->first.isSeq() || txIter->first != acctSeqProx) + { // Either... // o There are no queued sequence-based transactions equal to or // following acctSeqProx or // o acctSeqProx is not currently in the queue. // So acctSeqProx is as good as it gets. return acctSeqProx; + } // There are sequence-based transactions queued that follow acctSeqProx. // Locate the first opening to put a transaction into. @@ -1555,7 +1637,8 @@ TxQ::tryDirectApply( auto const [txnResult, didApply, metadata] = xrpl::apply(app, view, *tx, flags, j); JLOG(j_.trace()) << "New transaction " << transactionID - << (didApply ? " applied successfully with " : " failed with ") << transToken(txnResult); + << (didApply ? " applied successfully with " : " failed with ") + << transToken(txnResult); if (didApply) { @@ -1590,9 +1673,15 @@ TxQ::removeFromByFee( // queue, remove the transaction that is being replaced. auto deleteIter = byFee_.iterator_to((*replacedTxIter)->second); XRPL_ASSERT(deleteIter != byFee_.end(), "xrpl::TxQ::removeFromByFee : found in byFee"); - XRPL_ASSERT(&(*replacedTxIter)->second == &*deleteIter, "xrpl::TxQ::removeFromByFee : matching transaction"); - XRPL_ASSERT(deleteIter->seqProxy == tx->getSeqProxy(), "xrpl::TxQ::removeFromByFee : matching sequence"); - XRPL_ASSERT(deleteIter->account == (*tx)[sfAccount], "xrpl::TxQ::removeFromByFee : matching account"); + XRPL_ASSERT( + &(*replacedTxIter)->second == &*deleteIter, + "xrpl::TxQ::removeFromByFee : matching transaction"); + XRPL_ASSERT( + deleteIter->seqProxy == tx->getSeqProxy(), + "xrpl::TxQ::removeFromByFee : matching sequence"); + XRPL_ASSERT( + deleteIter->account == (*tx)[sfAccount], + "xrpl::TxQ::removeFromByFee : matching account"); erase(deleteIter); } @@ -1636,7 +1725,8 @@ TxQ::getTxRequiredFeeAndSeq(OpenView const& view, std::shared_ptr co std::uint32_t const accountSeq = sle ? (*sle)[sfSequence] : 0; std::uint32_t const availableSeq = nextQueuableSeqImpl(sle, lock).value(); return { - mulDiv(fee, baseFee, baseLevel).value_or(XRPAmount(std::numeric_limits::max())), + mulDiv(fee, baseFee, baseLevel) + .value_or(XRPAmount(std::numeric_limits::max())), accountSeq, availableSeq}; } @@ -1717,8 +1807,9 @@ TxQ::doRPC(Application& app) const drops[jss::base_fee] = to_string(baseFee); drops[jss::median_fee] = to_string(toDrops(metrics.medFeeLevel, baseFee)); - drops[jss::minimum_fee] = to_string( - toDrops(metrics.minProcessingFeeLevel, metrics.txCount >= metrics.txQMaxSize ? effectiveBaseFee : baseFee)); + drops[jss::minimum_fee] = to_string(toDrops( + metrics.minProcessingFeeLevel, + metrics.txCount >= metrics.txQMaxSize ? effectiveBaseFee : baseFee)); auto openFee = toDrops(metrics.openLedgerFeeLevel, effectiveBaseFee); if (effectiveBaseFee && toFeeLevel(openFee, effectiveBaseFee) < metrics.openLedgerFeeLevel) openFee += 1; @@ -1741,7 +1832,7 @@ setup_TxQ(Config const& config) set(setup.minimumTxnInLedger, "minimum_txn_in_ledger", section); set(setup.minimumTxnInLedgerSA, "minimum_txn_in_ledger_standalone", section); set(setup.targetTxnInLedger, "target_txn_in_ledger", section); - std::uint32_t max; + std::uint32_t max = 0; if (set(max, "maximum_txn_in_ledger", section)) { if (max < setup.minimumTxnInLedger) @@ -1771,7 +1862,8 @@ setup_TxQ(Config const& config) minimum_txn_in_ledger.) */ set(setup.normalConsensusIncreasePercent, "normal_consensus_increase_percent", section); - setup.normalConsensusIncreasePercent = std::clamp(setup.normalConsensusIncreasePercent, 0u, 1000u); + setup.normalConsensusIncreasePercent = + std::clamp(setup.normalConsensusIncreasePercent, 0u, 1000u); /* If this percentage is outside of the 0-100 range, the results are nonsensical (uint overflows happen, so the limit grows diff --git a/src/xrpld/app/misc/detail/ValidatorKeys.cpp b/src/xrpld/app/misc/detail/ValidatorKeys.cpp index 675ce4ac6f..59ddc6d702 100644 --- a/src/xrpld/app/misc/detail/ValidatorKeys.cpp +++ b/src/xrpld/app/misc/detail/ValidatorKeys.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include +#include namespace xrpl { ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) @@ -12,7 +12,8 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) if (config.exists(SECTION_VALIDATOR_TOKEN) && config.exists(SECTION_VALIDATION_SEED)) { configInvalid_ = true; - JLOG(j.fatal()) << "Cannot specify both [" SECTION_VALIDATION_SEED "] and [" SECTION_VALIDATOR_TOKEN "]"; + JLOG(j.fatal()) << "Cannot specify both [" SECTION_VALIDATION_SEED + "] and [" SECTION_VALIDATOR_TOKEN "]"; return; } @@ -45,7 +46,8 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) } else if (config.exists(SECTION_VALIDATION_SEED)) { - auto const seed = parseBase58(config.section(SECTION_VALIDATION_SEED).lines().front()); + auto const seed = + parseBase58(config.section(SECTION_VALIDATION_SEED).lines().front()); if (!seed) { configInvalid_ = true; diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 0edd0de9be..386b96387e 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -1,5 +1,3 @@ -#include -#include #include #include @@ -7,12 +5,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include @@ -158,7 +158,7 @@ ValidatorList::load( status = PublisherStatus::revoked; } - if (publisherLists_.count(id)) + if (publisherLists_.contains(id)) { JLOG(j_.warn()) << "Duplicate validator list publisher key: " << key; continue; @@ -198,7 +198,8 @@ ValidatorList::load( auto const [_, inserted] = keyListings_.insert({*localPubKey_, listThreshold_}); if (inserted) { - JLOG(j_.debug()) << "Added own master key " << toBase58(TokenType::NodePublic, *localPubKey_); + JLOG(j_.debug()) << "Added own master key " + << toBase58(TokenType::NodePublic, *localPubKey_); } } @@ -300,7 +301,8 @@ ValidatorList::buildFileData( case 2: { Json::Value blobs(Json::arrayValue); - auto add = [&blobs, &outerManifest = pubCollection.rawManifest](PublisherList const& pubList) { + auto add = [&blobs, + &outerManifest = pubCollection.rawManifest](PublisherList const& pubList) { auto& blob = blobs.append(Json::objectValue); blob[jss::blob] = pubList.rawBlob; blob[jss::signature] = pubList.rawSignature; @@ -327,7 +329,8 @@ ValidatorList::buildFileData( } void -ValidatorList::cacheValidatorFile(ValidatorList::lock_guard const& lock, PublicKey const& pubKey) const +ValidatorList::cacheValidatorFile(ValidatorList::lock_guard const& lock, PublicKey const& pubKey) + const { if (dataPath_.empty()) return; @@ -348,7 +351,8 @@ ValidatorList::cacheValidatorFile(ValidatorList::lock_guard const& lock, PublicK if (ec) { // Log and ignore any file I/O exceptions - JLOG(j_.error()) << "Problem writing " << filename << " " << ec.value() << ": " << ec.message(); + JLOG(j_.error()) << "Problem writing " << filename << " " << ec.value() << ": " + << ec.message(); } } @@ -360,15 +364,16 @@ ValidatorList::parseBlobs(std::uint32_t version, Json::Value const& body) switch (version) { case 1: { - if (!body.isMember(jss::blob) || !body[jss::blob].isString() || !body.isMember(jss::signature) || - !body[jss::signature].isString() || + if (!body.isMember(jss::blob) || !body[jss::blob].isString() || + !body.isMember(jss::signature) || !body[jss::signature].isString() || // If the v2 field is present, the VL is malformed body.isMember(jss::blobs_v2)) return {}; ValidatorBlobInfo& info = result.emplace_back(); info.blob = body[jss::blob].asString(); info.signature = body[jss::signature].asString(); - XRPL_ASSERT(result.size() == 1, "xrpl::ValidatorList::parseBlobs : single element result"); + XRPL_ASSERT( + result.size() == 1, "xrpl::ValidatorList::parseBlobs : single element result"); return result; } // Treat unknown versions as if they're the latest version. This @@ -494,39 +499,38 @@ splitMessageParts( smallMsg.set_manifest(blob.manifest()); XRPL_ASSERT( - Message::totalSize(smallMsg) <= maximumMessageSize, "xrpl::splitMessageParts : maximum message size"); + Message::totalSize(smallMsg) <= maximumMessageSize, + "xrpl::splitMessageParts : maximum message size"); - messages.emplace_back(std::make_shared(smallMsg, protocol::mtVALIDATOR_LIST), sha512Half(smallMsg), 1); + messages.emplace_back( + std::make_shared(smallMsg, protocol::mtVALIDATOR_LIST), + sha512Half(smallMsg), + 1); return messages.back().numVLs; } - else + + std::optional smallMsg; + smallMsg.emplace(); + smallMsg->set_version(largeMsg.version()); + smallMsg->set_manifest(largeMsg.manifest()); + + for (std::size_t i = begin; i < end; ++i) { - std::optional smallMsg; - smallMsg.emplace(); - smallMsg->set_version(largeMsg.version()); - smallMsg->set_manifest(largeMsg.manifest()); - - for (std::size_t i = begin; i < end; ++i) - { - *smallMsg->add_blobs() = largeMsg.blobs(i); - } - - if (Message::totalSize(*smallMsg) > maxSize) - { - // free up the message space - smallMsg.reset(); - return splitMessage(messages, largeMsg, maxSize, begin, end); - } - else - { - messages.emplace_back( - std::make_shared(*smallMsg, protocol::mtVALIDATOR_LIST_COLLECTION), - sha512Half(*smallMsg), - smallMsg->blobs_size()); - return messages.back().numVLs; - } + *smallMsg->add_blobs() = largeMsg.blobs(i); } - return 0; + + if (Message::totalSize(*smallMsg) > maxSize) + { + // free up the message space + smallMsg.reset(); + return splitMessage(messages, largeMsg, maxSize, begin, end); + } + + messages.emplace_back( + std::make_shared(*smallMsg, protocol::mtVALIDATOR_LIST_COLLECTION), + sha512Half(*smallMsg), + smallMsg->blobs_size()); + return messages.back().numVLs; } // Build a v1 protocol message using only the current VL @@ -555,7 +559,8 @@ buildValidatorListMessage( Message::totalSize(msg) <= maximumMessageSize, "xrpl::buildValidatorListMessage(ValidatorBlobInfo) : maximum " "message size"); - messages.emplace_back(std::make_shared(msg, protocol::mtVALIDATOR_LIST), sha512Half(msg), 1); + messages.emplace_back( + std::make_shared(msg, protocol::mtVALIDATOR_LIST), sha512Half(msg), 1); return 1; } @@ -598,12 +603,12 @@ buildValidatorListMessage( // split into smaller messages return splitMessage(messages, msg, maxSize); } - else - { - messages.emplace_back( - std::make_shared(msg, protocol::mtVALIDATOR_LIST_COLLECTION), sha512Half(msg), msg.blobs_size()); - return messages.back().numVLs; - } + + messages.emplace_back( + std::make_shared(msg, protocol::mtVALIDATOR_LIST_COLLECTION), + sha512Half(msg), + msg.blobs_size()); + return messages.back().numVLs; } [[nodiscard]] @@ -624,35 +629,45 @@ ValidatorList::buildValidatorListMessages( "xrpl::ValidatorList::buildValidatorListMessages : empty messages " "input"); auto const& [currentSeq, currentBlob] = *blobInfos.begin(); - auto numVLs = std::accumulate(messages.begin(), messages.end(), 0, [](std::size_t total, MessageWithHash const& m) { - return total + m.numVLs; - }); + auto numVLs = std::accumulate( + messages.begin(), messages.end(), 0, [](std::size_t total, MessageWithHash const& m) { + return total + m.numVLs; + }); if (messageVersion == 2 && peerSequence < maxSequence) { // Version 2 if (messages.empty()) { - numVLs = buildValidatorListMessage(messages, peerSequence, rawVersion, rawManifest, blobInfos, maxSize); + numVLs = buildValidatorListMessage( + messages, peerSequence, rawVersion, rawManifest, blobInfos, maxSize); if (messages.empty()) + { // No message was generated. Create an empty placeholder so we // dont' repeat the work later. messages.emplace_back(); + } } // Don't send it next time. return {maxSequence, numVLs}; } - else if (messageVersion == 1 && peerSequence < currentSeq) + if (messageVersion == 1 && peerSequence < currentSeq) { // Version 1 if (messages.empty()) { numVLs = buildValidatorListMessage( - messages, rawVersion, currentBlob.manifest ? *currentBlob.manifest : rawManifest, currentBlob, maxSize); + messages, + rawVersion, + currentBlob.manifest ? *currentBlob.manifest : rawManifest, + currentBlob, + maxSize); if (messages.empty()) + { // No message was generated. Create an empty placeholder so we // dont' repeat the work later. messages.emplace_back(); + } } // Don't send it next time. @@ -675,9 +690,15 @@ ValidatorList::sendValidatorList( HashRouter& hashRouter, beast::Journal j) { - std::size_t const messageVersion = peer.supportsFeature(ProtocolFeature::ValidatorList2Propagation) ? 2 - : peer.supportsFeature(ProtocolFeature::ValidatorListPropagation) ? 1 - : 0; + std::size_t messageVersion = 0; + if (peer.supportsFeature(ProtocolFeature::ValidatorList2Propagation)) + { + messageVersion = 2; + } + else if (peer.supportsFeature(ProtocolFeature::ValidatorListPropagation)) + { + messageVersion = 1; + } if (!messageVersion) return; auto const [newPeerSequence, numVLs] = buildValidatorListMessages( @@ -703,21 +724,28 @@ ValidatorList::sendValidatorList( } // The only way sent wil be false is if the messages was too big, and // thus there will only be one entry without a message - XRPL_ASSERT(sent || messages.size() == 1, "xrpl::ValidatorList::sendValidatorList : sent or one message"); + XRPL_ASSERT( + sent || messages.size() == 1, + "xrpl::ValidatorList::sendValidatorList : sent or one message"); if (sent) { if (messageVersion > 1) - JLOG(j.debug()) << "Sent " << messages.size() << " validator list collection(s) containing " << numVLs - << " validator list(s) for " << strHex(publisherKey) << " with sequence range " - << peerSequence << ", " << newPeerSequence << " to " << peer.fingerprint(); + { + JLOG(j.debug()) << "Sent " << messages.size() + << " validator list collection(s) containing " << numVLs + << " validator list(s) for " << strHex(publisherKey) + << " with sequence range " << peerSequence << ", " + << newPeerSequence << " to " << peer.fingerprint(); + } else { XRPL_ASSERT( numVLs == 1, "xrpl::ValidatorList::sendValidatorList : one validator " "list"); - JLOG(j.debug()) << "Sent validator list for " << strHex(publisherKey) << " with sequence " - << newPeerSequence << " to " << peer.fingerprint(); + JLOG(j.debug()) << "Sent validator list for " << strHex(publisherKey) + << " with sequence " << newPeerSequence << " to " + << peer.fingerprint(); } } } @@ -738,7 +766,16 @@ ValidatorList::sendValidatorList( { std::vector messages; sendValidatorList( - peer, peerSequence, publisherKey, maxSequence, rawVersion, rawManifest, blobInfos, messages, hashRouter, j); + peer, + peerSequence, + publisherKey, + maxSequence, + rawVersion, + rawManifest, + blobInfos, + messages, + hashRouter, + j); } // static @@ -802,14 +839,15 @@ ValidatorList::broadcastBlobs( // the peer, and foreach provides a const& for (auto& peer : overlay.getActivePeers()) { - if (toSkip->count(peer->id()) == 0) + if (!toSkip->contains(peer->id())) { auto const peerSequence = peer->publisherListSequence(publisherKey).value_or(0); if (peerSequence < maxSequence) { if (blobInfos.empty()) buildBlobInfos(blobInfos, lists); - auto const v2 = peer->supportsFeature(ProtocolFeature::ValidatorList2Propagation); + auto const v2 = + peer->supportsFeature(ProtocolFeature::ValidatorList2Propagation); sendValidatorList( *peer, peerSequence, @@ -873,7 +911,14 @@ ValidatorList::applyListsAndBroadcast( { auto const& pubCollection = publisherLists_[*result.publisherKey]; - broadcastBlobs(*result.publisherKey, pubCollection, *pubCollection.maxSequence, hash, overlay, hashRouter, j_); + broadcastBlobs( + *result.publisherKey, + pubCollection, + *pubCollection.maxSequence, + hash, + overlay, + hashRouter, + j_); } return result; @@ -887,7 +932,8 @@ ValidatorList::applyLists( std::string siteUri, std::optional const& hash /* = {} */) { - if (std::count(std::begin(supportedListVersions), std::end(supportedListVersions), version) != 1) + if (std::count(std::begin(supportedListVersions), std::end(supportedListVersions), version) != + 1) return PublisherListStats{ListDisposition::unsupported_version}; std::lock_guard lock{mutex_}; @@ -895,23 +941,33 @@ ValidatorList::applyLists( PublisherListStats result; for (auto const& blobInfo : blobs) { - auto stats = - applyList(manifest, blobInfo.manifest, blobInfo.blob, blobInfo.signature, version, siteUri, hash, lock); + auto stats = applyList( + manifest, + blobInfo.manifest, + blobInfo.blob, + blobInfo.signature, + version, + siteUri, + hash, + lock); if (stats.bestDisposition() < result.bestDisposition() || - (stats.bestDisposition() == result.bestDisposition() && stats.sequence > result.sequence)) + (stats.bestDisposition() == result.bestDisposition() && + stats.sequence > result.sequence)) { stats.mergeDispositions(result); result = std::move(stats); } else + { result.mergeDispositions(stats); + } ///////// } // Clean up the collection, because some of the processing may have made it // inconsistent - if (result.publisherKey && publisherLists_.count(*result.publisherKey)) + if (result.publisherKey && publisherLists_.contains(*result.publisherKey)) { auto& pubCollection = publisherLists_[*result.publisherKey]; auto& remaining = pubCollection.remaining; @@ -967,9 +1023,13 @@ ValidatorList::updatePublisherList( { // Decrement list count for removed keys if (keyListings_[*iOld] <= 1) + { keyListings_.erase(*iOld); + } else + { --keyListings_[*iOld]; + } ++iOld; } else @@ -988,15 +1048,18 @@ ValidatorList::updatePublisherList( { auto m = deserializeManifest(base64_decode(valManifest)); - if (!m || !keyListings_.count(m->masterKey)) + if (!m || !keyListings_.contains(m->masterKey)) { - JLOG(j_.warn()) << "List for " << strHex(pubKey) << " contained untrusted validator manifest"; + JLOG(j_.warn()) << "List for " << strHex(pubKey) + << " contained untrusted validator manifest"; continue; } - if (auto const r = validatorManifests_.applyManifest(std::move(*m)); r == ManifestDisposition::invalid) + if (auto const r = validatorManifests_.applyManifest(std::move(*m)); + r == ManifestDisposition::invalid) { - JLOG(j_.warn()) << "List for " << strHex(pubKey) << " contained invalid validator manifest"; + JLOG(j_.warn()) << "List for " << strHex(pubKey) + << " contained invalid validator manifest"; } } } @@ -1046,15 +1109,17 @@ ValidatorList::applyList( PublicKey pubKey = *pubKeyOpt; if (result > ListDisposition::pending) { - if (publisherLists_.count(pubKey)) + if (publisherLists_.contains(pubKey)) { auto const& pubCollection = publisherLists_[pubKey]; if (pubCollection.maxSequence && - (result == ListDisposition::same_sequence || result == ListDisposition::known_sequence)) + (result == ListDisposition::same_sequence || + result == ListDisposition::known_sequence)) { // We've seen something valid list for this publisher // already, so return what we know about it. - return PublisherListStats{result, pubKey, pubCollection.status, *pubCollection.maxSequence}; + return PublisherListStats{ + result, pubKey, pubCollection.status, *pubCollection.maxSequence}; } } return PublisherListStats{result}; @@ -1063,18 +1128,21 @@ ValidatorList::applyList( // Update publisher's list auto& pubCollection = publisherLists_[pubKey]; auto const sequence = list[jss::sequence].asUInt(); - auto const accepted = (result == ListDisposition::accepted || result == ListDisposition::expired); + auto const accepted = + (result == ListDisposition::accepted || result == ListDisposition::expired); if (accepted) - pubCollection.status = - result == ListDisposition::accepted ? PublisherStatus::available : PublisherStatus::expired; + { + pubCollection.status = result == ListDisposition::accepted ? PublisherStatus::available + : PublisherStatus::expired; + } pubCollection.rawManifest = globalManifest; if (!pubCollection.maxSequence || sequence > *pubCollection.maxSequence) pubCollection.maxSequence = sequence; Json::Value const& newList = list[jss::validators]; std::vector oldList; - if (accepted && pubCollection.remaining.count(sequence) != 0) + if (accepted && pubCollection.remaining.contains(sequence)) { // We've seen this list before and stored it in "remaining". The // normal expected process is that the processed list would have @@ -1089,15 +1157,18 @@ ValidatorList::applyList( // Remove the entry in "remaining" pubCollection.remaining.erase(sequence); // Done - XRPL_ASSERT(publisher.sequence == sequence, "xrpl::ValidatorList::applyList : publisher sequence match"); + XRPL_ASSERT( + publisher.sequence == sequence, + "xrpl::ValidatorList::applyList : publisher sequence match"); } else { auto& publisher = accepted ? pubCollection.current : pubCollection.remaining[sequence]; publisher.sequence = sequence; - publisher.validFrom = TimeKeeper::time_point{ - TimeKeeper::duration{list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}}; - publisher.validUntil = TimeKeeper::time_point{TimeKeeper::duration{list[jss::expiration].asUInt()}}; + publisher.validFrom = TimeKeeper::time_point{TimeKeeper::duration{ + list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}}; + publisher.validUntil = + TimeKeeper::time_point{TimeKeeper::duration{list[jss::expiration].asUInt()}}; publisher.siteUri = std::move(siteUri); publisher.rawBlob = blob; publisher.rawSignature = signature; @@ -1118,11 +1189,13 @@ ValidatorList::applyList( if (val.isObject() && val.isMember(jss::validation_public_key) && val[jss::validation_public_key].isString()) { - std::optional const ret = strUnHex(val[jss::validation_public_key].asString()); + std::optional const ret = + strUnHex(val[jss::validation_public_key].asString()); if (!ret || !publicKeyType(makeSlice(*ret))) { - JLOG(j_.error()) << "Invalid node identity: " << val[jss::validation_public_key].asString(); + JLOG(j_.error()) + << "Invalid node identity: " << val[jss::validation_public_key].asString(); } else { @@ -1147,7 +1220,8 @@ ValidatorList::applyList( pubCollection.rawVersion = std::max(pubCollection.rawVersion, 2u); } - PublisherListStats const applyResult{result, pubKey, pubCollection.status, *pubCollection.maxSequence}; + PublisherListStats const applyResult{ + result, pubKey, pubCollection.status, *pubCollection.maxSequence}; if (accepted) { @@ -1220,7 +1294,7 @@ ValidatorList::verify( std::string const& blob, std::string const& signature) { - if (!publisherLists_.count(manifest.masterKey)) + if (!publisherLists_.contains(manifest.masterKey)) return {ListDisposition::untrusted, {}}; PublicKey masterPubKey = manifest.masterKey; @@ -1249,25 +1323,36 @@ ValidatorList::verify( if (!r.parse(data, list)) return {ListDisposition::invalid, masterPubKey}; - if (list.isMember(jss::sequence) && list[jss::sequence].isInt() && list.isMember(jss::expiration) && - list[jss::expiration].isInt() && (!list.isMember(jss::effective) || list[jss::effective].isInt()) && + if (list.isMember(jss::sequence) && list[jss::sequence].isInt() && + list.isMember(jss::expiration) && list[jss::expiration].isInt() && + (!list.isMember(jss::effective) || list[jss::effective].isInt()) && list.isMember(jss::validators) && list[jss::validators].isArray()) { auto const sequence = list[jss::sequence].asUInt(); - auto const validFrom = TimeKeeper::time_point{ - TimeKeeper::duration{list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}}; - auto const validUntil = TimeKeeper::time_point{TimeKeeper::duration{list[jss::expiration].asUInt()}}; + auto const validFrom = TimeKeeper::time_point{TimeKeeper::duration{ + list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}}; + auto const validUntil = + TimeKeeper::time_point{TimeKeeper::duration{list[jss::expiration].asUInt()}}; auto const now = timeKeeper_.now(); auto const& listCollection = publisherLists_[masterPubKey]; if (validUntil <= validFrom) + { return {ListDisposition::invalid, masterPubKey}; - else if (sequence < listCollection.current.sequence) + } + if (sequence < listCollection.current.sequence) + { return {ListDisposition::stale, masterPubKey}; - else if (sequence == listCollection.current.sequence) + } + if (sequence == listCollection.current.sequence) + { return {ListDisposition::same_sequence, masterPubKey}; - else if (validUntil <= now) + } + if (validUntil <= now) + { return {ListDisposition::expired, masterPubKey}; - else if (validFrom > now) + } + if (validFrom > now) + { // Not yet valid. Return pending if one of the following is true // * There's no maxSequence, indicating this is the first blob seen // for this publisher @@ -1279,10 +1364,11 @@ ValidatorList::verify( // prevents the risk of missing valid data. Else return // known_sequence return !listCollection.maxSequence || sequence > *listCollection.maxSequence || - (listCollection.remaining.count(sequence) == 0 && + (!listCollection.remaining.contains(sequence) && validFrom < listCollection.remaining.at(*listCollection.maxSequence).validFrom) ? std::make_pair(ListDisposition::pending, masterPubKey) : std::make_pair(ListDisposition::known_sequence, masterPubKey); + } } else { @@ -1298,14 +1384,14 @@ ValidatorList::listed(PublicKey const& identity) const std::shared_lock read_lock{mutex_}; auto const pubKey = validatorManifests_.getMasterKey(identity); - return keyListings_.find(pubKey) != keyListings_.end(); + return keyListings_.contains(pubKey); } bool ValidatorList::trusted(ValidatorList::shared_lock const&, PublicKey const& identity) const { auto const pubKey = validatorManifests_.getMasterKey(identity); - return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end(); + return trustedMasterKeys_.contains(pubKey); } bool @@ -1321,7 +1407,7 @@ ValidatorList::getListedKey(PublicKey const& identity) const std::shared_lock read_lock{mutex_}; auto const pubKey = validatorManifests_.getMasterKey(identity); - if (keyListings_.find(pubKey) != keyListings_.end()) + if (keyListings_.contains(pubKey)) return pubKey; return std::nullopt; } @@ -1330,7 +1416,7 @@ std::optional ValidatorList::getTrustedKey(ValidatorList::shared_lock const&, PublicKey const& identity) const { auto const pubKey = validatorManifests_.getMasterKey(identity); - if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end()) + if (trustedMasterKeys_.contains(pubKey)) return pubKey; return std::nullopt; } @@ -1347,7 +1433,7 @@ bool ValidatorList::trustedPublisher(PublicKey const& identity) const { std::shared_lock read_lock{mutex_}; - return identity.size() && publisherLists_.count(identity) && + return identity.size() && publisherLists_.contains(identity) && publisherLists_.at(identity).status < PublisherStatus::revoked; } @@ -1380,9 +1466,13 @@ ValidatorList::removePublisherList( continue; if (iVal->second <= 1) + { keyListings_.erase(iVal); + } else + { --iVal->second; + } } iList->second.current.list.clear(); @@ -1394,7 +1484,7 @@ ValidatorList::removePublisherList( std::size_t ValidatorList::count(ValidatorList::shared_lock const&) const { - return publisherLists_.size() + (localPublisherList.list.size() > 0); + return publisherLists_.size() + (!localPublisherList.list.empty()); } std::size_t @@ -1425,9 +1515,13 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const { (void)sequence; if (check.validFrom <= chainedExpiration) + { chainedExpiration = check.validUntil; + } else + { break; + } } // Earliest @@ -1437,7 +1531,7 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const } } - if (localPublisherList.list.size() > 0) + if (!localPublisherList.list.empty()) { PublisherList collection = localPublisherList; // Unfetched @@ -1486,9 +1580,13 @@ ValidatorList::getJson() const x[jss::expiration] = to_string(*when); if (*when > timeKeeper_.now()) + { x[jss::status] = "active"; + } else + { x[jss::status] = "expired"; + } } } else @@ -1548,7 +1646,8 @@ ValidatorList::getJson() const appendList(future, r); // Race conditions can happen, so make this check "fuzzy" XRPL_ASSERT( - future.validFrom > timeKeeper_.now() + 600s, "xrpl::ValidatorList::getJson : minimum valid from"); + future.validFrom > timeKeeper_.now() + 600s, + "xrpl::ValidatorList::getJson : minimum valid from"); } if (remaining.size()) curr[jss::remaining] = std::move(remaining); @@ -1595,13 +1694,14 @@ ValidatorList::for_each_listed(std::function func) } void -ValidatorList::for_each_available(std::function const& blobInfos, - PublicKey const& pubKey, - std::size_t maxSequence, - uint256 const& hash)> func) const +ValidatorList::for_each_available( + std::function const& blobInfos, + PublicKey const& pubKey, + std::size_t maxSequence, + uint256 const& hash)> func) const { std::shared_lock read_lock{mutex_}; @@ -1609,7 +1709,9 @@ ValidatorList::for_each_available(std::function -ValidatorList::getAvailable(std::string_view pubKey, std::optional forceVersion /* = {} */) +ValidatorList::getAvailable( + std::string_view pubKey, + std::optional forceVersion /* = {} */) { std::shared_lock read_lock{mutex_}; @@ -1646,7 +1750,10 @@ ValidatorList::getAvailable(std::string_view pubKey, std::optional 0) @@ -1688,7 +1795,8 @@ ValidatorList::calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize auto const errorThreshold = std::min( listThreshold_, // publisherLists_.size() - listThreshold_ + 1); - XRPL_ASSERT(errorThreshold > 0, "xrpl::ValidatorList::calculateQuorum : nonzero error threshold"); + XRPL_ASSERT( + errorThreshold > 0, "xrpl::ValidatorList::calculateQuorum : nonzero error threshold"); if (unavailable >= errorThreshold) return std::numeric_limits::max(); } @@ -1727,7 +1835,8 @@ ValidatorList::calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize // Note that the negative UNL protocol introduced the // AbsoluteMinimumQuorum which is 60% of the original UNL size. The // effective quorum should not be lower than it. - return static_cast(std::max(std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f))); + return static_cast( + std::max(std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f))); } TrustChanges @@ -1758,7 +1867,8 @@ ValidatorList::updateTrusted( if (iter != remaining.end() && iter->second.validFrom <= closeTime) { // Find the LAST candidate that is ready to go live. - for (auto next = std::next(iter); next != remaining.end() && next->second.validFrom <= closeTime; + for (auto next = std::next(iter); + next != remaining.end() && next->second.validFrom <= closeTime; ++iter, ++next) { XRPL_ASSERT( @@ -1775,13 +1885,17 @@ ValidatorList::updateTrusted( auto sequence = iter->first; auto& candidate = iter->second; auto& current = collection.current; - XRPL_ASSERT(candidate.validFrom <= closeTime, "xrpl::ValidatorList::updateTrusted : maximum time"); + XRPL_ASSERT( + candidate.validFrom <= closeTime, + "xrpl::ValidatorList::updateTrusted : maximum time"); auto const oldList = current.list; current = std::move(candidate); if (collection.status != PublisherStatus::available) collection.status = PublisherStatus::available; - XRPL_ASSERT(current.sequence == sequence, "xrpl::ValidatorList::updateTrusted : sequence match"); + XRPL_ASSERT( + current.sequence == sequence, + "xrpl::ValidatorList::updateTrusted : sequence match"); // If the list is expired, remove the validators so they don't // get processed in. The expiration check below will do the rest // of the work @@ -1804,7 +1918,8 @@ ValidatorList::updateTrusted( // Remove if expired // ValidatorLists specified in the local config file never expire. // Hence, the below steps are not relevant for localPublisherList - if (collection.status == PublisherStatus::available && collection.current.validUntil <= closeTime) + if (collection.status == PublisherStatus::available && + collection.current.validUntil <= closeTime) { removePublisherList(lock, pubKey, PublisherStatus::expired); ops.setUNLBlocked(); @@ -1830,7 +1945,9 @@ ValidatorList::updateTrusted( } else { - XRPL_ASSERT(kit->second >= listThreshold_, "xrpl::ValidatorList::updateTrusted : count meets threshold"); + XRPL_ASSERT( + kit->second >= listThreshold_, + "xrpl::ValidatorList::updateTrusted : count meets threshold"); ++it; } } @@ -1868,7 +1985,7 @@ ValidatorList::updateTrusted( { for (auto const& k : trustedMasterKeys_) { - if (negativeUNL_.count(k)) + if (negativeUNL_.contains(k)) --effectiveUnlSize; } hash_set negUnlNodeIDs; @@ -1878,22 +1995,23 @@ ValidatorList::updateTrusted( } for (auto const& nid : seenValidators) { - if (negUnlNodeIDs.count(nid)) + if (negUnlNodeIDs.contains(nid)) --seenSize; } } quorum_ = calculateQuorum(unlSize, effectiveUnlSize, seenSize); - JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of " << unlSize << " trusted validators (" - << trustChanges.added.size() << " added, " << trustChanges.removed.size() << " removed)"; + JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of " << unlSize + << " trusted validators (" << trustChanges.added.size() << " added, " + << trustChanges.removed.size() << " removed)"; if (unlSize < quorum_) { - JLOG(j_.warn()) << "New quorum of " << quorum_ << " exceeds the number of trusted validators (" << unlSize - << ")"; + JLOG(j_.warn()) << "New quorum of " << quorum_ + << " exceeds the number of trusted validators (" << unlSize << ")"; } - if ((publisherLists_.size() || localPublisherList.list.size()) && unlSize == 0) + if ((!publisherLists_.empty() || !localPublisherList.list.empty()) && unlSize == 0) { // No validators. Lock down. ops.setUNLBlocked(); @@ -1944,14 +2062,13 @@ ValidatorList::negativeUNLFilter(std::vector>&& va ret.begin(), ret.end(), [&](auto const& v) -> bool { - if (auto const masterKey = getTrustedKey(read_lock, v->getSignerPublic()); masterKey) + if (auto const masterKey = getTrustedKey(read_lock, v->getSignerPublic()); + masterKey) { - return negativeUNL_.count(*masterKey); - } - else - { - return false; + return negativeUNL_.contains(*masterKey); } + + return false; }), ret.end()); } diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp index 7d440028cf..8db29c0bb0 100644 --- a/src/xrpld/app/misc/detail/ValidatorSite.cpp +++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp @@ -52,7 +52,9 @@ ValidatorSite::Site::Resource::Resource(std::string uri_) : uri{std::move(uri_)} pUrl.port = 443; } else + { throw std::runtime_error("Unsupported scheme: '" + pUrl.scheme + "'"); + } } ValidatorSite::Site::Site(std::string uri) @@ -61,12 +63,14 @@ ValidatorSite::Site::Site(std::string uri) , redirCount{0} , refreshInterval{default_refresh_interval} , nextRefresh{clock_type::now()} - , lastRequestEndpoint{} , lastRequestSuccessful{false} { } -ValidatorSite::ValidatorSite(Application& app, std::optional j, std::chrono::seconds timeout) +ValidatorSite::ValidatorSite( + Application& app, + std::optional j, + std::chrono::seconds timeout) : app_{app} , j_{j ? *j : app_.logs().journal("ValidatorSite")} , timer_{app_.getIOContext()} @@ -112,7 +116,9 @@ ValidatorSite::load(std::vector const& siteURIs) } bool -ValidatorSite::load(std::vector const& siteURIs, std::lock_guard const& lock_sites) +ValidatorSite::load( + std::vector const& siteURIs, + std::lock_guard const& lock_sites) { // If no sites are provided, act as if a site failed to load. if (siteURIs.empty()) @@ -172,7 +178,7 @@ ValidatorSite::stop() { timer_.cancel(); } - catch (boost::system::system_error const&) + catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) { } stopping_ = false; @@ -181,10 +187,13 @@ ValidatorSite::stop() } void -ValidatorSite::setTimer(std::lock_guard const& site_lock, std::lock_guard const& state_lock) +ValidatorSite::setTimer( + std::lock_guard const& site_lock, + std::lock_guard const& state_lock) { - auto next = std::min_element( - sites_.begin(), sites_.end(), [](Site const& a, Site const& b) { return a.nextRefresh < b.nextRefresh; }); + auto next = std::min_element(sites_.begin(), sites_.end(), [](Site const& a, Site const& b) { + return a.nextRefresh < b.nextRefresh; + }); if (next != sites_.end()) { @@ -192,7 +201,8 @@ ValidatorSite::setTimer(std::lock_guard const& site_lock, std::lock_ cv_.notify_all(); timer_.expires_at(next->nextRefresh); auto idx = std::distance(sites_.begin(), next); - timer_.async_wait([this, idx](boost::system::error_code const& ec) { this->onTimer(idx, ec); }); + timer_.async_wait( + [this, idx](boost::system::error_code const& ec) { this->onTimer(idx, ec); }); } } @@ -213,17 +223,19 @@ ValidatorSite::makeRequest( { timer_.cancel_one(); } - catch (boost::system::system_error const&) + catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) { } }; - auto onFetch = [this, siteIdx, timeoutCancel]( - error_code const& err, endpoint_type const& endpoint, detail::response_type&& resp) { - timeoutCancel(); - onSiteFetch(err, endpoint, std::move(resp), siteIdx); - }; + auto onFetch = + [this, siteIdx, timeoutCancel]( + error_code const& err, endpoint_type const& endpoint, detail::response_type&& resp) { + timeoutCancel(); + onSiteFetch(err, endpoint, std::move(resp), siteIdx); + }; - auto onFetchFile = [this, siteIdx, timeoutCancel](error_code const& err, std::string const& resp) { + auto onFetchFile = [this, siteIdx, timeoutCancel]( + error_code const& err, std::string const& resp) { timeoutCancel(); onTextFetch(err, resp, siteIdx); }; @@ -258,7 +270,8 @@ ValidatorSite::makeRequest( else { BOOST_ASSERT(resource->pUrl.scheme == "file"); - sp = std::make_shared(resource->pUrl.path, app_.getIOContext(), onFetchFile); + sp = std::make_shared( + resource->pUrl.path, app_.getIOContext(), onFetchFile); } sites_[siteIdx].lastRequestSuccessful = false; @@ -268,7 +281,9 @@ ValidatorSite::makeRequest( // than requestTimeout_ to complete std::lock_guard lock_state{state_mutex_}; timer_.expires_after(requestTimeout_); - timer_.async_wait([this, siteIdx](boost::system::error_code const& ec) { this->onRequestTimeout(siteIdx, ec); }); + timer_.async_wait([this, siteIdx](boost::system::error_code const& ec) { + this->onRequestTimeout(siteIdx, ec); + }); } void @@ -287,7 +302,9 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec) // first, which will leave activeResource empty. auto const& site = sites_[siteIdx]; if (site.activeResource) + { JLOG(j_.warn()) << "Request for " << site.activeResource->uri << " took too long"; + } else JLOG(j_.error()) << "Request took too long, but a response has " "already been processed"; @@ -322,7 +339,10 @@ ValidatorSite::onTimer(std::size_t siteIdx, error_code const& ec) { JLOG(j_.error()) << "Exception in " << __func__ << ": " << ex.what(); onSiteFetch( - boost::system::error_code{-1, boost::system::generic_category()}, {}, detail::response_type{}, siteIdx); + boost::system::error_code{-1, boost::system::generic_category()}, + {}, + detail::response_type{}, + siteIdx); } } @@ -337,7 +357,8 @@ ValidatorSite::parseJsonResponse( Json::Value body; if (!r.parse(res.data(), body)) { - JLOG(j_.warn()) << "Unable to parse JSON response from " << sites_[siteIdx].activeResource->uri; + JLOG(j_.warn()) << "Unable to parse JSON response from " + << sites_[siteIdx].activeResource->uri; throw std::runtime_error{"bad json"}; } return body; @@ -345,10 +366,11 @@ ValidatorSite::parseJsonResponse( auto const [valid, version, blobs] = [&body]() { // Check the easy fields first - bool valid = body.isObject() && body.isMember(jss::manifest) && body[jss::manifest].isString() && - body.isMember(jss::version) && body[jss::version].isInt(); + bool valid = body.isObject() && body.isMember(jss::manifest) && + body[jss::manifest].isString() && body.isMember(jss::version) && + body[jss::version].isInt(); // Check the version-specific blob & signature fields - std::uint32_t version; + std::uint32_t version = 0; std::vector blobs; if (valid) { @@ -361,18 +383,22 @@ ValidatorSite::parseJsonResponse( if (!valid) { - JLOG(j_.warn()) << "Missing fields in JSON response from " << sites_[siteIdx].activeResource->uri; + JLOG(j_.warn()) << "Missing fields in JSON response from " + << sites_[siteIdx].activeResource->uri; throw std::runtime_error{"missing fields"}; } auto const manifest = body[jss::manifest].asString(); - XRPL_ASSERT(version == body[jss::version].asUInt(), "xrpl::ValidatorSite::parseJsonResponse : version match"); + XRPL_ASSERT( + version == body[jss::version].asUInt(), + "xrpl::ValidatorSite::parseJsonResponse : version match"); auto const& uri = sites_[siteIdx].activeResource->uri; auto const hash = sha512Half(manifest, blobs, version); auto const applyResult = app_.validators().applyListsAndBroadcast( manifest, version, blobs, uri, hash, app_.overlay(), app_.getHashRouter(), app_.getOPs()); - sites_[siteIdx].lastRefreshStatus.emplace(Site::Status{clock_type::now(), applyResult.bestDisposition(), ""}); + sites_[siteIdx].lastRefreshStatus.emplace( + Site::Status{clock_type::now(), applyResult.bestDisposition(), ""}); for (auto const& [disp, count] : applyResult.dispositions) { @@ -382,28 +408,34 @@ ValidatorSite::parseJsonResponse( JLOG(j_.debug()) << "Applied " << count << " new validator list(s) from " << uri; break; case ListDisposition::expired: - JLOG(j_.debug()) << "Applied " << count << " expired validator list(s) from " << uri; + JLOG(j_.debug()) << "Applied " << count << " expired validator list(s) from " + << uri; break; case ListDisposition::same_sequence: - JLOG(j_.debug()) << "Ignored " << count << " validator list(s) with current sequence from " << uri; + JLOG(j_.debug()) << "Ignored " << count + << " validator list(s) with current sequence from " << uri; break; case ListDisposition::pending: - JLOG(j_.debug()) << "Processed " << count << " future validator list(s) from " << uri; + JLOG(j_.debug()) << "Processed " << count << " future validator list(s) from " + << uri; break; case ListDisposition::known_sequence: - JLOG(j_.debug()) << "Ignored " << count << " validator list(s) with future known sequence from " << uri; + JLOG(j_.debug()) << "Ignored " << count + << " validator list(s) with future known sequence from " << uri; break; case ListDisposition::stale: JLOG(j_.warn()) << "Ignored " << count << "stale validator list(s) from " << uri; break; case ListDisposition::untrusted: - JLOG(j_.warn()) << "Ignored " << count << " untrusted validator list(s) from " << uri; + JLOG(j_.warn()) << "Ignored " << count << " untrusted validator list(s) from " + << uri; break; case ListDisposition::invalid: JLOG(j_.warn()) << "Ignored " << count << " invalid validator list(s) from " << uri; break; case ListDisposition::unsupported_version: - JLOG(j_.warn()) << "Ignored " << count << " unsupported version validator list(s) from " << uri; + JLOG(j_.warn()) << "Ignored " << count + << " unsupported version validator list(s) from " << uri; break; default: BOOST_ASSERT(false); @@ -413,8 +445,10 @@ ValidatorSite::parseJsonResponse( if (body.isMember(jss::refresh_interval) && body[jss::refresh_interval].isNumeric()) { using namespace std::chrono_literals; - std::chrono::minutes const refresh = - std::clamp(std::chrono::minutes{body[jss::refresh_interval].asUInt()}, 1min, std::chrono::minutes{24h}); + std::chrono::minutes const refresh = std::clamp( + std::chrono::minutes{body[jss::refresh_interval].asUInt()}, + 1min, + std::chrono::minutes{24h}); sites_[siteIdx].refreshInterval = refresh; sites_[siteIdx].nextRefresh = clock_type::now() + sites_[siteIdx].refreshInterval; } @@ -422,13 +456,13 @@ ValidatorSite::parseJsonResponse( std::shared_ptr ValidatorSite::processRedirect( - detail::response_type& res, + detail::response_type const& res, std::size_t siteIdx, std::lock_guard const& sites_lock) { using namespace boost::beast::http; std::shared_ptr newLocation; - if (res.find(field::location) == res.end() || res[field::location].empty()) + if (!res.contains(field::location) || res[field::location].empty()) { JLOG(j_.warn()) << "Request for validator list at " << sites_[siteIdx].activeResource->uri << " returned a redirect with no Location."; @@ -437,12 +471,14 @@ ValidatorSite::processRedirect( if (sites_[siteIdx].redirCount == max_redirects) { - JLOG(j_.warn()) << "Exceeded max redirects for validator list at " << sites_[siteIdx].loadedResource->uri; + JLOG(j_.warn()) << "Exceeded max redirects for validator list at " + << sites_[siteIdx].loadedResource->uri; throw std::runtime_error{"max redirects"}; } - JLOG(j_.debug()) << "Got redirect for validator list from " << sites_[siteIdx].activeResource->uri - << " to new location " << res[field::location]; + JLOG(j_.debug()) << "Got redirect for validator list from " + << sites_[siteIdx].activeResource->uri << " to new location " + << res[field::location]; try { @@ -463,14 +499,15 @@ void ValidatorSite::onSiteFetch( boost::system::error_code const& ec, endpoint_type const& endpoint, - detail::response_type&& res, + detail::response_type const& res, std::size_t siteIdx) { std::lock_guard lock_sites{sites_mutex_}; { if (endpoint != endpoint_type{}) sites_[siteIdx].lastRequestEndpoint = endpoint; - JLOG(j_.debug()) << "Got completion for " << sites_[siteIdx].activeResource->uri << " " << endpoint; + JLOG(j_.debug()) << "Got completion for " << sites_[siteIdx].activeResource->uri << " " + << endpoint; auto onError = [&](std::string const& errMsg, bool retry) { sites_[siteIdx].lastRefreshStatus.emplace( Site::Status{clock_type::now(), ListDisposition::invalid, errMsg}); @@ -483,8 +520,8 @@ ValidatorSite::onSiteFetch( }; if (ec) { - JLOG(j_.warn()) << "Problem retrieving from " << sites_[siteIdx].activeResource->uri << " " << endpoint - << " " << ec.value() << ":" << ec.message(); + JLOG(j_.warn()) << "Problem retrieving from " << sites_[siteIdx].activeResource->uri + << " " << endpoint << " " << ec.value() << ":" << ec.message(); onError("fetch error", true); } else @@ -508,7 +545,8 @@ ValidatorSite::onSiteFetch( "xrpl::ValidatorSite::onSiteFetch : non-null " "validator"); // for perm redirects, also update our starting URI - if (res.result() == status::moved_permanently || res.result() == status::permanent_redirect) + if (res.result() == status::moved_permanently || + res.result() == status::permanent_redirect) { sites_[siteIdx].startingResource = newLocation; } @@ -517,8 +555,9 @@ ValidatorSite::onSiteFetch( // state update/notify below } default: { - JLOG(j_.warn()) << "Request for validator list at " << sites_[siteIdx].activeResource->uri - << " " << endpoint << " returned bad status: " << res.result_int(); + JLOG(j_.warn()) << "Request for validator list at " + << sites_[siteIdx].activeResource->uri << " " << endpoint + << " returned bad status: " << res.result_int(); onError("bad result code", true); } } @@ -540,7 +579,10 @@ ValidatorSite::onSiteFetch( } void -ValidatorSite::onTextFetch(boost::system::error_code const& ec, std::string const& res, std::size_t siteIdx) +ValidatorSite::onTextFetch( + boost::system::error_code const& ec, + std::string const& res, + std::size_t siteIdx) { std::lock_guard lock_sites{sites_mutex_}; { @@ -548,8 +590,8 @@ ValidatorSite::onTextFetch(boost::system::error_code const& ec, std::string cons { if (ec) { - JLOG(j_.warn()) << "Problem retrieving from " << sites_[siteIdx].activeResource->uri << " " - << ec.value() << ": " << ec.message(); + JLOG(j_.warn()) << "Problem retrieving from " << sites_[siteIdx].activeResource->uri + << " " << ec.value() << ": " << ec.message(); throw std::runtime_error{"fetch error"}; } diff --git a/src/xrpld/app/misc/detail/WorkBase.h b/src/xrpld/app/misc/detail/WorkBase.h index e5fd72f118..35f04efe00 100644 --- a/src/xrpld/app/misc/detail/WorkBase.h +++ b/src/xrpld/app/misc/detail/WorkBase.h @@ -24,7 +24,8 @@ protected: using endpoint_type = boost::asio::ip::tcp::endpoint; public: - using callback_type = std::function; + using callback_type = + std::function; protected: using socket_type = boost::asio::ip::tcp::socket; @@ -130,14 +131,20 @@ WorkBase::run() { if (!strand_.running_in_this_thread()) return boost::asio::post( - ios_, boost::asio::bind_executor(strand_, std::bind(&WorkBase::run, impl().shared_from_this()))); + ios_, + boost::asio::bind_executor( + strand_, std::bind(&WorkBase::run, impl().shared_from_this()))); resolver_.async_resolve( host_, port_, boost::asio::bind_executor( strand_, - std::bind(&WorkBase::onResolve, impl().shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + std::bind( + &WorkBase::onResolve, + impl().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } template @@ -149,7 +156,8 @@ WorkBase::cancel() return boost::asio::post( ios_, - boost::asio::bind_executor(strand_, std::bind(&WorkBase::cancel, impl().shared_from_this()))); + boost::asio::bind_executor( + strand_, std::bind(&WorkBase::cancel, impl().shared_from_this()))); } error_code ec; @@ -180,7 +188,11 @@ WorkBase::onResolve(error_code const& ec, results_type results) results, boost::asio::bind_executor( strand_, - std::bind(&WorkBase::onConnect, impl().shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + std::bind( + &WorkBase::onConnect, + impl().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } template @@ -209,7 +221,8 @@ WorkBase::onStart() impl().stream(), req_, boost::asio::bind_executor( - strand_, std::bind(&WorkBase::onRequest, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind(&WorkBase::onRequest, impl().shared_from_this(), std::placeholders::_1))); } template @@ -224,7 +237,8 @@ WorkBase::onRequest(error_code const& ec) readBuf_, res_, boost::asio::bind_executor( - strand_, std::bind(&WorkBase::onResponse, impl().shared_from_this(), std::placeholders::_1))); + strand_, + std::bind(&WorkBase::onResponse, impl().shared_from_this(), std::placeholders::_1))); } template diff --git a/src/xrpld/app/misc/detail/WorkFile.h b/src/xrpld/app/misc/detail/WorkFile.h index 8d2d4edb50..896d7ddc71 100644 --- a/src/xrpld/app/misc/detail/WorkFile.h +++ b/src/xrpld/app/misc/detail/WorkFile.h @@ -60,7 +60,8 @@ WorkFile::run() { if (!strand_.running_in_this_thread()) return boost::asio::post( - ios_, boost::asio::bind_executor(strand_, std::bind(&WorkFile::run, shared_from_this()))); + ios_, + boost::asio::bind_executor(strand_, std::bind(&WorkFile::run, shared_from_this()))); error_code ec; auto const fileContents = getFileContents(ec, path_, megabytes(1)); diff --git a/src/xrpld/app/misc/detail/WorkSSL.cpp b/src/xrpld/app/misc/detail/WorkSSL.cpp index 47902e900a..3ae0db1a96 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.cpp +++ b/src/xrpld/app/misc/detail/WorkSSL.cpp @@ -32,7 +32,10 @@ WorkSSL::onConnect(error_code const& ec) { auto err = ec ? ec : context_.postConnectVerify(stream_, host_); if (err) - return fail(err); + { + fail(err); + return; + } stream_.async_handshake( boost::asio::ssl::stream_base::client, @@ -44,7 +47,10 @@ void WorkSSL::onHandshake(error_code const& ec) { if (ec) - return fail(ec); + { + fail(ec); + return; + } onStart(); } diff --git a/src/xrpld/app/misc/detail/setup_HashRouter.cpp b/src/xrpld/app/misc/detail/setup_HashRouter.cpp new file mode 100644 index 0000000000..0cc61f0730 --- /dev/null +++ b/src/xrpld/app/misc/detail/setup_HashRouter.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include + +namespace xrpl { + +HashRouter::Setup +setup_HashRouter(Config const& config) +{ + using namespace std::chrono; + + HashRouter::Setup setup; + auto const& section = config.section("hashrouter"); + + std::int32_t tmp{}; + + if (set(tmp, "hold_time", section)) + { + if (tmp < 12) + { + Throw( + "HashRouter hold time must be at least 12 seconds (the " + "approximate validation time for three ledgers)."); + } + setup.holdTime = seconds(tmp); + } + if (set(tmp, "relay_time", section)) + { + if (tmp < 8) + { + Throw( + "HashRouter relay time must be at least 8 seconds (the " + "approximate validation time for two ledgers)."); + } + setup.relayTime = seconds(tmp); + } + if (setup.relayTime > setup.holdTime) + { + Throw("HashRouter relay time must be less than or equal to hold time"); + } + + return setup; +} + +} // namespace xrpl diff --git a/src/xrpld/app/misc/make_NetworkOPs.h b/src/xrpld/app/misc/make_NetworkOPs.h new file mode 100644 index 0000000000..7dce966f04 --- /dev/null +++ b/src/xrpld/app/misc/make_NetworkOPs.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include + +namespace xrpl { + +class LedgerMaster; +class ValidatorKeys; + +std::unique_ptr +make_NetworkOPs( + ServiceRegistry& registry, + NetworkOPs::clock_type& clock, + bool standalone, + std::size_t minPeerCount, + bool start_valid, + JobQueue& job_queue, + LedgerMaster& ledgerMaster, + ValidatorKeys const& validatorKeys, + boost::asio::io_context& io_svc, + beast::Journal journal, + beast::insight::Collector::ptr const& collector); + +} // namespace xrpl diff --git a/src/xrpld/app/misc/setup_HashRouter.h b/src/xrpld/app/misc/setup_HashRouter.h new file mode 100644 index 0000000000..9b767d0170 --- /dev/null +++ b/src/xrpld/app/misc/setup_HashRouter.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace xrpl { + +// Forward declaration +class Config; + +/** Create HashRouter setup from configuration */ +HashRouter::Setup +setup_HashRouter(Config const& config); + +} // namespace xrpl diff --git a/src/xrpld/app/paths/AMMLiquidity.h b/src/xrpld/app/paths/AMMLiquidity.h index 441c3b6e3d..71b8dbb12a 100644 --- a/src/xrpld/app/paths/AMMLiquidity.h +++ b/src/xrpld/app/paths/AMMLiquidity.h @@ -1,13 +1,12 @@ #pragma once -#include -#include -#include - #include #include #include #include +#include +#include +#include namespace xrpl { diff --git a/src/xrpld/app/paths/AMMOffer.h b/src/xrpld/app/paths/AMMOffer.h index ebaa7311c0..aa6132dfce 100644 --- a/src/xrpld/app/paths/AMMOffer.h +++ b/src/xrpld/app/paths/AMMOffer.h @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/src/xrpld/app/paths/AccountCurrencies.cpp b/src/xrpld/app/paths/AccountCurrencies.cpp index 8e51999018..92ba61e00e 100644 --- a/src/xrpld/app/paths/AccountCurrencies.cpp +++ b/src/xrpld/app/paths/AccountCurrencies.cpp @@ -3,7 +3,10 @@ namespace xrpl { hash_set -accountSourceCurrencies(AccountID const& account, std::shared_ptr const& lrCache, bool includeXRP) +accountSourceCurrencies( + AccountID const& account, + std::shared_ptr const& lrCache, + bool includeXRP) { hash_set currencies; @@ -34,7 +37,10 @@ accountSourceCurrencies(AccountID const& account, std::shared_ptr -accountDestCurrencies(AccountID const& account, std::shared_ptr const& lrCache, bool includeXRP) +accountDestCurrencies( + AccountID const& account, + std::shared_ptr const& lrCache, + bool includeXRP) { hash_set currencies; diff --git a/src/xrpld/app/paths/AccountCurrencies.h b/src/xrpld/app/paths/AccountCurrencies.h index eb8cc92aec..d8459de7f2 100644 --- a/src/xrpld/app/paths/AccountCurrencies.h +++ b/src/xrpld/app/paths/AccountCurrencies.h @@ -7,9 +7,15 @@ namespace xrpl { hash_set -accountDestCurrencies(AccountID const& account, std::shared_ptr const& cache, bool includeXRP); +accountDestCurrencies( + AccountID const& account, + std::shared_ptr const& cache, + bool includeXRP); hash_set -accountSourceCurrencies(AccountID const& account, std::shared_ptr const& lrLedger, bool includeXRP); +accountSourceCurrencies( + AccountID const& account, + std::shared_ptr const& lrLedger, + bool includeXRP); } // namespace xrpl diff --git a/src/xrpld/app/paths/PathRequest.cpp b/src/xrpld/app/paths/PathRequest.cpp index e2a3e14485..f82a207552 100644 --- a/src/xrpld/app/paths/PathRequest.cpp +++ b/src/xrpld/app/paths/PathRequest.cpp @@ -1,10 +1,7 @@ #include -#include -#include #include #include #include -#include #include #include #include @@ -14,6 +11,9 @@ #include #include #include +#include +#include +#include #include #include @@ -86,7 +86,8 @@ PathRequest::~PathRequest() full += "ms"; } stream << iIdentifier << " complete:" << fast << full - << " total:" << duration_cast(steady_clock::now() - created_).count() << "ms"; + << " total:" << duration_cast(steady_clock::now() - created_).count() + << "ms"; } bool @@ -285,7 +286,8 @@ PathRequest::parseJson(Json::Value const& jvParams) convert_all_ = saDstAmount == STAmount(saDstAmount.issue(), 1u, 0, true); if ((saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) || - (saDstAmount.getCurrency() == badCurrency()) || (!convert_all_ && saDstAmount <= beast::zero)) + (saDstAmount.getCurrency() == badCurrency()) || + (!convert_all_ && saDstAmount <= beast::zero)) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; @@ -404,10 +406,8 @@ PathRequest::parseJson(Json::Value const& jvParams) jvStatus = rpcError(rpcDOMAIN_MALFORMED); return PFR_PJ_INVALID; } - else - { - domain = num; - } + + domain = num; } return PFR_PJ_NOCHANGE; @@ -449,11 +449,23 @@ PathRequest::getPathFinder( if (i != currency_map.end()) return i->second; auto pathfinder = std::make_unique( - cache, *raSrcAccount, *raDstAccount, currency, std::nullopt, dst_amount, saSendMax, domain, app_); + cache, + *raSrcAccount, + *raDstAccount, + currency, + std::nullopt, + dst_amount, + saSendMax, + domain, + app_); if (pathfinder->findPaths(level, continueCallback)) + { pathfinder->computePathRanks(max_paths_, continueCallback); + } else + { pathfinder.reset(); // It's a bad request - clear it. + } return currency_map[currency] = std::move(pathfinder); } @@ -490,9 +502,11 @@ PathRequest::findPaths( { if (continueCallback && !continueCallback()) break; - JLOG(m_journal.debug()) << iIdentifier << " Trying to find paths: " << STAmount(issue, 1).getFullText(); + JLOG(m_journal.debug()) << iIdentifier + << " Trying to find paths: " << STAmount(issue, 1).getFullText(); - auto& pathfinder = getPathFinder(cache, currency_map, issue.currency, dst_amount, level, continueCallback); + auto& pathfinder = + getPathFinder(cache, currency_map, issue.currency, dst_amount, level, continueCallback); if (!pathfinder) { JLOG(m_journal.debug()) << iIdentifier << " No paths found"; @@ -500,8 +514,8 @@ PathRequest::findPaths( } STPath fullLiquidityPath; - auto ps = - pathfinder->getBestPaths(max_paths_, fullLiquidityPath, mContext[issue], issue.account, continueCallback); + auto ps = pathfinder->getBestPaths( + max_paths_, fullLiquidityPath, mContext[issue], issue.account, continueCallback); mContext[issue] = ps; auto const& sourceAccount = [&] { @@ -514,7 +528,8 @@ PathRequest::findPaths( return *raSrcAccount; }(); - STAmount saMaxAmount = saSendMax.value_or(STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true)); + STAmount saMaxAmount = + saSendMax.value_or(STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true)); JLOG(m_journal.debug()) << iIdentifier << " Paths found, calling rippleCalc"; @@ -552,13 +567,15 @@ PathRequest::findPaths( domain, // --> Domain. app_.logs()); - if (rc.result() != tesSUCCESS) + if (!isTesSuccess(rc.result())) { - JLOG(m_journal.warn()) << iIdentifier << " Failed with covering path " << transHuman(rc.result()); + JLOG(m_journal.warn()) + << iIdentifier << " Failed with covering path " << transHuman(rc.result()); } else { - JLOG(m_journal.debug()) << iIdentifier << " Extra path element gives " << transHuman(rc.result()); + JLOG(m_journal.debug()) + << iIdentifier << " Extra path element gives " << transHuman(rc.result()); } } @@ -582,7 +599,8 @@ PathRequest::findPaths( } else { - JLOG(m_journal.debug()) << iIdentifier << " rippleCalc returns " << transHuman(rc.result()); + JLOG(m_journal.debug()) + << iIdentifier << " rippleCalc returns " << transHuman(rc.result()); } } @@ -636,9 +654,13 @@ PathRequest::doUpdate( { // first pass if (loaded || fast) + { iLevel = app_.config().PATH_SEARCH_FAST; + } else + { iLevel = app_.config().PATH_SEARCH; + } } else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast) { @@ -650,7 +672,8 @@ PathRequest::doUpdate( else if (bLastSuccess) { // decrement, if possible - if (iLevel > app_.config().PATH_SEARCH || (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))) + if (iLevel > app_.config().PATH_SEARCH || + (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))) --iLevel; } else diff --git a/src/xrpld/app/paths/PathRequest.h b/src/xrpld/app/paths/PathRequest.h index 864d2cb932..0ffc6c6e2c 100644 --- a/src/xrpld/app/paths/PathRequest.h +++ b/src/xrpld/app/paths/PathRequest.h @@ -3,11 +3,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -105,7 +105,11 @@ private: Returns false if the source currencies are invalid. */ bool - findPaths(std::shared_ptr const&, int const, Json::Value&, std::function const&); + findPaths( + std::shared_ptr const&, + int const, + Json::Value&, + std::function const&); int parseJson(Json::Value const&); @@ -135,7 +139,7 @@ private: std::optional domain; - bool convert_all_; + bool convert_all_{}; std::recursive_mutex mIndexLock; LedgerIndex mLastIndex; diff --git a/src/xrpld/app/paths/PathRequests.cpp b/src/xrpld/app/paths/PathRequests.cpp index 2c1ef8cbad..61db1e58ef 100644 --- a/src/xrpld/app/paths/PathRequests.cpp +++ b/src/xrpld/app/paths/PathRequests.cpp @@ -24,7 +24,8 @@ PathRequests::getLineCache(std::shared_ptr const& ledger, bool a std::uint32_t const lineSeq = lineCache ? lineCache->getLedger()->seq() : 0; std::uint32_t const lgrSeq = ledger->seq(); - JLOG(mJournal.debug()) << "getLineCache has cache for " << lineSeq << ", considering " << lgrSeq; + JLOG(mJournal.debug()) << "getLineCache has cache for " << lineSeq << ", considering " + << lgrSeq; if ((lineSeq == 0) || // no ledger (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger @@ -35,7 +36,8 @@ PathRequests::getLineCache(std::shared_ptr const& ledger, bool a // Assign to the local before the member, because the member is a // weak_ptr, and will immediately discard it if there are no other // references. - lineCache_ = lineCache = std::make_shared(ledger, app_.journal("RippleLineCache")); + lineCache_ = lineCache = + std::make_shared(ledger, app_.journal("RippleLineCache")); } return lineCache; } @@ -58,7 +60,8 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) bool newRequests = app_.getLedgerMaster().isNewPathRequest(); bool mustBreak = false; - JLOG(mJournal.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", " << requests.size() << " requests"; + JLOG(mJournal.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", " + << requests.size() << " requests"; int processed = 0, removed = 0; @@ -92,7 +95,9 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) return (bool)getSubscriber(request); }; if (!request->needsUpdate(newRequests, cache->getLedger()->seq())) + { remove = false; + } else { if (auto ipSub = getSubscriber(request)) @@ -130,14 +135,15 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) // Remove any dangling weak pointers or weak // pointers that refer to this path request. - auto ret = std::remove_if(requests_.begin(), requests_.end(), [&removed, &request](auto const& wl) { - auto r = wl.lock(); + auto ret = std::remove_if( + requests_.begin(), requests_.end(), [&removed, &request](auto const& wl) { + auto r = wl.lock(); - if (r && r != request) - return false; - ++removed; - return true; - }); + if (r && r != request) + return false; + ++removed; + return true; + }); requests_.erase(ret, requests_.end()); } @@ -180,7 +186,8 @@ PathRequests::updateAll(std::shared_ptr const& inLedger) } } while (!app_.getJobQueue().isStopping()); - JLOG(mJournal.debug()) << "updateAll complete: " << processed << " processed and " << removed << " removed"; + JLOG(mJournal.debug()) << "updateAll complete: " << processed << " processed and " << removed + << " removed"; } bool @@ -238,7 +245,8 @@ PathRequests::makeLegacyPathRequest( { // This assignment must take place before the // completion function is called - req = std::make_shared(app_, completion, consumer, ++mLastIdentifier, *this, mJournal); + req = std::make_shared( + app_, completion, consumer, ++mLastIdentifier, *this, mJournal); auto [valid, jvRes] = req->doCreate(getLineCache(inLedger, false), request); @@ -268,7 +276,8 @@ PathRequests::doLegacyPathRequest( { auto cache = std::make_shared(inLedger, app_.journal("RippleLineCache")); - auto req = std::make_shared(app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal); + auto req = + std::make_shared(app_, [] {}, consumer, ++mLastIdentifier, *this, mJournal); auto [valid, jvRes] = req->doCreate(cache, request); if (valid) diff --git a/src/xrpld/app/paths/PathRequests.h b/src/xrpld/app/paths/PathRequests.h index f89ca3c4da..98f4be9fd7 100644 --- a/src/xrpld/app/paths/PathRequests.h +++ b/src/xrpld/app/paths/PathRequests.h @@ -14,7 +14,10 @@ class PathRequests { public: /** A collection of all PathRequest instances. */ - PathRequests(Application& app, beast::Journal journal, beast::insight::Collector::ptr const& collector) + PathRequests( + Application& app, + beast::Journal journal, + beast::insight::Collector::ptr const& collector) : app_(app), mJournal(journal), mLastIdentifier(0) { mFast = collector->make_event("pathfind_fast"); diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/app/paths/Pathfinder.cpp index b64ce0cc20..c777fcb2f7 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/app/paths/Pathfinder.cpp @@ -1,7 +1,5 @@ -#include #include #include -#include #include #include @@ -9,7 +7,9 @@ #include #include #include +#include #include +#include #include @@ -62,7 +62,10 @@ struct AccountCandidate }; bool -compareAccountCandidate(std::uint32_t seq, AccountCandidate const& first, AccountCandidate const& second) +compareAccountCandidate( + std::uint32_t seq, + AccountCandidate const& first, + AccountCandidate const& second) { if (first.priority < second.priority) return false; @@ -92,7 +95,7 @@ struct PathCost }; using PathCostList = std::vector; -static PathTable mPathTable; +PathTable mPathTable; std::string pathTypeToString(Pathfinder::PathType const& type) @@ -153,7 +156,9 @@ Pathfinder::Pathfinder( , mSrcCurrency(uSrcCurrency) , mSrcIssuer(uSrcIssuer) , mSrcAmount(srcAmount.value_or(STAmount( - Issue{uSrcCurrency, uSrcIssuer.value_or(isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)}, + Issue{ + uSrcCurrency, + uSrcIssuer.value_or(isXRP(uSrcCurrency) ? xrpAccount() : uSrcAccount)}, 1u, 0, true))) @@ -165,7 +170,8 @@ Pathfinder::Pathfinder( , j_(app.journal("Pathfinder")) { XRPL_ASSERT( - !uSrcIssuer || isXRP(uSrcCurrency) == isXRP(uSrcIssuer.value()), "xrpl::Pathfinder::Pathfinder : valid inputs"); + !uSrcIssuer || isXRP(uSrcCurrency) == isXRP(uSrcIssuer.value()), + "xrpl::Pathfinder::Pathfinder : valid inputs"); } bool @@ -183,7 +189,8 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue // below - why don't we do it each time we return false? } - if (mSrcAccount == mDstAccount && mDstAccount == mEffectiveDst && mSrcCurrency == mDstAmount.getCurrency()) + if (mSrcAccount == mDstAccount && mDstAccount == mEffectiveDst && + mSrcCurrency == mDstAmount.getCurrency()) { // No need to send to same account with same currency. JLOG(j_.debug()) << "Tried to send to same issuer"; @@ -207,8 +214,8 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue auto issuerString = mSrcIssuer ? to_string(*mSrcIssuer) : std::string("none"); JLOG(j_.trace()) << "findPaths>" << " mSrcAccount=" << mSrcAccount << " mDstAccount=" << mDstAccount - << " mDstAmount=" << mDstAmount.getFullText() << " mSrcCurrency=" << mSrcCurrency - << " mSrcIssuer=" << issuerString; + << " mDstAmount=" << mDstAmount.getFullText() + << " mSrcCurrency=" << mSrcCurrency << " mSrcIssuer=" << issuerString; if (!mLedger) { @@ -245,14 +252,15 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue auto const reserve = STAmount(mLedger->fees().reserve); if (mDstAmount < reserve) { - JLOG(j_.debug()) << "New account not getting enough funding: " << mDstAmount << " < " << reserve; + JLOG(j_.debug()) << "New account not getting enough funding: " << mDstAmount << " < " + << reserve; return false; } } // Now compute the payment type from the types of the source and destination // currencies. - PaymentType paymentType; + PaymentType paymentType = pt_XRP_to_XRP; if (bSrcXrp && bDstXrp) { // XRP -> XRP @@ -330,9 +338,17 @@ Pathfinder::getPathLiquidity( rcInput.partialPaymentAllowed = true; auto rc = path::RippleCalc::rippleCalculate( - sandbox, mSrcAmount, minDstAmount, mDstAccount, mSrcAccount, pathSet, mDomain, app_.logs(), &rcInput); + sandbox, + mSrcAmount, + minDstAmount, + mDstAccount, + mSrcAccount, + pathSet, + mDomain, + app_.logs(), + &rcInput); // If we can't get even the minimum liquidity requested, we're done. - if (rc.result() != tesSUCCESS) + if (!isTesSuccess(rc.result())) return rc.result(); qualityOut = getRate(rc.actualAmountOut, rc.actualAmountIn); @@ -362,7 +378,8 @@ Pathfinder::getPathLiquidity( } catch (std::exception const& e) { - JLOG(j_.info()) << "checkpath: exception (" << e.what() << ") " << path.getJson(JsonOptions::none); + JLOG(j_.info()) << "checkpath: exception (" << e.what() << ") " + << path.getJson(JsonOptions::none); return tefEXCEPTION; } } @@ -449,7 +466,8 @@ Pathfinder::rankPaths( std::vector& rankedPaths, std::function const& continueCallback) { - JLOG(j_.trace()) << "rankPaths with " << paths.size() << " candidates, and " << maxPaths << " maximum"; + JLOG(j_.trace()) << "rankPaths with " << paths.size() << " candidates, and " << maxPaths + << " maximum"; rankedPaths.clear(); rankedPaths.reserve(paths.size()); @@ -473,9 +491,10 @@ Pathfinder::rankPaths( if (!currentPath.empty()) { STAmount liquidity; - uint64_t uQuality; - auto const resultCode = getPathLiquidity(currentPath, saMinDstAmount, liquidity, uQuality); - if (resultCode != tesSUCCESS) + uint64_t uQuality = 0; + auto const resultCode = + getPathLiquidity(currentPath, saMinDstAmount, liquidity, uQuality); + if (!isTesSuccess(resultCode)) { JLOG(j_.debug()) << "findPaths: dropping : " << transToken(resultCode) << ": " << currentPath.getJson(JsonOptions::none); @@ -496,7 +515,9 @@ Pathfinder::rankPaths( // length of path // A better PathRank is lower, best are sorted to the beginning. std::sort( - rankedPaths.begin(), rankedPaths.end(), [&](Pathfinder::PathRank const& a, Pathfinder::PathRank const& b) { + rankedPaths.begin(), + rankedPaths.end(), + [&](Pathfinder::PathRank const& a, Pathfinder::PathRank const& b) { // 1) Higher quality (lower cost) is better if (!convert_all_ && a.quality != b.quality) return a.quality < b.quality; @@ -522,12 +543,14 @@ Pathfinder::getBestPaths( AccountID const& srcIssuer, std::function const& continueCallback) { - JLOG(j_.debug()) << "findPaths: " << mCompletePaths.size() << " paths and " << extraPaths.size() << " extras"; + JLOG(j_.debug()) << "findPaths: " << mCompletePaths.size() << " paths and " << extraPaths.size() + << " extras"; if (mCompletePaths.empty() && extraPaths.empty()) return mCompletePaths; - XRPL_ASSERT(fullLiquidityPath.empty(), "xrpl::Pathfinder::getBestPaths : first empty path result"); + XRPL_ASSERT( + fullLiquidityPath.empty(), "xrpl::Pathfinder::getBestPaths : first empty path result"); bool const issuerIsSender = isXRP(mSrcCurrency) || (srcIssuer == mSrcAccount); std::vector extraPathRanks; @@ -551,17 +574,29 @@ Pathfinder::getBestPaths( bool useExtraPath = false; if (pathsIterator == mPathRanks.end()) + { useExtraPath = true; + } else if (extraPathsIterator == extraPathRanks.end()) + { usePath = true; + } else if (extraPathsIterator->quality < pathsIterator->quality) + { useExtraPath = true; + } else if (extraPathsIterator->quality > pathsIterator->quality) + { usePath = true; + } else if (extraPathsIterator->liquidity > pathsIterator->liquidity) + { useExtraPath = true; + } else if (extraPathsIterator->liquidity < pathsIterator->liquidity) + { usePath = true; + } else { // Risk is high they have identical liquidity @@ -615,7 +650,8 @@ Pathfinder::getBestPaths( { // We found an extra path that can move the whole amount. fullLiquidityPath = (startsWithIssuer ? removeIssuer(path) : path); - JLOG(j_.debug()) << "Found extra full path: " << fullLiquidityPath.getJson(JsonOptions::none); + JLOG(j_.debug()) << "Found extra full path: " + << fullLiquidityPath.getJson(JsonOptions::none); } else { @@ -625,7 +661,8 @@ Pathfinder::getBestPaths( if (remaining > beast::zero) { - XRPL_ASSERT(fullLiquidityPath.empty(), "xrpl::Pathfinder::getBestPaths : second empty path result"); + XRPL_ASSERT( + fullLiquidityPath.empty(), "xrpl::Pathfinder::getBestPaths : second empty path result"); JLOG(j_.info()) << "Paths could not send " << remaining << " of " << mDstAmount; } else @@ -639,8 +676,8 @@ bool Pathfinder::issueMatchesOrigin(Issue const& issue) { bool matchingCurrency = (issue.currency == mSrcCurrency); - bool matchingAccount = - isXRP(issue.currency) || (mSrcIssuer && issue.account == mSrcIssuer) || issue.account == mSrcAccount; + bool matchingAccount = isXRP(issue.currency) || (mSrcIssuer && issue.account == mSrcIssuer) || + issue.account == mSrcAccount; return matchingCurrency && matchingAccount; } @@ -686,7 +723,8 @@ Pathfinder::getPathsOut( } else if ( rspEntry.getBalance() <= beast::zero && - (!rspEntry.getLimitPeer() || -rspEntry.getBalance() >= rspEntry.getLimitPeer() || + (!rspEntry.getLimitPeer() || + -rspEntry.getBalance() >= rspEntry.getLimitPeer() || (bAuthRequired && !rspEntry.getAuth()))) { } @@ -730,7 +768,9 @@ Pathfinder::addLinks( } STPathSet& -Pathfinder::addPathsForType(PathType const& pathType, std::function const& continueCallback) +Pathfinder::addPathsForType( + PathType const& pathType, + std::function const& continueCallback) { JLOG(j_.debug()) << "addPathsForType " << CollectionAndDelimiter(pathType, ", "); // See if the set of paths for this type already exists. @@ -752,8 +792,8 @@ Pathfinder::addPathsForType(PathType const& pathType, std::function STPathSet const& parentPaths = addPathsForType(parentPathType, continueCallback); STPathSet& pathsOut = mPaths[pathType]; - JLOG(j_.debug()) << "getPaths< adding onto '" << pathTypeToString(parentPathType) << "' to get '" - << pathTypeToString(pathType) << "'"; + JLOG(j_.debug()) << "getPaths< adding onto '" << pathTypeToString(parentPathType) + << "' to get '" << pathTypeToString(pathType) << "'"; int initialSize = mCompletePaths.size(); @@ -801,7 +841,10 @@ Pathfinder::addPathsForType(PathType const& pathType, std::function } bool -Pathfinder::isNoRipple(AccountID const& fromAccount, AccountID const& toAccount, Currency const& currency) +Pathfinder::isNoRipple( + AccountID const& fromAccount, + AccountID const& toAccount, + Currency const& currency) { auto sleRipple = mLedger->read(keylet::line(toAccount, fromAccount, currency)); @@ -827,7 +870,8 @@ Pathfinder::isNoRippleOut(STPath const& currentPath) // If there's only one item in the path, return true if that item specifies // no ripple on the output. A path with no ripple on its output can't be // followed by a link with no ripple on its input. - auto const& fromAccount = (currentPath.size() == 1) ? mSrcAccount : (currentPath.end() - 2)->getAccountID(); + auto const& fromAccount = + (currentPath.size() == 1) ? mSrcAccount : (currentPath.end() - 2)->getAccountID(); auto const& toAccount = endElement.getAccountID(); return isNoRipple(fromAccount, toAccount, endElement.getCurrency()); } @@ -874,7 +918,8 @@ Pathfinder::addLink( { if (mDstAmount.native() && !currentPath.empty()) { // non-default path to XRP destination - JLOG(j_.trace()) << "complete path found ax: " << currentPath.getJson(JsonOptions::none); + JLOG(j_.trace()) << "complete path found ax: " + << currentPath.getJson(JsonOptions::none); addUniquePath(mCompletePaths, currentPath); } } @@ -891,7 +936,8 @@ Pathfinder::addLink( bool const bDestOnly(addFlags & afAC_LAST); if (auto const lines = mRLCache->getRippleLines( - uEndAccount, bIsNoRippleOut ? LineDirection::incoming : LineDirection::outgoing)) + uEndAccount, + bIsNoRippleOut ? LineDirection::incoming : LineDirection::outgoing)) { auto& rippleLines = *lines; @@ -941,8 +987,8 @@ Pathfinder::addLink( // this is a complete path if (!currentPath.empty()) { - JLOG(j_.trace()) - << "complete path found ae: " << currentPath.getJson(JsonOptions::none); + JLOG(j_.trace()) << "complete path found ae: " + << currentPath.getJson(JsonOptions::none); addUniquePath(mCompletePaths, currentPath); } } @@ -960,7 +1006,12 @@ Pathfinder::addLink( { // save this candidate int out = getPathsOut( - uEndCurrency, acct, direction, bIsEndCurrency, mEffectiveDst, continueCallback); + uEndCurrency, + acct, + direction, + bIsEndCurrency, + mEffectiveDst, + continueCallback); if (out) candidates.push_back({out, acct}); } @@ -973,14 +1024,21 @@ Pathfinder::addLink( candidates.begin(), candidates.end(), std::bind( - compareAccountCandidate, mLedger->seq(), std::placeholders::_1, std::placeholders::_2)); + compareAccountCandidate, + mLedger->seq(), + std::placeholders::_1, + std::placeholders::_2)); int count = candidates.size(); // allow more paths from source if ((count > 10) && (uEndAccount != mSrcAccount)) + { count = 10; + } else if (count > 50) + { count = 50; + } auto it = candidates.begin(); while (count-- != 0) @@ -1010,14 +1068,16 @@ Pathfinder::addLink( // to XRP only if (!bOnXRP && app_.getOrderBookDB().isBookToXRP({uEndCurrency, uEndIssuer}, mDomain)) { - STPathElement pathElement(STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()); + STPathElement pathElement( + STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()); incompletePaths.assembleAdd(currentPath, pathElement); } } else { bool bDestOnly = (addFlags & afOB_LAST) != 0; - auto books = app_.getOrderBookDB().getBooksByTakerPays({uEndCurrency, uEndIssuer}, mDomain); + auto books = + app_.getOrderBookDB().getBooksByTakerPays({uEndCurrency, uEndIssuer}, mDomain); JLOG(j_.trace()) << books.size() << " books found from this currency/issuer"; for (auto const& book : books) @@ -1025,7 +1085,8 @@ Pathfinder::addLink( if (continueCallback && !continueCallback()) return; if (!currentPath.hasSeen(xrpAccount(), book.out.currency, book.out.account) && - !issueMatchesOrigin(book.out) && (!bDestOnly || (book.out.currency == mDstAmount.getCurrency()))) + !issueMatchesOrigin(book.out) && + (!bDestOnly || (book.out.currency == mDstAmount.getCurrency()))) { STPath newPath(currentPath); @@ -1033,19 +1094,24 @@ Pathfinder::addLink( { // to XRP // add the order book itself - newPath.emplace_back(STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()); + newPath.emplace_back( + STPathElement::typeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()); if (mDstAmount.getCurrency().isZero()) { // destination is XRP, add account and path is // complete - JLOG(j_.trace()) << "complete path found bx: " << currentPath.getJson(JsonOptions::none); + JLOG(j_.trace()) << "complete path found bx: " + << currentPath.getJson(JsonOptions::none); addUniquePath(mCompletePaths, newPath); } else + { incompletePaths.push_back(newPath); + } } - else if (!currentPath.hasSeen(book.out.account, book.out.currency, book.out.account)) + else if (!currentPath.hasSeen( + book.out.account, book.out.currency, book.out.account)) { // Don't want the book if we've already seen the issuer // book -> account -> book @@ -1074,10 +1140,13 @@ Pathfinder::addLink( { // We skipped a required issuer } - else if (book.out.account == mEffectiveDst && book.out.currency == mDstAmount.getCurrency()) + else if ( + book.out.account == mEffectiveDst && + book.out.currency == mDstAmount.getCurrency()) { // with the destination account, this path is // complete - JLOG(j_.trace()) << "complete path found ba: " << currentPath.getJson(JsonOptions::none); + JLOG(j_.trace()) << "complete path found ba: " + << currentPath.getJson(JsonOptions::none); addUniquePath(mCompletePaths, newPath); } else @@ -1086,7 +1155,10 @@ Pathfinder::addLink( incompletePaths.assembleAdd( newPath, STPathElement( - STPathElement::typeAccount, book.out.account, book.out.currency, book.out.account)); + STPathElement::typeAccount, + book.out.account, + book.out.currency, + book.out.account)); } } } @@ -1104,6 +1176,7 @@ makePath(char const* string) while (true) { + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (*string++) { case 's': // source diff --git a/src/xrpld/app/paths/RippleLineCache.cpp b/src/xrpld/app/paths/RippleLineCache.cpp index aaef368cff..ac3e28e579 100644 --- a/src/xrpld/app/paths/RippleLineCache.cpp +++ b/src/xrpld/app/paths/RippleLineCache.cpp @@ -11,8 +11,9 @@ RippleLineCache::RippleLineCache(std::shared_ptr const& ledger, RippleLineCache::~RippleLineCache() { - JLOG(journal_.debug()) << "destroyed for ledger " << ledger_->header().seq << " with " << lines_.size() - << " accounts and " << totalLineCount_ << " distinct trust lines."; + JLOG(journal_.debug()) << "destroyed for ledger " << ledger_->header().seq << " with " + << lines_.size() << " accounts and " << totalLineCount_ + << " distinct trust lines."; } std::shared_ptr> @@ -21,54 +22,61 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct auto const hash = hasher_(accountID); AccountKey key(accountID, direction, hash); AccountKey otherkey( - accountID, direction == LineDirection::outgoing ? LineDirection::incoming : LineDirection::outgoing, hash); + accountID, + direction == LineDirection::outgoing ? LineDirection::incoming : LineDirection::outgoing, + hash); std::lock_guard sl(mLock); - auto [it, inserted] = [&]() { - if (auto otheriter = lines_.find(otherkey); otheriter != lines_.end()) - { - // The whole point of using the direction flag is to reduce the - // number of trust line objects held in memory. Ensure that there is - // only a single set of trustlines in the cache per account. - auto const size = otheriter->second ? otheriter->second->size() : 0; - JLOG(journal_.info()) << "Request for " << (direction == LineDirection::outgoing ? "outgoing" : "incoming") - << " trust lines for account " << accountID << " found " << size - << (direction == LineDirection::outgoing ? " incoming" : " outgoing") - << " trust lines. " - << (direction == LineDirection::outgoing ? "Deleting the subset of incoming" - : "Returning the superset of outgoing") - << " trust lines. "; - if (direction == LineDirection::outgoing) + auto [it, inserted] = + [&]() { + if (auto otheriter = lines_.find(otherkey); otheriter != lines_.end()) { - // This request is for the outgoing set, but there is already a - // subset of incoming lines in the cache. Erase that subset - // to be replaced by the full set. The full set will be built - // below, and will be returned, if needed, on subsequent calls - // for either value of outgoing. - XRPL_ASSERT(size <= totalLineCount_, "xrpl::RippleLineCache::getRippleLines : maximum lines"); - totalLineCount_ -= size; - lines_.erase(otheriter); + // The whole point of using the direction flag is to reduce the + // number of trust line objects held in memory. Ensure that there is + // only a single set of trustlines in the cache per account. + auto const size = otheriter->second ? otheriter->second->size() : 0; + JLOG(journal_.info()) + << "Request for " + << (direction == LineDirection::outgoing ? "outgoing" : "incoming") + << " trust lines for account " << accountID << " found " << size + << (direction == LineDirection::outgoing ? " incoming" : " outgoing") + << " trust lines. " + << (direction == LineDirection::outgoing ? "Deleting the subset of incoming" + : "Returning the superset of outgoing") + << " trust lines. "; + if (direction == LineDirection::outgoing) + { + // This request is for the outgoing set, but there is already a + // subset of incoming lines in the cache. Erase that subset + // to be replaced by the full set. The full set will be built + // below, and will be returned, if needed, on subsequent calls + // for either value of outgoing. + XRPL_ASSERT( + size <= totalLineCount_, + "xrpl::RippleLineCache::getRippleLines : maximum lines"); + totalLineCount_ -= size; + lines_.erase(otheriter); + } + else + { + // This request is for the incoming set, but there is + // already a superset of the outgoing trust lines in the cache. + // The path finding engine will disregard the non-rippling trust + // lines, so to prevent them from being stored twice, return the + // outgoing set. + key = otherkey; + return std::pair{otheriter, false}; + } } - else - { - // This request is for the incoming set, but there is - // already a superset of the outgoing trust lines in the cache. - // The path finding engine will disregard the non-rippling trust - // lines, so to prevent them from being stored twice, return the - // outgoing set. - key = otherkey; - return std::pair{otheriter, false}; - } - } - return lines_.emplace(key, nullptr); - }(); + return lines_.emplace(key, nullptr); + }(); if (inserted) { XRPL_ASSERT(it->second == nullptr, "xrpl::RippleLineCache::getRippleLines : null lines"); auto lines = PathFindTrustLine::getItems(accountID, *ledger_, direction); - if (lines.size()) + if (!lines.empty()) { it->second = std::make_shared>(std::move(lines)); totalLineCount_ += it->second->size(); @@ -76,12 +84,16 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct } XRPL_ASSERT( - !it->second || (it->second->size() > 0), "xrpl::RippleLineCache::getRippleLines : null or nonempty lines"); + !it->second || (it->second->size() > 0), + "xrpl::RippleLineCache::getRippleLines : null or nonempty lines"); auto const size = it->second ? it->second->size() : 0; - JLOG(journal_.trace()) << "getRippleLines for ledger " << ledger_->header().seq << " found " << size - << (key.direction_ == LineDirection::outgoing ? " outgoing" : " incoming") << " lines for " - << (inserted ? "new " : "existing ") << accountID << " out of a total of " << lines_.size() - << " accounts and " << totalLineCount_ << " trust lines"; + JLOG(journal_.trace()) << "getRippleLines for ledger " << ledger_->header().seq << " found " + << size + << (key.direction_ == LineDirection::outgoing ? " outgoing" + : " incoming") + << " lines for " << (inserted ? "new " : "existing ") << accountID + << " out of a total of " << lines_.size() << " accounts and " + << totalLineCount_ << " trust lines"; return it->second; } diff --git a/src/xrpld/app/paths/RippleLineCache.h b/src/xrpld/app/paths/RippleLineCache.h index 5dbdb414ee..c4ddad6c81 100644 --- a/src/xrpld/app/paths/RippleLineCache.h +++ b/src/xrpld/app/paths/RippleLineCache.h @@ -67,7 +67,8 @@ private: bool operator==(AccountKey const& lhs) const { - return hash_value_ == lhs.hash_value_ && account_ == lhs.account_ && direction_ == lhs.direction_; + return hash_value_ == lhs.hash_value_ && account_ == lhs.account_ && + direction_ == lhs.direction_; } std::size_t diff --git a/src/xrpld/app/paths/TrustLine.cpp b/src/xrpld/app/paths/TrustLine.cpp index f34dcbdf81..963e1402be 100644 --- a/src/xrpld/app/paths/TrustLine.cpp +++ b/src/xrpld/app/paths/TrustLine.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -38,14 +39,20 @@ PathFindTrustLine::makeItem(AccountID const& accountID, std::shared_ptr std::vector -getTrustLineItems(AccountID const& accountID, ReadView const& view, LineDirection direction = LineDirection::outgoing) +getTrustLineItems( + AccountID const& accountID, + ReadView const& view, + LineDirection direction = LineDirection::outgoing) { std::vector items; - forEachItem(view, accountID, [&items, &accountID, &direction](std::shared_ptr const& sleCur) { - auto ret = T::makeItem(accountID, sleCur); - if (ret && (direction == LineDirection::outgoing || !ret->getNoRipple())) - items.push_back(std::move(*ret)); - }); + forEachItem( + view, + accountID, + [&items, &accountID, &direction](std::shared_ptr const& sleCur) { + auto ret = T::makeItem(accountID, sleCur); + if (ret && (direction == LineDirection::outgoing || !ret->getNoRipple())) + items.push_back(std::move(*ret)); + }); // This list may be around for a while, so free up any unneeded // capacity items.shrink_to_fit(); @@ -55,7 +62,10 @@ getTrustLineItems(AccountID const& accountID, ReadView const& view, LineDirectio } // namespace detail std::vector -PathFindTrustLine::getItems(AccountID const& accountID, ReadView const& view, LineDirection direction) +PathFindTrustLine::getItems( + AccountID const& accountID, + ReadView const& view, + LineDirection direction) { return detail::getTrustLineItems(accountID, view, direction); } diff --git a/src/xrpld/app/paths/detail/AMMLiquidity.cpp b/src/xrpld/app/paths/detail/AMMLiquidity.cpp index ebbb51ce25..72ee8eb261 100644 --- a/src/xrpld/app/paths/detail/AMMLiquidity.cpp +++ b/src/xrpld/app/paths/detail/AMMLiquidity.cpp @@ -41,8 +41,10 @@ AMMLiquidity::generateFibSeqOffer(TAmounts const& balances { TAmounts cur{}; - cur.in = - toAmount(getIssue(balances.in), InitialFibSeqPct * initialBalances_.in, Number::rounding_mode::upward); + cur.in = toAmount( + getIssue(balances.in), + InitialFibSeqPct * initialBalances_.in, + Number::rounding_mode::upward); cur.out = swapAssetIn(initialBalances_, cur.in, tradingFee_); if (ammContext_.curIters() == 0) @@ -55,10 +57,14 @@ AMMLiquidity::generateFibSeqOffer(TAmounts const& balances 196418, 317811, 514229, 832040, 1346269}; // clang-format on - XRPL_ASSERT(!ammContext_.maxItersReached(), "xrpl::AMMLiquidity::generateFibSeqOffer : maximum iterations"); + XRPL_ASSERT( + !ammContext_.maxItersReached(), + "xrpl::AMMLiquidity::generateFibSeqOffer : maximum iterations"); cur.out = toAmount( - getIssue(balances.out), cur.out * fib[ammContext_.curIters() - 1], Number::rounding_mode::downward); + getIssue(balances.out), + cur.out * fib[ammContext_.curIters() - 1], + Number::rounding_mode::downward); // swapAssetOut() returns negative in this case if (cur.out >= balances.out) Throw("AMMLiquidity: generateFibSeqOffer exceeds the balance"); @@ -74,11 +80,17 @@ constexpr T maxAmount() { if constexpr (std::is_same_v) + { return XRPAmount(STAmount::cMaxNative); + } else if constexpr (std::is_same_v) + { return IOUAmount(STAmount::cMaxValue / 2, STAmount::cMaxOffset); + } else if constexpr (std::is_same_v) + { return STAmount(STAmount::cMaxValue / 2, STAmount::cMaxOffset); + } } template @@ -102,18 +114,18 @@ AMMLiquidity::maxOffer(TAmounts const& balances, Rules con balances, Quality{balances}); } - else - { - auto const out = maxOut(balances.out, issueOut()); - if (out <= TOut{0} || out >= balances.out) - return std::nullopt; - return AMMOffer(*this, {swapAssetOut(balances, out, tradingFee_), out}, balances, Quality{balances}); - } + + auto const out = maxOut(balances.out, issueOut()); + if (out <= TOut{0} || out >= balances.out) + return std::nullopt; + return AMMOffer( + *this, {swapAssetOut(balances, out, tradingFee_), out}, balances, Quality{balances}); } template std::optional> -AMMLiquidity::getOffer(ReadView const& view, std::optional const& clobQuality) const +AMMLiquidity::getOffer(ReadView const& view, std::optional const& clobQuality) + const { // Can't generate more offers if multi-path. if (ammContext_.maxItersReached()) @@ -129,8 +141,8 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c } JLOG(j_.trace()) << "AMMLiquidity::getOffer balances " << to_string(initialBalances_.in) << " " - << to_string(initialBalances_.out) << " new balances " << to_string(balances.in) << " " - << to_string(balances.out); + << to_string(initialBalances_.out) << " new balances " + << to_string(balances.in) << " " << to_string(balances.out); // Can't generate AMM with a better quality than CLOB's // quality if AMM's Spot Price quality is less than CLOB quality or is @@ -140,8 +152,9 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c // to the requested clobQuality but not exactly and potentially SPQ may keep // on approaching clobQuality for many iterations. Checking for the quality // threshold prevents this scenario. - if (auto const spotPriceQ = Quality{balances}; - clobQuality && (spotPriceQ <= clobQuality || withinRelativeDistance(spotPriceQ, *clobQuality, Number(1, -7)))) + if (auto const spotPriceQ = Quality{balances}; clobQuality && + (spotPriceQ <= clobQuality || + withinRelativeDistance(spotPriceQ, *clobQuality, Number(1, -7)))) { JLOG(j_.trace()) << "AMMLiquidity::getOffer, higher clob quality"; return std::nullopt; @@ -157,7 +170,7 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c return std::nullopt; return AMMOffer(*this, amounts, balances, Quality{amounts}); } - else if (!clobQuality) + if (!clobQuality) { // If there is no CLOB to compare against, return the largest // amount, which doesn't overflow. The size is going to be @@ -166,11 +179,12 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c // nullopt if the pool is small. return maxOffer(balances, view.rules()); } - else if (auto const amounts = changeSpotPriceQuality(balances, *clobQuality, tradingFee_, view.rules(), j_)) + if (auto const amounts = + changeSpotPriceQuality(balances, *clobQuality, tradingFee_, view.rules(), j_)) { return AMMOffer(*this, *amounts, balances, Quality{*amounts}); } - else if (view.rules().enabled(fixAMMv1_2)) + if (view.rules().enabled(fixAMMv1_2)) { if (auto const maxAMMOffer = maxOffer(balances, view.rules()); maxAMMOffer && Quality{maxAMMOffer->amount()} > *clobQuality) @@ -181,9 +195,11 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c { JLOG(j_.error()) << "AMMLiquidity::getOffer overflow " << e.what(); if (!view.rules().enabled(fixAMMOverflowOffer)) + { return maxOffer(balances, view.rules()); - else - return std::nullopt; + } + + return std::nullopt; } catch (std::exception const& e) { @@ -196,13 +212,15 @@ AMMLiquidity::getOffer(ReadView const& view, std::optional c { if (offer->amount().in > beast::zero && offer->amount().out > beast::zero) { - JLOG(j_.trace()) << "AMMLiquidity::getOffer, created " << to_string(offer->amount().in) << "/" << issueIn_ - << " " << to_string(offer->amount().out) << "/" << issueOut_; + JLOG(j_.trace()) << "AMMLiquidity::getOffer, created " << to_string(offer->amount().in) + << "/" << issueIn_ << " " << to_string(offer->amount().out) << "/" + << issueOut_; return offer; } - JLOG(j_.debug()) << "AMMLiquidity::getOffer, no valid offer " << ammContext_.multiPath() << " " - << ammContext_.curIters() << " " << (clobQuality ? clobQuality->rate() : STAmount{}) << " " + JLOG(j_.debug()) << "AMMLiquidity::getOffer, no valid offer " << ammContext_.multiPath() + << " " << ammContext_.curIters() << " " + << (clobQuality ? clobQuality->rate() : STAmount{}) << " " << to_string(balances.in) << " " << to_string(balances.out); } diff --git a/src/xrpld/app/paths/detail/AMMOffer.cpp b/src/xrpld/app/paths/detail/AMMOffer.cpp index dd6b021689..4871d2251a 100644 --- a/src/xrpld/app/paths/detail/AMMOffer.cpp +++ b/src/xrpld/app/paths/detail/AMMOffer.cpp @@ -11,7 +11,11 @@ AMMOffer::AMMOffer( TAmounts const& amounts, TAmounts const& balances, Quality const& quality) - : ammLiquidity_(ammLiquidity), amounts_(amounts), balances_(balances), quality_(quality), consumed_(false) + : ammLiquidity_(ammLiquidity) + , amounts_(amounts) + , balances_(balances) + , quality_(quality) + , consumed_(false) { } @@ -54,7 +58,10 @@ AMMOffer::consume(ApplyView& view, TAmounts const& consume template TAmounts -AMMOffer::limitOut(TAmounts const& offerAmount, TOut const& limit, bool roundUp) const +AMMOffer::limitOut( + TAmounts const& offerAmount, + TOut const& limit, + bool roundUp) const { // Change the offer size proportionally to the original offer quality // to keep the strands quality order unchanged. The taker pays slightly @@ -76,12 +83,14 @@ AMMOffer::limitOut(TAmounts const& offerAmount, TOut const template TAmounts -AMMOffer::limitIn(TAmounts const& offerAmount, TIn const& limit, bool roundUp) const +AMMOffer::limitIn(TAmounts const& offerAmount, TIn const& limit, bool roundUp) + const { // See the comments above in limitOut(). if (ammLiquidity_.multiPath()) { - if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixReducedOffersV2)) + if (auto const& rules = getCurrentTransactionRules(); + rules && rules->enabled(fixReducedOffersV2)) return quality().ceil_in_strict(offerAmount, limit, roundUp); return quality().ceil_in(offerAmount, limit); @@ -104,24 +113,27 @@ AMMOffer::checkInvariant(TAmounts const& consumed, beast:: { if (consumed.in > amounts_.in || consumed.out > amounts_.out) { - JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " << to_string(consumed.in) << " " - << to_string(consumed.out) << " amounts " << to_string(amounts_.in) << " " - << to_string(amounts_.out); + JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " << to_string(consumed.in) + << " " << to_string(consumed.out) << " amounts " << to_string(amounts_.in) + << " " << to_string(amounts_.out); return false; } Number const product = balances_.in * balances_.out; - auto const newBalances = TAmounts{balances_.in + consumed.in, balances_.out - consumed.out}; + auto const newBalances = + TAmounts{balances_.in + consumed.in, balances_.out - consumed.out}; Number const newProduct = newBalances.in * newBalances.out; if (newProduct >= product || withinRelativeDistance(product, newProduct, Number{1, -7})) return true; - JLOG(j.error()) << "AMMOffer::checkInvariant failed: balances " << to_string(balances_.in) << " " - << to_string(balances_.out) << " new balances " << to_string(newBalances.in) << " " - << to_string(newBalances.out) << " product/newProduct " << product << " " << newProduct << " diff " - << (product != Number{0} ? to_string((product - newProduct) / product) : "undefined"); + JLOG(j.error()) << "AMMOffer::checkInvariant failed: balances " << to_string(balances_.in) + << " " << to_string(balances_.out) << " new balances " + << to_string(newBalances.in) << " " << to_string(newBalances.out) + << " product/newProduct " << product << " " << newProduct << " diff " + << (product != Number{0} ? to_string((product - newProduct) / product) + : "undefined"); return false; } diff --git a/src/xrpld/app/paths/detail/BookStep.cpp b/src/xrpld/app/paths/detail/BookStep.cpp index b9c8aa4e67..e0053956a8 100644 --- a/src/xrpld/app/paths/detail/BookStep.cpp +++ b/src/xrpld/app/paths/detail/BookStep.cpp @@ -1,19 +1,20 @@ -#include #include #include -#include -#include -#include #include #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include @@ -63,7 +64,7 @@ protected: std::optional cache_; -public: +private: BookStep(StrandContext const& ctx, Issue const& in, Issue const& out) : book_(in, out, ctx.domainID) , strandSrc_(ctx.strandSrc) @@ -74,6 +75,7 @@ public: { if (auto const ammSle = ctx.view.read(keylet::amm(in, out)); ammSle && ammSle->getFieldAmount(sfLPTokenBalance) != beast::zero) + { ammLiquidity_.emplace( ctx.view, (*ammSle)[sfAccount], @@ -82,8 +84,10 @@ public: out, ctx.ammContext, ctx.j); + } } +public: Book const& book() const { @@ -128,10 +132,18 @@ public: offersUsed() const override; std::pair - revImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set& ofrsToRm, TOut const& out); + revImp( + PaymentSandbox& sb, + ApplyView& afView, + boost::container::flat_set& ofrsToRm, + TOut const& out); std::pair - fwdImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set& ofrsToRm, TIn const& in); + fwdImp( + PaymentSandbox& sb, + ApplyView& afView, + boost::container::flat_set& ofrsToRm, + TIn const& in); std::pair validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override; @@ -152,8 +164,8 @@ protected: { std::ostringstream ostr; ostr << name << ": " - << "\ninIss: " << book_.in.account << "\noutIss: " << book_.out.account << "\ninCur: " << book_.in.currency - << "\noutCur: " << book_.out.currency; + << "\ninIss: " << book_.in.account << "\noutIss: " << book_.out.account + << "\ninCur: " << book_.in.currency << "\noutCur: " << book_.out.currency; return ostr.str(); } @@ -180,7 +192,11 @@ private: // Return the unfunded and bad offers and the number of offers consumed. template std::pair, std::uint32_t> - forEachOffer(PaymentSandbox& sb, ApplyView& afView, DebtDirection prevStepDebtDir, Callback& callback) const; + forEachOffer( + PaymentSandbox& sb, + ApplyView& afView, + DebtDirection prevStepDebtDir, + Callback& callback) const; // Offer is either TOffer or AMMOffer template