diff --git a/.clang-tidy b/.clang-tidy index 26c7995631..d8a7cb1b6f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -8,12 +8,14 @@ Checks: "-*, 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, @@ -30,9 +32,12 @@ Checks: "-*, 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, @@ -49,19 +54,35 @@ Checks: "-*, 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, @@ -71,71 +92,48 @@ Checks: "-*, misc-throw-by-value-catch-by-reference, misc-unused-alias-decls, misc-unused-using-decls, - readability-duplicate-include, - readability-enum-initial-value, - readability-misleading-indentation, - readability-non-const-parameter, - readability-redundant-declaration, - readability-reference-to-constructed-temporary, modernize-deprecated-headers, modernize-make-shared, modernize-make-unique, performance-implicit-conversion-in-loop, performance-move-constructor-init, - performance-trivially-destructible + 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 " # --- -# more checks that have some issues that need to be resolved: -# -# bugprone-crtp-constructor-accessibility, -# bugprone-inc-dec-in-conditions, -# bugprone-reserved-identifier, -# bugprone-move-forwarding-reference, -# bugprone-unused-local-non-trivial-variable, -# bugprone-switch-missing-default-case, -# bugprone-suspicious-stringview-data-usage, -# bugprone-suspicious-missing-comma, -# bugprone-pointer-arithmetic-on-polymorphic-object, -# bugprone-optional-value-conversion, -# bugprone-too-small-loop-variable, -# bugprone-unused-return-value, -# bugprone-use-after-move, -# bugprone-unhandled-self-assignment, -# bugprone-unused-raii, -# -# cppcoreguidelines-misleading-capture-default-by-value, -# cppcoreguidelines-init-variables, -# cppcoreguidelines-pro-type-member-init, -# cppcoreguidelines-pro-type-static-cast-downcast, -# cppcoreguidelines-use-default-member-init, -# cppcoreguidelines-rvalue-reference-param-not-moved, +# checks that have some issues that need to be resolved: # # llvm-namespace-comment, # misc-const-correctness, # misc-include-cleaner, # misc-redundant-expression, # -# readability-avoid-nested-conditional-operator, -# readability-avoid-return-with-void-value, -# readability-braces-around-statements, -# readability-container-contains, -# readability-container-size-empty, # readability-convert-member-functions-to-static, -# readability-const-return-type, -# readability-else-after-return, # readability-implicit-bool-conversion, # readability-inconsistent-declaration-parameter-name, # readability-identifier-naming, -# readability-make-member-function-const, # readability-math-missing-parentheses, -# readability-redundant-inline-specifier, -# readability-redundant-member-init, -# readability-redundant-casting, -# readability-redundant-string-init, # readability-simplify-boolean-expr, -# readability-static-definition-in-anonymous-namespace, # readability-suspicious-call-argument, -# readability-use-std-min-max, # readability-static-accessed-through-instance, # # modernize-concat-nested-namespaces, @@ -159,7 +157,7 @@ Checks: "-*, # --- # CheckOptions: - # readability-braces-around-statements.ShortStatementLines: 2 + readability-braces-around-statements.ShortStatementLines: 2 # readability-identifier-naming.MacroDefinitionCase: UPPER_CASE # readability-identifier-naming.ClassCase: CamelCase # readability-identifier-naming.StructCase: CamelCase @@ -194,7 +192,7 @@ CheckOptions: # 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 + 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)$' diff --git a/.cmake-format.yaml b/.cmake-format.yaml deleted file mode 100644 index 40c1101208..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: 100 - _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/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. @@ -1052,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/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/HTTPClient.h b/include/xrpl/net/HTTPClient.h index 9ea5f98a37..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, diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 34d78fc50b..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 }; 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 6a3b81f5ae..1a28349540 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -209,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; diff --git a/include/xrpl/protocol/PublicKey.h b/include/xrpl/protocol/PublicKey.h index e806f6ffeb..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*; 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/STObject.h b/include/xrpl/protocol/STObject.h index 09eff8c3d2..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; @@ -401,7 +401,7 @@ public: getStyle(SField const& field) const; bool - hasMatchingEntry(STBase const&); + hasMatchingEntry(STBase const&) const; bool operator==(STObject const& o) const; diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 72bd38d5d6..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; @@ -122,7 +125,7 @@ public: getMetaSQL( Serializer rawTxn, std::uint32_t inLedger, - char status, + TxnSql status, std::string const& escapedMetaData) const; std::vector 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/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/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/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index e0b3e67878..93385b56e7 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -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, @@ -58,6 +58,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{}, @@ -73,7 +76,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, @@ -94,7 +97,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, @@ -107,7 +110,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, @@ -121,7 +124,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, @@ -137,7 +140,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, @@ -151,7 +154,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, @@ -167,7 +170,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, @@ -180,7 +183,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, @@ -196,6 +199,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{}, @@ -207,6 +213,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{}, @@ -222,7 +231,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, @@ -238,7 +247,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, @@ -252,7 +261,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, @@ -264,7 +273,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, @@ -279,7 +288,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, @@ -293,7 +302,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, @@ -309,7 +318,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, @@ -327,7 +336,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, @@ -340,7 +349,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, @@ -356,7 +365,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, @@ -368,7 +377,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, @@ -382,7 +391,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, @@ -395,7 +404,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, @@ -410,7 +419,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, @@ -424,7 +433,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, @@ -442,7 +451,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, @@ -459,7 +468,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, @@ -473,7 +482,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, @@ -489,7 +498,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, @@ -502,7 +511,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, @@ -617,7 +626,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, @@ -630,6 +639,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, @@ -638,7 +650,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, @@ -655,7 +667,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, @@ -667,7 +679,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, @@ -680,7 +692,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, @@ -697,7 +709,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, @@ -709,7 +721,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, @@ -726,7 +738,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, @@ -739,7 +751,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, @@ -753,6 +765,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, @@ -763,6 +778,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, @@ -775,7 +793,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, @@ -789,7 +807,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, @@ -802,7 +820,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, @@ -814,7 +832,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, @@ -827,10 +845,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, ({ @@ -845,10 +863,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, ({ @@ -860,10 +878,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, ({ @@ -872,10 +890,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, ({ @@ -885,10 +903,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, ({ @@ -900,10 +918,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, ({ @@ -914,7 +932,7 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, /** This transaction type batches together transactions. */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegable, @@ -929,10 +947,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}, @@ -946,10 +964,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}, @@ -957,10 +975,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}, @@ -969,10 +987,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}, @@ -984,10 +1002,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}, @@ -996,10 +1014,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}, @@ -1023,10 +1041,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}, @@ -1034,10 +1052,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 @@ -1048,10 +1066,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}, @@ -1063,7 +1081,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/jss.h b/include/xrpl/protocol/jss.h index 6e40fb5ede..8b810d50d1 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -112,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 @@ -303,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 @@ -467,6 +469,7 @@ 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); 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/include/xrpl/server/InfoSub.h b/include/xrpl/server/InfoSub.h index 97cdf1aa35..d45f4d7740 100644 --- a/include/xrpl/server/InfoSub.h +++ b/include/xrpl/server/InfoSub.h @@ -173,7 +173,7 @@ public: send(Json::Value const& jvObj, bool broadcast) = 0; std::uint64_t - getSeq(); + getSeq() const; void onSendEmpty(); diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 0e88cf92e1..8d816f60f8 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -114,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; @@ -210,7 +209,7 @@ public: checkPermission(ReadView const& view, STTx const& tx); ///////////////////////////////////////////////////// - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER ticketDelete( ApplyView& view, diff --git a/include/xrpl/tx/applySteps.h b/include/xrpl/tx/applySteps.h index 2e76e20f5d..9ce6dfb1e2 100644 --- a/include/xrpl/tx/applySteps.h +++ b/include/xrpl/tx/applySteps.h @@ -213,7 +213,7 @@ public: , flags(ctx_.flags) , j(ctx_.j) , ter(ter_) - , likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags)) + , likelyToClaimFee(isTesSuccess(ter) || isTecClaimHardFail(ter, flags)) { } diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index 5ded5980da..e0ad65f14c 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -27,6 +27,33 @@ namespace xrpl { * 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 { @@ -48,7 +75,11 @@ public: /** * @brief called after all ledger entries have been visited to determine - * the final status of the check + * 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 @@ -101,7 +132,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -121,7 +152,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -167,7 +198,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -184,7 +215,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -202,7 +233,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -221,7 +252,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -240,7 +271,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -256,7 +287,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -276,7 +307,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -297,7 +328,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index b6533c263d..68f866d362 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -25,7 +25,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + 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 index 8a88ca1c63..5bb5f90437 100644 --- a/include/xrpl/tx/invariants/NFTInvariant.h +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -36,7 +36,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -64,7 +64,7 @@ public: visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; } // namespace xrpl diff --git a/include/xrpl/tx/paths/BookTip.h b/include/xrpl/tx/paths/BookTip.h index 69d3ec70d8..98036a86eb 100644 --- a/include/xrpl/tx/paths/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/include/xrpl/tx/paths/detail/StrandFlow.h b/include/xrpl/tx/paths/detail/StrandFlow.h index 929aca0663..2a94b9b968 100644 --- a/include/xrpl/tx/paths/detail/StrandFlow.h +++ b/include/xrpl/tx/paths/detail/StrandFlow.h @@ -1,7 +1,9 @@ #pragma once #include -#include +#include +#include +#include #include #include #include @@ -10,8 +12,8 @@ #include #include #include -#include -#include +#include +#include #include diff --git a/include/xrpl/tx/transactors/Credentials.h b/include/xrpl/tx/transactors/Credentials.h deleted file mode 100644 index 8c96f40526..0000000000 --- a/include/xrpl/tx/transactors/Credentials.h +++ /dev/null @@ -1,77 +0,0 @@ -#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; -}; - -//------------------------------------------------------------------------------ - -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; -}; - -//------------------------------------------------------------------------------ - -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/Escrow.h b/include/xrpl/tx/transactors/Escrow.h deleted file mode 100644 index 0fd627e5a9..0000000000 --- a/include/xrpl/tx/transactors/Escrow.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class EscrowCreate : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -//------------------------------------------------------------------------------ - -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; -}; - -//------------------------------------------------------------------------------ - -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/PayChan.h b/include/xrpl/tx/transactors/PayChan.h deleted file mode 100644 index cc23f029f2..0000000000 --- a/include/xrpl/tx/transactors/PayChan.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class PayChanCreate : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit PayChanCreate(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -using PaymentChannelCreate = PayChanCreate; - -//------------------------------------------------------------------------------ - -class PayChanFund : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; - - explicit PayChanFund(ApplyContext& ctx) : Transactor(ctx) - { - } - - static TxConsequences - makeTxConsequences(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - TER - doApply() override; -}; - -using PaymentChannelFund = PayChanFund; - -//------------------------------------------------------------------------------ - -class PayChanClaim : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit PayChanClaim(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; -}; - -using PaymentChannelClaim = PayChanClaim; - -} // namespace xrpl diff --git a/include/xrpl/tx/transactors/DeleteAccount.h b/include/xrpl/tx/transactors/account/AccountDelete.h similarity index 77% rename from include/xrpl/tx/transactors/DeleteAccount.h rename to include/xrpl/tx/transactors/account/AccountDelete.h index f55888ee00..81a11152ed 100644 --- a/include/xrpl/tx/transactors/DeleteAccount.h +++ b/include/xrpl/tx/transactors/account/AccountDelete.h @@ -4,12 +4,12 @@ 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/include/xrpl/tx/transactors/SetAccount.h b/include/xrpl/tx/transactors/account/AccountSet.h similarity index 82% rename from include/xrpl/tx/transactors/SetAccount.h rename to include/xrpl/tx/transactors/account/AccountSet.h index 3abfcb43bb..0940ab0739 100644 --- a/include/xrpl/tx/transactors/SetAccount.h +++ b/include/xrpl/tx/transactors/account/AccountSet.h @@ -5,12 +5,12 @@ 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) { } @@ -33,6 +33,4 @@ public: doApply() override; }; -using AccountSet = SetAccount; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/SetRegularKey.h b/include/xrpl/tx/transactors/account/SetRegularKey.h similarity index 100% rename from include/xrpl/tx/transactors/SetRegularKey.h rename to include/xrpl/tx/transactors/account/SetRegularKey.h diff --git a/include/xrpl/tx/transactors/SetSignerList.h b/include/xrpl/tx/transactors/account/SignerListSet.h similarity index 86% rename from include/xrpl/tx/transactors/SetSignerList.h rename to include/xrpl/tx/transactors/account/SignerListSet.h index 4c2489922d..2529f0d36c 100644 --- a/include/xrpl/tx/transactors/SetSignerList.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -11,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. @@ -26,7 +26,7 @@ private: public: static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker}; - explicit SetSignerList(ApplyContext& ctx) : Transactor(ctx) + explicit SignerListSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -41,7 +41,7 @@ public: void preCompute() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER removeFromLedger( ServiceRegistry& registry, @@ -70,6 +70,4 @@ private: writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const; }; -using SignerListSet = SetSignerList; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/XChainBridge.h b/include/xrpl/tx/transactors/bridge/XChainBridge.h similarity index 100% rename from include/xrpl/tx/transactors/XChainBridge.h rename to include/xrpl/tx/transactors/bridge/XChainBridge.h diff --git a/include/xrpl/tx/transactors/Check/CashCheck.h b/include/xrpl/tx/transactors/check/CheckCancel.h similarity index 73% rename from include/xrpl/tx/transactors/Check/CashCheck.h rename to include/xrpl/tx/transactors/check/CheckCancel.h index 138790cf34..f16c6d2827 100644 --- a/include/xrpl/tx/transactors/Check/CashCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCancel.h @@ -4,12 +4,12 @@ namespace xrpl { -class CashCheck : public Transactor +class CheckCancel : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CashCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCancel(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCash = CashCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Check/CancelCheck.h b/include/xrpl/tx/transactors/check/CheckCash.h similarity index 71% rename from include/xrpl/tx/transactors/Check/CancelCheck.h rename to include/xrpl/tx/transactors/check/CheckCash.h index 8a0e42c7ca..c64d45061d 100644 --- a/include/xrpl/tx/transactors/Check/CancelCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCash.h @@ -4,12 +4,12 @@ namespace xrpl { -class CancelCheck : public Transactor +class CheckCash : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CancelCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCash(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCancel = CancelCheck; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Check/CreateCheck.h b/include/xrpl/tx/transactors/check/CheckCreate.h similarity index 71% rename from include/xrpl/tx/transactors/Check/CreateCheck.h rename to include/xrpl/tx/transactors/check/CheckCreate.h index 98950b68f2..2b5aa9123d 100644 --- a/include/xrpl/tx/transactors/Check/CreateCheck.h +++ b/include/xrpl/tx/transactors/check/CheckCreate.h @@ -4,12 +4,12 @@ namespace xrpl { -class CreateCheck : public Transactor +class CheckCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx) + explicit CheckCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -23,6 +23,4 @@ public: doApply() override; }; -using CheckCreate = CreateCheck; - } // 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/include/xrpl/tx/transactors/Delegate/DelegateSet.h b/include/xrpl/tx/transactors/delegate/DelegateSet.h similarity index 93% rename from include/xrpl/tx/transactors/Delegate/DelegateSet.h rename to include/xrpl/tx/transactors/delegate/DelegateSet.h index 452a6d8305..cc3a8fe4cb 100644 --- a/include/xrpl/tx/transactors/Delegate/DelegateSet.h +++ b/include/xrpl/tx/transactors/delegate/DelegateSet.h @@ -22,7 +22,7 @@ public: TER doApply() override; - // Interface used by DeleteAccount + // Interface used by AccountDelete static TER deleteDelegate( ApplyView& view, diff --git a/include/xrpl/tx/transactors/Delegate/DelegateUtils.h b/include/xrpl/tx/transactors/delegate/DelegateUtils.h similarity index 100% rename from include/xrpl/tx/transactors/Delegate/DelegateUtils.h rename to include/xrpl/tx/transactors/delegate/DelegateUtils.h diff --git a/include/xrpl/tx/transactors/AMM/AMMBid.h b/include/xrpl/tx/transactors/dex/AMMBid.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMBid.h rename to include/xrpl/tx/transactors/dex/AMMBid.h diff --git a/include/xrpl/tx/transactors/AMM/AMMClawback.h b/include/xrpl/tx/transactors/dex/AMMClawback.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMClawback.h rename to include/xrpl/tx/transactors/dex/AMMClawback.h diff --git a/include/xrpl/tx/transactors/AMM/AMMContext.h b/include/xrpl/tx/transactors/dex/AMMContext.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMContext.h rename to include/xrpl/tx/transactors/dex/AMMContext.h diff --git a/include/xrpl/tx/transactors/AMM/AMMCreate.h b/include/xrpl/tx/transactors/dex/AMMCreate.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMCreate.h rename to include/xrpl/tx/transactors/dex/AMMCreate.h diff --git a/include/xrpl/tx/transactors/AMM/AMMDelete.h b/include/xrpl/tx/transactors/dex/AMMDelete.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMDelete.h rename to include/xrpl/tx/transactors/dex/AMMDelete.h diff --git a/include/xrpl/tx/transactors/AMM/AMMDeposit.h b/include/xrpl/tx/transactors/dex/AMMDeposit.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMDeposit.h rename to include/xrpl/tx/transactors/dex/AMMDeposit.h diff --git a/include/xrpl/tx/transactors/AMM/AMMHelpers.h b/include/xrpl/tx/transactors/dex/AMMHelpers.h similarity index 99% rename from include/xrpl/tx/transactors/AMM/AMMHelpers.h rename to include/xrpl/tx/transactors/dex/AMMHelpers.h index 1db9176538..e2735ea80f 100644 --- a/include/xrpl/tx/transactors/AMM/AMMHelpers.h +++ b/include/xrpl/tx/transactors/dex/AMMHelpers.h @@ -370,7 +370,7 @@ changeSpotPriceQuality( if (!amounts) { JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " - << to_string(pool.out) << " " << quality << " " << tfee << std::endl; + << to_string(pool.out) << " " << quality << " " << tfee; return std::nullopt; } @@ -639,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 @@ -672,9 +672,9 @@ getRoundedLPTokens( 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 diff --git a/include/xrpl/tx/transactors/AMM/AMMUtils.h b/include/xrpl/tx/transactors/dex/AMMUtils.h similarity index 98% rename from include/xrpl/tx/transactors/AMM/AMMUtils.h rename to include/xrpl/tx/transactors/dex/AMMUtils.h index 18db7e6555..77ad18106e 100644 --- a/include/xrpl/tx/transactors/AMM/AMMUtils.h +++ b/include/xrpl/tx/transactors/dex/AMMUtils.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/include/xrpl/tx/transactors/AMM/AMMVote.h b/include/xrpl/tx/transactors/dex/AMMVote.h similarity index 100% rename from include/xrpl/tx/transactors/AMM/AMMVote.h rename to include/xrpl/tx/transactors/dex/AMMVote.h diff --git a/include/xrpl/tx/transactors/AMM/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h similarity index 99% rename from include/xrpl/tx/transactors/AMM/AMMWithdraw.h rename to include/xrpl/tx/transactors/dex/AMMWithdraw.h index c15bb68644..5328b03abb 100644 --- a/include/xrpl/tx/transactors/AMM/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace xrpl { diff --git a/include/xrpl/tx/transactors/Offer/CancelOffer.h b/include/xrpl/tx/transactors/dex/OfferCancel.h similarity index 73% rename from include/xrpl/tx/transactors/Offer/CancelOffer.h rename to include/xrpl/tx/transactors/dex/OfferCancel.h index 6107c3211f..c4c57ff3b4 100644 --- a/include/xrpl/tx/transactors/Offer/CancelOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCancel.h @@ -5,12 +5,12 @@ 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) { } @@ -24,6 +24,4 @@ public: doApply() override; }; -using OfferCancel = CancelOffer; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Offer/CreateOffer.h b/include/xrpl/tx/transactors/dex/OfferCreate.h similarity index 93% rename from include/xrpl/tx/transactors/Offer/CreateOffer.h rename to include/xrpl/tx/transactors/dex/OfferCreate.h index cb15d08ace..2179180054 100644 --- a/include/xrpl/tx/transactors/Offer/CreateOffer.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -9,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) { } @@ -74,6 +74,4 @@ private: std::function)> const& setDir); }; -using OfferCreate = CreateOffer; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/PermissionedDEXHelpers.h b/include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h similarity index 100% rename from include/xrpl/tx/transactors/PermissionedDEXHelpers.h rename to include/xrpl/tx/transactors/dex/PermissionedDEXHelpers.h diff --git a/include/xrpl/tx/transactors/DID.h b/include/xrpl/tx/transactors/did/DIDDelete.h similarity index 61% rename from include/xrpl/tx/transactors/DID.h rename to include/xrpl/tx/transactors/did/DIDDelete.h index 8ab80bc694..037a57bb02 100644 --- a/include/xrpl/tx/transactors/DID.h +++ b/include/xrpl/tx/transactors/did/DIDDelete.h @@ -4,24 +4,6 @@ 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..221ffee070 --- /dev/null +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class EscrowCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(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/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/include/xrpl/tx/transactors/Lending/LendingHelpers.h b/include/xrpl/tx/transactors/lending/LendingHelpers.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LendingHelpers.h rename to include/xrpl/tx/transactors/lending/LendingHelpers.h diff --git a/include/xrpl/tx/transactors/Lending/LoanBrokerCoverClawback.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanBrokerCoverClawback.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h diff --git a/include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h diff --git a/include/xrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.h rename to include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h diff --git a/include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h rename to include/xrpl/tx/transactors/lending/LoanBrokerDelete.h diff --git a/include/xrpl/tx/transactors/Lending/LoanBrokerSet.h b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanBrokerSet.h rename to include/xrpl/tx/transactors/lending/LoanBrokerSet.h diff --git a/include/xrpl/tx/transactors/Lending/LoanDelete.h b/include/xrpl/tx/transactors/lending/LoanDelete.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanDelete.h rename to include/xrpl/tx/transactors/lending/LoanDelete.h diff --git a/include/xrpl/tx/transactors/Lending/LoanManage.h b/include/xrpl/tx/transactors/lending/LoanManage.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanManage.h rename to include/xrpl/tx/transactors/lending/LoanManage.h diff --git a/include/xrpl/tx/transactors/Lending/LoanPay.h b/include/xrpl/tx/transactors/lending/LoanPay.h similarity index 100% rename from include/xrpl/tx/transactors/Lending/LoanPay.h rename to include/xrpl/tx/transactors/lending/LoanPay.h diff --git a/include/xrpl/tx/transactors/Lending/LoanSet.h b/include/xrpl/tx/transactors/lending/LoanSet.h similarity index 96% rename from include/xrpl/tx/transactors/Lending/LoanSet.h rename to include/xrpl/tx/transactors/lending/LoanSet.h index e9916b9d6b..86101d0735 100644 --- a/include/xrpl/tx/transactors/Lending/LoanSet.h +++ b/include/xrpl/tx/transactors/lending/LoanSet.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace xrpl { diff --git a/include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h similarity index 100% rename from include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h diff --git a/include/xrpl/tx/transactors/NFT/NFTokenBurn.h b/include/xrpl/tx/transactors/nft/NFTokenBurn.h similarity index 100% rename from include/xrpl/tx/transactors/NFT/NFTokenBurn.h rename to include/xrpl/tx/transactors/nft/NFTokenBurn.h diff --git a/include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h similarity index 100% rename from include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h diff --git a/include/xrpl/tx/transactors/NFT/NFTokenCreateOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h similarity index 100% rename from include/xrpl/tx/transactors/NFT/NFTokenCreateOffer.h rename to include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h diff --git a/include/xrpl/tx/transactors/NFT/NFTokenMint.h b/include/xrpl/tx/transactors/nft/NFTokenMint.h similarity index 94% rename from include/xrpl/tx/transactors/NFT/NFTokenMint.h rename to include/xrpl/tx/transactors/nft/NFTokenMint.h index c3a7db4581..d4eeba2bf0 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenMint.h +++ b/include/xrpl/tx/transactors/nft/NFTokenMint.h @@ -2,7 +2,7 @@ #include #include -#include +#include namespace xrpl { diff --git a/include/xrpl/tx/transactors/NFT/NFTokenModify.h b/include/xrpl/tx/transactors/nft/NFTokenModify.h similarity index 100% rename from include/xrpl/tx/transactors/NFT/NFTokenModify.h rename to include/xrpl/tx/transactors/nft/NFTokenModify.h diff --git a/include/xrpl/tx/transactors/NFT/NFTokenUtils.h b/include/xrpl/tx/transactors/nft/NFTokenUtils.h similarity index 94% rename from include/xrpl/tx/transactors/NFT/NFTokenUtils.h rename to include/xrpl/tx/transactors/nft/NFTokenUtils.h index a5e2b30354..33aab068c6 100644 --- a/include/xrpl/tx/transactors/NFT/NFTokenUtils.h +++ b/include/xrpl/tx/transactors/nft/NFTokenUtils.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,7 @@ removeToken( ApplyView& view, AccountID const& owner, uint256 const& nftokenID, - std::shared_ptr&& page); + std::shared_ptr const& page); /** Deletes the given token offer. @@ -95,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 @@ -109,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 @@ -123,7 +124,7 @@ tokenOfferCreateApply( uint256 const& nftokenID, XRPAmount const& priorBalance, beast::Journal j, - std::uint32_t txFlags = lsfSellNFToken); + std::uint32_t txFlags = tfSellNFToken); TER checkTrustlineAuthorized( diff --git a/include/xrpl/tx/transactors/DeleteOracle.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h similarity index 79% rename from include/xrpl/tx/transactors/DeleteOracle.h rename to include/xrpl/tx/transactors/oracle/OracleDelete.h index bff33205aa..9d1e6d3a44 100644 --- a/include/xrpl/tx/transactors/DeleteOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -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) { } @@ -39,6 +39,4 @@ public: beast::Journal j); }; -using OracleDelete = DeleteOracle; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/SetOracle.h b/include/xrpl/tx/transactors/oracle/OracleSet.h similarity index 77% rename from include/xrpl/tx/transactors/SetOracle.h rename to include/xrpl/tx/transactors/oracle/OracleSet.h index be1a7b8821..d879d14a84 100644 --- a/include/xrpl/tx/transactors/SetOracle.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -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/include/xrpl/tx/transactors/DepositPreauth.h b/include/xrpl/tx/transactors/payment/DepositPreauth.h similarity index 94% rename from include/xrpl/tx/transactors/DepositPreauth.h rename to include/xrpl/tx/transactors/payment/DepositPreauth.h index 24050da600..b397a81748 100644 --- a/include/xrpl/tx/transactors/DepositPreauth.h +++ b/include/xrpl/tx/transactors/payment/DepositPreauth.h @@ -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/include/xrpl/tx/transactors/Payment.h b/include/xrpl/tx/transactors/payment/Payment.h similarity index 100% rename from include/xrpl/tx/transactors/Payment.h rename to include/xrpl/tx/transactors/payment/Payment.h 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/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h new file mode 100644 index 0000000000..7fdacf9b3a --- /dev/null +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace xrpl { + +class PaymentChannelCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit PaymentChannelCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(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/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/include/xrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h similarity index 100% rename from include/xrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.h rename to include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h diff --git a/include/xrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h similarity index 100% rename from include/xrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.h rename to include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h diff --git a/include/xrpl/tx/transactors/Batch.h b/include/xrpl/tx/transactors/system/Batch.h similarity index 100% rename from include/xrpl/tx/transactors/Batch.h rename to include/xrpl/tx/transactors/system/Batch.h diff --git a/include/xrpl/tx/transactors/Change.h b/include/xrpl/tx/transactors/system/Change.h similarity index 100% rename from include/xrpl/tx/transactors/Change.h rename to include/xrpl/tx/transactors/system/Change.h diff --git a/include/xrpl/tx/transactors/LedgerStateFix.h b/include/xrpl/tx/transactors/system/LedgerStateFix.h similarity index 100% rename from include/xrpl/tx/transactors/LedgerStateFix.h rename to include/xrpl/tx/transactors/system/LedgerStateFix.h diff --git a/include/xrpl/tx/transactors/CreateTicket.h b/include/xrpl/tx/transactors/system/TicketCreate.h similarity index 91% rename from include/xrpl/tx/transactors/CreateTicket.h rename to include/xrpl/tx/transactors/system/TicketCreate.h index 867ad99c12..88d83c37a7 100644 --- a/include/xrpl/tx/transactors/CreateTicket.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -4,7 +4,7 @@ namespace xrpl { -class CreateTicket : public Transactor +class TicketCreate : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; @@ -25,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. @@ -41,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) { } @@ -61,6 +61,4 @@ public: doApply() override; }; -using TicketCreate = CreateTicket; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Clawback.h b/include/xrpl/tx/transactors/token/Clawback.h similarity index 100% rename from include/xrpl/tx/transactors/Clawback.h rename to include/xrpl/tx/transactors/token/Clawback.h diff --git a/include/xrpl/tx/transactors/MPT/MPTokenAuthorize.h b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h similarity index 100% rename from include/xrpl/tx/transactors/MPT/MPTokenAuthorize.h rename to include/xrpl/tx/transactors/token/MPTokenAuthorize.h diff --git a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h similarity index 100% rename from include/xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h diff --git a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h similarity index 100% rename from include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h diff --git a/include/xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h similarity index 100% rename from include/xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h rename to include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h diff --git a/include/xrpl/tx/transactors/SetTrust.h b/include/xrpl/tx/transactors/token/TrustSet.h similarity index 81% rename from include/xrpl/tx/transactors/SetTrust.h rename to include/xrpl/tx/transactors/token/TrustSet.h index 47ec26b6ad..2e67aaeded 100644 --- a/include/xrpl/tx/transactors/SetTrust.h +++ b/include/xrpl/tx/transactors/token/TrustSet.h @@ -5,12 +5,12 @@ 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) { } @@ -30,6 +30,4 @@ public: doApply() override; }; -using TrustSet = SetTrust; - } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Vault/VaultClawback.h b/include/xrpl/tx/transactors/vault/VaultClawback.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultClawback.h rename to include/xrpl/tx/transactors/vault/VaultClawback.h diff --git a/include/xrpl/tx/transactors/Vault/VaultCreate.h b/include/xrpl/tx/transactors/vault/VaultCreate.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultCreate.h rename to include/xrpl/tx/transactors/vault/VaultCreate.h diff --git a/include/xrpl/tx/transactors/Vault/VaultDelete.h b/include/xrpl/tx/transactors/vault/VaultDelete.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultDelete.h rename to include/xrpl/tx/transactors/vault/VaultDelete.h diff --git a/include/xrpl/tx/transactors/Vault/VaultDeposit.h b/include/xrpl/tx/transactors/vault/VaultDeposit.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultDeposit.h rename to include/xrpl/tx/transactors/vault/VaultDeposit.h diff --git a/include/xrpl/tx/transactors/Vault/VaultSet.h b/include/xrpl/tx/transactors/vault/VaultSet.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultSet.h rename to include/xrpl/tx/transactors/vault/VaultSet.h diff --git a/include/xrpl/tx/transactors/Vault/VaultWithdraw.h b/include/xrpl/tx/transactors/vault/VaultWithdraw.h similarity index 100% rename from include/xrpl/tx/transactors/Vault/VaultWithdraw.h rename to include/xrpl/tx/transactors/vault/VaultWithdraw.h 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 06f19b27ee..a09d77d794 100644 --- a/src/libxrpl/basics/Archive.cpp +++ b/src/libxrpl/basics/Archive.cpp @@ -51,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); @@ -67,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 63d3a56c40..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)); } diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index 3e36b7d458..9cebd1f04a 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -417,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 index 1b0932b39d..ed20ac6c94 100644 --- a/src/libxrpl/basics/MallocTrim.cpp +++ b/src/libxrpl/basics/MallocTrim.cpp @@ -51,7 +51,7 @@ 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, resident; + long size = 0, resident = 0; if (!(iss >> size >> resident)) return -1; diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 4582ae73cd..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(); @@ -258,7 +256,7 @@ Number::Guard::doRoundUp( } bringIntoRange(negative, mantissa, exponent, minMantissa); if (exponent > maxExponent) - throw std::overflow_error(location); + Throw(std::string(location)); } template @@ -284,7 +282,7 @@ Number::Guard::doRoundDown( // 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)) @@ -298,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; } @@ -913,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 7c0a93a930..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) { @@ -374,7 +374,7 @@ public: 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, 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 fa06ac2cdc..31888ec99c 100644 --- a/src/libxrpl/basics/base64.cpp +++ b/src/libxrpl/basics/base64.cpp @@ -77,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; } @@ -116,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: @@ -168,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 885275be9e..74aab709d8 100644 --- a/src/libxrpl/basics/make_SSLContext.cpp +++ b/src/libxrpl/basics/make_SSLContext.cpp @@ -239,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) @@ -271,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; } @@ -298,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 5248657479..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_; @@ -48,8 +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 b63911e9f4..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 @@ -85,7 +86,7 @@ setCurrentThreadNameImpl(std::string_view name) sizeof(boundedName), "%.*s", static_cast(maxThreadNameLength), - name.data()); + name.data()); // NOLINT(bugprone-suspicious-stringview-data-usage) pthread_setname_np(pthread_self(), boundedName); diff --git a/src/libxrpl/beast/core/SemanticVersion.cpp b/src/libxrpl/beast/core/SemanticVersion.cpp index 06cd622722..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)) @@ -68,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)) @@ -234,27 +234,43 @@ 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; @@ -263,18 +279,26 @@ compare(SemanticVersion const& lhs, SemanticVersion const& rhs) { // 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)) { @@ -284,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/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index 143bc51bd8..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}; }; //------------------------------------------------------------------------------ @@ -194,8 +194,8 @@ 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}; }; //------------------------------------------------------------------------------ @@ -470,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(); @@ -504,7 +505,7 @@ 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) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -586,7 +587,7 @@ 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) + : m_impl(impl), m_name(name) { m_impl->add(*this); } @@ -675,7 +676,7 @@ 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) + : m_impl(impl), m_name(name) { m_impl->add(*this); } diff --git a/src/libxrpl/beast/net/IPEndpoint.cpp b/src/libxrpl/beast/net/IPEndpoint.cpp index 701181ef88..f8352f4318 100644 --- a/src/libxrpl/beast/net/IPEndpoint.cpp +++ b/src/libxrpl/beast/net/IPEndpoint.cpp @@ -95,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)) { @@ -159,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 f714ad8871..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) { } @@ -237,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 @@ -301,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; } @@ -371,9 +379,13 @@ void PropertyStream::add(std::string const& key, bool value) { if (value) + { add(key, "true"); + } else + { add(key, "false"); + } } void @@ -464,9 +476,13 @@ void PropertyStream::add(bool value) { if (value) + { add("true"); + } else + { add("false"); + } } void diff --git a/src/libxrpl/core/detail/JobQueue.cpp b/src/libxrpl/core/detail/JobQueue.cpp index 80fb8d2536..9adbf6312b 100644 --- a/src/libxrpl/core/detail/JobQueue.cpp +++ b/src/libxrpl/core/detail/JobQueue.cpp @@ -331,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/Workers.cpp b/src/libxrpl/core/detail/Workers.cpp index a5e331bd44..c037169a29 100644 --- a/src/libxrpl/core/detail/Workers.cpp +++ b/src/libxrpl/core/detail/Workers.cpp @@ -205,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 54d3e6f9d0..f7c1e675cb 100644 --- a/src/libxrpl/crypto/RFC1751.cpp +++ b/src/libxrpl/crypto/RFC1751.cpp @@ -198,10 +198,10 @@ char const* RFC1751::s_dictionary[2048] = { 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"); @@ -226,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); @@ -245,11 +245,11 @@ RFC1751::btoe(std::string& strHuman, std::string const& strData) 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"); @@ -285,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'; + } } } @@ -336,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) 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/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 d28268454f..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; @@ -384,9 +390,13 @@ Reader::readString() c = getNextChar(); if (c == '\\') + { getNextChar(); + } else if (c == '"') + { break; + } } return c == '"'; @@ -395,7 +405,7 @@ Reader::readString() bool Reader::readObject(Token& tokenStart, unsigned depth) { - Token tokenName; + Token tokenName{}; std::string name; currentValue() = Value(objectValue); @@ -420,7 +430,7 @@ Reader::readObject(Token& tokenStart, unsigned depth) if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); - Token colon; + Token colon{}; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { @@ -440,7 +450,7 @@ 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 && @@ -470,7 +480,7 @@ Reader::readArray(Token& tokenStart, unsigned depth) if (*current_ == ']') // empty array { - Token endArray; + Token endArray{}; readToken(endArray); return true; } @@ -487,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); @@ -578,9 +588,13 @@ Reader::decodeNumber(Token& 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; @@ -591,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) @@ -646,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); @@ -689,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; @@ -721,29 +737,30 @@ 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; @@ -757,8 +774,10 @@ Reader::decodeUnicodeEscapeSequence( unsigned int& unicode) { if (end - current < 4) + { return addError( "Bad unicode escape sequence in string: four digits expected.", token, current); + } unicode = 0; @@ -768,17 +787,25 @@ Reader::decodeUnicodeEscapeSequence( 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; @@ -799,7 +826,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); - Token skip; + Token skip{}; while (true) { @@ -870,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 60197d75f0..94b077d224 100644 --- a/src/libxrpl/json/json_value.cpp +++ b/src/libxrpl/json/json_value.cpp @@ -98,8 +98,11 @@ Value::CZString::CZString(CZString const& other) other.index_ != noDuplication && other.cstr_ != 0 ? valueAllocator()->makeMemberName(other.cstr_) : other.cstr_) - , index_( - other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_) + , index_([&]() -> int { + if (!other.cstr_) + return other.index_; + return other.index_ == noDuplication ? noDuplication : duplicate; + }()) { } @@ -157,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) { @@ -225,7 +228,7 @@ Value::Value(std::string const& value) : type_(stringValue), allocated_(true) 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()); } @@ -254,7 +257,9 @@ Value::Value(Value const& other) : type_(other.type_) allocated_ = true; } else + { value_.string_ = 0; + } break; @@ -351,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 @@ -360,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; } @@ -696,7 +707,7 @@ Value::asBool() const case arrayValue: case objectValue: - return value_.map_->size() != 0; + return !value_.map_->empty(); // LCOV_EXCL_START default: @@ -743,10 +754,10 @@ Value::isConvertibleTo(ValueType other) const (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: diff --git a/src/libxrpl/json/json_valueiterator.cpp b/src/libxrpl/json/json_valueiterator.cpp index e40e674ee7..e49ad50f9a 100644 --- a/src/libxrpl/json/json_valueiterator.cpp +++ b/src/libxrpl/json/json_valueiterator.cpp @@ -13,7 +13,7 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) +ValueIteratorBase::ValueIteratorBase() : isNull_(true) { } diff --git a/src/libxrpl/json/json_writer.cpp b/src/libxrpl/json/json_writer.cpp index 13baf95845..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(); @@ -429,9 +435,13 @@ void StyledWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { document_ += value; + } } void @@ -529,7 +539,9 @@ StyledStreamWriter::writeValue(Value const& value) Value::Members members(value.getMemberNames()); if (members.empty()) + { pushValue("{}"); + } else { writeWithIndent("{"); @@ -564,7 +576,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) unsigned size = value.size(); if (size == 0) + { pushValue("[]"); + } else { bool isArrayMultiLine = isMultilineArray(value); @@ -581,7 +595,9 @@ StyledStreamWriter::writeArrayValue(Value const& value) Value const& childValue = value[index]; if (hasChildValue) + { writeWithIndent(childValues_[index]); + } else { writeIndent(); @@ -654,9 +670,13 @@ void StyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) + { childValues_.push_back(value); + } else + { *document_ << value; + } } void diff --git a/src/libxrpl/ledger/AcceptedLedgerTx.cpp b/src/libxrpl/ledger/AcceptedLedgerTx.cpp index f0d243f9b6..005d48e6f6 100644 --- a/src/libxrpl/ledger/AcceptedLedgerTx.cpp +++ b/src/libxrpl/ledger/AcceptedLedgerTx.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/ledger/ApplyStateTable.cpp b/src/libxrpl/ledger/ApplyStateTable.cpp index e31b39b437..a0074c0b53 100644 --- a/src/libxrpl/ledger/ApplyStateTable.cpp +++ b/src/libxrpl/ledger/ApplyStateTable.cpp @@ -107,7 +107,7 @@ ApplyStateTable::apply( Mods newMod; for (auto& item : items_) { - SField const* type; + SField const* type = nullptr; switch (item.second.first) { default: @@ -169,9 +169,11 @@ 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) @@ -513,7 +515,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; diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index 4ba5945e8e..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,8 +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; @@ -84,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 } } diff --git a/src/libxrpl/ledger/BookListeners.cpp b/src/libxrpl/ledger/BookListeners.cpp index 9ba25b783a..22b78e46bb 100644 --- a/src/libxrpl/ledger/BookListeners.cpp +++ b/src/libxrpl/ledger/BookListeners.cpp @@ -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 0a0283c3b8..0000000000 --- a/src/libxrpl/ledger/Credit.cpp +++ /dev/null @@ -1,60 +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/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index 5b94be5da8..b5e358053c 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -185,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 @@ -198,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)}; } diff --git a/src/libxrpl/ledger/PaymentSandbox.cpp b/src/libxrpl/ledger/PaymentSandbox.cpp index e211917466..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 @@ -53,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; + } } } @@ -101,11 +107,9 @@ DeferredCredits::adjustments( 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 @@ -179,11 +183,13 @@ PaymentSandbox::balanceHook( 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; } diff --git a/src/libxrpl/ledger/RawStateTable.cpp b/src/libxrpl/ledger/RawStateTable.cpp index fcd8126c87..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,9 +126,13 @@ private: { ++iter1_; if (iter1_ == end1_) + { sle1_ = nullptr; + } else + { sle1_ = iter1_->second.sle; + } } void diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 837d5dfee6..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,125 +24,6 @@ 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 - -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 @@ -156,120 +39,6 @@ hasExpired(ReadView const& view, std::optional const& exp) return exp && (view.parentCloseTime() >= tp{d{*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, @@ -311,32 +80,6 @@ isVaultPseudoAccountFrozen( return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1); } -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, @@ -348,456 +91,6 @@ isLPTokenFrozen( 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, @@ -896,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) { @@ -1013,30 +292,6 @@ 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, @@ -1052,164 +307,6 @@ dirLink( 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. @@ -1321,7 +418,7 @@ 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; } @@ -1347,1810 +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, - /*bAuth=*/false, - /*bNoRipple=*/true, - /*bFreeze=*/false, - /*deepFreeze*/ false, - /*saBalance=*/STAmount{Issue{currency, noAccount()}}, - /*saLimit=*/STAmount{Issue{currency, dstId}}, - /*uSrcQualityIn=*/0, - /*uSrcQualityOut=*/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, @@ -3191,7 +484,7 @@ cleanupOnAccountDelete( // 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 @@ -3227,427 +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 96% rename from src/libxrpl/ledger/CredentialHelpers.cpp rename to src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 04438e09ad..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 @@ -186,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; } } @@ -197,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; @@ -318,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 @@ -337,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); + } } } 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 91a64a1ca9..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. @@ -472,7 +478,7 @@ public: private: using pointer = std::shared_ptr; - bool mSSL; + bool mSSL{}; AutoSocket mSocket; boost::asio::ip::tcp::resolver mResolver; @@ -490,7 +496,7 @@ private: std::string mBody; unsigned short const mPort; std::size_t const maxResponseSize_; - int mStatus; + int mStatus{}; std::function mBuild; std::function< bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> @@ -502,7 +508,7 @@ private: boost::system::error_code mShutdown; std::deque mDeqSites; - std::chrono::seconds mTimeout; + std::chrono::seconds mTimeout{}; beast::Journal j_; }; diff --git a/src/libxrpl/net/RegisterSSLCerts.cpp b/src/libxrpl/net/RegisterSSLCerts.cpp index 87c2eb81b7..1f21a76ddb 100644 --- a/src/libxrpl/net/RegisterSSLCerts.cpp +++ b/src/libxrpl/net/RegisterSSLCerts.cpp @@ -79,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 ed7753f68e..98fe78c489 100644 --- a/src/libxrpl/nodestore/BatchWriter.cpp +++ b/src/libxrpl/nodestore/BatchWriter.cpp @@ -75,7 +75,7 @@ BatchWriter::writeBatch() } } - BatchWriteReport report; + BatchWriteReport report{}; report.writeCount = set.size(); auto const before = std::chrono::steady_clock::now(); diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index d1452dba86..a24379aea9 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -29,7 +29,7 @@ DatabaseNodeImp::fetchNodeObject( bool duplicate) { std::shared_ptr nodeObject = nullptr; - Status status; + Status status = ok; try { diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index e058fa76ac..6cf51fbf31 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -101,7 +101,7 @@ DatabaseRotatingImp::fetchNodeObject( bool duplicate) { auto fetch = [&](std::shared_ptr const& backend) { - Status status; + Status status = ok; std::shared_ptr nodeObject; try { diff --git a/src/libxrpl/nodestore/backend/MemoryFactory.cpp b/src/libxrpl/nodestore/backend/MemoryFactory.cpp index b11d90610a..0366fe3573 100644 --- a/src/libxrpl/nodestore/backend/MemoryFactory.cpp +++ b/src/libxrpl/nodestore/backend/MemoryFactory.cpp @@ -142,9 +142,13 @@ public: std::shared_ptr 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 c79938bcf8..2d8cffa85a 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -181,7 +181,7 @@ public: Status fetch(uint256 const& hash, std::shared_ptr* pno) override { - Status status; + Status status = ok; pno->reset(); nudb::error_code ec; db_.fetch( @@ -216,9 +216,13 @@ public: std::shared_ptr nObj; Status status = fetch(h, &nObj); if (status != ok) + { results.push_back({}); + } else + { results.push_back(nObj); + } } return {results, ok}; @@ -239,7 +243,7 @@ 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); @@ -251,7 +255,7 @@ public: 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) diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index 01bc74f5ed..67c329bb4a 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -166,8 +166,10 @@ 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()); + } } m_options.table_factory.reset(NewBlockBasedTableFactory(table_options)); @@ -177,8 +179,10 @@ public: 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()); + } } std::string s1, s2; @@ -210,8 +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()); + } m_db.reset(db); } @@ -304,9 +310,13 @@ public: std::shared_ptr 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/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index c7531f8376..7d9a2047a9 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -80,7 +81,7 @@ getVersionString() std::string const& getFullVersionString() { - static std::string const value = "rippled-" + getVersionString(); + static std::string const value = systemName() + "-" + getVersionString(); return value; } diff --git a/src/libxrpl/protocol/Feature.cpp b/src/libxrpl/protocol/Feature.cpp index 28ff1547df..f91e164c92 100644 --- a/src/libxrpl/protocol/Feature.cpp +++ b/src/libxrpl/protocol/Feature.cpp @@ -250,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. */ @@ -302,7 +306,7 @@ FeatureCollections::featureToName(uint256 const& f) const return feature ? feature->name : to_string(f); } -static FeatureCollections featureCollections; +FeatureCollections featureCollections; } // namespace @@ -395,10 +399,20 @@ featureToName(uint256 const& f) #pragma push_macro("XRPL_RETIRE_FIX") #undef XRPL_RETIRE_FIX +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(#name, supported, vote); + uint256 const feature##name = \ + registerFeature(enforceValidFeatureName([] { return #name; }), supported, vote); #define XRPL_FIX(name, supported, vote) \ - uint256 const fix##name = registerFeature("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/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 45ab20f057..61a64b2a54 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -137,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 @@ -219,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 diff --git a/src/libxrpl/protocol/Issue.cpp b/src/libxrpl/protocol/Issue.cpp index 1a56007729..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; diff --git a/src/libxrpl/protocol/NFTokenID.cpp b/src/libxrpl/protocol/NFTokenID.cpp index b533fa53c0..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; @@ -154,7 +154,7 @@ insertNFTokenID( { 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 b1a08081c0..e7ebbcf84b 100644 --- a/src/libxrpl/protocol/NFTokenOfferID.cpp +++ b/src/libxrpl/protocol/NFTokenOfferID.cpp @@ -29,7 +29,7 @@ canHaveNFTokenOfferID( return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index 991f9d4d82..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,7 +128,7 @@ 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); @@ -145,8 +147,10 @@ Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules 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); diff --git a/src/libxrpl/protocol/PublicKey.cpp b/src/libxrpl/protocol/PublicKey.cpp index 3251cb84ac..2c63ddac64 100644 --- a/src/libxrpl/protocol/PublicKey.cpp +++ b/src/libxrpl/protocol/PublicKey.cpp @@ -171,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"); @@ -270,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/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 5a97a33185..4ef052d2cc 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -77,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 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 9503da57a2..92ce129825 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -181,7 +181,7 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) } STAmount::STAmount(SField const& name, std::int64_t mantissa) - : STBase(name), mAsset(xrpIssue()), mOffset(0) + : STBase(name), mAsset(xrpIssue()), mValue(0), mOffset(0), mIsNegative(false) { set(mantissa); } @@ -223,9 +223,13 @@ 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(); } @@ -305,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; } @@ -722,9 +730,13 @@ STAmount::getText() const // Assemble the output: if (pre_from == pre_to) + { ret.append(1, '0'); + } else + { ret.append(pre_from, pre_to); + } if (post_to != post_from) { @@ -751,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()) { @@ -767,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); } @@ -839,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 @@ -859,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; @@ -869,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; } 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/STIssue.cpp b/src/libxrpl/protocol/STIssue.cpp index 5887b5c21f..1d37554251 100644 --- a/src/libxrpl/protocol/STIssue.cpp +++ b/src/libxrpl/protocol/STIssue.cpp @@ -100,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 8d0d56a737..aa97b6b3a2 100644 --- a/src/libxrpl/protocol/STLedgerEntry.cpp +++ b/src/libxrpl/protocol/STLedgerEntry.cpp @@ -33,9 +33,11 @@ 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))); + } set(format->getSOTemplate()); @@ -43,14 +45,14 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k. } STLedgerEntry::STLedgerEntry(SerialIter& sit, uint256 const& index) - : STObject(sfLedgerEntry), key_(index) + : STObject(sfLedgerEntry), key_(index), type_(ltANY) { set(sit); setSLEType(); } STLedgerEntry::STLedgerEntry(STObject const& object, uint256 const& index) - : STObject(object), key_(index) + : STObject(object), key_(index), type_(ltANY) { setSLEType(); } @@ -118,8 +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))); + } return ret; } diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index cdee274f67..aab4bdc0f5 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -179,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 { @@ -198,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}; diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 829f8eaeef..fe9e95cc82 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -46,7 +46,7 @@ STObject::STObject(STObject&& other) { } -STObject::STObject(SField const& name) : STBase(name), mType(nullptr) +STObject::STObject(SField const& name) : STBase(name) { } @@ -62,8 +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"); @@ -141,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()); + } } } @@ -216,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); @@ -269,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()); @@ -291,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(); } diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index d72db32282..0bf0c827af 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -267,8 +267,10 @@ parseUint16( } } if (!ret) + { return parseUnsigned( field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -320,8 +322,10 @@ parseUint32( } } if (!ret) + { return parseUnsigned( field, json_name, fieldName, name, value, error); + } } catch (std::exception const&) { @@ -449,7 +453,7 @@ parseLeaf( { auto const str = value.asString(); - std::uint64_t val; + std::uint64_t val = 0; bool const useBase10 = field.shouldMeta(SField::sMD_BaseTen); diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 098ca1a400..00a2acd788 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -199,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}; } @@ -211,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, @@ -323,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); @@ -351,7 +367,7 @@ 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 @@ -359,7 +375,7 @@ std::string STTx::getMetaSQL( Serializer rawTxn, std::uint32_t inLedger, - char status, + TxnSql status, std::string const& escapedMetaData) const { static boost::format bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); @@ -370,8 +386,8 @@ STTx::getMetaSQL( return str( boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() % - toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % status % rTxn % - escapedMetaData); + toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % + safe_cast(status) % rTxn % escapedMetaData); } static Expected @@ -486,9 +502,11 @@ multiSignHelper( errorWhat = e.what(); } if (!validSig) + { return Unexpected( std::string("Invalid signature on account ") + toBase58(accountID) + errorWhat.value_or("") + "."); + } } // All signatures verified. return {}; @@ -562,7 +580,7 @@ STTx::getBatchTransactionIDs() const // 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)); @@ -694,9 +712,9 @@ invalidMPTAmountInTx(STObject const& tx) { if (auto const& field = tx.peekAtField(e.sField()); (field.getSType() == STI_AMOUNT && - static_cast(field).holds()) || + safe_downcast(field).holds()) || (field.getSType() == STI_ISSUE && - static_cast(field).holds())) + safe_downcast(field).holds())) { if (e.supportMPT() != soeMPTSupported) return true; diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index 6218bb6db6..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; } diff --git a/src/libxrpl/protocol/STVector256.cpp b/src/libxrpl/protocol/STVector256.cpp index 886fed5d2d..6357e2f2cb 100644 --- a/src/libxrpl/protocol/STVector256.cpp +++ b/src/libxrpl/protocol/STVector256.cpp @@ -19,8 +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())); + } auto const cnt = slice.size() / uint256::size(); diff --git a/src/libxrpl/protocol/SecretKey.cpp b/src/libxrpl/protocol/SecretKey.cpp index 4f0963e72d..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); diff --git a/src/libxrpl/protocol/Seed.cpp b/src/libxrpl/protocol/Seed.cpp index 0e0bb7fb2b..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()); diff --git a/src/libxrpl/protocol/Serializer.cpp b/src/libxrpl/protocol/Serializer.cpp index 483e6b516f..08d221f75b 100644 --- a/src/libxrpl/protocol/Serializer.cpp +++ b/src/libxrpl/protocol/Serializer.cpp @@ -112,8 +112,10 @@ Serializer::addFieldID(int type, int name) 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 @@ -187,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; } @@ -206,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) @@ -230,7 +232,9 @@ Serializer::addEncoded(int length) numBytes = 3; } else + { Throw("lenlen"); + } return addRaw(&bytes[0], numBytes); } @@ -466,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/tokens.cpp b/src/libxrpl/protocol/tokens.cpp index 867c69a802..b46591725f 100644 --- a/src/libxrpl/protocol/tokens.cpp +++ b/src/libxrpl/protocol/tokens.cpp @@ -329,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 {}; @@ -610,12 +610,12 @@ encodeBase58Token( 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); } @@ -637,7 +637,7 @@ encodeBase58Token( 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) @@ -654,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/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp index 3ba0d4c980..baadeef00a 100644 --- a/src/libxrpl/rdb/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -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 { 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/server/InfoSub.cpp b/src/libxrpl/server/InfoSub.cpp index 0ec432883d..5857e4d6ae 100644 --- a/src/libxrpl/server/InfoSub.cpp +++ b/src/libxrpl/server/InfoSub.cpp @@ -52,7 +52,7 @@ InfoSub::getConsumer() } std::uint64_t -InfoSub::getSeq() +InfoSub::getSeq() const { return mSeq; } @@ -68,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 @@ -79,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 d32a579d8d..db2ea450a2 100644 --- a/src/libxrpl/server/JSONRPCUtil.cpp +++ b/src/libxrpl/server/JSONRPCUtil.cpp @@ -17,7 +17,7 @@ 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{}; #ifndef _MSC_VER @@ -68,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/libxrpl/server/LoadFeeTrack.cpp b/src/libxrpl/server/LoadFeeTrack.cpp index e3867f36f5..e206b50863 100644 --- a/src/libxrpl/server/LoadFeeTrack.cpp +++ b/src/libxrpl/server/LoadFeeTrack.cpp @@ -4,6 +4,7 @@ #include #include +#include #include namespace xrpl { @@ -19,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; @@ -45,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 9e73d9f007..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; diff --git a/src/libxrpl/server/State.cpp b/src/libxrpl/server/State.cpp index f41a0c61b3..371f2b4d0a 100644 --- a/src/libxrpl/server/State.cpp +++ b/src/libxrpl/server/State.cpp @@ -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; diff --git a/src/libxrpl/server/Vacuum.cpp b/src/libxrpl/server/Vacuum.cpp index cb31c6fa7a..89764b777a 100644 --- a/src/libxrpl/server/Vacuum.cpp +++ b/src/libxrpl/server/Vacuum.cpp @@ -25,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/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index 15457b2d2e..a6aacfabc9 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -122,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* @@ -417,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)) @@ -437,7 +442,9 @@ SHAMap::belowHelper( i = init; // descend and reset loop } else + { incr(i); // scan next branch + } } return nullptr; } @@ -471,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)) @@ -496,7 +503,7 @@ 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); + auto const leaf = safe_downcast(node); XRPL_ASSERT( leaf->peekItem() || (leaf == root_.get()), "xrpl::SHAMap::onlyBelow : valid inner node"); return leaf->peekItem(); @@ -578,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)); } @@ -611,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)); } @@ -662,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()) { @@ -673,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()) { @@ -683,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) { @@ -696,7 +711,7 @@ SHAMap::delItem(uint256 const& id) { if (!node->isEmptyBranch(i)) { - node->setChild(i, intr_ptr::SharedPtr{}); + node->setChild(i, TreeNodeType{}); break; } } @@ -764,7 +779,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr 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()))) { @@ -779,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_)); } @@ -1088,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)) @@ -1105,7 +1120,9 @@ SHAMap::dump(bool hash) const } } else + { ++leafCount; + } } while (!stack.empty()); JLOG(journal_.info()) << leafCount << " resident leaves"; diff --git a/src/libxrpl/shamap/SHAMapDelta.cpp b/src/libxrpl/shamap/SHAMapDelta.cpp index 9a6342343f..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,12 +95,16 @@ SHAMap::walkBranch( if (!emptyBranch) { // otherMapItem was unmatched, must add - if (isFirstMap) // this is first map, so other item is from second + 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))); + } if (--maxCount <= 0) return false; @@ -132,8 +147,8 @@ 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()) @@ -161,23 +176,24 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const } 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)) @@ -194,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 { @@ -305,9 +324,11 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis if (nextNode) { if (nextNode->isInner()) + { nodeStack.push( intr_ptr::static_pointer_cast( nextNode)); + } } else { diff --git a/src/libxrpl/shamap/SHAMapInnerNode.cpp b/src/libxrpl/shamap/SHAMapInnerNode.cpp index 7c344d9264..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,8 +61,8 @@ 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(); @@ -125,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; } @@ -185,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) { @@ -253,9 +257,11 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) auto const dstIsBranch = [&] { if (child) + { return isBranch_ | (1u << m); - else - return isBranch_ & ~(1u << m); + } + + return isBranch_ & ~(1u << m); }(); auto const dstToAllocate = popcnt16(dstIsBranch); diff --git a/src/libxrpl/shamap/SHAMapLeafNode.cpp b/src/libxrpl/shamap/SHAMapLeafNode.cpp index 49dd99e935..3776de3a35 100644 --- a/src/libxrpl/shamap/SHAMapLeafNode.cpp +++ b/src/libxrpl/shamap/SHAMapLeafNode.cpp @@ -51,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 f7cc80e250..6536e628ef 100644 --- a/src/libxrpl/shamap/SHAMapNodeID.cpp +++ b/src/libxrpl/shamap/SHAMapNodeID.cpp @@ -107,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 d80ddb1e43..c6e4c3dcb0 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -12,7 +13,7 @@ SHAMap::visitLeaves( { visitNodes([&leafFunction](SHAMapTreeNode& node) { if (!node.isInner()) - leafFunction(static_cast(node).peekItem()); + leafFunction(safe_downcast(node).peekItem()); return true; }); } @@ -45,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 @@ -104,7 +107,7 @@ SHAMap::visitDifferences( using StackEntry = std::pair; std::stack> stack; - stack.push({static_cast(root_.get()), SHAMapNodeID{}}); + stack.push({safe_downcast(root_.get()), SHAMapNodeID{}}); while (!stack.empty()) { @@ -127,12 +130,12 @@ SHAMap::visitDifferences( if (next->isInner()) { 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)) + safe_downcast(next)->peekItem()->key(), childHash)) { if (!function(*next)) return; @@ -163,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; @@ -201,12 +204,13 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) 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; @@ -305,7 +309,7 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) // 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}; + 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); @@ -356,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(); } @@ -399,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); @@ -413,7 +419,7 @@ SHAMap::getNodeFat( 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; @@ -438,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)) @@ -530,12 +536,12 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF auto currNode = root_.get(); while (currNode->isInner() && - !static_cast(currNode)->isFullBelow(generation) && + !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; @@ -570,7 +576,7 @@ SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncF if (newNode->isLeaf()) { auto const& actualKey = - static_cast(newNode.get())->peekItem()->key(); + safe_downcast(newNode.get())->peekItem()->key(); // Validate that this leaf belongs at the target position auto const expectedNodeID = SHAMapNodeID::createID(node.getDepth(), actualKey); @@ -642,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; @@ -652,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()) @@ -663,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)) @@ -704,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; @@ -729,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 @@ -800,7 +806,7 @@ SHAMap::verifyProofPath(uint256 const& rootHash, uint256 const& key, std::vector if (node->isInner()) { auto nodeId = SHAMapNodeID::createID(depth, key); - hash = static_cast(node.get()) + hash = safe_downcast(node.get()) ->getChildHash(selectBranch(nodeId, key)); } else diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 9da1028b3a..6e334b5563 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -2,8 +2,11 @@ #include #include #include -#include #include +#include +#include +#include +#include #include #include #include @@ -14,8 +17,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -191,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 @@ -352,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; @@ -382,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; } @@ -562,9 +552,13 @@ Transactor::ticketDelete( if (auto ticketCount = (*sleAccount)[~sfTicketCount]) { if (*ticketCount == 1) + { sleAccount->makeFieldAbsent(sfTicketCount); + } else + { ticketCount = *ticketCount - 1; + } } else { @@ -606,15 +600,14 @@ Transactor::apply() 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)) @@ -639,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); @@ -999,7 +994,7 @@ removeDeletedTrustLines( 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"; } @@ -1023,9 +1018,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 @@ -1110,7 +1103,7 @@ Transactor::operator()() } auto result = ctx_.preclaimResult; - if (result == tesSUCCESS) + if (isTesSuccess(result)) result = apply(); // No transaction can return temUNKNOWN from apply, diff --git a/src/libxrpl/tx/apply.cpp b/src/libxrpl/tx/apply.cpp index def82aca18..8fd898366e 100644 --- a/src/libxrpl/tx/apply.cpp +++ b/src/libxrpl/tx/apply.cpp @@ -50,8 +50,10 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules) } if (any(flags & SF_SIGBAD)) + { // Signature is known bad return {Validity::SigBad, "Transaction has bad signature."}; + } if (!any(flags & SF_SIGGOOD)) { @@ -66,14 +68,18 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules) // 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; @@ -149,7 +155,7 @@ applyBatchTransactions( auto const parentBatchId = batchTxn.getTransactionID(); auto const mode = batchTxn.getFlags(); - auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx&& tx) { + auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) { OpenView perTxBatchView(batch_view, batchView); auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, tapBATCH, j); @@ -189,7 +195,9 @@ applyBatchTransactions( break; } else if (mode & tfOnlyOne) + { break; + } } return applied != 0; diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index ad4a185078..6c6b3f8c98 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -344,6 +344,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open { auto secondFlight = [&]() { if (preflightResult.parentBatchId) + { return preflight( registry, view.rules(), @@ -351,6 +352,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open preflightResult.tx, preflightResult.flags, preflightResult.j); + } return preflight( registry, @@ -383,7 +385,7 @@ preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, Open try { - if (ctx->preflightResult != tesSUCCESS) + if (!isTesSuccess(ctx->preflightResult)) return {*ctx, ctx->preflightResult}; return {*ctx, invoke_preclaim(*ctx)}; } diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp index d98c0a6f50..b64be6192b 100644 --- a/src/libxrpl/tx/invariants/AMMInvariant.cpp +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -3,8 +3,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -55,8 +55,10 @@ validBalances( 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; } @@ -152,7 +154,7 @@ ValidAMM::finalizeDelete(bool enforce, TER res, beast::Journal const& j) const if (ammAccount_) { // LCOV_EXCL_START - std::string const msg = (res == tesSUCCESS) ? "AMM object is not deleted on tesSUCCESS" + 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) @@ -236,7 +238,9 @@ ValidAMM::finalizeDeposit( // LCOV_EXCL_STOP } else if (!generalInvariant(tx, view, ZeroAllowed::No, j) && enforce) + { return false; + } return true; } @@ -271,7 +275,7 @@ ValidAMM::finalize( { // Delete may return tecINCOMPLETE if there are too many // trustlines to delete. - if (result != tesSUCCESS && result != tecINCOMPLETE) + if (!isTesSuccess(result) && result != tecINCOMPLETE) return true; bool const enforce = view.rules().enabled(fixAMMv1_3); diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp index 858c4cdcb8..f38d260782 100644 --- a/src/libxrpl/tx/invariants/FreezeInvariant.cpp +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -157,9 +157,13 @@ TransfersNotFrozen::recordBalance(Issue const& issue, BalanceChange change) "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 diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 79c593c57c..16108472aa 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -147,7 +149,7 @@ XRPNotCreated::finalize( TER const, XRPAmount const fee, ReadView const&, - beast::Journal const& j) + 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. @@ -207,7 +209,7 @@ XRPBalanceChecks::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -251,7 +253,7 @@ NoBadOffers::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -344,7 +346,7 @@ NoZeroEscrow::finalize( TER const, XRPAmount const, ReadView const& rv, - beast::Journal const& j) + beast::Journal const& j) const { if (bad_) { @@ -373,20 +375,22 @@ AccountRootsNotDeleted::finalize( TER const result, XRPAmount const, ReadView const&, - beast::Journal const& j) + 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) && result == tesSUCCESS) + 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!"; @@ -396,7 +400,7 @@ AccountRootsNotDeleted::finalize( // 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) && result == tesSUCCESS && accountsDeleted_ == 1) + if (hasPrivilege(tx, mayDeleteAcct) && isTesSuccess(result) && accountsDeleted_ == 1) return true; if (accountsDeleted_ == 0) @@ -488,8 +492,9 @@ AccountRootsDeletedClean::finalize( return false; } // Simple types - for (auto const& [keyletfunc, _, __] : directAccountKeylets) + 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; } @@ -564,7 +569,7 @@ LedgerEntryTypesMatch::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if ((!typeMismatch_) && (!invalidTypeAdded_)) return true; @@ -606,7 +611,7 @@ NoXRPTrustLines::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (!xrpTrustLine_) return true; @@ -642,7 +647,7 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (!deepFreezeWithoutFreeze_) return true; @@ -675,7 +680,7 @@ ValidNewAccountRoot::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (accountsCreated_ == 0) return true; @@ -688,7 +693,7 @@ ValidNewAccountRoot::finalize( } // From this point on we know exactly one account was created. - if (hasPrivilege(tx, createAcct | createPseudoAcct) && result == tesSUCCESS) + if (hasPrivilege(tx, createAcct | createPseudoAcct) && isTesSuccess(result)) { bool const pseudoAccount = (pseudoAccount_ && @@ -750,12 +755,12 @@ ValidClawback::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (tx.getTxnType() != ttCLAWBACK) return true; - if (result == tesSUCCESS) + if (isTesSuccess(result)) { if (trustlinesChanged > 1) { @@ -813,8 +818,10 @@ ValidPseudoAccounts::visitEntry( std::shared_ptr const& after) { if (isDelete) + { // Deletion is ignored return; + } if (after && after->getType() == ltACCOUNT_ROOT) { @@ -902,8 +909,10 @@ NoModifiedUnmodifiableFields::visitEntry( std::shared_ptr const& after) { if (isDelete || !before) + { // Creation and deletion are ignored return; + } changedEntries_.emplace(before, after); } diff --git a/src/libxrpl/tx/invariants/LoanInvariant.cpp b/src/libxrpl/tx/invariants/LoanInvariant.cpp index 01c4da46ac..0df7e409d1 100644 --- a/src/libxrpl/tx/invariants/LoanInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanInvariant.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 20957b8d43..a3a5bf897d 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -19,15 +19,21 @@ ValidMPTIssuance::visitEntry( 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_++; @@ -44,9 +50,9 @@ ValidMPTIssuance::finalize( TER const result, XRPAmount const _fee, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { - if (result == tesSUCCESS) + if (isTesSuccess(result)) { auto const& rules = view.rules(); [[maybe_unused]] @@ -122,26 +128,25 @@ ValidMPTIssuance::finalize( "succeeded but created MPT issuances"; return false; } - else if (mptIssuancesDeleted_ > 0) + if (mptIssuancesDeleted_ > 0) { JLOG(j.fatal()) << "Invariant failed: MPT authorize " "succeeded but deleted issuances"; return false; } - else if (lendingProtocolEnabled && mptokensCreated_ + mptokensDeleted_ > 1) + if (lendingProtocolEnabled && mptokensCreated_ + mptokensDeleted_ > 1) { JLOG(j.fatal()) << "Invariant failed: MPT authorize succeeded " "but created/deleted bad number mptokens"; return false; } - else if (submittedByIssuer && (mptokensCreated_ > 0 || mptokensDeleted_ > 0)) + if (submittedByIssuer && (mptokensCreated_ > 0 || mptokensDeleted_ > 0)) { JLOG(j.fatal()) << "Invariant failed: MPT authorize submitted by issuer " "succeeded but created/deleted mptokens"; return false; } - else if ( - !submittedByIssuer && hasPrivilege(tx, mustAuthorizeMPT) && + if (!submittedByIssuer && hasPrivilege(tx, mustAuthorizeMPT) && (mptokensCreated_ + mptokensDeleted_ != 1)) { // if the holder submitted this tx, then a mptoken must be diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp index db06896023..cf00dc9290 100644 --- a/src/libxrpl/tx/invariants/NFTInvariant.cpp +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include namespace xrpl { @@ -120,7 +120,7 @@ ValidNFTokenPage::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (badLink_) { @@ -196,7 +196,7 @@ NFTokenCountTracking::finalize( TER const result, XRPAmount const, ReadView const& view, - beast::Journal const& j) + beast::Journal const& j) const { if (!hasPrivilege(tx, changeNFTCounts)) { @@ -219,14 +219,14 @@ NFTokenCountTracking::finalize( if (tx.getTxnType() == ttNFTOKEN_MINT) { - if (result == tesSUCCESS && beforeMintedTotal >= afterMintedTotal) + if (isTesSuccess(result) && beforeMintedTotal >= afterMintedTotal) { JLOG(j.fatal()) << "Invariant failed: successful minting didn't increase " "the number of minted tokens."; return false; } - if (result != tesSUCCESS && beforeMintedTotal != afterMintedTotal) + if (!isTesSuccess(result) && beforeMintedTotal != afterMintedTotal) { JLOG(j.fatal()) << "Invariant failed: failed minting changed the " "number of minted tokens."; @@ -243,7 +243,7 @@ NFTokenCountTracking::finalize( if (tx.getTxnType() == ttNFTOKEN_BURN) { - if (result == tesSUCCESS) + if (isTesSuccess(result)) { if (beforeBurnedTotal >= afterBurnedTotal) { @@ -253,7 +253,7 @@ NFTokenCountTracking::finalize( } } - if (result != tesSUCCESS && beforeBurnedTotal != afterBurnedTotal) + if (!isTesSuccess(result) && beforeBurnedTotal != afterBurnedTotal) { JLOG(j.fatal()) << "Invariant failed: failed burning changed the " "number of burned tokens."; diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp index 2ece1f3fc0..e932a6ba09 100644 --- a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -24,9 +24,13 @@ ValidPermissionedDEX::visitEntry( 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 @@ -46,7 +50,7 @@ ValidPermissionedDEX::finalize( beast::Journal const& j) { auto const txType = tx.getTxnType(); - if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || result != tesSUCCESS) + if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || !isTesSuccess(result)) return true; // For each offercreate transaction, check if diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 77acbe12c6..02eaee0552 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -1,7 +1,7 @@ #include // #include -#include +#include #include #include #include @@ -89,10 +89,11 @@ ValidPermissionedDomain::finalize( if (view.rules().enabled(fixPermissionedDomainInvariant)) { // No permissioned domains should be affected if the transaction failed - if (result != tesSUCCESS) - // If nothing changed, all is good. If there were changes, that's - // bad. + if (!isTesSuccess(result)) + { + // If nothing changed, all is good. If there were changes, that's bad. return sleStatus_.empty(); + } if (sleStatus_.size() > 1) { @@ -152,7 +153,7 @@ ValidPermissionedDomain::finalize( } else { - if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS || + if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || !isTesSuccess(result) || sleStatus_.empty()) return true; return check(sleStatus_[0], j); diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index c3db3a563a..f0dd82c2f8 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -157,7 +158,7 @@ ValidVault::finalize( return true; // Not a vault operation } - else if (!(hasPrivilege(tx, mustModifyVault) || hasPrivilege(tx, mayModifyVault))) + if (!(hasPrivilege(tx, mustModifyVault) || hasPrivilege(tx, mayModifyVault))) { JLOG(j.fatal()) << // "Invariant failed: vault updated by a wrong transaction type"; @@ -239,7 +240,7 @@ ValidVault::finalize( return result; } - else if (txnType == ttVAULT_DELETE) + if (txnType == ttVAULT_DELETE) { JLOG(j.fatal()) << "Invariant failed: vault deletion succeeded without " "deleting a vault"; diff --git a/src/libxrpl/tx/paths/BookTip.cpp b/src/libxrpl/tx/paths/BookTip.cpp index f00da6d7c7..5611a081c3 100644 --- a/src/libxrpl/tx/paths/BookTip.cpp +++ b/src/libxrpl/tx/paths/BookTip.cpp @@ -1,3 +1,5 @@ +#include +#include #include namespace xrpl { diff --git a/src/libxrpl/tx/paths/Flow.cpp b/src/libxrpl/tx/paths/Flow.cpp index 2178997049..5a706ea812 100644 --- a/src/libxrpl/tx/paths/Flow.cpp +++ b/src/libxrpl/tx/paths/Flow.cpp @@ -1,12 +1,12 @@ #include -#include +#include #include #include #include #include #include #include -#include +#include namespace xrpl { @@ -15,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); @@ -78,7 +82,7 @@ flow( domainID, j); - if (toStrandsTer != tesSUCCESS) + if (!isTesSuccess(toStrandsTer)) { path::RippleCalc::Output result; result.setResult(toStrandsTer); diff --git a/src/libxrpl/tx/paths/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp index 9d2a94e3a8..b7dc431b23 100644 --- a/src/libxrpl/tx/paths/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -1,9 +1,11 @@ #include #include +#include +#include #include #include #include -#include +#include namespace xrpl { @@ -95,8 +97,10 @@ accountFundsHelper( beast::Journal j) { if (issue.account == id) + { // self funded return amtDefault; + } return toAmount( accountHolds(view, id, issue.currency, issue.account, freezeHandling, j)); diff --git a/src/libxrpl/tx/transactors/Credentials.cpp b/src/libxrpl/tx/transactors/Credentials.cpp deleted file mode 100644 index ce95d8144c..0000000000 --- a/src/libxrpl/tx/transactors/Credentials.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#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; - -// ------- CREATE -------------------------- - -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, ctx_.tx.getFieldU32(sfExpiration)); - } - - 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 (mPriorBalance < 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 - { - auto const page = - view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject)); - JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key) - << ": " << (page ? "success" : "failure"); - if (!page) - return tecDIR_FULL; - sleCred->setFieldU64(sfSubjectNode, *page); - view().update(view().peek(keylet::account(subject))); - } - - view().insert(sleCred); - - return tesSUCCESS; -} - -// ------- DELETE -------------------------- - -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_); -} - -// ------- APPLY -------------------------- - -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 (mPriorBalance < 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/Escrow.cpp b/src/libxrpl/tx/transactors/Escrow.cpp deleted file mode 100644 index 8ea2de24d9..0000000000 --- a/src/libxrpl/tx/transactors/Escrow.cpp +++ /dev/null @@ -1,1262 +0,0 @@ -#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; - -/* - 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; -} - -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; - - // 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]) - 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; - } - } - - 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); ter != tesSUCCESS) - return ter; - - // If the issuer has requireAuth set, check if the destination is authorized - if (auto const ter = requireAuth(ctx.view, amount.issue(), dest); ter != tesSUCCESS) - 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); - ter != tesSUCCESS) - 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); - ter != tesSUCCESS) - 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); ter != tesSUCCESS) - 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 (ter != tesSUCCESS) - 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 (ter != tesSUCCESS) - return ter; // LCOV_EXCL_LINE - return tesSUCCESS; -} - -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 reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); - - if (mSourceBalance < reserve) - return tecINSUFFICIENT_RESERVE; - - // Check reserve and funds availability - if (isXRP(amount)) - { - if (mSourceBalance < 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]; - - 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, 1, ctx_.journal); - ctx_.view().update(sle); - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -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) -{ - return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); -} - -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)) - return temMALFORMED; - - 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); - } - } - - if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) - return err; - - 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)); - } - - 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); ter != tesSUCCESS) - 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); - ter != tesSUCCESS) - 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)) - { - 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 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; -} - -template -static 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 <> -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; - bool const issuerHigh = issuer > receiver; - - if (senderIssuer) - return tecINTERNAL; // LCOV_EXCL_LINE - - if (receiverIssuer) - return tesSUCCESS; - - if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) - { - // 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. " - "Insufficent 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(issuerHigh ? sfLowLimit : sfHighLimit); - - STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance); - - // flip the sign of the line balance if the issuer is not high - if (!issuerHigh) - 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 (ter != tesSUCCESS) - return ter; // LCOV_EXCL_LINE - } - return tesSUCCESS; -} - -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) -{ - 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); -} - -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)) - 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; - - // 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; - } - - // NOTE: Escrow payments cannot be used to fund accounts. - AccountID const destID = (*slep)[sfDestination]; - auto const sled = ctx_.view().peek(keylet::account(destID)); - if (!sled) - return tecNO_DST; - - if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal); - !isTesSuccess(err)) - return err; - - 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, - mPriorBalance, - 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); - - // Adjust source owner count - auto const sle = ctx_.view().peek(keylet::account(account)); - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); - ctx_.view().update(sle); - - // Remove escrow from ledger - ctx_.view().erase(slep); - return tesSUCCESS; -} - -//------------------------------------------------------------------------------ - -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); ter != tesSUCCESS) - 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); - ter != tesSUCCESS) - 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, - mPriorBalance, - 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 - } - } - } - - adjustOwnerCount(ctx_.view(), sle, -1, 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/PayChan.cpp b/src/libxrpl/tx/transactors/PayChan.cpp deleted file mode 100644 index 265030bebf..0000000000 --- a/src/libxrpl/tx/transactors/PayChan.cpp +++ /dev/null @@ -1,542 +0,0 @@ -#include -#include -#include -#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. - - PaymentChannelCreate - - Create a unidirectional channel. The parameters are: - Destination - The recipient at the end of the channel. - Amount - The amount of XRP to deposit in the channel immediately. - SettleDelay - The amount of time everyone but the recipient must wait for a - superior claim. - PublicKey - The key that will sign claims against the channel. - CancelAfter (optional) - Any channel transaction that touches this channel after the - `CancelAfter` time will close it. - DestinationTag (optional) - Destination tags allow the different accounts inside of a Hosted - Wallet to be mapped back onto the Ripple ledger. The destination tag - tells the server to which account in the Hosted Wallet the funds are - intended to go to. Required if the destination has lsfRequireDestTag - set. - SourceTag (optional) - Source tags allow the different accounts inside of a Hosted Wallet - to be mapped back onto the Ripple ledger. Source tags are similar to - destination tags but are for the channel owner to identify their own - transactions. - - PaymentChannelFund - - Add additional funds to the payment channel. Only the channel owner may - use this transaction. The parameters are: - Channel - The 256-bit ID of the channel. - Amount - The amount of XRP to add. - Expiration (optional) - Time the channel closes. The transaction will fail if the expiration - times does not satisfy the SettleDelay constraints. - - PaymentChannelClaim - - Place a claim against an existing channel. The parameters are: - Channel - The 256-bit ID of the channel. - Balance (optional) - The total amount of XRP delivered after this claim is processed - (optional, not needed if just closing). Amount (optional) The amount of XRP - the signature is for (not needed if equal to Balance or just closing the - line). Signature (optional) Authorization for the balance above, signed by - the owner (optional, not needed if closing or owner is performing the - transaction). The signature if for the following message: CLM\0 followed by - the 256-bit channel ID, and a 64-bit integer drops. PublicKey (optional) The - public key that made the signature (optional, required if a signature is - present) Flags tfClose Request that the channel be closed tfRenew Request - that the channel's expiration be reset. Only the owner may renew a channel. - -*/ - -//------------------------------------------------------------------------------ - -static 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; -} - -//------------------------------------------------------------------------------ - -TxConsequences -PayChanCreate::makeTxConsequences(PreflightContext const& ctx) -{ - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; -} - -NotTEC -PayChanCreate::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 -PayChanCreate::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 -PayChanCreate::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; -} - -//------------------------------------------------------------------------------ - -TxConsequences -PayChanFund::makeTxConsequences(PreflightContext const& ctx) -{ - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; -} - -NotTEC -PayChanFund::preflight(PreflightContext const& ctx) -{ - if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) - return temBAD_AMOUNT; - - return tesSUCCESS; -} - -TER -PayChanFund::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; -} - -//------------------------------------------------------------------------------ - -bool -PayChanClaim::checkExtraFeatures(PreflightContext const& ctx) -{ - return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); -} - -std::uint32_t -PayChanClaim::getFlagsMask(PreflightContext const&) -{ - return tfPaymentChannelClaimMask; -} - -NotTEC -PayChanClaim::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 -PayChanClaim::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 -PayChanClaim::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::PayChanClaim::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/DeleteAccount.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp similarity index 86% rename from src/libxrpl/tx/transactors/DeleteAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountDelete.cpp index 79ce03ca9a..fed265c125 100644 --- a/src/libxrpl/tx/transactors/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -1,25 +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; @@ -28,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; @@ -41,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); @@ -79,7 +82,7 @@ removeSignersFromLedger( std::shared_ptr const& sleDel, beast::Journal j) { - return SetSignerList::removeFromLedger(registry, view, account, j); + return SignerListSet::removeFromLedger(registry, view, account, j); } TER @@ -142,7 +145,7 @@ removeOracleFromLedger( std::shared_ptr const& sleDel, beast::Journal j) { - return DeleteOracle::deleteOracle(view, sleDel, account, j); + return OracleDelete::deleteOracle(view, sleDel, account, j); } TER @@ -203,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]}; @@ -233,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; @@ -302,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 @@ -324,14 +327,14 @@ 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 @@ -360,35 +363,36 @@ DeleteAccount::doApply() // 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"); + (*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/libxrpl/tx/transactors/SetAccount.cpp b/src/libxrpl/tx/transactors/account/AccountSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/SetAccount.cpp rename to src/libxrpl/tx/transactors/account/AccountSet.cpp index 64b52d12d5..33759c6ec4 100644 --- a/src/libxrpl/tx/transactors/SetAccount.cpp +++ b/src/libxrpl/tx/transactors/account/AccountSet.cpp @@ -1,19 +1,20 @@ #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(); @@ -37,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; @@ -126,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; @@ -150,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) @@ -199,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]; @@ -262,7 +263,7 @@ SetAccount::preclaim(PreclaimContext const& ctx) } TER -SetAccount::doApply() +AccountSet::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -491,7 +492,7 @@ SetAccount::doApply() if (messageKey.empty()) { - JLOG(j_.debug()) << "set message key"; + JLOG(j_.debug()) << "clear message key"; sle->makeFieldAbsent(sfMessageKey); } else @@ -565,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/libxrpl/tx/transactors/SetRegularKey.cpp b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp similarity index 96% rename from src/libxrpl/tx/transactors/SetRegularKey.cpp rename to src/libxrpl/tx/transactors/account/SetRegularKey.cpp index e26e6b1bd5..08175b196f 100644 --- a/src/libxrpl/tx/transactors/SetRegularKey.cpp +++ b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/SetSignerList.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp similarity index 91% rename from src/libxrpl/tx/transactors/SetSignerList.cpp rename to src/libxrpl/tx/transactors/account/SignerListSet.cpp index 858fb33bff..fe9e80d7e0 100644 --- a/src/libxrpl/tx/transactors/SetSignerList.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -1,13 +1,14 @@ #include #include -#include +#include +#include #include #include #include #include #include #include -#include +#include #include #include @@ -19,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. @@ -51,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) @@ -78,7 +79,7 @@ SetSignerList::preflight(PreflightContext const& ctx) 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) + if (!isTesSuccess(ter)) { return ter; } @@ -88,7 +89,7 @@ SetSignerList::preflight(PreflightContext const& ctx) } TER -SetSignerList::doApply() +SignerListSet::doApply() { // Perform the operation preCompute() decided on. switch (do_) @@ -103,28 +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"); + isTesSuccess(std::get<0>(result)), + "xrpl::SignerListSet::preCompute : result is tesSUCCESS"); XRPL_ASSERT( std::get<3>(result) != unknown, - "xrpl::SetSignerList::preCompute : result is known operation"); + "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 @@ -204,7 +205,7 @@ removeSignersFromLedger( } TER -SetSignerList::removeFromLedger( +SignerListSet::removeFromLedger( ServiceRegistry& registry, ApplyView& view, AccountID const& account, @@ -219,7 +220,7 @@ SetSignerList::removeFromLedger( } NotTEC -SetSignerList::validateQuorumAndSignerEntries( +SignerListSet::validateQuorumAndSignerEntries( std::uint32_t quorum, std::vector const& signers, AccountID const& account, @@ -239,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()) { @@ -278,7 +279,7 @@ SetSignerList::validateQuorumAndSignerEntries( } TER -SetSignerList::replaceSignerList() +SignerListSet::replaceSignerList() { auto const accountKeylet = keylet::account(account_); auto const ownerDirKeylet = keylet::ownerDir(account_); @@ -305,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. @@ -334,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 @@ -353,7 +354,7 @@ SetSignerList::destroySignerList() } 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/libxrpl/tx/transactors/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp similarity index 97% rename from src/libxrpl/tx/transactors/XChainBridge.cpp rename to src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 44a1974f93..9ac755cf4d 100644 --- a/src/libxrpl/tx/transactors/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -21,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -267,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 @@ -337,7 +339,7 @@ enum class DepositAuthPolicy { normal, dstCanBypass }; struct TransferHelperSubmittingAccountInfo { AccountID account; - STAmount preFeeBalance; + STAmount preFeeBalance_; STAmount postFeeBalance; }; @@ -423,7 +425,7 @@ transferHelper( if (!submittingAccountInfo || submittingAccountInfo->account != src || submittingAccountInfo->postFeeBalance != curBal) return curBal; - return submittingAccountInfo->preFeeBalance; + return submittingAccountInfo->preFeeBalance_; }(); if (availableBalance < amt + reserve) @@ -508,15 +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 @@ -530,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; } @@ -868,7 +870,13 @@ applyClaimAttestations( XChainClaimAttestations curAtts{sleClaimID->getFieldArray(sfXChainClaimAttestations)}; auto const newAttResult = onNewAttestations( - curAtts, view, &atts[0], &atts[0] + atts.size(), quorum, signersList, j); + 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()); @@ -975,7 +983,7 @@ applyCreateAccountAttestations( struct ScopeResult { OnNewAttestationResult newAttResult; - bool createCID; + bool createCID{}; XChainCreateAccountAttestations curAtts; }; @@ -1020,13 +1028,21 @@ applyCreateAccountAttestations( XChainCreateAccountAttestations curAtts = [&] { if (sleClaimID) + { return XChainCreateAccountAttestations{ sleClaimID->getFieldArray(sfXChainCreateAccountAttestations)}; + } return XChainCreateAccountAttestations{}; }(); auto const newAttResult = onNewAttestations( - curAtts, view, &atts[0], &atts[0] + atts.size(), quorum, signersList, j); + curAtts, + view, + &atts[0], + &atts[0] + atts.size(), // NOLINT(bugprone-pointer-arithmetic-on-polymorphic-object) + quorum, + signersList, + j); if (!createCID) { @@ -1190,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; }; @@ -1220,11 +1238,17 @@ 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); @@ -1585,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 + } } { @@ -1618,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; }(); @@ -1683,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); @@ -1810,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) @@ -1840,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); @@ -1851,7 +1898,7 @@ XChainCommit::doApply() // Support dipping into reserves to pay the fee TransferHelperSubmittingAccountInfo submittingAccountInfo{ - account_, mPriorBalance, mSourceBalance}; + account_, preFeeBalance_, (*sleAccount)[sfBalance]}; auto const thTer = transferHelper( psb, @@ -2081,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); @@ -2120,7 +2173,7 @@ XChainCreateAccountCommit::doApply() // Support dipping into reserves to pay the fee TransferHelperSubmittingAccountInfo submittingAccountInfo{ - account_, mPriorBalance, mSourceBalance}; + account_, preFeeBalance_, (*sle)[sfBalance]}; STAmount const toTransfer = amount + reward; auto const thTer = transferHelper( psb, diff --git a/src/libxrpl/tx/transactors/Check/CancelCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp similarity index 87% rename from src/libxrpl/tx/transactors/Check/CancelCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCancel.cpp index c9a947ae6a..2d08b2f1b0 100644 --- a/src/libxrpl/tx/transactors/Check/CancelCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -1,22 +1,23 @@ #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) @@ -25,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. @@ -49,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) diff --git a/src/libxrpl/tx/transactors/Check/CashCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp similarity index 97% rename from src/libxrpl/tx/transactors/Check/CashCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCash.cpp index c459ef9b67..7fa53c7352 100644 --- a/src/libxrpl/tx/transactors/Check/CashCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -1,19 +1,21 @@ #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]; @@ -44,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) @@ -196,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. @@ -265,12 +267,14 @@ CashCheck::doApply() } 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) + !isTesSuccess(ter)) { // The transfer failed. Return the error code. return ter; @@ -309,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."; @@ -389,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(); diff --git a/src/libxrpl/tx/transactors/Check/CreateCheck.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp similarity index 94% rename from src/libxrpl/tx/transactors/Check/CreateCheck.cpp rename to src/libxrpl/tx/transactors/check/CheckCreate.cpp index 7ec4746d20..50554a1beb 100644 --- a/src/libxrpl/tx/transactors/Check/CreateCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -1,15 +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]) { @@ -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)); @@ -128,7 +131,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) } TER -CreateCheck::doApply() +CheckCreate::doApply() { auto const sle = view().peek(keylet::account(account_)); if (!sle) @@ -140,7 +143,7 @@ CreateCheck::doApply() { STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } 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/libxrpl/tx/transactors/Delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp similarity index 73% rename from src/libxrpl/tx/transactors/Delegate/DelegateSet.cpp rename to src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 69ec707dd2..9fb17c4d1f 100644 --- a/src/libxrpl/tx/transactors/Delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -1,9 +1,11 @@ #include #include +#include +#include #include #include #include -#include +#include namespace xrpl { @@ -41,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; } @@ -59,38 +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; } + auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + if (permissions.empty()) + return tecINTERNAL; // LCOV_EXCL_LINE + STAmount const reserve{ ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + 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/libxrpl/tx/transactors/Delegate/DelegateUtils.cpp b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp similarity index 95% rename from src/libxrpl/tx/transactors/Delegate/DelegateUtils.cpp rename to src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp index eceb73503f..d9d74a1212 100644 --- a/src/libxrpl/tx/transactors/Delegate/DelegateUtils.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp @@ -1,5 +1,5 @@ #include -#include +#include namespace xrpl { NotTEC diff --git a/src/libxrpl/tx/transactors/AMM/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp similarity index 93% rename from src/libxrpl/tx/transactors/AMM/AMMBid.cpp rename to src/libxrpl/tx/transactors/dex/AMMBid.cpp index 8ba7970472..a1ace702dc 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -4,9 +4,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -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; @@ -193,14 +193,22 @@ 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); @@ -214,7 +222,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour // 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; @@ -245,7 +253,7 @@ 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; @@ -253,23 +261,30 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour << "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 { @@ -308,7 +323,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour auctionSlot[sfAccount], toSTAmount(lpTokens.issue(), refund), ctx_.journal); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Bid: failed to refund."; return {res, false}; @@ -318,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/libxrpl/tx/transactors/AMM/AMMClawback.cpp b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp similarity index 94% rename from src/libxrpl/tx/transactors/AMM/AMMClawback.cpp rename to src/libxrpl/tx/transactors/dex/AMMClawback.cpp index c9ea1e44eb..a5fda15033 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMClawback.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp @@ -4,10 +4,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include @@ -102,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; @@ -128,8 +128,7 @@ 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; @@ -138,7 +137,6 @@ AMMClawback::applyGuts(Sandbox& sb) !res) return res.error(); // LCOV_EXCL_LINE } - auto const expected = ammHolds(sb, *ammSle, asset, asset2, FreezeHandling::fhIGNORE_FREEZE, ctx_.journal); @@ -151,11 +149,14 @@ 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) = @@ -172,9 +173,11 @@ AMMClawback::applyGuts(Sandbox& sb) 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::Yes, - mPriorBalance, + preFeeBalance_, ctx_.journal); + } else + { std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = equalWithdrawMatchingOneAmount( sb, @@ -186,8 +189,9 @@ AMMClawback::applyGuts(Sandbox& sb) lptAMMBalance, holdLPtokens, *clawAmount); + } - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; // LCOV_EXCL_LINE auto const res = @@ -200,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 @@ -235,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. @@ -251,8 +256,9 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::Yes, - mPriorBalance, + preFeeBalance_, ctx_.journal); + } auto const& rules = sb.rules(); if (rules.enabled(fixAMMClawbackRounding)) @@ -282,7 +288,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } @@ -301,7 +307,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( 0, FreezeHandling::fhIGNORE_FREEZE, WithdrawAll::No, - mPriorBalance, + preFeeBalance_, ctx_.journal); } diff --git a/src/libxrpl/tx/transactors/AMM/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp similarity index 91% rename from src/libxrpl/tx/transactors/AMM/AMMCreate.cpp rename to src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 53933df629..dd717948b3 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -1,13 +1,14 @@ #include #include #include +#include #include #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -72,13 +73,13 @@ AMMCreate::preclaim(PreclaimContext const& ctx) 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; @@ -163,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()); } @@ -231,7 +233,7 @@ 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}; @@ -244,22 +246,22 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J // 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}; @@ -267,7 +269,7 @@ 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}; @@ -284,7 +286,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J 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/libxrpl/tx/transactors/AMM/AMMDelete.cpp b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp similarity index 88% rename from src/libxrpl/tx/transactors/AMM/AMMDelete.cpp rename to src/libxrpl/tx/transactors/dex/AMMDelete.cpp index 39b7c9a11b..0fbaa4e23e 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMDelete.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -45,7 +45,7 @@ AMMDelete::doApply() auto const ter = deleteAMMAccount(sb, ctx_.tx[sfAsset].get(), ctx_.tx[sfAsset2].get(), j_); - if (ter == tesSUCCESS || ter == tecINCOMPLETE) + if (isTesSuccess(ter) || ter == tecINCOMPLETE) sb.apply(ctx_.rawView()); return ter; diff --git a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp similarity index 97% rename from src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp rename to src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index dad1bc536a..cae83ccef1 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -1,11 +1,12 @@ #include #include +#include #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -17,7 +18,6 @@ AMMDeposit::checkExtraFeatures(PreflightContext const& ctx) std::uint32_t AMMDeposit::getFlagsMask(PreflightContext const& ctx) - { return tfAMMDepositMask; } @@ -357,6 +357,7 @@ AMMDeposit::applyGuts(Sandbox& sb) &lptAMMBalance = lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalDepositLimit( sb, ammAccountID, @@ -367,16 +368,24 @@ AMMDeposit::applyGuts(Sandbox& sb) *amount2, lpTokensDeposit, tfee); + } if (subTxType & tfOneAssetLPToken) + { return singleDepositTokens( sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee); + } if (subTxType & tfLimitLPToken) + { return singleDepositEPrice( sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee); + } if (subTxType & tfSingleAsset) + { return singleDeposit( sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee); + } if (subTxType & tfLPToken) + { return equalDepositTokens( sb, ammAccountID, @@ -387,9 +396,12 @@ AMMDeposit::applyGuts(Sandbox& sb) amount, amount2, tfee); + } if (subTxType & tfTwoAssetIfEmpty) + { return equalDepositInEmptyState( sb, ammAccountID, *amount, *amount2, lptAMMBalance.issue(), tfee); + } // should not happen. // LCOV_EXCL_START JLOG(j_.error()) << "AMM Deposit: invalid options."; @@ -397,7 +409,7 @@ AMMDeposit::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result == tesSUCCESS) + if (isTesSuccess(result)) { XRPL_ASSERT( newLPTokenBalance > beast::zero, @@ -411,7 +423,7 @@ AMMDeposit::applyGuts(Sandbox& sb) sb.update(ammSle); } - return {result, result == tesSUCCESS}; + return {result, isTesSuccess(result)}; } TER @@ -463,7 +475,9 @@ AMMDeposit::deposit( depositAmount.issue(), FreezeHandling::fhIGNORE_FREEZE, ctx_.journal) >= depositAmount) + { return tesSUCCESS; + } return tecUNFUNDED_AMM; }; @@ -505,7 +519,7 @@ AMMDeposit::deposit( auto res = accountSend( view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual; return {res, STAmount{}}; @@ -524,7 +538,7 @@ AMMDeposit::deposit( res = accountSend( view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << *amount2DepositActual; @@ -534,7 +548,7 @@ AMMDeposit::deposit( // 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{}}; @@ -647,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, @@ -667,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, @@ -692,6 +713,7 @@ AMMDeposit::equalDepositLimit( std::nullopt, lpTokensDepositMin, tfee); + } return {tecAMM_FAILED, STAmount{}}; } @@ -718,9 +740,11 @@ AMMDeposit::singleDeposit( 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] = @@ -821,9 +845,11 @@ AMMDeposit::singleDepositEPrice( 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] = @@ -832,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, @@ -844,6 +871,7 @@ AMMDeposit::singleDepositEPrice( std::nullopt, std::nullopt, tfee); + } } // LPTokens is asset out => E = b / t diff --git a/src/libxrpl/tx/transactors/AMM/AMMHelpers.cpp b/src/libxrpl/tx/transactors/dex/AMMHelpers.cpp similarity index 89% rename from src/libxrpl/tx/transactors/AMM/AMMHelpers.cpp rename to src/libxrpl/tx/transactors/dex/AMMHelpers.cpp index 5d5af6da03..20ffab52ca 100644 --- a/src/libxrpl/tx/transactors/AMM/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: @@ -72,12 +70,10 @@ ammAssetIn( { 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: @@ -99,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: @@ -131,12 +125,10 @@ ammAssetOut( 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 @@ -194,29 +186,37 @@ 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 std::make_tuple(amountActual, std::nullopt, lpTokensActual); } XRPL_ASSERT( @@ -241,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 @@ -257,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)) @@ -290,9 +292,9 @@ getRoundedLPTokens( 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)) diff --git a/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp b/src/libxrpl/tx/transactors/dex/AMMUtils.cpp similarity index 95% rename from src/libxrpl/tx/transactors/AMM/AMMUtils.cpp rename to src/libxrpl/tx/transactors/dex/AMMUtils.cpp index 55d71ced4f..91891ce86f 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMUtils.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMUtils.cpp @@ -1,9 +1,10 @@ #include +#include #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -51,9 +52,13 @@ ammHolds( 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; @@ -64,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 @@ -153,7 +158,7 @@ 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() < @@ -164,8 +169,10 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account) if (auctionSlot.isFieldPresent(sfAuthAccounts)) { for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts)) + { if (acct[~sfAccount] == account) return auctionSlot[sfDiscountedFee]; + } } } } @@ -259,7 +266,7 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo } if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j); - ter != tesSUCCESS) + !isTesSuccess(ter)) return ter; auto const ownerDirKeylet = keylet::ownerDir(ammAccountID); @@ -322,13 +329,21 @@ initializeFeeAuctionVote( 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 @@ -389,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) @@ -416,9 +437,13 @@ verifyAndAdjustLPTokenBalance( std::shared_ptr& ammSle, AccountID const& account) { - if (auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); !res) + auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account); + if (!res.has_value()) + { return Unexpected(res.error()); - else if (res.value()) + } + + if (res.value()) { if (withinRelativeDistance( lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3})) diff --git a/src/libxrpl/tx/transactors/AMM/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp similarity index 92% rename from src/libxrpl/tx/transactors/AMM/AMMVote.cpp rename to src/libxrpl/tx/transactors/dex/AMMVote.cpp index 549d9705b5..2096eca0f0 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -35,15 +35,17 @@ 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); + } + 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."; @@ -134,16 +136,22 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou 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; @@ -174,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/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp similarity index 97% rename from src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp rename to src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index f58dbaefc6..b822ff6b84 100644 --- a/src/libxrpl/tx/transactors/AMM/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -2,9 +2,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -305,6 +305,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) &lptAMMBalance = lptAMMBalance]() -> std::pair { if (subTxType & tfTwoAsset) + { return equalWithdrawLimit( sb, *ammSle, @@ -315,7 +316,9 @@ AMMWithdraw::applyGuts(Sandbox& sb) *amount, *amount2, tfee); + } if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll) + { return singleWithdrawTokens( sb, *ammSle, @@ -325,12 +328,17 @@ AMMWithdraw::applyGuts(Sandbox& sb) *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); + } if (subTxType & tfLPToken || subTxType & tfWithdrawAll) { return equalWithdrawTokens( @@ -351,7 +359,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) // LCOV_EXCL_STOP }(); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return {result, false}; auto const res = deleteAMMAccountIfEmpty( @@ -414,7 +422,7 @@ AMMWithdraw::withdraw( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, j_); return {ter, newLPTokenBalance}; } @@ -449,6 +457,7 @@ AMMWithdraw::withdraw( auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] = [&]() -> std::tuple, STAmount> { if (withdrawAll == WithdrawAll::No) + { return adjustAmountsByLPTokens( amountBalance, amountWithdraw, @@ -457,6 +466,7 @@ AMMWithdraw::withdraw( lpTokensWithdraw, tfee, IsDeposit::No); + } return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw); }(); @@ -528,7 +538,7 @@ 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)); @@ -545,7 +555,7 @@ AMMWithdraw::withdraw( // Withdraw amountWithdraw auto res = accountSend( view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual; @@ -556,12 +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) + if (!isTesSuccess(res)) { // LCOV_EXCL_START JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual; @@ -572,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"; @@ -628,7 +638,7 @@ AMMWithdraw::equalWithdrawTokens( tfee, FreezeHandling::fhZERO_IF_FROZEN, isWithdrawAll(ctx_.tx), - mPriorBalance, + preFeeBalance_, ctx_.journal); return {ter, newLPTokenBalance}; } @@ -647,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) @@ -676,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) @@ -697,7 +707,7 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, lpTokensWithdraw, tfee, - freezeHanding, + freezeHandling, WithdrawAll::Yes, priorBalance, journal); @@ -731,7 +741,7 @@ AMMWithdraw::equalWithdrawTokens( lptAMMBalance, tokensAdj, tfee, - freezeHanding, + freezeHandling, withdrawAll, priorBalance, journal); @@ -821,7 +831,9 @@ AMMWithdraw::equalWithdrawLimit( // LCOV_EXCL_STOP } else if (amountWithdraw > amount) + { return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE + } return withdraw( view, ammSle, @@ -857,9 +869,11 @@ AMMWithdraw::singleWithdraw( 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] = @@ -973,9 +987,11 @@ AMMWithdraw::singleWithdrawEPrice( 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; }; diff --git a/src/libxrpl/tx/transactors/Offer/CancelOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp similarity index 83% rename from src/libxrpl/tx/transactors/Offer/CancelOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCancel.cpp index 3e5d2441e1..9d60347778 100644 --- a/src/libxrpl/tx/transactors/Offer/CancelOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp @@ -1,16 +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; } @@ -20,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]; @@ -42,7 +43,7 @@ CancelOffer::preclaim(PreclaimContext const& ctx) //------------------------------------------------------------------------------ TER -CancelOffer::doApply() +OfferCancel::doApply() { auto const offerSequence = ctx_.tx[sfOfferSequence]; diff --git a/src/libxrpl/tx/transactors/Offer/CreateOffer.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp similarity index 94% rename from src/libxrpl/tx/transactors/Offer/CreateOffer.cpp rename to src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 9516926693..d52fa94877 100644 --- a/src/libxrpl/tx/transactors/Offer/CreateOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -2,18 +2,22 @@ #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]}; @@ -24,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; @@ -33,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 @@ -45,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; @@ -123,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]; @@ -178,7 +182,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) { auto result = checkAcceptAsset(ctx.view, ctx.flags, id, ctx.j, Issue(uPaysCurrency, uPaysIssuerID)); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) return result; } @@ -194,7 +198,7 @@ CreateOffer::preclaim(PreclaimContext const& ctx) } TER -CreateOffer::checkAcceptAsset( +OfferCreate::checkAcceptAsset( ReadView const& view, ApplyFlags const flags, AccountID const id, @@ -202,7 +206,7 @@ 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)); @@ -214,8 +218,10 @@ CreateOffer::checkAcceptAsset( 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) @@ -242,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) @@ -270,7 +268,7 @@ CreateOffer::checkAcceptAsset( } std::pair -CreateOffer::flowCross( +OfferCreate::flowCross( PaymentSandbox& psb, PaymentSandbox& psbCancel, Amounts const& takerAmount, @@ -343,14 +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}; + } } // Call the payment engine's flow() to do the actual work. @@ -408,17 +410,21 @@ CreateOffer::flowCross( // gateway's transfer rate. STAmount nonGatewayAmountIn = result.actualAmountIn; if (gatewayXferRate.value != QUALITY_ONE) + { 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); @@ -431,7 +437,7 @@ CreateOffer::flowCross( afterCross.out -= result.actualAmountOut; XRPL_ASSERT( afterCross.out >= beast::zero, - "xrpl::CreateOffer::flowCross : minimum offer"); + "xrpl::OfferCreate::flowCross : minimum offer"); if (afterCross.out < beast::zero) afterCross.out.clear(); afterCross.in = mulRound(afterCross.out, rate, takerAmount.in.issue(), true); @@ -450,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 += "/"; @@ -459,7 +465,7 @@ CreateOffer::format_amount(STAmount const& amount) } TER -CreateOffer::applyHybrid( +OfferCreate::applyHybrid( Sandbox& sb, std::shared_ptr sleOffer, Keylet const& offerKey, @@ -505,7 +511,7 @@ CreateOffer::applyHybrid( } std::pair -CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) +OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) { using beast::zero; @@ -563,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(); @@ -635,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()) @@ -649,7 +655,7 @@ 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}; @@ -657,10 +663,10 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) XRPL_ASSERT( saTakerGets.issue() == place_offer.in.issue(), - "xrpl::CreateOffer::applyGuts : taker gets issue match"); + "xrpl::OfferCreate::applyGuts : taker gets issue match"); XRPL_ASSERT( saTakerPays.issue() == place_offer.out.issue(), - "xrpl::CreateOffer::applyGuts : taker pays issue match"); + "xrpl::OfferCreate::applyGuts : taker pays issue match"); if (takerAmount != place_offer) crossed = true; @@ -690,9 +696,9 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) XRPL_ASSERT( saTakerPays > zero && saTakerGets > zero, - "xrpl::CreateOffer::applyGuts : taker pays and gets positive"); + "xrpl::OfferCreate::applyGuts : taker pays and gets positive"); - if (result != tesSUCCESS) + if (!isTesSuccess(result)) { JLOG(j_.debug()) << "final result: " << transToken(result); return {result, true}; @@ -719,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}; } @@ -733,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 @@ -741,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); } @@ -833,7 +841,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { auto const res = applyHybrid(sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return {res, true}; // LCOV_EXCL_LINE } @@ -848,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. @@ -861,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/libxrpl/tx/transactors/PermissionedDomain/PermissionedDEXHelpers.cpp b/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp similarity index 94% rename from src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDEXHelpers.cpp rename to src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp index 7a4fa62078..d857795e39 100644 --- a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDEXHelpers.cpp +++ b/src/libxrpl/tx/transactors/dex/PermissionedDEXHelpers.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace xrpl { namespace permissioned_dex { 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/libxrpl/tx/transactors/DID.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp similarity index 76% rename from src/libxrpl/tx/transactors/DID.cpp rename to src/libxrpl/tx/transactors/did/DIDSet.cpp index ade60221df..cd5c9bbc96 100644 --- a/src/libxrpl/tx/transactors/DID.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -1,10 +1,11 @@ #include #include -#include +#include +#include #include #include #include -#include +#include namespace xrpl { @@ -41,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)); @@ -134,54 +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..1250d950cd --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -0,0 +1,207 @@ +#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 + } + } + } + + adjustOwnerCount(ctx_.view(), sle, -1, 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..8b823de160 --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -0,0 +1,506 @@ +#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; +} + +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; + + // 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]) + 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; + } + } + + 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; +} + +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 reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); + + 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]; + + 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, 1, 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..8a05b2b160 --- /dev/null +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -0,0 +1,385 @@ +#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) +{ + return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials); +} + +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)) + return temMALFORMED; + + 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); + } + } + } + + if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err)) + return err; + + 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)); + } + + 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)) + { + 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 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)) + 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; + + // 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; + } + + // NOTE: Escrow payments cannot be used to fund accounts. + AccountID const destID = (*slep)[sfDestination]; + auto const sled = ctx_.view().peek(keylet::account(destID)); + if (!sled) + return tecNO_DST; + + if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal); + !isTesSuccess(err)) + return err; + + 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); + + // Adjust source owner count + auto const sle = ctx_.view().peek(keylet::account(account)); + adjustOwnerCount(ctx_.view(), sle, -1, 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/libxrpl/tx/transactors/Lending/LendingHelpers.cpp b/src/libxrpl/tx/transactors/lending/LendingHelpers.cpp similarity index 98% rename from src/libxrpl/tx/transactors/Lending/LendingHelpers.cpp rename to src/libxrpl/tx/transactors/lending/LendingHelpers.cpp index b0626b4650..ad4cd8440d 100644 --- a/src/libxrpl/tx/transactors/Lending/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 { @@ -856,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; } @@ -1635,24 +1637,26 @@ loanMakePayment( 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, @@ -1664,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"); @@ -1704,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, @@ -1727,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. @@ -1858,13 +1868,17 @@ 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()); + } } } diff --git a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp similarity index 97% rename from src/libxrpl/tx/transactors/Lending/LoanBrokerCoverClawback.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 870af54f94..24aa3f8bf2 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -1,7 +1,8 @@ -#include +#include // +#include #include -#include +#include namespace xrpl { @@ -118,14 +119,14 @@ 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 diff --git a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp similarity index 95% rename from src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index d112959fea..4630e6a360 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -1,7 +1,8 @@ -#include +#include // +#include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp similarity index 93% rename from src/libxrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index 43ff3659ef..6946992376 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -1,9 +1,11 @@ -#include +#include // -#include +#include +#include +#include #include -#include -#include +#include +#include namespace xrpl { @@ -167,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/libxrpl/tx/transactors/Lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp similarity index 96% rename from src/libxrpl/tx/transactors/Lending/LoanBrokerDelete.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 989c26d024..a755db7942 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -1,7 +1,9 @@ -#include +#include // +#include +#include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/Lending/LoanBrokerSet.cpp rename to src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 1ede32dc23..f8813ddbef 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -1,7 +1,9 @@ -#include +#include // +#include +#include #include -#include +#include namespace xrpl { @@ -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/libxrpl/tx/transactors/Lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp similarity index 96% rename from src/libxrpl/tx/transactors/Lending/LoanDelete.cpp rename to src/libxrpl/tx/transactors/lending/LoanDelete.cpp index fa3ccba7da..39b28f5110 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -1,7 +1,8 @@ -#include +#include // +#include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp similarity index 98% rename from src/libxrpl/tx/transactors/Lending/LoanManage.cpp rename to src/libxrpl/tx/transactors/lending/LoanManage.cpp index c066c4b3a9..d507ba5499 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -1,8 +1,9 @@ -#include +#include // +#include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp similarity index 98% rename from src/libxrpl/tx/transactors/Lending/LoanPay.cpp rename to src/libxrpl/tx/transactors/lending/LoanPay.cpp index 6b1f2acf7c..8739cb645a 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -1,11 +1,12 @@ -#include +#include // #include +#include #include #include #include -#include -#include +#include +#include #include @@ -56,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. @@ -67,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) { @@ -78,24 +83,32 @@ 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); @@ -517,9 +530,11 @@ LoanPay::doApply() 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; diff --git a/src/libxrpl/tx/transactors/Lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/Lending/LoanSet.cpp rename to src/libxrpl/tx/transactors/lending/LoanSet.cpp index 5e45bd5a9a..f046a24961 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -1,8 +1,10 @@ -#include +#include // +#include +#include #include #include -#include +#include namespace xrpl { @@ -62,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; @@ -80,17 +83,16 @@ 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( + 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) @@ -150,12 +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); @@ -266,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)) { @@ -474,7 +478,7 @@ LoanSet::doApply() { auto const ownerCount = borrowerSle->at(sfOwnerCount); auto const balance = - account_ == borrower ? mPriorBalance : borrowerSle->at(sfBalance).value().xrp(); + account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); if (balance < view.fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; } @@ -492,9 +496,11 @@ LoanSet::doApply() 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; @@ -513,9 +519,11 @@ LoanSet::doApply() 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/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp similarity index 97% rename from src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 90a28a2e7c..15a745b8dd 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -1,9 +1,10 @@ #include +#include #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -129,12 +130,12 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -180,19 +181,19 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; if (!so) { res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -248,21 +249,21 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; if (!bo) { res = nft::checkTrustlineAuthorized( ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } auto const res = nft::checkTrustlineDeepFrozen( ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get()); - if (res != tesSUCCESS) + 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); @@ -294,12 +297,12 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto res = nft::checkTrustlineAuthorized( ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; res = nft::checkTrustlineDeepFrozen( ctx.view, nftMinter, ctx.j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } } @@ -321,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; @@ -361,7 +364,7 @@ NFTokenAcceptOffer::transferNFToken( // 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 diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenBurn.cpp b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp similarity index 96% rename from src/libxrpl/tx/transactors/NFT/NFTokenBurn.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp index 65ef9e7542..6d9ee56c51 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenBurn.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp similarity index 95% rename from src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index ce2cdf83c5..df0561e076 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include #include diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenCreateOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp similarity index 91% rename from src/libxrpl/tx/transactors/NFT/NFTokenCreateOffer.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp index 6fe911ef5b..eaca8b3380 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenCreateOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -42,7 +42,7 @@ 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)) @@ -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/libxrpl/tx/transactors/NFT/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp similarity index 93% rename from src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index a32a659bbd..7bebbd0070 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -47,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) ? tfNFTokenMintMask : tfNFTokenMintMaskWithoutMutable - : 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; } @@ -76,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; } @@ -202,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 @@ -259,8 +266,10 @@ NFTokenMint::doApply() InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); if (nfTokenTemplate == nullptr) + { // Should never happen. return tecINTERNAL; // LCOV_EXCL_LINE + } auto const nftokenID = createNFTokenID( extractNFTokenFlagsFromTxFlags(ctx_.tx.getFlags()), @@ -277,7 +286,7 @@ NFTokenMint::doApply() }); if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken)); - ret != tesSUCCESS) + !isTesSuccess(ret)) return ret; if (ctx_.tx.isFieldPresent(sfAmount)) @@ -293,7 +302,7 @@ NFTokenMint::doApply() ctx_.tx[~sfExpiration], ctx_.tx.getSeqProxy(), nftokenID, - mPriorBalance, + preFeeBalance_, j_); !isTesSuccess(ter)) return ter; @@ -308,7 +317,7 @@ NFTokenMint::doApply() ownerCountAfter > ownerCountBefore) { if (auto const reserve = view().fees().accountReserve(ownerCountAfter); - mPriorBalance < reserve) + preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenModify.cpp b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp similarity index 89% rename from src/libxrpl/tx/transactors/NFT/NFTokenModify.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenModify.cpp index 316bcb0f27..1b2c9c2ab0 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenModify.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp @@ -1,7 +1,7 @@ #include #include -#include -#include +#include +#include namespace xrpl { @@ -13,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/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp similarity index 97% rename from src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp rename to src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp index 383929d54e..f7c12b5488 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp @@ -1,11 +1,15 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include -#include +#include #include #include @@ -101,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. @@ -218,9 +224,13 @@ changeTokenURI( return tecINTERNAL; // LCOV_EXCL_LINE if (uri) + { nftIter->setFieldVL(sfURI, *uri); + } else if (nftIter->isFieldPresent(sfURI)) + { nftIter->makeFieldAbsent(sfURI); + } view.update(page); return tesSUCCESS; @@ -343,7 +353,7 @@ removeToken( ApplyView& view, AccountID const& owner, uint256 const& nftokenID, - std::shared_ptr&& curr) + std::shared_ptr const& curr) { // We found a page, but the given NFT may not be in it. auto arr = curr->getFieldArray(sfNFTokens); @@ -368,9 +378,11 @@ removeToken( 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)); + } } return page2; @@ -396,11 +408,13 @@ removeToken( cnt--; if (cnt != 0) + { adjustOwnerCount( view, view.peek(keylet::account(owner)), cnt, beast::Journal{beast::Journal::getNullSink()}); + } return tesSUCCESS; } @@ -448,9 +462,13 @@ removeToken( // 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); } @@ -459,9 +477,13 @@ removeToken( { // Make our next page point to our previous page: if (prev) + { next->setFieldH256(sfPreviousPageMin, prev->key()); + } else + { next->makeFieldAbsent(sfPreviousPageMin); + } view.update(next); } @@ -526,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; } @@ -564,10 +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!"); + } } if (maxDeletableOffers == deletedOffersCount) @@ -619,8 +647,8 @@ 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)) @@ -696,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; } @@ -727,9 +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."); + } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); } @@ -760,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)) { @@ -891,7 +925,7 @@ tokenOfferCreatePreclaim( // unauthorized trustlines with balance auto const res = nft::checkTrustlineAuthorized(view, acctID, j, amount.asset().get()); - if (res != tesSUCCESS) + if (!isTesSuccess(res)) return res; } return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/DeleteOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp similarity index 77% rename from src/libxrpl/tx/transactors/DeleteOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index ebaffd334a..bde403f821 100644 --- a/src/libxrpl/tx/transactors/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,7 @@ DeleteOracle::preclaim(PreclaimContext const& ctx) } TER -DeleteOracle::deleteOracle( +OracleDelete::deleteOracle( ApplyView& view, std::shared_ptr const& sle, AccountID const& account, @@ -68,7 +70,7 @@ DeleteOracle::deleteOracle( } 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/libxrpl/tx/transactors/SetOracle.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/SetOracle.cpp rename to src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 140938e2d7..061dd16b0c 100644 --- a/src/libxrpl/tx/transactors/SetOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -1,10 +1,12 @@ #include #include +#include +#include #include #include #include #include -#include +#include namespace xrpl { @@ -17,7 +19,7 @@ tokenPairKey(STObject const& pair) } NotTEC -SetOracle::preflight(PreflightContext const& ctx) +OracleSet::preflight(PreflightContext const& ctx) { auto const& dataSeries = ctx.tx.getFieldArray(sfPriceDataSeries); if (dataSeries.empty()) @@ -38,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) @@ -77,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 @@ -92,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 @@ -111,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()) @@ -168,7 +180,7 @@ setPriceDataInnerObjTemplate(STObject& obj) } TER -SetOracle::doApply() +OracleSet::doApply() { auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]); diff --git a/src/libxrpl/tx/transactors/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp similarity index 96% rename from src/libxrpl/tx/transactors/DepositPreauth.cpp rename to src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 6857816d1f..60b8dcd823 100644 --- a/src/libxrpl/tx/transactors/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -1,10 +1,12 @@ #include -#include #include +#include +#include +#include #include #include #include -#include +#include #include @@ -147,7 +149,7 @@ DepositPreauth::doApply() STAmount const reserve{ view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -194,7 +196,7 @@ DepositPreauth::doApply() STAmount const reserve{ view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } @@ -249,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/libxrpl/tx/transactors/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp similarity index 94% rename from src/libxrpl/tx/transactors/Payment.cpp rename to src/libxrpl/tx/transactors/payment/Payment.cpp index b98bbf11bd..38765016b5 100644 --- a/src/libxrpl/tx/transactors/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -1,14 +1,18 @@ #include -#include #include +#include +#include +#include +#include #include #include +#include #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -33,15 +37,19 @@ getMaxSourceAmount( 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 @@ -231,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; @@ -282,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. @@ -293,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. @@ -435,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(); @@ -453,19 +465,19 @@ 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) + !isTesSuccess(ter)) return ter; if (auto err = verifyDepositPreauth( @@ -513,7 +525,7 @@ Payment::doApply() PaymentSandbox pv(&view()); auto res = accountSend(pv, account_, dstAccountID, amountDeliver, ctx_.journal); - if (res == tesSUCCESS) + if (isTesSuccess(res)) { pv.apply(ctx_.rawView()); @@ -524,7 +536,9 @@ Payment::doApply() ctx_.deliver(amountDeliver); } else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY) + { res = tecPATH_PARTIAL; + } return res; } @@ -544,16 +558,16 @@ 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) + JLOG(j_.trace()) << "Delay transaction: Insufficient funds: " << to_string(preFeeBalance_) << " / " << to_string(dstAmount.xrp() + mmm) << " (" << to_string(reserve) << ")"; @@ -601,7 +615,7 @@ Payment::doApply() } // 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/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp similarity index 93% rename from src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp rename to src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 861bb934be..565631b3fd 100644 --- a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -1,6 +1,7 @@ #include +#include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp similarity index 94% rename from src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.cpp rename to src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 15bb79b239..e8df51c9d9 100644 --- a/src/libxrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -1,7 +1,9 @@ -#include +#include // -#include #include +#include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp similarity index 99% rename from src/libxrpl/tx/transactors/Batch.cpp rename to src/libxrpl/tx/transactors/system/Batch.cpp index 67bb1f26e4..b2a111a182 100644 --- a/src/libxrpl/tx/transactors/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include namespace xrpl { @@ -105,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(); + } } } @@ -301,7 +305,7 @@ Batch::preflight(PreflightContext const& ctx) auto const innerAccount = stx.getAccountID(sfAccount); if (auto const preflightResult = xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, tapBATCH, ctx.j); - preflightResult.ter != tesSUCCESS) + !isTesSuccess(preflightResult.ter)) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " << "inner txn preflight failed: " << transHuman(preflightResult.ter) diff --git a/src/libxrpl/tx/transactors/Change.cpp b/src/libxrpl/tx/transactors/system/Change.cpp similarity index 99% rename from src/libxrpl/tx/transactors/Change.cpp rename to src/libxrpl/tx/transactors/system/Change.cpp index 07b6cf7abe..c84d056c24 100644 --- a/src/libxrpl/tx/transactors/Change.cpp +++ b/src/libxrpl/tx/transactors/system/Change.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include @@ -236,9 +236,13 @@ Change::applyAmendment() } if (newMajorities.empty()) + { amendmentObject->makeFieldAbsent(sfMajorities); + } else + { amendmentObject->setFieldArray(sfMajorities, newMajorities); + } view().update(amendmentObject); diff --git a/src/libxrpl/tx/transactors/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp similarity index 65% rename from src/libxrpl/tx/transactors/LedgerStateFix.cpp rename to src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index 28028b6f2b..0ce0720ba0 100644 --- a/src/libxrpl/tx/transactors/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -35,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. @@ -53,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/libxrpl/tx/transactors/CreateTicket.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp similarity index 91% rename from src/libxrpl/tx/transactors/CreateTicket.cpp rename to src/libxrpl/tx/transactors/system/TicketCreate.cpp index b1991c6e77..a064c158d1 100644 --- a/src/libxrpl/tx/transactors/CreateTicket.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -1,21 +1,23 @@ #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) @@ -25,7 +27,7 @@ CreateTicket::preflight(PreflightContext const& ctx) } 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)); @@ -51,7 +53,7 @@ CreateTicket::preclaim(PreclaimContext const& ctx) } TER -CreateTicket::doApply() +TicketCreate::doApply() { SLE::pointer const sleAccountRoot = view().peek(keylet::account(account_)); if (!sleAccountRoot) @@ -65,7 +67,7 @@ CreateTicket::doApply() XRPAmount const reserve = view().fees().accountReserve(sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); - if (mPriorBalance < reserve) + if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; } diff --git a/src/libxrpl/tx/transactors/Clawback.cpp b/src/libxrpl/tx/transactors/token/Clawback.cpp similarity index 97% rename from src/libxrpl/tx/transactors/Clawback.cpp rename to src/libxrpl/tx/transactors/token/Clawback.cpp index 789017e3ec..e8ba3cc0c5 100644 --- a/src/libxrpl/tx/transactors/Clawback.cpp +++ b/src/libxrpl/tx/transactors/token/Clawback.cpp @@ -1,10 +1,12 @@ #include +#include +#include #include #include #include #include #include -#include +#include namespace xrpl { @@ -168,9 +170,13 @@ 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&) { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp similarity index 95% rename from src/libxrpl/tx/transactors/MPT/MPTokenAuthorize.cpp rename to src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 846b25ca38..77551b372b 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -1,8 +1,11 @@ #include +#include +#include +#include #include #include #include -#include +#include namespace xrpl { @@ -161,7 +164,7 @@ MPTokenAuthorize::doApply() auto const& tx = ctx_.tx; return authorizeMPToken( ctx_.view(), - mPriorBalance, + preFeeBalance_, tx[sfMPTokenIssuanceID], account_, ctx_.journal, diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp similarity index 94% rename from src/libxrpl/tx/transactors/MPT/MPTokenIssuanceCreate.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index ae697aa670..58accf0271 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -1,7 +1,9 @@ #include +#include +#include #include #include -#include +#include namespace xrpl { @@ -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; } @@ -138,7 +140,7 @@ MPTokenIssuanceCreate::doApply() view(), j_, { - .priorBalance = mPriorBalance, + .priorBalance = preFeeBalance_, .account = account_, .sequence = tx.getSeqValue(), .flags = tx.getFlags(), diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp similarity index 92% rename from src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 4b4d7c53f5..8ec1f37886 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -1,7 +1,8 @@ #include +#include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp similarity index 95% rename from src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp rename to src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index 2ae6bde083..dc5827419b 100644 --- a/src/libxrpl/tx/transactors/MPT/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -123,7 +123,7 @@ 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(); @@ -158,9 +158,13 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) // For readability two separate `if` rather than `||` of two conditions 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 @@ -240,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 @@ -251,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) @@ -283,17 +299,25 @@ 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) diff --git a/src/libxrpl/tx/transactors/SetTrust.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp similarity index 93% rename from src/libxrpl/tx/transactors/SetTrust.cpp rename to src/libxrpl/tx/transactors/token/TrustSet.cpp index 268d626432..22588b977a 100644 --- a/src/libxrpl/tx/transactors/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -1,13 +1,15 @@ #include #include +#include +#include #include #include #include #include #include #include -#include -#include +#include +#include namespace { @@ -46,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; @@ -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(); @@ -167,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]; @@ -214,7 +216,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) // pass } else + { return tecNO_PERMISSION; + } } // In general, trust lines to pseudo accounts are not permitted, unless @@ -224,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))) @@ -233,14 +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)) { @@ -249,7 +259,9 @@ SetTrust::preclaim(PreclaimContext const& ctx) // else pass } else + { return tecPSEUDO_ACCOUNT; + } } // Checking all freeze/deep freeze flag invariants. @@ -298,7 +310,7 @@ SetTrust::preclaim(PreclaimContext const& ctx) } TER -SetTrust::doApply() +TrustSet::doApply() { TER terResult = tesSUCCESS; @@ -376,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; @@ -473,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) { @@ -570,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."; @@ -598,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/libxrpl/tx/transactors/Vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp similarity index 98% rename from src/libxrpl/tx/transactors/Vault/VaultClawback.cpp rename to src/libxrpl/tx/transactors/vault/VaultClawback.cpp index bce53c706d..a650aed310 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -8,7 +10,7 @@ #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; diff --git a/src/libxrpl/tx/transactors/Vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp similarity index 88% rename from src/libxrpl/tx/transactors/Vault/VaultCreate.cpp rename to src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 230651fd9b..0fc074bae2 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -10,9 +13,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace xrpl { @@ -50,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]) @@ -63,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; } @@ -137,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); @@ -147,7 +154,7 @@ 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()) @@ -197,16 +204,20 @@ 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); + authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal); !isTesSuccess(err)) return err; @@ -214,7 +225,7 @@ VaultCreate::doApply() if (txFlags & tfVaultPrivate) { if (auto const err = authorizeMPToken( - view(), mPriorBalance, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); + view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, account_); !isTesSuccess(err)) return err; } diff --git a/src/libxrpl/tx/transactors/Vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp similarity index 96% rename from src/libxrpl/tx/transactors/Vault/VaultDelete.cpp rename to src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 0b3aef19a8..1e2e46e165 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -1,11 +1,14 @@ #include +#include +#include +#include #include #include #include #include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp similarity index 93% rename from src/libxrpl/tx/transactors/Vault/VaultDeposit.cpp rename to src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index c3a9327f2a..04b249d211 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -1,5 +1,8 @@ -#include #include +#include +#include +#include +#include #include #include #include @@ -9,8 +12,8 @@ #include #include #include -#include -#include +#include +#include namespace xrpl { @@ -101,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 +151,7 @@ VaultDeposit::doApply() if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner)) { if (auto const err = enforceMPTokenAuthorization( - ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_); + ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_); !isTesSuccess(err)) return err; } @@ -156,7 +161,7 @@ VaultDeposit::doApply() if (!view().exists(keylet::mptoken(mptIssuanceID, account_))) { if (auto const err = authorizeMPToken( - view(), mPriorBalance, mptIssuanceID->value(), account_, ctx_.journal); + view(), preFeeBalance_, mptIssuanceID->value(), account_, ctx_.journal); !isTesSuccess(err)) return err; } @@ -169,7 +174,7 @@ VaultDeposit::doApply() 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, @@ -196,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."; diff --git a/src/libxrpl/tx/transactors/Vault/VaultSet.cpp b/src/libxrpl/tx/transactors/vault/VaultSet.cpp similarity index 99% rename from src/libxrpl/tx/transactors/Vault/VaultSet.cpp rename to src/libxrpl/tx/transactors/vault/VaultSet.cpp index c27c9aae3c..532edcdcef 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultSet.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultSet.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/Vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp similarity index 96% rename from src/libxrpl/tx/transactors/Vault/VaultWithdraw.cpp rename to src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 3e468df2fc..e4c671b462 100644 --- a/src/libxrpl/tx/transactors/Vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -1,5 +1,7 @@ -#include #include +#include +#include +#include #include #include #include @@ -7,7 +9,7 @@ #include #include #include -#include +#include namespace xrpl { @@ -144,7 +146,9 @@ VaultWithdraw::doApply() assetsWithdrawn = *maybeAssets; } else + { return tefINTERNAL; // LCOV_EXCL_LINE + } } catch (std::overflow_error const&) { @@ -230,7 +234,7 @@ VaultWithdraw::doApply() associateAsset(*vault, vaultAsset); return doWithdraw( - view(), ctx_.tx, account_, dstAcct, vaultAccount, mPriorBalance, assetsWithdrawn, j_); + view(), ctx_.tx, account_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_); } } // namespace xrpl diff --git a/src/test/app/AMMCalc_test.cpp b/src/test/app/AMMCalc_test.cpp index e6e68a95ce..c1068b08ba 100644 --- a/src/test/app/AMMCalc_test.cpp +++ b/src/test/app/AMMCalc_test.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include @@ -43,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; @@ -120,7 +124,9 @@ class AMMCalc_test : public beast::unit_test::suite break; } else + { return std::nullopt; + } } return rates; } @@ -398,13 +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; + } 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 114a96b081..9033fe2bdd 100644 --- a/src/test/app/AMMClawback_test.cpp +++ b/src/test/app/AMMClawback_test.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace xrpl { namespace test { @@ -531,11 +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})); + } else + { 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)); @@ -554,17 +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})); + } else + { 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)); @@ -575,19 +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})); + } if (!features[fixAMMv1_3]) + { 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)); @@ -597,27 +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})); + } 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)); + } 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)); + } // gw clawback 4000 USD, exceeding the current balance. We // will clawback all. @@ -685,27 +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})); + } else + { 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})); + } else + { 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})); + } else + { BEAST_EXPECT( amm2.expectBalances(EUR(3000), XRP(9000), IOUAmount{5196152422706631, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -725,28 +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))); + } 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})); + } else if (!features[fixAMMClawbackRounding]) + { 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})); + } 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})); @@ -762,32 +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})); + } else if (!features[fixAMMClawbackRounding]) + { 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)); @@ -797,32 +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})); + } aliceXrpBalance = env.balance(alice, XRP); if (!features[fixAMMv1_3] && !features[fixAMMClawbackRounding]) + { 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})); + } else if (features[fixAMMv1_3] && features[fixAMMClawbackRounding]) + { 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 @@ -835,35 +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})); + } 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})); + } else if (!features[fixAMMClawbackRounding]) + { 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 @@ -903,14 +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})); + } else if (!features[fixAMMClawbackRounding]) + { 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})); + } // gw2 claw back 2000 EUR from bob in amm2, which exceeds bob's // balance. All bob's lptokens will be consumed, which corresponds @@ -933,14 +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})); + } else if (!features[fixAMMClawbackRounding]) + { 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})); + } } } @@ -999,38 +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})); + } else + { 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})); + } else + { 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})); + } else + { 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))); @@ -1044,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))); @@ -1070,11 +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)); + } else + { BEAST_EXPECT( env.balance(bob, EUR) == STAmount(EUR, UINT64_C(4999999999999999), -12)); + } env.require(balance(carol, USD(3000))); env.require(balance(carol, EUR(2750))); @@ -1082,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))); @@ -1134,25 +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})); + } else + { 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})); + } else + { 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})); + } else + { BEAST_EXPECT( amm.expectBalances(USD(13000), XRP(2600), IOUAmount{5813776741499451, -9})); + } env.close(); auto aliceXrpBalance = env.balance(alice, XRP); @@ -1162,27 +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})); + } else + { 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(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})); + } else + { 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))); @@ -1237,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)); @@ -1254,10 +1458,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)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); @@ -1279,10 +1487,14 @@ 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, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. @@ -1300,10 +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, EUR) == EUR(7750)); } @@ -1460,17 +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})); + } else + { 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)); @@ -1482,30 +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})); + } else if (!features[fixAMMClawbackRounding]) + { 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})); + } 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})); @@ -1518,30 +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})); + } else if (!features[fixAMMClawbackRounding]) + { 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})); + } 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)); @@ -1791,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 @@ -1813,10 +2073,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)); + } // 250 EUR goes back to carol. BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750)); @@ -1838,10 +2102,14 @@ 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, EUR) == EUR(7750)); // gw clawback all USD from alice and set tfClawTwoAssets. @@ -1859,10 +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, EUR) == EUR(7750)); } } @@ -1911,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))); + } else + { 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 diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 06f82418da..93ad92af3d 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -12,8 +12,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -1971,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; }); @@ -2633,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)); diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index fc801be816..a0a38b9791 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -7,13 +7,14 @@ #include #include +#include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include @@ -739,16 +740,20 @@ 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, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); @@ -766,14 +771,18 @@ 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, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN)); ammAlice.deposit( @@ -837,14 +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)); + } else + { amm.deposit( bob, XRP(10), std::nullopt, std::nullopt, std::nullopt, ter(tesSUCCESS)); + } } // Insufficient XRP balance @@ -1687,8 +1700,10 @@ private: 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})); + } }, std::nullopt, 0, @@ -1707,8 +1722,10 @@ private: 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})); + } }, std::nullopt, 0, @@ -1932,11 +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})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{9'000'000'001}, USD(10'000), IOUAmount{9'486'832'98050514, -8})); + } }, std::nullopt, 0, @@ -1953,7 +1974,7 @@ private: // Withdraw all tokens. testAMM([&](AMM& ammAlice, Env& env) { env(trust(carol, STAmount{ammAlice.lptIssue(), 10'000})); - // Can SetTrust only for AMM LP tokens + // Can TrustSet only for AMM LP tokens env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), ter(tecNO_PERMISSION)); env.close(); @@ -2000,11 +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())); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens())); + } BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0})); }, std::nullopt, @@ -2057,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})); }, @@ -2083,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, @@ -2142,11 +2179,15 @@ private: // 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})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4})); + } }, std::nullopt, 0, @@ -2342,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)); @@ -2670,11 +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})); + } else + { 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(); @@ -2701,11 +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})); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2})); + } }, std::nullopt, 0, @@ -2735,10 +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)); + } // Discounted trade for (int i = 0; i < 10; ++i) { @@ -2774,15 +2827,19 @@ private: 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)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( XRPAmount{13'000'000'003}, STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens)); + } } ammTokens = ammAlice.getLPTokensBalance(); // Trade with the fee @@ -2818,51 +2875,67 @@ private: else { if (!features[fixAMMv1_3]) + { 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)); + } // 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)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( 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)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( 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)); + } else + { BEAST_EXPECT(ammAlice.expectBalances( 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))); @@ -2896,13 +2969,17 @@ private: // 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)); + } else if (!features[fixAMMv1_3]) + { BEAST_EXPECT( env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620544), -11)); + } ammTokens = ammAlice.getLPTokensBalance(); for (int i = 0; i < 10; ++i) { @@ -3089,7 +3166,7 @@ private: 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)); } { @@ -4339,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(); @@ -4397,17 +4474,21 @@ private: prep( [&](Env& env) { if (!features[fixAMMv1_1]) + { 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}), txflags(tfPassive)); + } }, [&](Env& env) { BEAST_EXPECT( @@ -4528,11 +4609,15 @@ private: // 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 @@ -4543,25 +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)); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { 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)); + } // 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, @@ -4808,49 +4905,73 @@ 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})); + } else if (features[fixAMMv1_3]) + { BEAST_EXPECT(ammAlice.expectBalances( 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(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})); + } 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})); + } else if (features[fixAMMv1_1] && !features[fixAMMv1_3]) + { 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})); + } else if (features[fixAMMv1_3]) + { 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( @@ -4988,7 +5109,7 @@ 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, @@ -5154,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); @@ -5227,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); @@ -5252,6 +5373,7 @@ private: if (rates.first == 1.5) { if (!features[fixAMMv1_1]) + { BEAST_EXPECT(expectOffers( env, ed, @@ -5259,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, @@ -5267,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, @@ -5278,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, @@ -5286,6 +5414,7 @@ private: {{Amounts{ STAmount{ETH, UINT64_C(325'299461620748), -12}, STAmount{USD, UINT64_C(243'974596215561), -12}}}})); + } } } else if (i == 2) @@ -5426,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); @@ -5701,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) { @@ -5741,7 +5878,7 @@ private: takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)}; } - else if (isXRP(poolOut)) + if (isXRP(poolOut)) { auto const takerGets = STAmount{xrpIssue(), 1}; return Amounts{ @@ -5871,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; @@ -6117,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)); @@ -6636,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); } @@ -6653,9 +6798,13 @@ private: auto const res = root2(amount * amount2); if (shouldFail) + { BEAST_EXPECT(res < lptBalance); + } else + { BEAST_EXPECT(res >= lptBalance); + } } void diff --git a/src/test/app/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp index 87c6474be2..7af917a0b9 100644 --- a/src/test/app/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -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) @@ -307,9 +308,13 @@ public: // 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); + } } }; diff --git a/src/test/app/AmendmentTable_test.cpp b/src/test/app/AmendmentTable_test.cpp index 3b14c8b622..d8431e5696 100644 --- a/src/test/app/AmendmentTable_test.cpp +++ b/src/test/app/AmendmentTable_test.cpp @@ -390,7 +390,7 @@ 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")); @@ -404,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() @@ -526,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; @@ -719,7 +719,7 @@ public: 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_) @@ -766,7 +766,7 @@ public: 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 diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index f429299f01..ec12fbb21f 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace xrpl { namespace test { @@ -2259,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__); } @@ -2273,7 +2273,7 @@ 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(), @@ -2292,7 +2292,7 @@ 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(), @@ -2308,7 +2308,7 @@ class Batch_test : public beast::unit_test::suite STParsedJSONObject parsed("test", jt.jv); Serializer s; - parsed.object->add(s); + parsed.object->add(s); // NOLINT(bugprone-unchecked-optional-access) submitAndValidate( "Fully signed", s.slice(), __LINE__, std::nullopt, std::nullopt, !withBatch); } @@ -2322,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(), @@ -2347,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(), @@ -3280,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(); @@ -3373,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 diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 05c0870329..671f8aab1a 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -272,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); @@ -528,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); @@ -562,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); @@ -592,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); @@ -663,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); @@ -688,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); @@ -755,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); } @@ -838,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); } @@ -890,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); } @@ -945,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); } @@ -1018,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 @@ -1655,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); } } @@ -1815,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))); @@ -1852,7 +1853,7 @@ class Check_test : public beast::unit_test::suite } // Operators to make using the class more convenient. - operator Account const() const + operator Account() const { return acct; } diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 3c3d8b6df8..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 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 f478bc89ee..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_; @@ -33,7 +33,7 @@ public: { auto onFetch = [&](error_code const& errorCode, endpoint_type const& endpoint, - xrpl::detail::response_type&& resp) { + xrpl::detail::response_type const& resp) { BEAST_EXPECT(!errorCode); lastEndpoint_ = endpoint; resolved_[endpoint.address().to_string()]++; @@ -76,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 31a394eeeb..2618a95d0d 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -45,11 +45,9 @@ 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"}; @@ -92,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); @@ -204,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 @@ -1614,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. { @@ -1660,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 { @@ -1684,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/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 547d9d8b62..ad401499bc 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -1283,8 +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()); + } } BEAST_EXPECT(std::ranges::is_sorted(readCreds)); diff --git a/src/test/app/Discrepancy_test.cpp b/src/test/app/Discrepancy_test.cpp index 08a8a01753..3f808b37d8 100644 --- a/src/test/app/Discrepancy_test.cpp +++ b/src/test/app/Discrepancy_test.cpp @@ -93,11 +93,17 @@ 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) { @@ -108,11 +114,15 @@ class Discrepancy_test : public beast::unit_test::suite ? node[sfFinalFields.fieldName] : node[sfNewFields.fieldName]; if (prevFields) + { sumPrev += beast::lexicalCastThrow( prevFields[sfBalance.fieldName].asString()); + } if (finalFields) + { sumFinal += beast::lexicalCastThrow( finalFields[sfBalance.fieldName].asString()); + } } } // the difference in balances (final and prev) should be the diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 5cbeb0b94a..280356c494 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -1258,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))); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 4538f2d751..2e6ef718d6 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1250,7 +1250,7 @@ struct Escrow_test : public beast::unit_test::suite fee(baseFee)); auto const pf = preflight(env.app(), env.current()->rules(), *jtx.stx, tapNONE, env.journal); - BEAST_EXPECT(pf.ter == tesSUCCESS); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(1000)); @@ -1260,7 +1260,7 @@ 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); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -1270,7 +1270,7 @@ 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); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(baseFee)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 48f94943d8..f65a00530b 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/test/app/FixNFTokenPageLinks_test.cpp b/src/test/app/FixNFTokenPageLinks_test.cpp index 67dadac5e1..8022c52a5d 100644 --- a/src/test/app/FixNFTokenPageLinks_test.cpp +++ b/src/test/app/FixNFTokenPageLinks_test.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace xrpl { diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index c56cef39f8..1e9514f756 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -196,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); @@ -213,12 +215,17 @@ struct Flow_test : public beast::unit_test::suite txflags(tfNoRippleDirect)); env.require(balance(bob, USDA(90))); if (bobAliceQOut > 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}) @@ -472,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; }); diff --git a/src/test/app/HashRouter_test.cpp b/src/test/app/HashRouter_test.cpp index 753922a67d..58bace3ef2 100644 --- a/src/test/app/HashRouter_test.cpp +++ b/src/test/app/HashRouter_test.cpp @@ -231,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 @@ -243,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/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index a01026c8ef..8372bb340b 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include #include @@ -71,7 +74,7 @@ class Invariants_test : public beast::unit_test::suite Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None) { - return doInvariantCheck( + doInvariantCheck( test::jtx::Env(*this, defaultAmendments()), expect_logs, precheck, @@ -105,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, @@ -139,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:") || @@ -394,22 +398,21 @@ class Invariants_test : public beast::unit_test::suite 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)); @@ -2261,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); @@ -2290,8 +2295,10 @@ 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; + } ac.update(sleVault); if (args.sharesTotal) @@ -2324,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) @@ -2348,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) @@ -3499,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()); diff --git a/src/test/app/LPTokenTransfer_test.cpp b/src/test/app/LPTokenTransfer_test.cpp index 15a8837c4f..265ff7d2ef 100644 --- a/src/test/app/LPTokenTransfer_test.cpp +++ b/src/test/app/LPTokenTransfer_test.cpp @@ -299,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(); diff --git a/src/test/app/LedgerLoad_test.cpp b/src/test/app/LedgerLoad_test.cpp index 0a1ffc024a..46c372923e 100644 --- a/src/test/app/LedgerLoad_test.cpp +++ b/src/test/app/LedgerLoad_test.cpp @@ -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(); diff --git a/src/test/app/LedgerMaster_test.cpp b/src/test/app/LedgerMaster_test.cpp index 50a8a2ed02..b48579043e 100644 --- a/src/test/app/LedgerMaster_test.cpp +++ b/src/test/app/LedgerMaster_test.cpp @@ -78,6 +78,7 @@ 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" @@ -88,6 +89,7 @@ class LedgerMaster_test : public beast::unit_test::suite 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" diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 5568a90d03..54c12b465a 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -355,9 +355,13 @@ struct TestPeerSet : public PeerSet { int dropRate = 0; if (behavior == PeerSetBehavior::Drop50) + { dropRate = 50; + } else if (behavior == PeerSetBehavior::DropAll) + { dropRate = 100; + } if ((rand() % 100 + 1) <= dropRate) return; @@ -497,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; @@ -968,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); @@ -1095,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); @@ -1128,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 diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index aae60a252a..24661c18fb 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -8,9 +8,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 31cfb5c231..361f70209f 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include namespace xrpl { namespace test { @@ -91,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; @@ -674,38 +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); + 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"; @@ -713,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()) { @@ -729,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 @@ -817,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 = [&]() { @@ -837,12 +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); @@ -853,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) @@ -878,26 +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)); // 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) @@ -906,59 +901,51 @@ 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); + 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); + 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); + 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); + ter(tecFROZEN)); // preclaim:: tecFROZEN (deep frozen) - env(fclear(issuer, asfGlobalFreeze), THISLINE); - env(trust(issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze), 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), - THISLINE); + 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) @@ -977,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 { @@ -995,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(); } @@ -1007,30 +991,31 @@ 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) { @@ -1045,21 +1030,21 @@ class LoanBroker_test : public beast::unit_test::suite 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); + 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)); } } @@ -1082,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); @@ -1356,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 = [&]() { @@ -1371,20 +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; @@ -1395,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 @@ -1753,7 +1736,7 @@ 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)})); diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 607e84abeb..171e2eddb5 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -6,9 +6,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include @@ -81,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 @@ -150,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 @@ -232,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; @@ -698,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 @@ -773,10 +777,12 @@ protected: 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})); + } } void @@ -826,6 +832,7 @@ protected: roundPeriodicPayment(broker.asset, state.periodicPayment, state.loanScale)}; if (!showStepBalances) + { log << currencyLabel << " Payment components: " << "Payments remaining, " << "rawInterest, rawPrincipal, " @@ -833,6 +840,7 @@ protected: << "trackedValueDelta, trackedPrincipalDelta, " "trackedInterestDelta, trackedMgmtFeeDelta, special" << std::endl; + } // Include the service fee STAmount const totalDue = @@ -947,18 +955,21 @@ protected: (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; + << 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}; @@ -1016,8 +1027,10 @@ protected: { 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: " @@ -1091,8 +1104,10 @@ 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) @@ -1127,7 +1142,7 @@ protected: 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); @@ -1179,8 +1194,10 @@ protected: 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); @@ -1454,11 +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 @@ -1788,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; }(); @@ -1807,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)); @@ -1821,16 +1842,14 @@ protected: }; 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 @@ -1903,7 +1922,6 @@ 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)); @@ -2512,12 +2530,13 @@ protected: << ", " << 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()}; @@ -2569,8 +2588,10 @@ protected: 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(); @@ -3304,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); @@ -3328,8 +3348,7 @@ protected: fee(env.current()->fees().base * 5), paymentTotal(2), paymentInterval(3600 * 24), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); }, nullptr); } @@ -3728,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)); @@ -3740,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(); } @@ -3765,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); @@ -4220,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); @@ -4229,22 +4246,21 @@ 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 { @@ -4253,42 +4269,36 @@ protected: { 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, lender["IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze)); env(trust( - issuer, - (*pseudoBroker)["IOU"](1'000), - *pseudoBroker, - tfSetFreeze | tfSetDeepFreeze), - THISLINE); + 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 @@ -4640,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(); } @@ -5346,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); @@ -5943,7 +5953,7 @@ protected: 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); @@ -6014,7 +6024,7 @@ protected: 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); @@ -6220,7 +6230,7 @@ protected: 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); @@ -6257,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); @@ -6380,7 +6394,7 @@ protected: 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); @@ -6436,7 +6450,7 @@ protected: 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); @@ -7091,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 5a46331aa9..5a90a3a71e 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -1840,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. diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index 294d5210d9..609b7bdb89 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -99,7 +100,10 @@ public: 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; @@ -248,7 +252,6 @@ 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); @@ -348,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())); @@ -424,24 +429,15 @@ public: // 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/" @@ -456,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); } { @@ -584,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) @@ -621,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 @@ -637,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 @@ -726,15 +730,16 @@ public: 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 diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index adfcb12ee0..f96bfba2d2 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -486,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 { diff --git a/src/test/app/NFTokenAuth_test.cpp b/src/test/app/NFTokenAuth_test.cpp index 6fc92bc6cd..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 { diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index c75e22e954..25f5a0f880 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -96,14 +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; + } if (tokenCount > 1) + { std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() << std::endl; + } } } } @@ -256,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. @@ -273,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 diff --git a/src/test/app/NFTokenDir_test.cpp b/src/test/app/NFTokenDir_test.cpp index c186a3d9e5..73698f1f89 100644 --- a/src/test/app/NFTokenDir_test.cpp +++ b/src/test/app/NFTokenDir_test.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include @@ -57,14 +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; + } if (tokenCount > 1) + { std::cout << "last: " << state[i][sfNFTokens.jsonName][tokenCount - 1].toStyledString() << std::endl; + } } } } diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 6d30fc6854..ba61830758 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include @@ -171,7 +171,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite 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; @@ -269,7 +271,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite auto oneCheck = [this](char const* type, std::uint32_t found, std::uint32_t exp, int line) { if (found == exp) + { pass(); + } else { std::stringstream ss; @@ -2171,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); @@ -2347,7 +2355,9 @@ 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; @@ -2490,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; @@ -2905,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; @@ -2930,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. @@ -4791,7 +4801,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite 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(); @@ -6976,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; @@ -6988,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; @@ -6999,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; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index d22d907322..62edc844c7 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -382,7 +382,7 @@ public: ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -507,7 +507,7 @@ public: ter(expectedTer)); env.close(); - if (expectedTer == tesSUCCESS) + if (isTesSuccess(expectedTer)) { env.require(offers(carol, 0)); env.require(balance(carol, @@ -1208,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; @@ -1926,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"}; @@ -2281,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()); @@ -2592,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()); @@ -2772,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()); @@ -2984,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); @@ -3160,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()); @@ -3314,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)); @@ -3333,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)); @@ -4593,8 +4597,10 @@ 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])); + } }); // first offer @@ -4749,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)); @@ -4931,7 +4937,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance -= USD(100); takerUSDBalance += USD(100); @@ -4948,7 +4954,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); @@ -4965,7 +4971,7 @@ public: makerXRPBalance -= txfee(env, 1); takerXRPBalance -= txfee(env, 1); - if (err == tesSUCCESS) + if (isTesSuccess(err)) { makerUSDBalance += USD(100); takerUSDBalance -= USD(100); 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 c2de4f3f08..9315370809 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -206,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); } } @@ -1516,7 +1518,7 @@ public: } else { - BEAST_EXPECT(st.size() == 0); + BEAST_EXPECT(st.empty()); BEAST_EXPECT(equal(sa, XRP(0))); } }; diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index b217bb3687..5322349797 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -401,18 +401,23 @@ 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)); + // 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)); @@ -1010,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); + } } } @@ -1244,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) { diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 2cc2a188c3..70444637bf 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include @@ -183,8 +183,10 @@ class ElementComboIter hasAny(std::initializer_list sb) const { for (auto const s : sb) + { if (has(s)) return true; + } return false; } @@ -194,8 +196,10 @@ class ElementComboIter size_t result = 0; for (auto const s : sb) + { if (has(s)) result++; + } return result; } @@ -262,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 { @@ -271,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{})); + } } }; @@ -368,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); } @@ -407,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) @@ -636,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())}); @@ -654,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)); } } @@ -921,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})); } @@ -949,7 +965,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}, B{USD.issue(), xrpIssue(), std::nullopt}, XRPS{bob})); } diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index 8ecc66911c..2817bc0502 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -143,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; @@ -524,7 +525,8 @@ 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)), @@ -543,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 @@ -553,8 +556,10 @@ 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 @@ -889,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)); @@ -902,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 diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 39ce85d23d..c4bbd80e74 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -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); @@ -479,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/RCLValidations_test.cpp b/src/test/app/RCLValidations_test.cpp index 140ba9f1d7..a7941ea64f 100644 --- a/src/test/app/RCLValidations_test.cpp +++ b/src/test/app/RCLValidations_test.cpp @@ -112,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}); + } } } diff --git a/src/test/app/ReducedOffer_test.cpp b/src/test/app/ReducedOffer_test.cpp index 737d73e462..d0cf1b5324 100644 --- a/src/test/app/ReducedOffer_test.cpp +++ b/src/test/app/ReducedOffer_test.cpp @@ -92,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; diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index e323e61132..d7535d5578 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -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)); diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index 97a9a38e77..d7d3e281f4 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -190,7 +190,7 @@ public: 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()); + !getHash(ledgers[i]).empty()); } ledgerCheck(env, deleteInterval + 1, 2); @@ -229,7 +229,7 @@ public: store.getLastRotated() == lastRotated || i == lastRotated + deleteInterval - 2); BEAST_EXPECT( goodLedger(env, ledgers[i], std::to_string(i), true) && - getHash(ledgers[i]).length()); + !getHash(ledgers[i]).empty()); } store.rendezvous(); diff --git a/src/test/app/TheoreticalQuality_test.cpp b/src/test/app/TheoreticalQuality_test.cpp index 9b6d86dd5f..b4f6f3697d 100644 --- a/src/test/app/TheoreticalQuality_test.cpp +++ b/src/test/app/TheoreticalQuality_test.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace xrpl { namespace test { @@ -25,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])} { @@ -45,6 +47,7 @@ struct RippleCalcTestParams { assert(!pe.isMember(jss::currency) && !pe.isMember(jss::issuer)); p.emplace_back( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *parseBase58(pe[jss::account].asString()), std::nullopt, std::nullopt); @@ -54,9 +57,15 @@ struct RippleCalcTestParams 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 @@ -144,7 +153,7 @@ public: jtx::Env& env, jtx::Account const& acc, jtx::Account const& peer, - Currency const& currency) + Currency const& currency) const { using namespace jtx; IOU const iou{acc, currency}; @@ -237,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 @@ -255,7 +264,8 @@ class TheoreticalQuality_test : public beast::unit_test::suite for (auto const& strand : sr.second) { - Quality const theoreticalQ = *qualityUpperBound(sb, strand); + 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); @@ -488,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 760cce88be..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 @@ -286,11 +286,15 @@ class Ticket_test : public beast::unit_test::suite 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)) diff --git a/src/test/app/TrustAndBalance_test.cpp b/src/test/app/TrustAndBalance_test.cpp index 413ef6dd78..3a2aa8e7c9 100644 --- a/src/test/app/TrustAndBalance_test.cpp +++ b/src/test/app/TrustAndBalance_test.cpp @@ -343,12 +343,16 @@ 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) { diff --git a/src/test/app/SetTrust_test.cpp b/src/test/app/TrustSet_test.cpp similarity index 97% rename from src/test/app/SetTrust_test.cpp rename to src/test/app/TrustSet_test.cpp index 06aebcd915..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); @@ -223,9 +227,9 @@ public: } void - testTicketSetTrust(FeatureBitset features) + testTicketTrustSet(FeatureBitset features) { - testcase("SetTrust using a ticket"); + testcase("TrustSet using a ticket"); using namespace jtx; @@ -270,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}; @@ -284,8 +288,10 @@ public: badFlag *= 2) { if (badFlag & tfTrustSetMask) + { env(trust(alice, gw["USD"](100), static_cast(badFlag)), ter(temINVALID_FLAG)); + } } // trust amount can't be XRP @@ -405,7 +411,7 @@ public: void testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct) { - testcase << "SetTrust " << (createQuality ? "creates" : "removes") + testcase << "TrustSet " << (createQuality ? "creates" : "removes") << " quality of trustline for " << (createOnHighAcct ? "high" : "low") << " account"; @@ -570,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); @@ -593,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 94db50842a..59db7e0d5d 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -722,6 +722,7 @@ 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()); @@ -2309,7 +2310,7 @@ public: 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); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2321,7 +2322,7 @@ public: 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); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2331,7 +2332,7 @@ 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); + BEAST_EXPECT(isTesSuccess(pf.ter)); BEAST_EXPECT(!pf.consequences.isBlocker()); BEAST_EXPECT(pf.consequences.fee() == drops(10)); BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0)); @@ -2756,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); { @@ -3871,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 @@ -3939,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 diff --git a/src/test/app/ValidatorKeys_test.cpp b/src/test/app/ValidatorKeys_test.cpp index 32f6305a37..9efe318662 100644 --- a/src/test/app/ValidatorKeys_test.cpp +++ b/src/test/app/ValidatorKeys_test.cpp @@ -1,4 +1,5 @@ #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 @@ -65,17 +63,21 @@ public: // Keys/ID when using [validation_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); { @@ -92,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()); @@ -119,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 61d71b1883..32e899ad95 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -56,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; @@ -74,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; @@ -242,6 +245,7 @@ private: 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)); @@ -297,9 +301,9 @@ private: auto const localSigningPublic = parseBase58(TokenType::NodePublic, cfgKeys.front()); - BEAST_EXPECT(trustedKeys->load(*localSigningPublic, cfgKeys, emptyCfgPublishers)); - + 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)); @@ -332,6 +336,7 @@ private: app.config().legacy("database_path"), env.journal); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifests.applyManifest(*deserializeManifest(cfgManifest)); BEAST_EXPECT(trustedKeys->load(localSigningPublicOuter, cfgKeys, emptyCfgPublishers)); @@ -414,6 +419,7 @@ private: 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, @@ -452,6 +458,7 @@ private: 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, @@ -950,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); } @@ -959,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); } @@ -968,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); @@ -984,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); @@ -1034,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)); @@ -1064,7 +1079,9 @@ private: BEAST_EXPECT(trustedKeysOuter->trusted(*valKey)); } else + { fail(); + } } changes = trustedKeysOuter->updateTrusted( @@ -1110,6 +1127,7 @@ private: masterPublic, masterPrivate, signingPublic1, signingKeys1.second, 1)); BEAST_EXPECT( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) manifestsOuter.applyManifest(std::move(*m1)) == ManifestDisposition::accepted); BEAST_EXPECT(trustedKeysOuter->listed(masterPublic)); BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic)); @@ -1123,6 +1141,7 @@ private: 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)); @@ -1137,9 +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); + // NOLINTEND(bugprone-unchecked-optional-access) + BEAST_EXPECT(manifestsOuter.getSigningKey(masterPublic) == masterPublic); BEAST_EXPECT(manifestsOuter.revoked(masterPublic)); @@ -1250,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)); @@ -1446,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)); @@ -1601,14 +1631,28 @@ private: 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); + 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()); @@ -1649,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()); @@ -1673,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)); @@ -1701,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) @@ -2004,7 +2056,7 @@ private: { for (auto& n : nUnl_temp) { - if (nUnl.find(n) == nUnl.end()) + if (!nUnl.contains(n)) return false; } validators->updateTrusted( @@ -2322,7 +2374,7 @@ private: 1, 8, 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 @@ -2355,12 +2407,14 @@ private: 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 == + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) sha512Half(*expected.manifest, expected.blob, expected.signature, 1)); } @@ -2374,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 @@ -2473,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 @@ -2510,7 +2564,12 @@ private: pubSigningKeys.second, i < countRevoked ? revoked : 1)); publishers.push_back( - Publisher{i < countRevoked, publisherPublic, pubSigningKeys, manifest}); + Publisher{ + .revoked = i < countRevoked, + .pubKey = publisherPublic, + .signingKeys = pubSigningKeys, + .manifest = manifest, + }); } std::vector const emptyCfgKeys; @@ -2652,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) @@ -2920,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) @@ -3358,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) @@ -3540,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 5fc290eda5..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; @@ -225,8 +225,10 @@ 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()); @@ -307,8 +309,10 @@ 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)); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7ae9faf18f..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 @@ -67,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(); @@ -1331,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}); @@ -2277,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(); @@ -2623,13 +2639,13 @@ 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); + env(tx, ter{terNO_RIPPLE}); env.close(); } @@ -2637,14 +2653,14 @@ class Vault_test : public beast::unit_test::suite PrettyAsset shares = issuanceId(keylet); auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}); - env(tx1, THISLINE); + env(tx1); env.close(); // Charlie cannot receive funds 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(); { @@ -2657,7 +2673,7 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); } - env(pay(owner, charlie, shares(100)), THISLINE); + env(pay(owner, charlie, shares(100))); env.close(); // Charlie cannot withdraw @@ -2666,16 +2682,16 @@ class Vault_test : public beast::unit_test::suite 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}); @@ -3349,7 +3365,7 @@ class Vault_test : public beast::unit_test::suite 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)); @@ -4500,131 +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() { @@ -4656,7 +4547,7 @@ 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); @@ -4666,8 +4557,7 @@ class Vault_test : public beast::unit_test::suite env(vault.deposit( {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet); @@ -4680,14 +4570,13 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + 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); @@ -4700,8 +4589,7 @@ 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 @@ -4711,13 +4599,12 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}), - ter(tecNO_PERMISSION), - THISLINE); + 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); @@ -4735,19 +4622,23 @@ class Vault_test : public beast::unit_test::suite { 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(), }), - // 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); + expectedTer); env.close(); } @@ -4766,8 +4657,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = share(1).value(), }), - ter(tecLIMIT_EXCEEDED), - THISLINE); + ter(tecLIMIT_EXCEEDED)); env.close(); } @@ -4783,8 +4673,7 @@ class Vault_test : public beast::unit_test::suite }), // when owner is issuer implicit clawback fails asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) - : ter(tecWRONG_ASSET), - THISLINE); + : ter(tecWRONG_ASSET)); env.close(); } @@ -4804,8 +4693,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); } { @@ -4822,8 +4710,7 @@ class Vault_test : public beast::unit_test::suite .holder = owner, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); env.close(); } @@ -4840,8 +4727,7 @@ class Vault_test : public beast::unit_test::suite .holder = owner, .amount = share(vaultShareBalance(vaultKeylet)).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); // Now the vault is empty, clawback again fails env(vault.clawback({ @@ -4849,8 +4735,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = owner, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); env.close(); } }; @@ -4908,15 +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); + ter(tesSUCCESS)); env.close(); return std::make_pair(vault, vaultKeylet); @@ -4939,16 +4823,14 @@ 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; } @@ -4965,8 +4847,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset2(1).value(), }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { @@ -4979,8 +4860,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = issuer, }), - ter(tecWRONG_ASSET), - THISLINE); + ter(tecWRONG_ASSET)); } { @@ -4992,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, @@ -5001,8 +4880,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -5013,8 +4891,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = issuer, }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -5032,8 +4909,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = share(1).value(), }), - ter(tecNO_PERMISSION), - THISLINE); + ter(tecNO_PERMISSION)); } { @@ -5048,8 +4924,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(1).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { @@ -5063,8 +4938,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(100).value(), }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } { @@ -5078,8 +4952,7 @@ class Vault_test : public beast::unit_test::suite .id = vaultKeylet.key, .holder = depositor, }), - ter(tesSUCCESS), - THISLINE); + ter(tesSUCCESS)); } }; @@ -5154,19 +5027,19 @@ 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 @@ -5176,7 +5049,7 @@ class Vault_test : public beast::unit_test::suite 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); @@ -5199,26 +5072,26 @@ 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 @@ -5228,7 +5101,7 @@ class Vault_test : public beast::unit_test::suite 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); @@ -5251,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"; @@ -5280,7 +5153,7 @@ class Vault_test : public beast::unit_test::suite 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); @@ -5294,7 +5167,7 @@ class Vault_test : public beast::unit_test::suite { 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); @@ -5308,7 +5181,7 @@ class Vault_test : public beast::unit_test::suite { 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); @@ -5322,7 +5195,7 @@ class Vault_test : public beast::unit_test::suite { 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. @@ -5336,7 +5209,7 @@ 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 @@ -5344,7 +5217,7 @@ class Vault_test : public beast::unit_test::suite try { tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1 - env(tx, THISLINE); + env(tx); BEAST_EXPECTS(false, "Expected parse_error for mantissa larger than uint64 max"); } catch (parse_error const& e) @@ -5374,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 bf6bb0eb1a..041b73f0ca 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -32,6 +32,86 @@ 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) { @@ -196,6 +276,71 @@ struct Wasm_test : public beast::unit_test::suite env.close(); } + 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 testWasmFib() { @@ -723,8 +868,7 @@ struct Wasm_test : public beast::unit_test::suite // i32.const 1001)) auto const wasmHex = "0061736d01000000010c0260017f017f60027f7f017f03030200010503010001071a03066d656d6f727902" - "0005746573743100" - "0005746573743200010a0d02050041e8070b050041e9070b"; + "00057465737431000005746573743200010a0d02050041e8070b050041e9070b"; auto const wasm = hexToBytes(wasmHex); // good params, module is working properly @@ -864,23 +1008,6 @@ struct Wasm_test : public beast::unit_test::suite env.close(); } - template - std::size_t - uleb128(IT& it, std::uint16_t val) - { - std::size_t count = 0; - do - { - std::uint8_t byte = val & 0x7f; - val >>= 7; - if (val) - byte |= 0x80; - *it++ = byte; - ++count; - } while (val != 0); - return count; - } - void testOpcodes() { @@ -1394,6 +1521,7 @@ struct Wasm_test : public beast::unit_test::suite testWasmLib(); testBadWasm(); testWasmLedgerSqn(); + testImpExp(); testWasmFib(); diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index c74857fa86..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...); @@ -812,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); }; @@ -2142,14 +2142,22 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj 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)); } @@ -2336,9 +2344,13 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj 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)); } @@ -3826,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 @@ -3917,8 +3929,10 @@ private: verify() const { for (auto const& [acct, state] : accounts) + { if (!state.verify(env, acct)) return false; + } return true; } @@ -3997,12 +4011,12 @@ private: template class SmBase { - public: SmBase(std::shared_ptr const& chainstate, BridgeDef const& bridge) : bridge_(bridge), st_(chainstate) { } + public: ChainStateTrack& srcState() { @@ -4030,6 +4044,8 @@ private: protected: BridgeDef const& bridge_; std::shared_ptr st_; + + friend T; }; // -------------------------------------------------- @@ -4042,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)) { } @@ -4074,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; @@ -4155,7 +4171,7 @@ private: } private: - SmState sm_state; + SmState sm_state{st_initial}; AccountCreate cr; }; @@ -4169,7 +4185,7 @@ private: std::shared_ptr const& chainstate, BridgeDef const& bridge, Transfer xfer) - : Base(chainstate, bridge), xfer(std::move(xfer)), sm_state(st_initial) + : Base(chainstate, bridge), xfer(std::move(xfer)) { } @@ -4295,9 +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: @@ -4316,7 +4337,7 @@ private: private: Transfer xfer; - SmState sm_state; + SmState sm_state{st_initial}; }; // -------------------------------------------------- @@ -4364,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 @@ -4406,8 +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("doorXRPLocking"); doorXRPLocking = result.back(); result.emplace_back("doorUSDLocking"); @@ -4456,8 +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); + } return result; }(); diff --git a/src/test/app/tx/apply_test.cpp b/src/test/app/tx/apply_test.cpp index f21af50817..10b0544f49 100644 --- a/src/test/app/tx/apply_test.cpp +++ b/src/test/app/tx/apply_test.cpp @@ -31,7 +31,7 @@ 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)); { diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index eeae8ea49b..7dcd5c4ea1 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -3,1244 +3,920 @@ #include extern std::string const fibWasmHex = - "0061736d0100000001090260000060017f017f0303020001071b02115f5f" - "7761736d5f63616c6c5f63746f727300000366696200010a440202000b3f" - "01017f200045044041000f0b2000410348044041010f0b200041026a2100" - "0340200041036b100120016a2101200041026b220041044a0d000b200141" - "016a0b"; + "0061736d0100000001090260000060017f017f0303020001071b02115f5f7761736d5f63616c6c5f63746f72730000" + "0366696200010a440202000b3f01017f200045044041000f0b2000410348044041010f0b200041026a210003402000" + "41036b100120016a2101200041026b220041044a0d000b200141016a0b"; extern std::string const ledgerSqnWasmHex = "0061736d01000000010e0360027f7f017f6000006000017f02160103656e760e6765745f6c65646765725f73716e00" - "00030302010205030100" - "02063f0a7f01418088040b7f004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088" - "040b7f00418080080b7f" - "0041000b7f0041010b07aa010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f727300010666696e69" - "736800020c5f5f64736f" - "5f68616e646c6503010a5f5f646174615f656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869" - "676803040d5f5f676c6f" - "62616c5f6261736503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f" - "6261736503080c5f5f74" - "61626c655f6261736503090a3d0202000b3801037f230041106b220024002000410c6a410410002101200028020c21" - "02200041106a24002001" - "41054100200241054f1b20014100481b0b007f0970726f647563657273010c70726f6365737365642d62790105636c" - "616e675f31392e312e35" - "2d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374" - "20616234623561326462" - "353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f6665617475" - "726573042b0f6d757461" - "626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c74697661" - "6c7565"; + "0003030201020503010002063f0a7f01418088040b7f004180080b7f004180080b7f004180080b7f00418088040b7f" + "004180080b7f00418088040b7f00418080080b7f0041000b7f0041010b07aa010c066d656d6f72790200115f5f7761" + "736d5f63616c6c5f63746f727300010666696e69736800020c5f5f64736f5f68616e646c6503010a5f5f646174615f" + "656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f62616c5f626173" + "6503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f6261736503080c" + "5f5f7461626c655f6261736503090a3d0202000b3801037f230041106b220024002000410c6a410410002101200028" + "020c2102200041106a2400200141054100200241054f1b20014100481b0b007f0970726f647563657273010c70726f" + "6365737365642d62790105636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875" + "622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235613264623538323935386166316565333038" + "61373930636664623432626432343732302900490f7461726765745f6665617475726573042b0f6d757461626c652d" + "676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; extern std::string const allHostFunctionsWasmHex = - "0061736d0100000001540c60027f7f017f60037f7f7f017f60047f7f7f7f017f60017f017f" - "60067f7f7f7f7f7f017f60037f7f7f0060057f7f7f7f7f017f60037f7f7e017f60087f7f7f" - "7f7f7f7f7f017f60017f0060027f7f006000017f02ae061a08686f73745f6c69620c676574" - "5f74785f6669656c64000108686f73745f6c69620974726163655f6e756d000708686f7374" - "5f6c6962057472616365000608686f73745f6c69620e6765745f6c65646765725f73716e00" - "0008686f73745f6c6962166765745f706172656e745f6c65646765725f74696d6500000868" - "6f73745f6c6962166765745f706172656e745f6c65646765725f68617368000008686f7374" - "5f6c6962136765745f74785f6e65737465645f6669656c64000208686f73745f6c69621067" - "65745f74785f61727261795f6c656e000308686f73745f6c6962176765745f74785f6e6573" - "7465645f61727261795f6c656e000008686f73745f6c69621c6765745f63757272656e745f" - "6c65646765725f6f626a5f6669656c64000108686f73745f6c6962236765745f6375727265" - "6e745f6c65646765725f6f626a5f6e65737465645f6669656c64000208686f73745f6c6962" - "206765745f63757272656e745f6c65646765725f6f626a5f61727261795f6c656e00030868" - "6f73745f6c6962276765745f63757272656e745f6c65646765725f6f626a5f6e6573746564" - "5f61727261795f6c656e000008686f73745f6c69621063616368655f6c65646765725f6f62" - "6a000108686f73745f6c69621163726564656e7469616c5f6b65796c6574000808686f7374" - "5f6c69620d657363726f775f6b65796c6574000408686f73745f6c69620d6f7261636c655f" - "6b65796c6574000408686f73745f6c696213636f6d707574655f7368613531325f68616c66" - "000208686f73745f6c6962076765745f6e6674000408686f73745f6c69620b757064617465" - "5f64617461000008686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64" - "000208686f73745f6c69621b6765745f6c65646765725f6f626a5f6e65737465645f666965" - "6c64000608686f73745f6c6962186765745f6c65646765725f6f626a5f61727261795f6c65" - "6e000008686f73745f6c69621f6765745f6c65646765725f6f626a5f6e65737465645f6172" - "7261795f6c656e000108686f73745f6c69620e6163636f756e745f6b65796c657400020868" - "6f73745f6c69620d74726163655f6163636f756e740002030c0b090a05050b050001010300" - "05030100110619037f01418080c0000b7f0041af99c0000b7f0041b099c0000b072e04066d" - "656d6f727902000666696e697368001e0a5f5f646174615f656e6403010b5f5f686561705f" - "6261736503020ac61d0b990101027f230041306b220124002000027f418180202001411c6a" - "4114100022024114470440417f20022002417f4e1b210241010c010b200020012f001c3b00" - "01200041036a2001411e6a2d00003a0000200120012900233703082001200141286a290000" - "37000d200128001f21022000410d6a200129000d3700002000200129030837020841000b3a" - "000020002002360204200141306a24000b460020012d00004101460440418080c000410b20" - "013402041001000b20002001290001370000200041106a200141116a280000360000200041" - "086a200141096a2900003700000b1900200241214f0440000b200020023602042000200136" - "02000b1900200241094f0440000b20002002360204200020013602000bde1a01097f230041" - "b0036b22002400418b80c000411b41014100410010021a41a680c000411941014100410010" - "021a41e780c000412b41014100410010021a20004100360270024002400240024002400240" - "02400240200041f0006a220741041003220141004a0440419281c000411720002802702201" - "41187420014180fe03714108747220014108764180fe037120014118767272ad10011a2000" - "41003602900120004190016a220341041004220141004c0d0141a981c00041132000280290" - "01220141187420014180fe03714108747220014108764180fe037120014118767272ad1001" - "1a200041c8016a22024200370300200041c0016a22054200370300200041b8016a22044200" - "370300200042003703b001200041b0016a22064120100522014120470d0241bc81c0004113" - "20064120410110021a41cf81c000412041014100410010021a41dc82c000412e4101410041" - "0010021a200041a0016a410036020020004198016a42003703002000420037039001418180" - "2020034114100022014114470d03418a83c00041142003101f200042003703704188801820" - "074108100022014108470d04419e83c0004117420810011a41b583c0004128200741084101" - "10021a2000410036024841848008200041c8006a22034104100022014104470d0541dd83c0" - "00411520034104410110021a200041013b0034200242003703002005420037030020044200" - "370300200042003703b0010240200041346a4102200641201006220141004e044041f283c0" - "0041142001ad10011a200041286a20062001101c418684c000410d2000280228200028022c" - "410110021a0c010b419384c00041292001ac10011a0b41bc84c00041154183803c1007ac10" - "011a41d184c00041134189803c1007ac10011a0240200041346a41021008220141004e0440" - "41e484c00041142001ad10011a0c010b41f884c000412d2001ac10011a0b41a585c0004123" - "41014100410010021a41de86c000413341014100410010021a200042003703704182801820" - "0041f0006a220141081009220341004c0d0620034108460440419187c000412b420810011a" - "41bc87c000412f20014108410110021a0c080b41eb87c000412f2003ad10011a200041206a" - "200041f0006a2003101d419a88c000411720002802202000280224410110021a0c070b41bf" - "82c000411d2001ac10011a419b7f21020c070b419a82c00041252001ac10011a419a7f2102" - "0c060b41ef81c000412b2001ac10011a41997f21020c050b41b486c000412a2001ac10011a" - "41b77e21020c040b41f385c00041c1002001ac10011a41b67e21020c030b41c885c000412b" - "2001ac10011a41b57e21020c020b41b188c00041c5002003ac10011a0b200041a0016a4100" - "36020020004198016a4200370300200042003703900102404181802020004190016a220341" - "141009220141004a044041f688c000411e2003101f0c010b419489c00041332001ac10011a" - "0b200041013b0048200041c8016a4200370300200041c0016a4200370300200041b8016a42" - "00370300200042003703b0010240200041c8006a4102200041b0016a22014120100a220341" - "004e044041c789c000411c2003ad10011a200041186a20012003101c41e389c00041152000" - "280218200028021c410110021a0c010b41f889c00041392003ac10011a0b41b18ac0004124" - "4183803c100bac10011a0240200041c8006a4102100c220141004e044041d58ac000411c20" - "01ad10011a0c010b41f18ac000413d2001ac10011a0b41ae8bc00041284101410041001002" - "1a41d68bc000412f41014100410010021a200041b0016a2203101a200041f0006a22012003" - "101b200041a8016a4200370300200041a0016a420037030020004198016a42003703002000" - "42003703900102400240024002400240200120004190016a22031020220141204604402003" - "41204100100d220441004a044041858cc00041232004ad10011a2000420037034820042000" - "41c8006a220141081021220341004c0d022003410846044041a88cc000412a420810011a41" - "d28cc000412e20014108410110021a0c060b41808dc000412e2003ad10011a200041106a20" - "0041c8006a2003101d41ae8dc000411620002802102000280214410110021a0c050b41e68f" - "c000413c2004ac10011a200041c8016a4200370300200041c0016a4200370300200041b801" - "6a4200370300200042003703b0014101200041b0016a4120102122014100480d020c030b41" - "ba92c000412e2001ac10011a41ef7c21020c050b41c48dc000412b2003ac10011a0c020b41" - "a290c00041c1002001ac10011a0b200041013b00484101200041c8006a200041b0016a1022" - "2201410048044041e390c00041352001ac10011a0b4101102322014100480440419891c000" - "41322001ac10011a0b4101200041c8006a10242201410048044041ca91c00041392001ac10" - "011a0b418392c000413741014100410010021a0c010b200041013b0034200041c8016a4200" - "370300200041c0016a4200370300200041b8016a4200370300200042003703b00102402004" - "200041346a200041b0016a22011022220341004e044041ef8dc000411b2003ad10011a2000" - "41086a20012003101c418a8ec00041142000280208200028020c410110021a0c010b419e8e" - "c00041312003ac10011a0b41cf8ec000412320041023ac10011a02402004200041346a1024" - "220141004e044041f28ec000411b2001ad10011a0c010b418d8fc00041352001ac10011a0b" - "41c28fc000412441014100410010021a0b41e892c000412f41014100410010021a200041b0" - "016a2201101a200041346a22042001101b200041e0006a4200370300200041d8006a420037" - "0300200041d0006a420037030020004200370348024002400240024002402004200041c800" - "6a2203102022014120460440419793c000410f20034120410110021a20004188016a420037" - "030020004180016a4200370300200041f8006a420037030020004200370370024020044114" - "2004411441a693c0004109200041f0006a22014120100e220341004a044020002001200310" - "1c41ae93c000411220002802002000280204410110021a0c010b41c093c000413c2003ac10" - "011a0b200041a8016a22064200370300200041a0016a2202420037030020004198016a2205" - "4200370300200042003703900120004180808cc07e360268200041346a22034114200041e8" - "006a410420004190016a22084120100f22014120470d0141fc93c000410e20084120410110" - "021a200041c8016a4200370300200041c0016a4200370300200041b8016a42003703002000" - "42003703b001200041808080d00236026c20034114200041ec006a4104200041b0016a2204" - "4120101022014120470d02418a94c000410e20044120410110021a419894c0004124410141" - "00410010021a419195c000412541014100410010021a20004188016a420037030020004180" - "016a4200370300200041f8006a42003703002000420037037041b695c0004117200041f000" - "6a22034120101122014120470d0341cd95c000410b41b695c0004117410110021a41d895c0" - "00411120034120410110021a2004101a200041c8006a22072004101b200642003703002002" - "420037030020054200370300200042003703900102404100200422026b410371220320026a" - "220520024d0d0020030440200321010340200241003a0000200241016a2102200141016b22" - "010d000b0b200341016b4107490d000340200241003a0000200241076a41003a0000200241" - "066a41003a0000200241056a41003a0000200241046a41003a0000200241036a41003a0000" - "200241026a41003a0000200241016a41003a0000200241086a22022005470d000b0b200541" - "800220036b2201417c716a220220054b0440034020054100360200200541046a2205200249" - "0d000b0b024020022001410371220120026a22034f0d002001220504400340200241003a00" - "00200241016a2102200541016b22050d000b0b200141016b4107490d000340200241003a00" - "00200241076a41003a0000200241066a41003a0000200241056a41003a0000200241046a41" - "003a0000200241036a41003a0000200241026a41003a0000200241016a41003a0000200241" - "086a22022003470d000b0b0240200741142008412020044180021012220141004a044041e9" - "95c00041102001ad10011a20014181024f0d0641f995c000410920042001410110021a0c01" - "0b418296c000412e2001ac10011a0b41b096c000411241c296c00041074101100222014100" - "480d0541c996c000411d2001ad10011a41e696c0004111422a1001410048044041ad97c000" - "411a42a47b10011a41a47b21020c070b41f796c000411c420010011a41012102419397c000" - "411a41014100410010021a41ff97c000412941014100410010021a41a898c0004128101322" - "01412846044041d098c000412741a898c0004128410110021a41f798c000411e4101410041" - "0010021a41bf80c000412841014100410010021a0c070b419599c000411a2001ac10011a41" - "c37a21020c060b41f494c000411d2001ac10011a418b7c21020c050b41d894c000411c2001" - "ac10011a41897c21020c040b41bc94c000411c2001ac10011a41887c21020c030b41dd97c0" - "0041222001ac10011a41a77b21020c020b000b41c797c00041162001ac10011a41a57b2102" - "0b200041b0036a240020020b0d00200020012002411410191a0b0c00200041142001412010" - "180b0e002000418280182001200210140b0e002000200141022002412010150b0a00200041" - "83803c10160b0a0020002001410210170b0bb9190100418080c0000baf196572726f725f63" - "6f64653d3d3d3d20484f53542046554e4354494f4e532054455354203d3d3d54657374696e" - "6720323620686f73742066756e6374696f6e73535543434553533a20416c6c20686f737420" - "66756e6374696f6e20746573747320706173736564212d2d2d2043617465676f727920313a" - "204c6564676572204865616465722046756e6374696f6e73202d2d2d4c6564676572207365" - "7175656e6365206e756d6265723a506172656e74206c65646765722074696d653a50617265" - "6e74206c656467657220686173683a535543434553533a204c656467657220686561646572" - "2066756e6374696f6e734552524f523a206765745f706172656e745f6c65646765725f6861" - "73682077726f6e67206c656e6774683a4552524f523a206765745f706172656e745f6c6564" - "6765725f74696d65206661696c65643a4552524f523a206765745f6c65646765725f73716e" - "206661696c65643a2d2d2d2043617465676f727920323a205472616e73616374696f6e2044" - "6174612046756e6374696f6e73202d2d2d5472616e73616374696f6e204163636f756e743a" - "5472616e73616374696f6e20466565206c656e6774683a5472616e73616374696f6e204665" - "65202873657269616c697a65642058525020616d6f756e74293a5472616e73616374696f6e" - "2053657175656e63653a4e6573746564206669656c64206c656e6774683a4e657374656420" - "6669656c643a494e464f3a206765745f74785f6e65737465645f6669656c64206e6f742061" - "70706c696361626c653a5369676e657273206172726179206c656e6774683a4d656d6f7320" - "6172726179206c656e6774683a4e6573746564206172726179206c656e6774683a494e464f" - "3a206765745f74785f6e65737465645f61727261795f6c656e206e6f74206170706c696361" - "626c653a535543434553533a205472616e73616374696f6e20646174612066756e6374696f" - "6e734552524f523a206765745f74785f6669656c642853657175656e6365292077726f6e67" - "206c656e6774683a4552524f523a206765745f74785f6669656c6428466565292077726f6e" - "67206c656e67746820286578706563746564203820627974657320666f7220585250293a45" - "52524f523a206765745f74785f6669656c64284163636f756e74292077726f6e67206c656e" - "6774683a2d2d2d2043617465676f727920333a2043757272656e74204c6564676572204f62" - "6a6563742046756e6374696f6e73202d2d2d43757272656e74206f626a6563742062616c61" - "6e6365206c656e677468202858525020616d6f756e74293a43757272656e74206f626a6563" - "742062616c616e6365202873657269616c697a65642058525020616d6f756e74293a437572" - "72656e74206f626a6563742062616c616e6365206c656e67746820286e6f6e2d5852502061" - "6d6f756e74293a43757272656e74206f626a6563742062616c616e63653a494e464f3a2067" - "65745f63757272656e745f6c65646765725f6f626a5f6669656c642842616c616e63652920" - "6661696c656420286d6179206265206578706563746564293a43757272656e74206c656467" - "6572206f626a656374206163636f756e743a494e464f3a206765745f63757272656e745f6c" - "65646765725f6f626a5f6669656c64284163636f756e7429206661696c65643a4375727265" - "6e74206e6573746564206669656c64206c656e6774683a43757272656e74206e6573746564" - "206669656c643a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6e" - "65737465645f6669656c64206e6f74206170706c696361626c653a43757272656e74206f62" - "6a656374205369676e657273206172726179206c656e6774683a43757272656e74206e6573" - "746564206172726179206c656e6774683a494e464f3a206765745f63757272656e745f6c65" - "646765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c696361" - "626c653a535543434553533a2043757272656e74206c6564676572206f626a656374206675" - "6e6374696f6e732d2d2d2043617465676f727920343a20416e79204c6564676572204f626a" - "6563742046756e6374696f6e73202d2d2d5375636365737366756c6c792063616368656420" - "6f626a65637420696e20736c6f743a436163686564206f626a6563742062616c616e636520" - "6c656e677468202858525020616d6f756e74293a436163686564206f626a6563742062616c" - "616e6365202873657269616c697a65642058525020616d6f756e74293a436163686564206f" - "626a6563742062616c616e6365206c656e67746820286e6f6e2d58525020616d6f756e7429" - "3a436163686564206f626a6563742062616c616e63653a494e464f3a206765745f6c656467" - "65725f6f626a5f6669656c642842616c616e636529206661696c65643a436163686564206e" - "6573746564206669656c64206c656e6774683a436163686564206e6573746564206669656c" - "643a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206e" - "6f74206170706c696361626c653a436163686564206f626a656374205369676e6572732061" - "72726179206c656e6774683a436163686564206e6573746564206172726179206c656e6774" - "683a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c" - "656e206e6f74206170706c696361626c653a535543434553533a20416e79206c6564676572" - "206f626a6563742066756e6374696f6e73494e464f3a2063616368655f6c65646765725f6f" - "626a206661696c656420286578706563746564207769746820746573742066697874757265" - "73293a494e464f3a206765745f6c65646765725f6f626a5f6669656c64206661696c656420" - "617320657870656374656420286e6f20636163686564206f626a656374293a494e464f3a20" - "6765745f6c65646765725f6f626a5f6e65737465645f6669656c64206661696c6564206173" - "2065787065637465643a494e464f3a206765745f6c65646765725f6f626a5f61727261795f" - "6c656e206661696c65642061732065787065637465643a494e464f3a206765745f6c656467" - "65725f6f626a5f6e65737465645f61727261795f6c656e206661696c656420617320657870" - "65637465643a535543434553533a20416e79206c6564676572206f626a6563742066756e63" - "74696f6e732028696e7465726661636520746573746564294552524f523a206163636f756e" - "745f6b65796c6574206661696c656420666f722063616368696e6720746573743a2d2d2d20" - "43617465676f727920353a204b65796c65742047656e65726174696f6e2046756e6374696f" - "6e73202d2d2d4163636f756e74206b65796c65743a546573745479706543726564656e7469" - "616c206b65796c65743a494e464f3a2063726564656e7469616c5f6b65796c657420666169" - "6c656420286578706563746564202d20696e74657266616365206973737565293a45736372" - "6f77206b65796c65743a4f7261636c65206b65796c65743a535543434553533a204b65796c" - "65742067656e65726174696f6e2066756e6374696f6e734552524f523a206f7261636c655f" - "6b65796c6574206661696c65643a4552524f523a20657363726f775f6b65796c6574206661" - "696c65643a4552524f523a206163636f756e745f6b65796c6574206661696c65643a2d2d2d" - "2043617465676f727920363a205574696c6974792046756e6374696f6e73202d2d2d48656c" - "6c6f2c205852504c205741534d20776f726c6421496e70757420646174613a534841353132" - "2068616c6620686173683a4e46542064617461206c656e6774683a4e465420646174613a49" - "4e464f3a206765745f6e6674206661696c656420286578706563746564202d206e6f207375" - "6368204e4654293a54657374207472616365206d6573736167657061796c6f616454726163" - "652066756e6374696f6e206279746573207772697474656e3a54657374206e756d62657220" - "747261636554726163655f6e756d2066756e6374696f6e2073756363656564656453554343" - "4553533a205574696c6974792066756e6374696f6e734552524f523a2074726163655f6e75" - "6d2829206661696c65643a4552524f523a2074726163652829206661696c65643a4552524f" - "523a20636f6d707574655f7368613531325f68616c66206661696c65643a2d2d2d20436174" - "65676f727920373a2044617461205570646174652046756e6374696f6e73202d2d2d557064" - "61746564206c656467657220656e74727920646174612066726f6d205741534d2074657374" - "5375636365737366756c6c792075706461746564206c656467657220656e74727920776974" - "683a535543434553533a2044617461207570646174652066756e6374696f6e734552524f52" - "3a207570646174655f64617461206661696c65643a004d0970726f64756365727302086c61" - "6e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e38" - "372e30202831373036376539616320323032352d30352d303929002c0f7461726765745f66" - "65617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d657874"; + "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" + "6a200141096a2900003700000b1900200241214f0440000b20002002360204200020013602000b1900200241094f04" + "40000b20002002360204200020013602000bde1a01097f230041b0036b22002400418b80c000411b41014100410010" + "021a41a680c000411941014100410010021a41e780c000412b41014100410010021a20004100360270024002400240" + "02400240024002400240200041f0006a220741041003220141004a0440419281c00041172000280270220141187420" + "014180fe03714108747220014108764180fe037120014118767272ad10011a200041003602900120004190016a2203" + "41041004220141004c0d0141a981c0004113200028029001220141187420014180fe03714108747220014108764180" + "fe037120014118767272ad10011a200041c8016a22024200370300200041c0016a22054200370300200041b8016a22" + "044200370300200042003703b001200041b0016a22064120100522014120470d0241bc81c000411320064120410110" + "021a41cf81c000412041014100410010021a41dc82c000412e41014100410010021a200041a0016a41003602002000" + "4198016a420037030020004200370390014181802020034114100022014114470d03418a83c00041142003101f2000" + "42003703704188801820074108100022014108470d04419e83c0004117420810011a41b583c0004128200741084101" + "10021a2000410036024841848008200041c8006a22034104100022014104470d0541dd83c000411520034104410110" + "021a200041013b0034200242003703002005420037030020044200370300200042003703b0010240200041346a4102" + "200641201006220141004e044041f283c00041142001ad10011a200041286a20062001101c418684c000410d200028" + "0228200028022c410110021a0c010b419384c00041292001ac10011a0b41bc84c00041154183803c1007ac10011a41" + "d184c00041134189803c1007ac10011a0240200041346a41021008220141004e044041e484c00041142001ad10011a" + "0c010b41f884c000412d2001ac10011a0b41a585c000412341014100410010021a41de86c000413341014100410010" + "021a2000420037037041828018200041f0006a220141081009220341004c0d0620034108460440419187c000412b42" + "0810011a41bc87c000412f20014108410110021a0c080b41eb87c000412f2003ad10011a200041206a200041f0006a" + "2003101d419a88c000411720002802202000280224410110021a0c070b41bf82c000411d2001ac10011a419b7f2102" + "0c070b419a82c00041252001ac10011a419a7f21020c060b41ef81c000412b2001ac10011a41997f21020c050b41b4" + "86c000412a2001ac10011a41b77e21020c040b41f385c00041c1002001ac10011a41b67e21020c030b41c885c00041" + "2b2001ac10011a41b57e21020c020b41b188c00041c5002003ac10011a0b200041a0016a410036020020004198016a" + "4200370300200042003703900102404181802020004190016a220341141009220141004a044041f688c000411e2003" + "101f0c010b419489c00041332001ac10011a0b200041013b0048200041c8016a4200370300200041c0016a42003703" + "00200041b8016a4200370300200042003703b0010240200041c8006a4102200041b0016a22014120100a220341004e" + "044041c789c000411c2003ad10011a200041186a20012003101c41e389c00041152000280218200028021c41011002" + "1a0c010b41f889c00041392003ac10011a0b41b18ac00041244183803c100bac10011a0240200041c8006a4102100c" + "220141004e044041d58ac000411c2001ad10011a0c010b41f18ac000413d2001ac10011a0b41ae8bc0004128410141" + "00410010021a41d68bc000412f41014100410010021a200041b0016a2203101a200041f0006a22012003101b200041" + "a8016a4200370300200041a0016a420037030020004198016a42003703002000420037039001024002400240024002" + "40200120004190016a2203102022014120460440200341204100100d220441004a044041858cc00041232004ad1001" + "1a200042003703482004200041c8006a220141081021220341004c0d022003410846044041a88cc000412a42081001" + "1a41d28cc000412e20014108410110021a0c060b41808dc000412e2003ad10011a200041106a200041c8006a200310" + "1d41ae8dc000411620002802102000280214410110021a0c050b41e68fc000413c2004ac10011a200041c8016a4200" + "370300200041c0016a4200370300200041b8016a4200370300200042003703b0014101200041b0016a412010212201" + "4100480d020c030b41ba92c000412e2001ac10011a41ef7c21020c050b41c48dc000412b2003ac10011a0c020b41a2" + "90c00041c1002001ac10011a0b200041013b00484101200041c8006a200041b0016a10222201410048044041e390c0" + "0041352001ac10011a0b4101102322014100480440419891c00041322001ac10011a0b4101200041c8006a10242201" + "410048044041ca91c00041392001ac10011a0b418392c000413741014100410010021a0c010b200041013b00342000" + "41c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b0010240200420004134" + "6a200041b0016a22011022220341004e044041ef8dc000411b2003ad10011a200041086a20012003101c418a8ec000" + "41142000280208200028020c410110021a0c010b419e8ec00041312003ac10011a0b41cf8ec000412320041023ac10" + "011a02402004200041346a1024220141004e044041f28ec000411b2001ad10011a0c010b418d8fc00041352001ac10" + "011a0b41c28fc000412441014100410010021a0b41e892c000412f41014100410010021a200041b0016a2201101a20" + "0041346a22042001101b200041e0006a4200370300200041d8006a4200370300200041d0006a420037030020004200" + "370348024002400240024002402004200041c8006a2203102022014120460440419793c000410f2003412041011002" + "1a20004188016a420037030020004180016a4200370300200041f8006a420037030020004200370370024020044114" + "2004411441a693c0004109200041f0006a22014120100e220341004a0440200020012003101c41ae93c00041122000" + "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"; + "0061736d010000000105016000017f030201000608017f0141c0843d0b070a010666696e69736800000a1601140023" + "0045044041010f0b230041016b240010000b"; extern std::string const allKeyletsWasmHex = - "0061736d0100000001480960067f7f7f7f7f7f017f60047f7f7f7f017f60087f7f7f7f7f7f" - "7f7f017f60037f7f7f017f60037f7f7e017f60057f7f7f7f7f017f60057f7f7f7f7f006000" - "017f60037f7f7f000284051808686f73745f6c69620974726163655f6e756d000408686f73" - "745f6c6962057472616365000508686f73745f6c69621063616368655f6c65646765725f6f" - "626a000308686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000108" - "686f73745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669656c64" - "000308686f73745f6c69620d74726163655f6163636f756e74000108686f73745f6c69620e" - "6163636f756e745f6b65796c6574000108686f73745f6c69620b6c696e655f6b65796c6574" - "000208686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c69620c636865" - "636b5f6b65796c6574000008686f73745f6c69621163726564656e7469616c5f6b65796c65" - "74000208686f73745f6c69620f64656c65676174655f6b65796c6574000008686f73745f6c" - "6962166465706f7369745f707265617574685f6b65796c6574000008686f73745f6c69620a" - "6469645f6b65796c6574000108686f73745f6c69620d657363726f775f6b65796c65740000" - "08686f73745f6c6962136d70745f69737375616e63655f6b65796c6574000008686f73745f" - "6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c6962106e66745f6f6666" - "65725f6b65796c6574000008686f73745f6c69620c6f666665725f6b65796c657400000868" - "6f73745f6c69620e7061796368616e5f6b65796c6574000208686f73745f6c69621a706572" - "6d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73745f6c69620e7369" - "676e6572735f6b65796c6574000108686f73745f6c69620d7469636b65745f6b65796c6574" - "000008686f73745f6c69620c7661756c745f6b65796c657400000304030607080503010011" - "0619037f01418080c0000b7f0041ad8ac0000b7f0041b08ac0000b073e05066d656d6f7279" - "02000d6f626a6563745f65786973747300180666696e69736800190a5f5f646174615f656e" - "6403010b5f5f686561705f6261736503020ab73403a80502017f017e230041a0016b220524" - "00024020012d0000410146044041c280c000411620012802042201ac10001a200041013a00" - "00200020013602040c010b200541186a200141196a290000370300200541106a200141116a" - "290000370300200541086a200141096a290000370300200520012900013703002002200320" - "054120410110011a02402005412041001002220141004a04402004450440418b80c000410f" - "4285801410001a20014185801420054180016a412010032201412047044041a680c0004115" - "417f20012001417f4e1b2201ac10001a200041013a0000200020013602040c040b200541c2" - "006a20054182016a2d00003a0000200541f0006a20054197016a2900002206370300200541" - "286a22012005418f016a290000370300200541306a22022006370300200541386a22032005" - "419f016a2d00003a0000200520052f0080013b014020052005290087013703202005200528" - "008301360043200541df006a20032d00003a0000200541d7006a2002290300370000200541" - "cf006a200129030037000020052005290320370047419a80c000410c200541406b41204101" - "10011a0c020b418b80c000410f2004ac10001a2001200420054180016a4114100322014114" - "47044041a680c0004115417f20012001417f4e1b2201ac10001a200041013a000020002001" - "3602040c030b200541c2006a20054182016a2d00003a000020052005290087013703602005" - "2005418c016a290000370065200520052f0080013b01402005200529036037032020052005" - "2900653700252005200528008301360043200541cc006a2005290025370000200520052903" - "20370047419a80c000410c200541406b4114410110011a0c010b41bb80c00041072001ac10" - "001a200041013a0000200020013602040c010b20004180023b01000b200541a0016a24000b" - "8b2a02087f027e23004180076b2200240041d880c000412341014100410010011a02402000" - "027f02404181802020004188016a22024114100422014114460440200041066a2000418a01" - "6a22032d00003a00002000200029008f013703e001200020004194016a22042900003700e5" - "01200020002f0088013b0104200020002903e0013703d806200020002900e5013700dd0620" - "00200028008b01360007200041106a20002900dd06370000200020002903d80637000b41fb" - "80c0004108200041046a2205411410051a4183802020024114100422014114470d03200041" - "1a6a20032d00003a00002000200029008f013703e001200020042900003700e50120002000" - "2f0088013b0118200020002903e0013703d806200020002900e5013700dd06200020002800" - "8b0136001b200041246a20002900dd06370000200020002903d80637001f418381c000410c" - "200041186a411410051a200041a0016a2203420037030020004198016a2204420037030020" - "004190016a420037030020004200370388012005411420024120100622014120460d010240" - "20014100480440200020013602300c010b2000417f3602300b41010c020b0c020b200041c5" - "006a20032903003700002000413d6a2004290300370000200041356a20004190016a290300" - "370000200020002903880137002d41000b3a002c20004188016a2000412c6a418f81c00041" - "07418180201018024020002d0088014101460440200028028c01210141878ac00041124205" - "10001a0c010b41002101419681c000413541014100410010011a200041de006a41c4003a00" - "00200041d8006a4100360200200041e3006a41003a0000200041d5a6013b015c2000420037" - "03502000410036005f200041a0016a2203420037030020004198016a220442003703002000" - "4190016a2205420037030020004200370388010240200041046a4114200041186a41142000" - "41d0006a411420004188016a41201007220241204704400240200241004804402000200236" - "02680c010b2000417f3602680b410121010c010b200041fd006a2003290300370000200041" - "f5006a2004290300370000200041ed006a200529030037000020002000290388013700650b" - "200020013a006420004188016a200041e4006a41cb81c00041094100101820002d00880141" - "01460440200028028c01210141878ac0004112420510001a0c010b41d481c0004137410141" - "00410010011a200041f0016a200041286a2203280100360200200041e8016a200041206a22" - "04290100370300200041fc016a200041d8006a290300220837020020004184026a200041e0" - "006a2802002202360200200020002901183703e0012000200029035022093702f401200041" - "e8066a22012002360200200041e0066a22022008370300200020093703d806200041f4066a" - "2004290100370200200041fc066a2003280100360200200020002901183702ec0620004188" - "026a200041d8066a22034128101a2000418c016a200041e0016a41d000101a200041013602" - "8801200041f0066a220442003703002001420037030020024200370300200042003703d806" - "2000027f41998ac0004114200041b4016a4128200341201008220341204704400240200341" - "00480440200020033602e4010c010b2000417f3602e4010b41010c010b200041f9016a2004" - "290300370000200041f1016a2001290300370000200041e9016a2002290300370000200020" - "002903d8063700e10141000b3a00e001200041b4026a200041e0016a418b82c00041034181" - "8020101820002d00b402410146044020002802b802210141878ac0004112420610001a0c01" - "0b41002101418e82c000413141014100410010011a200041808080303602d806200041f801" - "6a22034200370300200041f0016a22044200370300200041e8016a22054200370300200042" - "003703e0010240200041046a4114200041d8066a4104200041e0016a412010092202412047" - "0440024020024100480440200020023602c0020c010b2000417f3602c0020b410121010c01" - "0b200041d5026a2003290300370000200041cd026a2004290300370000200041c5026a2005" - "290300370000200020002903e0013700bd020b200020013a00bc02200041e0016a200041bc" - "026a41bf82c000410541818020101820002d00e001410146044020002802e401210141878a" - "c0004112420610001a0c010b4100210141c482c000413341014100410010011a200041f801" - "6a22034200370300200041f0016a22044200370300200041e8016a22054200370300200042" - "003703e0010240200041046a220241142002411441f782c0004112200041e0016a4120100a" - "22024120470440024020024100480440200020023602e4020c010b2000417f3602e4020b41" - "0121010c010b200041f9026a2003290300370000200041f1026a2004290300370000200041" - "e9026a2005290300370000200020002903e0013700e1020b200020013a00e002200041e001" - "6a200041e0026a418983c000410a41988020101820002d00e001410146044020002802e401" - "210141878ac0004112420710001a0c010b41002101419383c000413841014100410010011a" - "200041f8016a22034200370300200041f0016a22044200370300200041e8016a2205420037" - "0300200042003703e0010240200041046a4114200041186a4114200041e0016a4120100b22" - "02412047044002402002410048044020002002360288030c010b2000417f360288030b4101" - "21010c010b2000419d036a200329030037000020004195036a20042903003700002000418d" - "036a2005290300370000200020002903e001370085030b200020013a008403200041e0016a" - "20004184036a41cb83c000410841818020101820002d00e001410146044020002802e40121" - "0141878ac0004112420810001a0c010b41d383c000413641014100410010011a230041206b" - "22012400200141186a22044200370300200141106a22054200370300200141086a22064200" - "37030020014200370300200041a8036a2202027f200041046a4114200041186a4114200141" - "20100c22034120470440024020034100480440200220033602040c010b2002417f3602040b" - "41010c010b20022001290300370001200241196a2004290300370000200241116a20052903" - "00370000200241096a200629030037000041000b3a0000200141206a2400200041e0016a20" - "02418984c000410e41818020101820002d00e001410146044020002802e401210141878ac0" - "004112420910001a0c010b419784c000413c41014100410010011a230041206b2201240020" - "0141186a22044200370300200141106a22054200370300200141086a220642003703002001" - "4200370300200041cc036a2202027f200041046a411420014120100d220341204704400240" - "20034100480440200220033602040c010b2002417f3602040b41010c010b20022001290300" - "370001200241196a2004290300370000200241116a2005290300370000200241096a200629" - "030037000041000b3a0000200141206a2400200041e0016a200241d384c000410341818020" - "101820002d00e001410146044020002802e401210141878ac0004112420a10001a0c010b41" - "d684c000413141014100410010011a230041306b22012400200141808080d80036020c2001" - "41286a22044200370300200141206a22054200370300200141186a22064200370300200142" - "00370310200041f0036a2202027f200041046a41142001410c6a4104200141106a4120100e" - "22034120470440024020034100480440200220033602040c010b2002417f3602040b41010c" - "010b20022001290310370001200241196a2004290300370000200241116a20052903003700" - "00200241096a200629030037000041000b3a0000200141306a2400200041e0016a20024187" - "85c000410641818020101820002d00e001410146044020002802e401210141878ac0004112" - "420b10001a0c010b418d85c000413441014100410010011a230041306b2201240020014180" - "8080e00036020c200141286a22044200370300200141206a22054200370300200141186a22" - "0642003703002001420037031020004194046a2202027f200041046a41142001410c6a4104" - "200141106a4120100f22034120470440024020034100480440200220033602040c010b2002" - "417f3602040b41010c010b20022001290310370001200241196a2004290300370000200241" - "116a2005290300370000200241096a200629030037000041000b3a0000200141306a240020" - "0041f4016a200041146a280100360200200041ec016a2000410c6a29010037020020002000" - "2901043702e401200041808080e0003602e001200041d8066a200241c185c000410b418480" - "20101820002d00d806410146044020002802dc06210141878ac0004112420c10001a0c010b" - "41cc85c000413941014100410010011a230041206b22012400200141186a22044200370300" - "200141106a22054200370300200141086a2206420037030020014200370300200041b8046a" - "2202027f200041e0016a4118200041186a4114200141201010220341204704400240200341" - "00480440200220033602040c010b2002417f3602040b41010c010b20022001290300370001" - "200241196a2004290300370000200241116a2005290300370000200241096a200629030037" - "000041000b3a0000200141206a2400200041d8066a2002418586c000410741818020101820" - "002d00d806410146044020002802dc06210141878ac0004112420d10001a0c010b418c86c0" - "00413541014100410010011a230041306b220124002001418080803036020c200141286a22" - "044200370300200141206a22054200370300200141186a2206420037030020014200370310" - "200041dc046a2202027f200041186a41142001410c6a4104200141106a4120101122034120" - "470440024020034100480440200220033602040c010b2002417f3602040b41010c010b2002" - "2001290310370001200241196a2004290300370000200241116a2005290300370000200241" - "096a200629030037000041000b3a0000200141306a2400200041d8066a200241c186c00041" - "0c41828020101820002d00d806410146044020002802dc06210141878ac0004112420d1000" - "1a0c010b41cd86c000413a41014100410010011a230041306b22012400200141808080e800" - "36020c200141286a22044200370300200141206a22054200370300200141186a2206420037" - "03002001420037031020004180056a2202027f200041046a41142001410c6a410420014110" - "6a4120101222034120470440024020034100480440200220033602040c010b2002417f3602" - "040b41010c010b20022001290310370001200241196a2004290300370000200241116a2005" - "290300370000200241096a200629030037000041000b3a0000200141306a2400200041d806" - "6a2002418787c000410541818020101820002d00d806410146044020002802dc0621014187" - "8ac0004112420d10001a0c010b418c87c000413341014100410010011a230041306b220124" - "00200141808080f00036020c200141286a22044200370300200141206a2205420037030020" - "0141186a2206420037030020014200370310200041a4056a2202027f200041046a41142000" - "41186a41142001410c6a4104200141106a4120101322034120470440024020034100480440" - "200220033602040c010b2002417f3602040b41010c010b2002200129031037000120024119" - "6a2004290300370000200241116a2005290300370000200241096a20062903003700004100" - "0b3a0000200141306a2400200041d8066a200241bf87c000410a41818020101820002d00d8" - "06410146044020002802dc06210141878ac0004112420e10001a0c010b41c987c000413841" - "014100410010011a230041306b22012400200141808080f80036020c200141286a22044200" - "370300200141206a22054200370300200141186a2206420037030020014200370310200041" - "c8056a2202027f200041046a41142001410c6a4104200141106a4120101422034120470440" - "024020034100480440200220033602040c010b2002417f3602040b41010c010b2002200129" - "0310370001200241196a2004290300370000200241116a2005290300370000200241096a20" - "0629030037000041000b3a0000200141306a2400200041d8066a2002418188c00041124182" - "8020101820002d00d806410146044020002802dc06210141878ac0004112420f10001a0c01" - "0b419388c00041c00041014100410010011a230041206b22012400200141186a2204420037" - "0300200141106a22054200370300200141086a2206420037030020014200370300200041ec" - "056a2202027f200041046a4114200141201015220341204704400240200341004804402002" - "20033602040c010b2002417f3602040b41010c010b20022001290300370001200241196a20" - "04290300370000200241116a2005290300370000200241096a200629030037000041000b3a" - "0000200141206a2400200041d8066a200241d388c000410a4100101820002d00d806410146" - "044020002802dc06210141878ac0004112421010001a0c010b41dd88c00041384101410041" - "0010011a230041306b22012400200141808080900136020c200141286a2204420037030020" - "0141206a22054200370300200141186a220642003703002001420037031020004190066a22" - "02027f200041046a41142001410c6a4104200141106a412010162203412047044002402003" - "4100480440200220033602040c010b2002417f3602040b41010c010b200220012903103700" - "01200241196a2004290300370000200241116a2005290300370000200241096a2006290300" - "37000041000b3a0000200141306a2400200041d8066a2002419589c0004106418180201018" - "20002d00d806410146044020002802dc06210141878ac0004112421210001a0c010b410121" - "01419b89c000413441014100410010011a230041306b22022400200241808080980136020c" - "200241286a22054200370300200241206a22064200370300200241186a2207420037030020" - "024200370310200041b4066a2203027f200041046a41142002410c6a4104200241106a4120" - "101722044120470440024020044100480440200320043602040c010b2003417f3602040b41" - "010c010b20032002290310370001200341196a2005290300370000200341116a2006290300" - "370000200341096a200729030037000041000b3a0000200241306a2400200041d8066a2003" - "41cf89c000410541818020101820002d00d806410146044020002802dc06210141878ac000" - "4112421310001a0c010b41d489c000413341014100410010011a0b20004180076a24002001" - "0f0b418080c000410b417f20012001417f4e1bac1000000bfd0401067f200241104f044002" - "4020002000410020006b41037122056a22044f0d0020012103200504402005210603402000" - "20032d00003a0000200341016a2103200041016a2100200641016b22060d000b0b20054101" - "6b4107490d000340200020032d00003a0000200041016a200341016a2d00003a0000200041" - "026a200341026a2d00003a0000200041036a200341036a2d00003a0000200041046a200341" - "046a2d00003a0000200041056a200341056a2d00003a0000200041066a200341066a2d0000" - "3a0000200041076a200341076a2d00003a0000200341086a2103200041086a22002004470d" - "000b0b2004200220056b2207417c7122086a21000240200120056a22064103714504402000" - "20044d0d0120062101034020042001280200360200200141046a2101200441046a22042000" - "490d000b0c010b200020044d0d002006410374220541187121032006417c71220241046a21" - "01410020056b41187121052002280200210203402004200220037620012802002202200574" - "72360200200141046a2101200441046a22042000490d000b0b20074103712102200620086a" - "21010b02402000200020026a22064f0d002002410771220304400340200020012d00003a00" - "00200141016a2101200041016a2100200341016b22030d000b0b200241016b4107490d0003" - "40200020012d00003a0000200041016a200141016a2d00003a0000200041026a200141026a" - "2d00003a0000200041036a200141036a2d00003a0000200041046a200141046a2d00003a00" - "00200041056a200141056a2d00003a0000200041066a200141066a2d00003a000020004107" - "6a200141076a2d00003a0000200141086a2101200041086a22002006470d000b0b0b0ba30a" - "0100418080c0000b990a6572726f725f636f64653d47657474696e67206669656c643a2046" - "69656c6420646174613a204572726f722067657474696e67206669656c643a204572726f72" - "3a204572726f722067657474696e67206b65796c65743a202424242424205354415254494e" - "47205741534d20455845435554494f4e2024242424244163636f756e743a44657374696e61" - "74696f6e3a4163636f756e744163636f756e74206f626a656374206578697374732c207072" - "6f63656564696e67207769746820657363726f772066696e6973682e54727573746c696e65" - "54727573746c696e65206f626a656374206578697374732c2070726f63656564696e672077" - "69746820657363726f772066696e6973682e414d4d414d4d206f626a656374206578697374" - "732c2070726f63656564696e67207769746820657363726f772066696e6973682e43686563" - "6b436865636b206f626a656374206578697374732c2070726f63656564696e672077697468" - "20657363726f772066696e6973682e7465726d73616e64636f6e646974696f6e7343726564" - "656e7469616c43726564656e7469616c206f626a656374206578697374732c2070726f6365" - "6564696e67207769746820657363726f772066696e6973682e44656c656761746544656c65" - "67617465206f626a656374206578697374732c2070726f63656564696e6720776974682065" - "7363726f772066696e6973682e4465706f736974507265617574684465706f736974507265" - "61757468206f626a656374206578697374732c2070726f63656564696e6720776974682065" - "7363726f772066696e6973682e444944444944206f626a656374206578697374732c207072" - "6f63656564696e67207769746820657363726f772066696e6973682e457363726f77457363" - "726f77206f626a656374206578697374732c2070726f63656564696e672077697468206573" - "63726f772066696e6973682e4d505449737375616e63654d505449737375616e6365206f62" - "6a656374206578697374732c2070726f63656564696e67207769746820657363726f772066" - "696e6973682e4d50546f6b656e4d50546f6b656e206f626a656374206578697374732c2070" - "726f63656564696e67207769746820657363726f772066696e6973682e4e46546f6b656e4f" - "666665724e46546f6b656e4f66666572206f626a656374206578697374732c2070726f6365" - "6564696e67207769746820657363726f772066696e6973682e4f666665724f66666572206f" - "626a656374206578697374732c2070726f63656564696e67207769746820657363726f7720" - "66696e6973682e5061794368616e6e656c5061794368616e6e656c206f626a656374206578" - "697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e50" - "65726d697373696f6e6564446f6d61696e5065726d697373696f6e6564446f6d61696e206f" - "626a656374206578697374732c2070726f63656564696e67207769746820657363726f7720" - "66696e6973682e5369676e65724c6973745369676e65724c697374206f626a656374206578" - "697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e54" - "69636b65745469636b6574206f626a656374206578697374732c2070726f63656564696e67" - "207769746820657363726f772066696e6973682e5661756c745661756c74206f626a656374" - "206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973" - "682e43757272656e74207365712076616c75653a004d0970726f64756365727302086c616e" - "6775616765010452757374000c70726f6365737365642d6279010572757374631d312e3837" - "2e30202831373036376539616320323032352d30352d303929002c0f7461726765745f6665" - "617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d657874"; + "0061736d0100000001480960067f7f7f7f7f7f017f60047f7f7f7f017f60087f7f7f7f7f7f7f7f017f60037f7f7f01" + "7f60037f7f7e017f60057f7f7f7f7f017f60057f7f7f7f7f006000017f60037f7f7f000284051808686f73745f6c69" + "620974726163655f6e756d000408686f73745f6c6962057472616365000508686f73745f6c69621063616368655f6c" + "65646765725f6f626a000308686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000108686f73" + "745f6c69621c6765745f63757272656e745f6c65646765725f6f626a5f6669656c64000308686f73745f6c69620d74" + "726163655f6163636f756e74000108686f73745f6c69620e6163636f756e745f6b65796c6574000108686f73745f6c" + "69620b6c696e655f6b65796c6574000208686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c6962" + "0c636865636b5f6b65796c6574000008686f73745f6c69621163726564656e7469616c5f6b65796c6574000208686f" + "73745f6c69620f64656c65676174655f6b65796c6574000008686f73745f6c6962166465706f7369745f7072656175" + "74685f6b65796c6574000008686f73745f6c69620a6469645f6b65796c6574000108686f73745f6c69620d65736372" + "6f775f6b65796c6574000008686f73745f6c6962136d70745f69737375616e63655f6b65796c6574000008686f7374" + "5f6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c6962106e66745f6f666665725f6b65796c6574" + "000008686f73745f6c69620c6f666665725f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b6579" + "6c6574000208686f73745f6c69621a7065726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73" + "745f6c69620e7369676e6572735f6b65796c6574000108686f73745f6c69620d7469636b65745f6b65796c65740000" + "08686f73745f6c69620c7661756c745f6b65796c6574000003040306070805030100110619037f01418080c0000b7f" + "0041ad8ac0000b7f0041b08ac0000b073e05066d656d6f727902000d6f626a6563745f65786973747300180666696e" + "69736800190a5f5f646174615f656e6403010b5f5f686561705f6261736503020ab73403a80502017f017e230041a0" + "016b22052400024020012d0000410146044041c280c000411620012802042201ac10001a200041013a000020002001" + "3602040c010b200541186a200141196a290000370300200541106a200141116a290000370300200541086a20014109" + "6a290000370300200520012900013703002002200320054120410110011a02402005412041001002220141004a0440" + "2004450440418b80c000410f4285801410001a20014185801420054180016a412010032201412047044041a680c000" + "4115417f20012001417f4e1b2201ac10001a200041013a0000200020013602040c040b200541c2006a20054182016a" + "2d00003a0000200541f0006a20054197016a2900002206370300200541286a22012005418f016a2900003703002005" + "41306a22022006370300200541386a22032005419f016a2d00003a0000200520052f0080013b014020052005290087" + "013703202005200528008301360043200541df006a20032d00003a0000200541d7006a2002290300370000200541cf" + "006a200129030037000020052005290320370047419a80c000410c200541406b4120410110011a0c020b418b80c000" + "410f2004ac10001a2001200420054180016a411410032201411447044041a680c0004115417f20012001417f4e1b22" + "01ac10001a200041013a0000200020013602040c030b200541c2006a20054182016a2d00003a000020052005290087" + "0137036020052005418c016a290000370065200520052f0080013b0140200520052903603703202005200529006537" + "00252005200528008301360043200541cc006a200529002537000020052005290320370047419a80c000410c200541" + "406b4114410110011a0c010b41bb80c00041072001ac10001a200041013a0000200020013602040c010b2000418002" + "3b01000b200541a0016a24000b8b2a02087f027e23004180076b2200240041d880c000412341014100410010011a02" + "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" + "10011a200041808080303602d806200041f8016a22034200370300200041f0016a22044200370300200041e8016a22" + "054200370300200042003703e0010240200041046a4114200041d8066a4104200041e0016a41201009220241204704" + "40024020024100480440200020023602c0020c010b2000417f3602c0020b410121010c010b200041d5026a20032903" + "00370000200041cd026a2004290300370000200041c5026a2005290300370000200020002903e0013700bd020b2000" + "20013a00bc02200041e0016a200041bc026a41bf82c000410541818020101820002d00e001410146044020002802e4" + "01210141878ac0004112420610001a0c010b4100210141c482c000413341014100410010011a200041f8016a220342" + "00370300200041f0016a22044200370300200041e8016a22054200370300200042003703e0010240200041046a2202" + "41142002411441f782c0004112200041e0016a4120100a22024120470440024020024100480440200020023602e402" + "0c010b2000417f3602e4020b410121010c010b200041f9026a2003290300370000200041f1026a2004290300370000" + "200041e9026a2005290300370000200020002903e0013700e1020b200020013a00e002200041e0016a200041e0026a" + "418983c000410a41988020101820002d00e001410146044020002802e401210141878ac0004112420710001a0c010b" + "41002101419383c000413841014100410010011a200041f8016a22034200370300200041f0016a2204420037030020" + "0041e8016a22054200370300200042003703e0010240200041046a4114200041186a4114200041e0016a4120100b22" + "02412047044002402002410048044020002002360288030c010b2000417f360288030b410121010c010b2000419d03" + "6a200329030037000020004195036a20042903003700002000418d036a2005290300370000200020002903e0013700" + "85030b200020013a008403200041e0016a20004184036a41cb83c000410841818020101820002d00e0014101460440" + "20002802e401210141878ac0004112420810001a0c010b41d383c000413641014100410010011a230041206b220124" + "00200141186a22044200370300200141106a22054200370300200141086a2206420037030020014200370300200041" + "a8036a2202027f200041046a4114200041186a411420014120100c2203412047044002402003410048044020022003" + "3602040c010b2002417f3602040b41010c010b20022001290300370001200241196a2004290300370000200241116a" + "2005290300370000200241096a200629030037000041000b3a0000200141206a2400200041e0016a2002418984c000" + "410e41818020101820002d00e001410146044020002802e401210141878ac0004112420910001a0c010b419784c000" + "413c41014100410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141" + "086a2206420037030020014200370300200041cc036a2202027f200041046a411420014120100d2203412047044002" + "4020034100480440200220033602040c010b2002417f3602040b41010c010b20022001290300370001200241196a20" + "04290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141206a240020" + "0041e0016a200241d384c000410341818020101820002d00e001410146044020002802e401210141878ac000411242" + "0a10001a0c010b41d684c000413141014100410010011a230041306b22012400200141808080d80036020c20014128" + "6a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041f0036a2202" + "027f200041046a41142001410c6a4104200141106a4120100e22034120470440024020034100480440200220033602" + "040c010b2002417f3602040b41010c010b20022001290310370001200241196a2004290300370000200241116a2005" + "290300370000200241096a200629030037000041000b3a0000200141306a2400200041e0016a2002418785c0004106" + "41818020101820002d00e001410146044020002802e401210141878ac0004112420b10001a0c010b418d85c0004134" + "41014100410010011a230041306b22012400200141808080e00036020c200141286a22044200370300200141206a22" + "054200370300200141186a220642003703002001420037031020004194046a2202027f200041046a41142001410c6a" + "4104200141106a4120100f22034120470440024020034100480440200220033602040c010b2002417f3602040b4101" + "0c010b20022001290310370001200241196a2004290300370000200241116a2005290300370000200241096a200629" + "030037000041000b3a0000200141306a2400200041f4016a200041146a280100360200200041ec016a2000410c6a29" + "0100370200200020002901043702e401200041808080e0003602e001200041d8066a200241c185c000410b41848020" + "101820002d00d806410146044020002802dc06210141878ac0004112420c10001a0c010b41cc85c000413941014100" + "410010011a230041206b22012400200141186a22044200370300200141106a22054200370300200141086a22064200" + "37030020014200370300200041b8046a2202027f200041e0016a4118200041186a4114200141201010220341204704" + "40024020034100480440200220033602040c010b2002417f3602040b41010c010b2002200129030037000120024119" + "6a2004290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141206a24" + "00200041d8066a2002418586c000410741818020101820002d00d806410146044020002802dc06210141878ac00041" + "12420d10001a0c010b418c86c000413541014100410010011a230041306b220124002001418080803036020c200141" + "286a22044200370300200141206a22054200370300200141186a2206420037030020014200370310200041dc046a22" + "02027f200041186a41142001410c6a4104200141106a41201011220341204704400240200341004804402002200336" + "02040c010b2002417f3602040b41010c010b20022001290310370001200241196a2004290300370000200241116a20" + "05290300370000200241096a200629030037000041000b3a0000200141306a2400200041d8066a200241c186c00041" + "0c41828020101820002d00d806410146044020002802dc06210141878ac0004112420d10001a0c010b41cd86c00041" + "3a41014100410010011a230041306b22012400200141808080e80036020c200141286a22044200370300200141206a" + "22054200370300200141186a220642003703002001420037031020004180056a2202027f200041046a41142001410c" + "6a4104200141106a4120101222034120470440024020034100480440200220033602040c010b2002417f3602040b41" + "010c010b20022001290310370001200241196a2004290300370000200241116a2005290300370000200241096a2006" + "29030037000041000b3a0000200141306a2400200041d8066a2002418787c000410541818020101820002d00d80641" + "0146044020002802dc06210141878ac0004112420d10001a0c010b418c87c000413341014100410010011a23004130" + "6b22012400200141808080f00036020c200141286a22044200370300200141206a22054200370300200141186a2206" + "420037030020014200370310200041a4056a2202027f200041046a4114200041186a41142001410c6a410420014110" + "6a4120101322034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b200220" + "01290310370001200241196a2004290300370000200241116a2005290300370000200241096a200629030037000041" + "000b3a0000200141306a2400200041d8066a200241bf87c000410a41818020101820002d00d8064101460440200028" + "02dc06210141878ac0004112420e10001a0c010b41c987c000413841014100410010011a230041306b220124002001" + "41808080f80036020c200141286a22044200370300200141206a22054200370300200141186a220642003703002001" + "4200370310200041c8056a2202027f200041046a41142001410c6a4104200141106a41201014220341204704400240" + "20034100480440200220033602040c010b2002417f3602040b41010c010b20022001290310370001200241196a2004" + "290300370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a24002000" + "41d8066a2002418188c000411241828020101820002d00d806410146044020002802dc06210141878ac0004112420f" + "10001a0c010b419388c00041c00041014100410010011a230041206b22012400200141186a22044200370300200141" + "106a22054200370300200141086a2206420037030020014200370300200041ec056a2202027f200041046a41142001" + "4120101522034120470440024020034100480440200220033602040c010b2002417f3602040b41010c010b20022001" + "290300370001200241196a2004290300370000200241116a2005290300370000200241096a20062903003700004100" + "0b3a0000200141206a2400200041d8066a200241d388c000410a4100101820002d00d806410146044020002802dc06" + "210141878ac0004112421010001a0c010b41dd88c000413841014100410010011a230041306b220124002001418080" + "80900136020c200141286a22044200370300200141206a22054200370300200141186a220642003703002001420037" + "031020004190066a2202027f200041046a41142001410c6a4104200141106a41201016220341204704400240200341" + "00480440200220033602040c010b2002417f3602040b41010c010b20022001290310370001200241196a2004290300" + "370000200241116a2005290300370000200241096a200629030037000041000b3a0000200141306a2400200041d806" + "6a2002419589c000410641818020101820002d00d806410146044020002802dc06210141878ac0004112421210001a" + "0c010b41012101419b89c000413441014100410010011a230041306b22022400200241808080980136020c20024128" + "6a22054200370300200241206a22064200370300200241186a2207420037030020024200370310200041b4066a2203" + "027f200041046a41142002410c6a4104200241106a4120101722044120470440024020044100480440200320043602" + "040c010b2003417f3602040b41010c010b20032002290310370001200341196a2005290300370000200341116a2006" + "290300370000200341096a200729030037000041000b3a0000200241306a2400200041d8066a200341cf89c0004105" + "41818020101820002d00d806410146044020002802dc06210141878ac0004112421310001a0c010b41d489c0004133" + "41014100410010011a0b20004180076a240020010f0b418080c000410b417f20012001417f4e1bac1000000bfd0401" + "067f200241104f0440024020002000410020006b41037122056a22044f0d0020012103200504402005210603402000" + "20032d00003a0000200341016a2103200041016a2100200641016b22060d000b0b200541016b4107490d0003402000" + "20032d00003a0000200041016a200341016a2d00003a0000200041026a200341026a2d00003a0000200041036a2003" + "41036a2d00003a0000200041046a200341046a2d00003a0000200041056a200341056a2d00003a0000200041066a20" + "0341066a2d00003a0000200041076a200341076a2d00003a0000200341086a2103200041086a22002004470d000b0b" + "2004200220056b2207417c7122086a21000240200120056a2206410371450440200020044d0d012006210103402004" + "2001280200360200200141046a2101200441046a22042000490d000b0c010b200020044d0d00200641037422054118" + "7121032006417c71220241046a2101410020056b411871210520022802002102034020042002200376200128020022" + "0220057472360200200141046a2101200441046a22042000490d000b0b20074103712102200620086a21010b024020" + "00200020026a22064f0d002002410771220304400340200020012d00003a0000200141016a2101200041016a210020" + "0341016b22030d000b0b200241016b4107490d000340200020012d00003a0000200041016a200141016a2d00003a00" + "00200041026a200141026a2d00003a0000200041036a200141036a2d00003a0000200041046a200141046a2d00003a" + "0000200041056a200141056a2d00003a0000200041066a200141066a2d00003a0000200041076a200141076a2d0000" + "3a0000200141086a2101200041086a22002006470d000b0b0b0ba30a0100418080c0000b990a6572726f725f636f64" + "653d47657474696e67206669656c643a204669656c6420646174613a204572726f722067657474696e67206669656c" + "643a204572726f723a204572726f722067657474696e67206b65796c65743a202424242424205354415254494e4720" + "5741534d20455845435554494f4e2024242424244163636f756e743a44657374696e6174696f6e3a4163636f756e74" + "4163636f756e74206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066" + "696e6973682e54727573746c696e6554727573746c696e65206f626a656374206578697374732c2070726f63656564" + "696e67207769746820657363726f772066696e6973682e414d4d414d4d206f626a656374206578697374732c207072" + "6f63656564696e67207769746820657363726f772066696e6973682e436865636b436865636b206f626a6563742065" + "78697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e7465726d73616e64636f" + "6e646974696f6e7343726564656e7469616c43726564656e7469616c206f626a656374206578697374732c2070726f" + "63656564696e67207769746820657363726f772066696e6973682e44656c656761746544656c6567617465206f626a" + "656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4465706f73" + "6974507265617574684465706f73697450726561757468206f626a656374206578697374732c2070726f6365656469" + "6e67207769746820657363726f772066696e6973682e444944444944206f626a656374206578697374732c2070726f" + "63656564696e67207769746820657363726f772066696e6973682e457363726f77457363726f77206f626a65637420" + "6578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4d505449737375616e" + "63654d505449737375616e6365206f626a656374206578697374732c2070726f63656564696e672077697468206573" + "63726f772066696e6973682e4d50546f6b656e4d50546f6b656e206f626a656374206578697374732c2070726f6365" + "6564696e67207769746820657363726f772066696e6973682e4e46546f6b656e4f666665724e46546f6b656e4f6666" + "6572206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e697368" + "2e4f666665724f66666572206f626a656374206578697374732c2070726f63656564696e6720776974682065736372" + "6f772066696e6973682e5061794368616e6e656c5061794368616e6e656c206f626a656374206578697374732c2070" + "726f63656564696e67207769746820657363726f772066696e6973682e5065726d697373696f6e6564446f6d61696e" + "5065726d697373696f6e6564446f6d61696e206f626a656374206578697374732c2070726f63656564696e67207769" + "746820657363726f772066696e6973682e5369676e65724c6973745369676e65724c697374206f626a656374206578" + "697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e5469636b65745469636b65" + "74206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e" + "5661756c745661756c74206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f" + "772066696e6973682e43757272656e74207365712076616c75653a004d0970726f64756365727302086c616e677561" + "6765010452757374000c70726f6365737365642d6279010572757374631d312e38372e302028313730363765396163" + "20323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c" + "732b087369676e2d657874"; extern std::string const codecovTestsWasmHex = "0061736d0100000001570b60067f7f7f7f7f7f017f60047f7f7f7f017f60027f7f017f60057f7f7f7f7f017f60037f" - "7f7f017f60077f7f7f7f" - "7f7f7f017f60087f7f7f7f7f7f7f7f017f60017f017f60037f7f7e017f60047f7f7f7f006000017f02990d3c08686f" - "73745f6c696205747261" - "6365000308686f73745f6c69620974726163655f6e756d000808686f73745f6c69620e6765745f6c65646765725f73" - "716e000208686f73745f" - "6c6962166765745f706172656e745f6c65646765725f74696d65000208686f73745f6c6962166765745f706172656e" - "745f6c65646765725f68" - "617368000208686f73745f6c69620c6765745f626173655f666565000208686f73745f6c696211616d656e646d656e" - "745f656e61626c656400" - "0208686f73745f6c69620c6765745f74785f6669656c64000408686f73745f6c69620e6163636f756e745f6b65796c" - "6574000108686f73745f" - "6c69621063616368655f6c65646765725f6f626a000408686f73745f6c69621c6765745f63757272656e745f6c6564" - "6765725f6f626a5f6669" - "656c64000408686f73745f6c6962146765745f6c65646765725f6f626a5f6669656c64000108686f73745f6c696213" - "6765745f74785f6e6573" - "7465645f6669656c64000108686f73745f6c6962236765745f63757272656e745f6c65646765725f6f626a5f6e6573" - "7465645f6669656c6400" - "0108686f73745f6c69621b6765745f6c65646765725f6f626a5f6e65737465645f6669656c64000308686f73745f6c" - "6962106765745f74785f" - "61727261795f6c656e000708686f73745f6c6962206765745f63757272656e745f6c65646765725f6f626a5f617272" - "61795f6c656e00070868" - "6f73745f6c6962186765745f6c65646765725f6f626a5f61727261795f6c656e000208686f73745f6c696217676574" - "5f74785f6e6573746564" - "5f61727261795f6c656e000208686f73745f6c6962276765745f63757272656e745f6c65646765725f6f626a5f6e65" - "737465645f6172726179" - "5f6c656e000208686f73745f6c69621f6765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e" - "000408686f73745f6c69" - "620b7570646174655f64617461000208686f73745f6c696213636f6d707574655f7368613531325f68616c66000108" - "686f73745f6c69620963" - "6865636b5f736967000008686f73745f6c6962076765745f6e6674000008686f73745f6c69620e6765745f6e66745f" - "69737375657200010868" - "6f73745f6c69620d6765745f6e66745f7461786f6e000108686f73745f6c69620d6765745f6e66745f666c61677300" - "0208686f73745f6c6962" - "146765745f6e66745f7472616e736665725f666565000208686f73745f6c69620e6765745f6e66745f73657269616c" - "000108686f73745f6c69" - "620d74726163655f6163636f756e74000108686f73745f6c69620c74726163655f616d6f756e74000108686f73745f" - "6c69620c636865636b5f" - "6b65796c6574000008686f73745f6c69620f666c6f61745f66726f6d5f75696e74000308686f73745f6c69620b6c69" - "6e655f6b65796c657400" - "0608686f73745f6c69620a616d6d5f6b65796c6574000008686f73745f6c69621163726564656e7469616c5f6b6579" - "6c6574000608686f7374" - "5f6c69620e6d70746f6b656e5f6b65796c6574000008686f73745f6c69621274726163655f6f70617175655f666c6f" - "6174000108686f73745f" - "6c69620d666c6f61745f636f6d70617265000108686f73745f6c696209666c6f61745f616464000508686f73745f6c" - "69620e666c6f61745f73" - "75627472616374000508686f73745f6c69620e666c6f61745f6d756c7469706c79000508686f73745f6c69620c666c" - "6f61745f646976696465" - "000508686f73745f6c69620a666c6f61745f726f6f74000008686f73745f6c696209666c6f61745f706f7700000868" - "6f73745f6c696209666c" - "6f61745f6c6f67000308686f73745f6c69620d657363726f775f6b65796c6574000008686f73745f6c6962136d7074" - "5f69737375616e63655f" - "6b65796c6574000008686f73745f6c6962106e66745f6f666665725f6b65796c6574000008686f73745f6c69620c6f" - "666665725f6b65796c65" - "74000008686f73745f6c69620d6f7261636c655f6b65796c6574000008686f73745f6c69620e7061796368616e5f6b" - "65796c6574000608686f" - "73745f6c69621a7065726d697373696f6e65645f646f6d61696e5f6b65796c6574000008686f73745f6c69620d7469" - "636b65745f6b65796c65" - "74000008686f73745f6c69620c7661756c745f6b65796c6574000008686f73745f6c69620f64656c65676174655f6b" - "65796c6574000008686f" - "73745f6c6962166465706f7369745f707265617574685f6b65796c6574000008686f73745f6c69620a6469645f6b65" - "796c6574000108686f73" - "745f6c69620e7369676e6572735f6b65796c65740001030302090a05030100110619037f01418080c0000b7f0041bb" - "a1c0000b7f0041c0a1c0" - "000b072e04066d656d6f727902000666696e697368003d0a5f5f646174615f656e6403010b5f5f686561705f626173" - "6503020abf2c02460002" - "40200020014704402002200341014100410010001a20004100480d01418b80c000410b2000ad1001000b2002200320" - "00ac10011a0f0b418b80" - "c000410b2000ac1001000bf52b020a7f017e23004190026b22002400419680c000412341014100410010001a200041" - "00360260200041e0006a" - "220241041002410441bd8cc000410e103c20004100360260200241041003410441cb8cc0004116103c200041f8006a" - "22044200370300200041" - "f0006a22014200370300200041e8006a2205420037030020004200370360200241201004412041e18cc0004116103c" - "20004100360260200241" - "041005410441f78cc000410c103c200041106a2207428182848890a0c08001370300200041186a2206428182848890" - "a0c08001370300200041" - "206a2209428182848890a0c080013703002000428182848890a0c0800137030841b980c000410e1006410141c780c0" - "004111103c200041086a" - "41201006410141c780c0004111103c418180202002411410072203411446044002402000412e6a200041e2006a2d00" - "003a0000200020002900" - "673703e8012000200041ec006a2900003700ed01200020002f00603b012c200020002903e8013703a8012000200029" - "00ed013700ad01200020" - "0028006336002f200041386a20002900ad01370000200020002903a801370033200442003703002001420037030020" - "05420037030020004200" - "3703602000412c6a2204411420024120100822034120470d00200041c2006a20002d00623a0000200041f0016a2203" - "200041ef006a29000022" - "0a370300200041cf006a200a370000200041d7006a200041f7006a290000370000200041df006a200041ff006a2d00" - "003a0000200020002f01" - "603b01402000200028006336004320002000290067370047200041406b412041001009410141d880c0004110103c20" - "01410036020020054200" - "370300200042003703604181802020024114100a411441838dc000411c103c20014100360200200542003703002000" - "42003703604101418180" - "2020024114100b4114419f8dc0004114103c200041043602a001200041818020360260200041f8016a220541003602" - "00200342003703002000" + "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" - "200042003703e8012002" - "20002802a00120014114100d411441c68dc0004123103c2005410036020020034200370300200042003703e8014101" - "200220002802a0012001" - "4114100e411441e98dc000411b103c4189803c100f412041e880c0004110103c4189803c1010412041f880c0004120" - "103c41014189803c1011" - "4120419881c0004118103c200220002802a0011012412041b081c0004117103c200220002802a0011013412041c781" - "c0004127103c41012002" - "20002802a0011014412041ee81c000411f103c2004411410154114418d82c000410b103c20004180026a2208420037" - "03002005420037030020" - "034200370300200042003703e801200220002802a001200141201016412041848ec0004113103c419882c000410c41" - "a482c000410b41af82c0" - "00410e1017410141bd82c0004109103c200041c0016a2009290300370300200041b8016a2006290300370300200041" - "b0016a20072903003703" - "00200020002903083703a801200541003b010020034200370300200042003703e80120044114200041a8016a220741" - "20200141121018411241" - "978ec0004107103c2005410036020020034200370300200042003703e801200741202001411410194114419e8ec000" - "410e103c200041003602" - "e8012007412020014104101a410441ac8ec000410d103c20074120101b410841c682c000410d103c20074120101c41" - "0a41d382c0004114103c" - "200041003602e8012007412020014104101d410441b98ec000410e103c41e782c000410d20044114101e410041f482" - "c000410d103c41e782c0" - "00410d418183c0004108101f4100418983c000410c103c41e782c000410d419583c0004108101f4100419d83c00041" - "11103c417f4104100441" - "7141ae83c000411e103c200041003602e8012001417f1004417141c78ec000411e103c200041ea016a41003a000020" - "0041003b01e801200141" - "031004417d41e58ec0004124103c200041003602e8012001418094ebdc031004417341898fc0004123103c4102100f" - "416f41cc83c000411f10" - "3c417f20002802a0011012417141eb83c000411f103c2002417f10124171418a84c000411f103c2002418120101241" - "7441a984c0004120103c" - "200041e094ebdc036a220620002802a0011012417341c984c000411f103c2008420037030020054200370300200342" - "00370300200042003703" - "e8012004411420064108200141201020417341ac8fc0004118103c2008420037030020054200370300200342003703" - "00200042003703e80120" - "04411420044114200141201020417141c48fc000411a103c2008420037030020054200370300200342003703002000" - "42003703e80120064108" - "2001412041001021417341de8fc0004117103c200842003703002005420037030020034200370300200042003703e8" - "01200220002802a00120" - "01412041001021417141f58fc0004120103c200620002802a00141011009417341e884c0004118103c200220002802" - "a0014101100941714180" - "85c000411a103c200842003703002005420037030020034200370300200042003703e801200620002802a001200141" - "2010084173419590c000" - "4116103c200842003703002005420037030020034200370300200042003703e801200220002802a001200141201008" - "417141ab90c000411810" - "3c200842003703002005420037030020034200370300200042003703e8012004411420044114200620002802a00120" - "0141201022417341c390" - "c000411c103c200842003703002005420037030020034200370300200042003703e801200441142004411420022000" - "2802a001200141201022" - "417141df90c000411e103c200842003703002005420037030020034200370300200042003703e80141a7a1c0004114" - "200620002802a0012001" - "41201023417341fd90c0004119103c200842003703002005420037030020034200370300200042003703e80141a7a1" - "c0004114200220002802" - "a0012001412010234171419691c000411f103c200842003703002005420037030020034200370300200042003703e8" - "0141a7a1c0004114419a" - "85c0004114200141201023417141b591c0004129103c20084200370300200542003703002003420037030020004200" - "3703e80141ae85c00041" - "2841a7a1c0004114200141201023417141de91c0004125103c200041dc016a2000413c6a280100360200200041d401" - "6a200041346a29010037" - "02002000200029012c3702cc01200041808080083602c801200041003b01e801200041c8016a2209411841a7a1c000" - "41142001410210234171" - "418392c000410e103c200620002802a001422a1001417341d685c0004111103c200041003b01e80141022001410210" - "07416f419192c000411b" - "103c200041003b01e801410220014102100a416f41ac92c000412b103c200041003b01e8014101410220014102100b" - "416f41d792c000412310" - "3c4102100f416f41cc83c000411f103c41021010416f41e785c000412f103c410141021011416f419686c000412710" - "3c41b980c00041812010" - "06417441bd86c000411f103c41b980c00041c1001006417441dc86c000411a103c200041003b01e801200241812020" - "014102100c417441fa92" - "c0004121103c200041003b01e801200241812020014102100d4174419b93c0004131103c200041003b01e801410120" - "0241812020014102100e" - "417441cc93c0004129103c20024181201012417441f686c0004125103c200241812010134174419b87c0004135103c" - "41012002418120101441" - "7441d087c000412d103c20024181201015417441fd87c0004119103c41e782c00041812041a482c000410b41af82c0" - "00410e1017417441bd82" - "c0004109103c41e782c000410d41a482c00041812041af82c000410e1017417441bd82c0004109103c41e782c00041" - "0d41a482c000410b41af" - "82c0004181201017417441bd82c0004109103c200041003b01e8012002418120200141021016417441f593c0004121" - "103c200041003b01e801" - "41a7a1c00041812041a7a1c00041142001410210234174419694c0004118103c200041003b01e80120044114200441" - "14200241812020014102" - "1024417441ae94c000411f103c200041003b01e801200941812020044114200141021025417441cd94c0004122103c" - "41e782c000410d200620" - "002802a001410010004173419688c000410f103c200042d487b6f4c7d4b1c0003700e00141e782c000410d200041e0" - "95ebdc036a2207410810" - "26417341a588c000411c103c41e782c000410d200620002802a001101f417341c188c0004116103c20074108200041" - "e0016a22064108102741" - "7341d788c0004118103c20064108200741081027417341ef88c0004118103c200041003b01e8012007410820064108" - "20014102410010284173" - "41ef94c0004114103c200041003b01e801200641082007410820014102410010284173418395c0004114103c200041" - "003b01e8012007410820" - "06410820014102410010294173419795c0004119103c200041003b01e8012006410820074108200141024100102941" - "7341b095c0004119103c" - "200041003b01e8012007410820064108200141024100102a417341c995c0004119103c200041003b01e80120064108" - "20074108200141024100" + "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" - "3c200041003b01e80120" - "06410820074108200141024100102b4173419296c0004117103c200041003b01e80120074108410320014102410010" - "2c417341a996c0004114" - "103c200041003b01e801200741084103200141024100102d417341bd96c0004113103c200041003b01e80120074108" - "200141024100102e4173" - "41d096c0004113103c200842003703002005420037030020034200370300200042003703e801200441142004411420" - "014120102f417141e396" - "c000411f103c200842003703002005420037030020034200370300200042003703e801200441142004411420014120" - "10304171418297c00041" - "25103c200842003703002005420037030020034200370300200042003703e801200441142004411420014120103141" - "7141a797c0004122103c" - "200842003703002005420037030020034200370300200042003703e8012004411420044114200141201032417141c9" - "97c000411e103c200842" - "003703002005420037030020034200370300200042003703e8012004411420044114200141201033417141e797c000" - "411f103c200842003703" - "002005420037030020034200370300200042003703e8012004411420044114200441142001412010344171418698c0" - "004120103c2008420037" - "03002005420037030020034200370300200042003703e8012004411420044114200141201035417141a698c000412c" - "103c2008420037030020" - "05420037030020034200370300200042003703e8012004411420044114200141201036417141d298c000411f103c20" - "08420037030020054200" - "37030020034200370300200042003703e8012004411420044114200141201037417141f198c000411e103c20022000" - "2802a001410010094171" - "418789c0004123103c200041003b01e80120044114200220002802a0012001410210184171418f99c000411a103c20" - "0041003b01e801200220" - "002802a001200141021019417141a999c0004121103c200041003b01e801200220002802a00120014102101a417141" - "ca99c0004120103c2002" - "20002802a001101b417141aa89c0004120103c200220002802a001101c417141ca89c0004127103c200041003602e8" - "01200220002802a00120" - "014104101d417141ea99c0004121103c200041003b01e801200220002802a0012001410210084171418b9ac0004124" - "103c2000418080800836" - "02e801200041003b018e02200220002802a001200141042000418e026a220341021020417141af9ac0004122103c20" - "0041003b018e02200220" - "002802a00122052004411420022005200341021024417141d19ac0004128103c200041003b018e0220044114200220" - "002802a0012205200220" - "05200341021024417141f99ac0004128103c200041003b018e02200220002802a00120044114200341021038417141" - "a19bc0004126103c2000" - "41003b018e0220044114200220002802a001200341021038417141c79bc0004126103c200041003b018e0220022000" - "2802a001200441142003" - "41021039417141ed9bc000412d103c200041003b018e0220044114200220002802a0012003410210394171419a9cc0" - "00412d103c200041003b" - "018e02200220002802a00120034102103a417141c79cc0004120103c200041003b018e02200220002802a001200141" - "0420034102102f417141" - "e79cc0004123103c200041003b018e02200220002802a00120044114419a85c00041142003410210224171418a9dc0" - "004122103c200041003b" - "018e0220044114200220002802a001419a85c0004114200341021022417141ac9dc0004122103c200041003b018e02" - "200220002802a0012001" - "4104200341021030417141ce9dc0004129103c200041003b018e0220094118200220002802a0012003410210254171" - "41f79dc0004124103c20" - "0041003b018e02200220002802a001200141042003410210314171419b9ec0004126103c200041003b018e02200220" - "002802a0012001410420" - "0341021032417141c19ec0004122103c200041003b018e02200220002802a00120014104200341021033417141e39e" - "c0004123103c20004100" - "3b018e02200220002802a0012004411420014104200341021034417141869fc0004125103c200041003b018e022004" - "4114200220002802a001" - "20014104200341021034417141ab9fc0004125103c200041003b018e02200220002802a00120014104200341021035" - "417141d09fc000413010" - "3c200041003b018e02200220002802a00120034102103b41714180a0c0004124103c200041003b018e022002200028" - "02a00120014104200341" - "021036417141a4a0c0004123103c200041003b018e02200220002802a00120014104200341021037417141c7a0c000" - "4122103c200041003b01" - "8e02200220002802a00141f189c0004120200341021018417141e9a0c000411d103c41e782c000410d200220002802" - "a001101e417141918ac0" - "004123103c41e796abdd03410d41f189c000412041001000417341b48ac0004110103c41e796abdd03410d20064108" - "1026417341c48ac00041" - "1d103c41e796abdd03410d20044114101e417341e18ac0004118103c41e796abdd03410d419583c0004108101f4173" - "41f98ac0004117103c20" - "0220002802a001200241812041001000417441908bc000410e103c2002418120420110014174419e8bc0004112103c" - "41e782c0004181202006" - "41081026417441b08bc000411b103c41e782c00041812020044114101e417441cb8bc0004116103c41e782c0004181" - "20419583c0004108101f" - "417441e18bc0004115103c41e782c000410d200220002802a001101f417141f68bc0004119103c200041003b018e02" - "200220002802a0012004" - "411420034102102541714186a1c0004121103c41e782c000410d200220002802a001410210004171418f8cc0004114" - "103c4101410020044114" - "101e410041a38cc000411a103c20004190026a240041010f0b0b418080c000410b417f20032003417f4e1bac100100" - "0b0b92210200418080c0" - "000bae056572726f725f636f64653d54455354204641494c45442424242424205354415254494e47205741534d2045" - "5845435554494f4e2024" - "24242424746573745f616d656e646d656e74616d656e646d656e745f656e61626c656463616368655f6c6564676572" - "5f6f626a6765745f7478" - "5f61727261795f6c656e6765745f63757272656e745f6c65646765725f6f626a5f61727261795f6c656e6765745f6c" - "65646765725f6f626a5f" - "61727261795f6c656e6765745f74785f6e65737465645f61727261795f6c656e6765745f63757272656e745f6c6564" - "6765725f6f626a5f6e65" - "737465645f61727261795f6c656e6765745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e7570" - "646174655f6461746174" - "657374206d65737361676574657374207075626b657974657374207369676e6174757265636865636b5f7369676765" - "745f6e66745f666c6167" - "736765745f6e66745f7472616e736665725f66656574657374696e6720747261636574726163655f6163636f756e74" - "400000000000005f7472" - "6163655f616d6f756e74400000000000000074726163655f616d6f756e745f7a65726f6765745f706172656e745f6c" - "65646765725f68617368" + "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" - "65737465645f61727261" - "795f6c656e5f6e65675f7074726765745f74785f6e65737465645f61727261795f6c656e5f6e65675f6c656e676574" - "5f74785f6e6573746564" - "5f61727261795f6c656e5f746f6f5f6c6f6e676765745f74785f6e65737465645f61727261795f6c656e5f7074725f" - "6f6f6263616368655f6c" - "65646765725f6f626a5f7074725f6f6f6263616368655f6c65646765725f6f626a5f77726f6e675f6c656e55534430" - "30303030303030303030" - "3030303030300041d685c0000bd11b74726163655f6e756d5f6f6f625f7374726765745f63757272656e745f6c6564" - "6765725f6f626a5f6172" - "7261795f6c656e5f696e76616c69645f736669656c646765745f6c65646765725f6f626a5f61727261795f6c656e5f" - "696e76616c69645f7366" - "69656c64616d656e646d656e745f656e61626c65645f746f6f5f6269675f736c696365616d656e646d656e745f656e" - "61626c65645f746f6f5f" - "6c6f6e676765745f74785f6e65737465645f61727261795f6c656e5f746f6f5f6269675f736c6963656765745f6375" - "7272656e745f6c656467" - "65725f6f626a5f6e65737465645f61727261795f6c656e5f746f6f5f6269675f736c6963656765745f6c6564676572" - "5f6f626a5f6e65737465" - "645f61727261795f6c656e5f746f6f5f6269675f736c6963657570646174655f646174615f746f6f5f6269675f736c" - "69636574726163655f6f" - "6f625f736c69636574726163655f6f70617175655f666c6f61745f6f6f625f736c69636574726163655f616d6f756e" - "745f6f6f625f736c6963" - "65666c6f61745f636f6d706172655f6f6f625f736c69636531666c6f61745f636f6d706172655f6f6f625f736c6963" - "653263616368655f6c65" - "646765725f6f626a5f77726f6e675f73697a655f75696e743235366765745f6e66745f666c6167735f77726f6e675f" - "73697a655f75696e7432" - "35366765745f6e66745f7472616e736665725f6665655f77726f6e675f73697a655f75696e74323536303030303030" - "30303030303030303030" - "3030303030303030303030303030303174726163655f6163636f756e745f77726f6e675f73697a655f6163636f756e" - "745f696474726163655f" - "6f6f625f737472696e6774726163655f6f70617175655f666c6f61745f6f6f625f737472696e6774726163655f6163" - "636f756e745f6f6f625f" - "737472696e6774726163655f616d6f756e745f6f6f625f737472696e6774726163655f746f6f5f6c6f6e6774726163" - "655f6e756d5f746f6f5f" - "6c6f6e6774726163655f6f70617175655f666c6f61745f746f6f5f6c6f6e6774726163655f6163636f756e745f746f" - "6f5f6c6f6e6774726163" - "655f616d6f756e745f746f6f5f6c6f6e6774726163655f616d6f756e745f77726f6e675f6c656e6774687472616365" - "5f696e76616c69645f61" - "735f68657874726163655f6163636f756e745f636865636b5f646573796e636765745f6c65646765725f73716e6765" - "745f706172656e745f6c" - "65646765725f74696d656765745f706172656e745f6c65646765725f686173686765745f626173655f666565676574" - "5f63757272656e745f6c" - "65646765725f6f626a5f6669656c646765745f6c65646765725f6f626a5f6669656c646765745f74785f6e65737465" - "645f6669656c64676574" - "5f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c646765745f6c65646765725f6f626a5f" - "6e65737465645f666965" - "6c64636f6d707574655f7368613531325f68616c666765745f6e66746765745f6e66745f6973737565726765745f6e" - "66745f7461786f6e6765" - "745f6e66745f73657269616c6765745f706172656e745f6c65646765725f686173685f6e65675f6c656e6765745f70" - "6172656e745f6c656467" - "65725f686173685f6275665f746f6f5f736d616c6c6765745f706172656e745f6c65646765725f686173685f6c656e" - "5f746f6f5f6c6f6e6763" - "6865636b5f6b65796c65745f6f6f625f6c656e5f753332636865636b5f6b65796c65745f77726f6e675f6c656e5f75" - "3332666c6f61745f6672" - "6f6d5f75696e745f6c656e5f6f6f62666c6f61745f66726f6d5f75696e745f77726f6e675f6c656e5f75696e743634" - "6163636f756e745f6b65" - "796c65745f6c656e5f6f6f626163636f756e745f6b65796c65745f77726f6e675f6c656e6c696e655f6b65796c6574" - "5f6c656e5f6f6f625f63" - "757272656e63796c696e655f6b65796c65745f77726f6e675f6c656e5f63757272656e6379616d6d5f6b65796c6574" - "5f6c656e5f6f6f625f61" - "7373657432616d6d5f6b65796c65745f6c656e5f77726f6e675f6c656e5f617373657432616d6d5f6b65796c65745f" - "6c656e5f77726f6e675f" - "6e6f6e5f7872705f63757272656e63795f6c656e616d6d5f6b65796c65745f6c656e5f77726f6e675f7872705f6375" - "7272656e63795f6c656e" - "616d6d5f6b65796c65745f6d70746765745f74785f6669656c645f696e76616c69645f736669656c646765745f6375" - "7272656e745f6c656467" - "65725f6f626a5f6669656c645f696e76616c69645f736669656c646765745f6c65646765725f6f626a5f6669656c64" - "5f696e76616c69645f73" - "6669656c646765745f74785f6e65737465645f6669656c645f746f6f5f6269675f736c6963656765745f6375727265" - "6e745f6c65646765725f" - "6f626a5f6e65737465645f6669656c645f746f6f5f6269675f736c6963656765745f6c65646765725f6f626a5f6e65" - "737465645f6669656c64" - "5f746f6f5f6269675f736c696365636f6d707574655f7368613531325f68616c665f746f6f5f6269675f736c696365" - "616d6d5f6b65796c6574" - "5f746f6f5f6269675f736c69636563726564656e7469616c5f6b65796c65745f746f6f5f6269675f736c6963656d70" - "746f6b656e5f6b65796c" - "65745f746f6f5f6269675f736c6963655f6d70746964666c6f61745f6164645f6f6f625f736c69636531666c6f6174" - "5f6164645f6f6f625f73" - "6c69636532666c6f61745f73756274726163745f6f6f625f736c69636531666c6f61745f73756274726163745f6f6f" - "625f736c69636532666c" - "6f61745f6d756c7469706c795f6f6f625f736c69636531666c6f61745f6d756c7469706c795f6f6f625f736c696365" - "32666c6f61745f646976" - "6964655f6f6f625f736c69636531666c6f61745f6469766964655f6f6f625f736c69636532666c6f61745f726f6f74" - "5f6f6f625f736c696365" - "666c6f61745f706f775f6f6f625f736c696365666c6f61745f6c6f675f6f6f625f736c696365657363726f775f6b65" - "796c65745f77726f6e67" - "5f73697a655f75696e7433326d70745f69737375616e63655f6b65796c65745f77726f6e675f73697a655f75696e74" - "33326e66745f6f666665" - "725f6b65796c65745f77726f6e675f73697a655f75696e7433326f666665725f6b65796c65745f77726f6e675f7369" - "7a655f75696e7433326f" - "7261636c655f6b65796c65745f77726f6e675f73697a655f75696e7433327061796368616e5f6b65796c65745f7772" - "6f6e675f73697a655f75" - "696e7433327065726d697373696f6e65645f646f6d61696e5f6b65796c65745f77726f6e675f73697a655f75696e74" - "33327469636b65745f6b" + "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" - "75696e7433326765745f" - "6e66745f77726f6e675f73697a655f75696e743235366765745f6e66745f6973737565725f77726f6e675f73697a65" - "5f75696e743235366765" - "745f6e66745f7461786f6e5f77726f6e675f73697a655f75696e743235366765745f6e66745f73657269616c5f7772" - "6f6e675f73697a655f75" - "696e743235366163636f756e745f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964636865636b" - "5f6b65796c65745f7772" - "6f6e675f73697a655f6163636f756e745f696463726564656e7469616c5f6b65796c65745f77726f6e675f73697a65" - "5f6163636f756e745f69" - "643163726564656e7469616c5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69643264656c6567" - "6174655f6b65796c6574" - "5f77726f6e675f73697a655f6163636f756e745f69643164656c65676174655f6b65796c65745f77726f6e675f7369" - "7a655f6163636f756e74" - "5f6964326465706f7369745f707265617574685f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69" - "64316465706f7369745f" - "707265617574685f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964326469645f6b65796c6574" - "5f77726f6e675f73697a" - "655f6163636f756e745f6964657363726f775f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964" - "6c696e655f6b65796c65" - "745f77726f6e675f73697a655f6163636f756e745f6964316c696e655f6b65796c65745f77726f6e675f73697a655f" - "6163636f756e745f6964" - "326d70745f69737375616e63655f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69646d70746f6b" - "656e5f6b65796c65745f" - "77726f6e675f73697a655f6163636f756e745f69646e66745f6f666665725f6b65796c65745f77726f6e675f73697a" - "655f6163636f756e745f" - "69646f666665725f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69646f7261636c655f6b65796c" - "65745f77726f6e675f73" - "697a655f6163636f756e745f69647061796368616e5f6b65796c65745f77726f6e675f73697a655f6163636f756e74" - "5f696431706179636861" - "6e5f6b65796c65745f77726f6e675f73697a655f6163636f756e745f6964327065726d697373696f6e65645f646f6d" - "61696e5f6b65796c6574" - "5f77726f6e675f73697a655f6163636f756e745f69647369676e6572735f6b65796c65745f77726f6e675f73697a65" - "5f6163636f756e745f69" - "647469636b65745f6b65796c65745f77726f6e675f73697a655f6163636f756e745f69647661756c745f6b65796c65" - "745f77726f6e675f7369" - "7a655f6163636f756e745f69646765745f6e66745f77726f6e675f73697a655f6163636f756e745f69646d70746f6b" - "656e5f6b65796c65745f" - "6d707469645f77726f6e675f6c656e677468004d0970726f64756365727302086c616e677561676501045275737400" - "0c70726f636573736564" - "2d6279010572757374631d312e38372e30202831373036376539616320323032352d30352d303929002c0f74617267" - "65745f66656174757265" - "73022b0f6d757461626c652d676c6f62616c732b087369676e2d657874"; + "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 = - "0061736d0100000001430860077f7f7f7f7f7f7f017f60057f7f7f7f7f017f60047f7f7f7f" - "017f60067f7f7f7f7f7f017f60047e7f7f7f017f60057f7e7f7f7f017f60037f7f7e017f60" - "00017f02c9020e08686f73745f6c6962057472616365000108686f73745f6c69620e666c6f" - "61745f66726f6d5f696e74000408686f73745f6c69621274726163655f6f70617175655f66" - "6c6f6174000208686f73745f6c69620f666c6f61745f66726f6d5f75696e74000108686f73" - "745f6c696209666c6f61745f736574000508686f73745f6c69620d666c6f61745f636f6d70" - "617265000208686f73745f6c696209666c6f61745f616464000008686f73745f6c69620e66" - "6c6f61745f7375627472616374000008686f73745f6c69620e666c6f61745f6d756c746970" - "6c79000008686f73745f6c69620c666c6f61745f646976696465000008686f73745f6c6962" - "09666c6f61745f706f77000308686f73745f6c69620974726163655f6e756d000608686f73" - "745f6c69620a666c6f61745f726f6f74000308686f73745f6c696209666c6f61745f6c6f67" - "00010302010705030100110619037f01418080c0000b7f0041d28ac0000b7f0041e08ac000" - "0b072e04066d656d6f727902000666696e697368000e0a5f5f646174615f656e6403010b5f" - "5f686561705f6261736503020af20e01ef0e01047f230041206b22012400418080c000411d" - "41014100410010001a200142003703100240428ce000200141106a22004108410010014108" - "460440419d80c00041172000410810021a41b480c000411e20004108410110001a0c010b41" - "d280c000411e41014100410010001a0b2001428ce0003703180240200141186a4108200141" - "106a2200410841001003410846044041f080c00041172000410810021a0c010b418781c000" - "411e41014100410010001a0b0240410242fb00200141106a22004108410010044108460440" - "41a581c00041212000410810021a0c010b41c681c000412641014100410010001a0b41ec81" - "c0004115418182c000410810021a419182c0004116418982c000410810021a41a782c00041" - "1b41014100410010001a2001420037031802404201200141186a2200410841001001410846" - "044041c282c000410f2000410810021a0c010b41d182c000411641014100410010001a0b02" - "40200141186a4108418182c0004108100545044041e782c000411b41014100410010001a0c" - "010b418283c000411b41014100410010001a0b0240200141186a4108418982c00041081005" - "4101460440419d83c000412341014100410010001a0c010b41c083c0004124410141004100" - "10001a0b0240418982c0004108200141186a41081005410246044041e483c0004123410141" - "00410010001a0c010b418784c000412441014100410010001a0b41ab84c000412041014100" - "410010001a200142d487b6f4c7d4b1c000370310410921000340200141106a220241084181" - "82c000410820024108410010061a200041016b22000d000b20014200370318420a20014118" - "6a22004108410010011a02402000410820024108100545044041cb84c00041144101410041" - "0010001a0c010b41df84c000411341014100410010001a0b410b21000340200141106a2202" - "4108418182c000410820024108410010071a200041016b22000d000b024020024108418982" - "c0004108100545044041f284c000411941014100410010001a0c010b418b85c00041184101" - "4100410010001a0b41a385c000412341014100410010001a20014200370300420a20014108" - "410010011a200142d487b6f4c7d4b1c000370308410621000340200141086a220241082001" - "410820024108410010081a200041016b22000d000b2001420037031042c0843d200141106a" - "22004108410010011a02402000410820024108100545044041c685c0004119410141004100" - "10001a0c010b41df85c000411841014100410010001a0b410721000340200141086a220241" - "082001410820024108410010091a200041016b22000d000b20014200370318417f42012001" - "41186a22004108410010041a02402002410820004108100545044041f785c0004117410141" - "00410010001a0c010b418e86c000411641014100410010001a0b41a486c000411741014100" - "410010001a20014200370308418182c00041084103200141086a220041084100100a1a41bb" - "86c00041122000410810021a418982c00041084106200041084100100a1a41cd86c0004118" - "2000410810021a200142003703104209200141106a22024108410010011a20024108410220" - "0041084100100a1a41e586c00041142000410810021a200241084100200041084100100a1a" - "41f986c00041172000410810021a200142003703184200200141186a22034108410010011a" - "200341084102200041084100100a1a419087c00041142000410810021a41a487c000413820" - "0341084100200041084100100aac100b1a41dc87c000411841014100410010001a20014200" - "370308420920004108410010011a20014200370310200041084102200241084100100c1a41" - "f487c00041122002410810021a200041084103200241084100100c1a418688c00041122002" - "410810021a2001420037031842c0843d20034108410010011a200341084103200241084100" - "100c1a419888c00041182002410810021a200341084106200241084100100c1a41b088c000" - "411c2002410810021a41cc88c000411741014100410010001a2001420037031042c0843d20" - "024108410010011a2001420037031820024108200341084100100d1a41e388c00041142003" - "410810021a41f788c000411a41014100410010001a20014200370318418182c00041084189" - "82c000410820034108410010081a0240418982c0004108200341081005450440419189c000" - "411641014100410010001a0c010b41a789c000411541014100410010001a0b418982c00041" - "08418982c0004108200141186a22004108410010081a0240418182c0004108200041081005" - "45044041bc89c000411741014100410010001a0c010b41d389c00041164101410041001000" - "1a0b41e989c000411a41014100410010001a2001420037031020014200370318420a200141" - "186a22024108410010011a418182c000410820024108200141106a22004108410010091a41" - "838ac00041192000410810021a418182c00041082000410820004108410010091a419c8ac0" - "00410f2000410810021a02402002410820004108100545044041ab8ac00041144101410041" - "0010001a0c010b41bf8ac000411341014100410010001a0b200141206a240041010b0bdc0a" - "0100418080c0000bd20a0a24242420746573745f666c6f61745f66726f6d5f7761736d2024" - "24242020666c6f61742066726f6d206936342031323330303a2020666c6f61742066726f6d" - "20693634203132333030206173204845583a2020666c6f61742066726f6d20693634203132" - "3330303a206661696c65642020666c6f61742066726f6d207536342031323330303a202066" - "6c6f61742066726f6d207536342031323330303a206661696c65642020666c6f6174206672" - "6f6d2065787020322c206d616e7469737361203132333a2020666c6f61742066726f6d2065" - "787020322c206d616e746973736120333a206661696c65642020666c6f61742066726f6d20" - "636f6e737420313ad4838d7ea4c6800094838d7ea4c680002020666c6f61742066726f6d20" - "636f6e7374202d313a0a24242420746573745f666c6f61745f636f6d706172652024242420" - "20666c6f61742066726f6d20313a2020666c6f61742066726f6d20313a206661696c656420" - "20666c6f61742066726f6d2031203d3d20464c4f41545f4f4e452020666c6f61742066726f" - "6d203120213d20464c4f41545f4f4e452020666c6f61742066726f6d2031203e20464c4f41" - "545f4e454741544956455f4f4e452020666c6f61742066726f6d203120213e20464c4f4154" - "5f4e454741544956455f4f4e452020464c4f41545f4e454741544956455f4f4e45203c2066" - "6c6f61742066726f6d20312020464c4f41545f4e454741544956455f4f4e4520213c20666c" - "6f61742066726f6d20310a24242420746573745f666c6f61745f6164645f73756274726163" - "742024242420207265706561746564206164643a20676f6f64202072657065617465642061" - "64643a20626164202072657065617465642073756274726163743a20676f6f642020726570" - "65617465642073756274726163743a206261640a24242420746573745f666c6f61745f6d75" - "6c7469706c795f6469766964652024242420207265706561746564206d756c7469706c793a" - "20676f6f6420207265706561746564206d756c7469706c793a206261642020726570656174" - "6564206469766964653a20676f6f6420207265706561746564206469766964653a20626164" - "0a24242420746573745f666c6f61745f706f77202424242020666c6f61742063756265206f" - "6620313a2020666c6f61742036746820706f776572206f66202d313a2020666c6f61742073" - "7175617265206f6620393a2020666c6f61742030746820706f776572206f6620393a202066" - "6c6f617420737175617265206f6620303a2020666c6f61742030746820706f776572206f66" - "20302028657870656374696e6720494e56414c49445f504152414d53206572726f72293a0a" - "24242420746573745f666c6f61745f726f6f74202424242020666c6f61742073717274206f" - "6620393a2020666c6f61742063627274206f6620393a2020666c6f61742063627274206f66" - "20313030303030303a2020666c6f61742036746820726f6f74206f6620313030303030303a" - "0a24242420746573745f666c6f61745f6c6f672024242420206c6f675f3130206f66203130" - "30303030303a0a24242420746573745f666c6f61745f6e65676174652024242420206e6567" - "61746520636f6e737420313a20676f6f6420206e656761746520636f6e737420313a206261" - "6420206e656761746520636f6e7374202d313a20676f6f6420206e656761746520636f6e73" - "74202d313a206261640a24242420746573745f666c6f61745f696e76657274202424242020" - "696e76657274206120666c6f61742066726f6d2031303a2020696e7665727420616761696e" - "3a2020696e766572742074776963653a20676f6f642020696e766572742074776963653a20" - "626164004d0970726f64756365727302086c616e6775616765010452757374000c70726f63" - "65737365642d6279010572757374631d312e38372e30202831373036376539616320323032" - "352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d" - "676c6f62616c732b087369676e2d657874"; + "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 @@ -1269,9 +945,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. @@ -1357,7624 +1033,4641 @@ 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"; + "0061736d010000000108026000006000017f030302000107120205737461727400000666696e69736800010801000a" + "0e02070003400c000b0b040041010b"; extern std::string const badAlignWasmHex = "0061736d01000000011b046000017f60057f7f7f7f7f017f60067f7f7f7f7f7f017f600000022a0203656e760f666c" - "6f61745f66726f6d5f75" - "696e74000103656e760c636865636b5f6b65796c6574000203050403000000050301000306470b7f004180080b7f00" - "418088020b7f00418008" - "0b7f00418088040b7f00418088040b7f00418088080b7f004180080b7f00418088080b7f004180800c0b7f0041000b" - "7f0041010b07cc011006" - "6d656d6f72790200115f5f7761736d5f63616c6c5f63746f72730002057465737431000307655f6461746131030005" - "7465737432000407655f" - "64617461320301047465737400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f7374" - "61636b5f6c6f7703040c" - "5f5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f6261736503070a5f5f" - "686561705f656e640308" - "0d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a99020402000b2801017f418108427f" - "370000418108410841a3" - "08410c41001000220041a40828020020004100481b0b5f01017f419a88024191a4cca00136010041928802428994ac" - "e0d0c1c3871037010041" - "8a88024281848ca0d0c0c1830837010041818802417f360000418a8802411441818802410441a38802412010012200" - "41a48802280200200041" - "00481b0b8a0101037f418108427f370000418108410841a308410c410010002100419a88024191a4cca00136010041" - "928802428994ace0d0c1" - "c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a4082802002101418a8802411441" - "818802410441a3880241" - "201001220241a4880228020020024100481b2000200120004100481b6a0b007f0970726f647563657273010c70726f" - "6365737365642d627901" - "05636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d" - "2f6c6c766d2d70726f6a" - "6563742061623462356132646235383239353861663165653330386137393063666462343262643234373230290049" - "0f7461726765745f6665" - "617475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74" - "797065732b0a6d756c74" - "6976616c7565"; + "6f61745f66726f6d5f75696e74000103656e760c636865636b5f6b65796c6574000203050403000000050301000306" + "470b7f004180080b7f00418088020b7f004180080b7f00418088040b7f00418088040b7f00418088080b7f00418008" + "0b7f00418088080b7f004180800c0b7f0041000b7f0041010b07cc0110066d656d6f72790200115f5f7761736d5f63" + "616c6c5f63746f72730002057465737431000307655f64617461310300057465737432000407655f64617461320301" + "047465737400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f737461636b5f6c6f77" + "03040c5f5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f626173650307" + "0a5f5f686561705f656e6403080d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a9902" + "0402000b2801017f418108427f370000418108410841a308410c41001000220041a40828020020004100481b0b5f01" + "017f419a88024191a4cca00136010041928802428994ace0d0c1c38710370100418a88024281848ca0d0c0c1830837" + "010041818802417f360000418a8802411441818802410441a3880241201001220041a4880228020020004100481b0b" + "8a0101037f418108427f370000418108410841a308410c410010002100419a88024191a4cca0013601004192880242" + "8994ace0d0c1c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a408280200210141" + "8a8802411441818802410441a3880241201001220241a4880228020020024100481b2000200120004100481b6a0b00" + "7f0970726f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e352d776173692d7364" + "6b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a6563742061623462356132" + "6462353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f666561" + "7475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d7479" + "7065732b0a6d756c746976616c7565"; extern std::string const thousandParamsHex = "0061736d0100000001f1070260000060e8077f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f017f030302000105" - "030100020638097f004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f00" - "418080080b7f0041000b" - "7f0041010b07a8010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f72730000047465737400010c5f" - "5f64736f5f68616e646c" - "6503000a5f5f646174615f656e6403010b5f5f737461636b5f6c6f7703020c5f5f737461636b5f6869676803030d5f" - "5f676c6f62616c5f6261" - "736503040b5f5f686561705f6261736503050a5f5f686561705f656e6403060d5f5f6d656d6f72795f626173650307" - "0c5f5f7461626c655f62" - "61736503080aa71e0202000ba11e00200020016a20026a20036a20046a20056a20066a20076a20086a20096a200a6a" - "200b6a200c6a200d6a20" - "0e6a200f6a20106a20116a20126a20136a20146a20156a20166a20176a20186a20196a201a6a201b6a201c6a201d6a" - "201e6a201f6a20206a20" - "216a20226a20236a20246a20256a20266a20276a20286a20296a202a6a202b6a202c6a202d6a202e6a202f6a20306a" - "20316a20326a20336a20" - "346a20356a20366a20376a20386a20396a203a6a203b6a203c6a203d6a203e6a203f6a20406a20416a20426a20436a" - "20446a20456a20466a20" - "476a20486a20496a204a6a204b6a204c6a204d6a204e6a204f6a20506a20516a20526a20536a20546a20556a20566a" - "20576a20586a20596a20" - "5a6a205b6a205c6a205d6a205e6a205f6a20606a20616a20626a20636a20646a20656a20666a20676a20686a20696a" - "206a6a206b6a206c6a20" - "6d6a206e6a206f6a20706a20716a20726a20736a20746a20756a20766a20776a20786a20796a207a6a207b6a207c6a" - "207d6a207e6a207f6a20" - "80016a2081016a2082016a2083016a2084016a2085016a2086016a2087016a2088016a2089016a208a016a208b016a" - "208c016a208d016a208e" - "016a208f016a2090016a2091016a2092016a2093016a2094016a2095016a2096016a2097016a2098016a2099016a20" - "9a016a209b016a209c01" - "6a209d016a209e016a209f016a20a0016a20a1016a20a2016a20a3016a20a4016a20a5016a20a6016a20a7016a20a8" - "016a20a9016a20aa016a" - "20ab016a20ac016a20ad016a20ae016a20af016a20b0016a20b1016a20b2016a20b3016a20b4016a20b5016a20b601" - "6a20b7016a20b8016a20" - "b9016a20ba016a20bb016a20bc016a20bd016a20be016a20bf016a20c0016a20c1016a20c2016a20c3016a20c4016a" - "20c5016a20c6016a20c7" - "016a20c8016a20c9016a20ca016a20cb016a20cc016a20cd016a20ce016a20cf016a20d0016a20d1016a20d2016a20" - "d3016a20d4016a20d501" - "6a20d6016a20d7016a20d8016a20d9016a20da016a20db016a20dc016a20dd016a20de016a20df016a20e0016a20e1" - "016a20e2016a20e3016a" - "20e4016a20e5016a20e6016a20e7016a20e8016a20e9016a20ea016a20eb016a20ec016a20ed016a20ee016a20ef01" - "6a20f0016a20f1016a20" - "f2016a20f3016a20f4016a20f5016a20f6016a20f7016a20f8016a20f9016a20fa016a20fb016a20fc016a20fd016a" - "20fe016a20ff016a2080" - "026a2081026a2082026a2083026a2084026a2085026a2086026a2087026a2088026a2089026a208a026a208b026a20" - "8c026a208d026a208e02" - "6a208f026a2090026a2091026a2092026a2093026a2094026a2095026a2096026a2097026a2098026a2099026a209a" - "026a209b026a209c026a" - "209d026a209e026a209f026a20a0026a20a1026a20a2026a20a3026a20a4026a20a5026a20a6026a20a7026a20a802" - "6a20a9026a20aa026a20" - "ab026a20ac026a20ad026a20ae026a20af026a20b0026a20b1026a20b2026a20b3026a20b4026a20b5026a20b6026a" - "20b7026a20b8026a20b9" - "026a20ba026a20bb026a20bc026a20bd026a20be026a20bf026a20c0026a20c1026a20c2026a20c3026a20c4026a20" - "c5026a20c6026a20c702" - "6a20c8026a20c9026a20ca026a20cb026a20cc026a20cd026a20ce026a20cf026a20d0026a20d1026a20d2026a20d3" - "026a20d4026a20d5026a" - "20d6026a20d7026a20d8026a20d9026a20da026a20db026a20dc026a20dd026a20de026a20df026a20e0026a20e102" - "6a20e2026a20e3026a20" - "e4026a20e5026a20e6026a20e7026a20e8026a20e9026a20ea026a20eb026a20ec026a20ed026a20ee026a20ef026a" - "20f0026a20f1026a20f2" - "026a20f3026a20f4026a20f5026a20f6026a20f7026a20f8026a20f9026a20fa026a20fb026a20fc026a20fd026a20" - "fe026a20ff026a208003" + "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" - "036a208d036a208e036a" - "208f036a2090036a2091036a2092036a2093036a2094036a2095036a2096036a2097036a2098036a2099036a209a03" - "6a209b036a209c036a20" - "9d036a209e036a209f036a20a0036a20a1036a20a2036a20a3036a20a4036a20a5036a20a6036a20a7036a20a8036a" - "20a9036a20aa036a20ab" - "036a20ac036a20ad036a20ae036a20af036a20b0036a20b1036a20b2036a20b3036a20b4036a20b5036a20b6036a20" - "b7036a20b8036a20b903" - "6a20ba036a20bb036a20bc036a20bd036a20be036a20bf036a20c0036a20c1036a20c2036a20c3036a20c4036a20c5" - "036a20c6036a20c7036a" - "20c8036a20c9036a20ca036a20cb036a20cc036a20cd036a20ce036a20cf036a20d0036a20d1036a20d2036a20d303" - "6a20d4036a20d5036a20" - "d6036a20d7036a20d8036a20d9036a20da036a20db036a20dc036a20dd036a20de036a20df036a20e0036a20e1036a" - "20e2036a20e3036a20e4" - "036a20e5036a20e6036a20e7036a20e8036a20e9036a20ea036a20eb036a20ec036a20ed036a20ee036a20ef036a20" - "f0036a20f1036a20f203" - "6a20f3036a20f4036a20f5036a20f6036a20f7036a20f8036a20f9036a20fa036a20fb036a20fc036a20fd036a20fe" - "036a20ff036a2080046a" - "2081046a2082046a2083046a2084046a2085046a2086046a2087046a2088046a2089046a208a046a208b046a208c04" - "6a208d046a208e046a20" - "8f046a2090046a2091046a2092046a2093046a2094046a2095046a2096046a2097046a2098046a2099046a209a046a" - "209b046a209c046a209d" - "046a209e046a209f046a20a0046a20a1046a20a2046a20a3046a20a4046a20a5046a20a6046a20a7046a20a8046a20" - "a9046a20aa046a20ab04" - "6a20ac046a20ad046a20ae046a20af046a20b0046a20b1046a20b2046a20b3046a20b4046a20b5046a20b6046a20b7" - "046a20b8046a20b9046a" - "20ba046a20bb046a20bc046a20bd046a20be046a20bf046a20c0046a20c1046a20c2046a20c3046a20c4046a20c504" - "6a20c6046a20c7046a20" - "c8046a20c9046a20ca046a20cb046a20cc046a20cd046a20ce046a20cf046a20d0046a20d1046a20d2046a20d3046a" - "20d4046a20d5046a20d6" - "046a20d7046a20d8046a20d9046a20da046a20db046a20dc046a20dd046a20de046a20df046a20e0046a20e1046a20" - "e2046a20e3046a20e404" - "6a20e5046a20e6046a20e7046a20e8046a20e9046a20ea046a20eb046a20ec046a20ed046a20ee046a20ef046a20f0" - "046a20f1046a20f2046a" - "20f3046a20f4046a20f5046a20f6046a20f7046a20f8046a20f9046a20fa046a20fb046a20fc046a20fd046a20fe04" - "6a20ff046a2080056a20" - "81056a2082056a2083056a2084056a2085056a2086056a2087056a2088056a2089056a208a056a208b056a208c056a" - "208d056a208e056a208f" - "056a2090056a2091056a2092056a2093056a2094056a2095056a2096056a2097056a2098056a2099056a209a056a20" - "9b056a209c056a209d05" - "6a209e056a209f056a20a0056a20a1056a20a2056a20a3056a20a4056a20a5056a20a6056a20a7056a20a8056a20a9" - "056a20aa056a20ab056a" - "20ac056a20ad056a20ae056a20af056a20b0056a20b1056a20b2056a20b3056a20b4056a20b5056a20b6056a20b705" - "6a20b8056a20b9056a20" - "ba056a20bb056a20bc056a20bd056a20be056a20bf056a20c0056a20c1056a20c2056a20c3056a20c4056a20c5056a" - "20c6056a20c7056a20c8" - "056a20c9056a20ca056a20cb056a20cc056a20cd056a20ce056a20cf056a20d0056a20d1056a20d2056a20d3056a20" - "d4056a20d5056a20d605" - "6a20d7056a20d8056a20d9056a20da056a20db056a20dc056a20dd056a20de056a20df056a20e0056a20e1056a20e2" - "056a20e3056a20e4056a" - "20e5056a20e6056a20e7056a20e8056a20e9056a20ea056a20eb056a20ec056a20ed056a20ee056a20ef056a20f005" - "6a20f1056a20f2056a20" - "f3056a20f4056a20f5056a20f6056a20f7056a20f8056a20f9056a20fa056a20fb056a20fc056a20fd056a20fe056a" - "20ff056a2080066a2081" - "066a2082066a2083066a2084066a2085066a2086066a2087066a2088066a2089066a208a066a208b066a208c066a20" - "8d066a208e066a208f06" - "6a2090066a2091066a2092066a2093066a2094066a2095066a2096066a2097066a2098066a2099066a209a066a209b" - "066a209c066a209d066a" - "209e066a209f066a20a0066a20a1066a20a2066a20a3066a20a4066a20a5066a20a6066a20a7066a20a8066a20a906" - "6a20aa066a20ab066a20" - "ac066a20ad066a20ae066a20af066a20b0066a20b1066a20b2066a20b3066a20b4066a20b5066a20b6066a20b7066a" - "20b8066a20b9066a20ba" - "066a20bb066a20bc066a20bd066a20be066a20bf066a20c0066a20c1066a20c2066a20c3066a20c4066a20c5066a20" - "c6066a20c7066a20c806" - "6a20c9066a20ca066a20cb066a20cc066a20cd066a20ce066a20cf066a20d0066a20d1066a20d2066a20d3066a20d4" - "066a20d5066a20d6066a" - "20d7066a20d8066a20d9066a20da066a20db066a20dc066a20dd066a20de066a20df066a20e0066a20e1066a20e206" - "6a20e3066a20e4066a20" - "e5066a20e6066a20e7066a20e8066a20e9066a20ea066a20eb066a20ec066a20ed066a20ee066a20ef066a20f0066a" - "20f1066a20f2066a20f3" - "066a20f4066a20f5066a20f6066a20f7066a20f8066a20f9066a20fa066a20fb066a20fc066a20fd066a20fe066a20" - "ff066a2080076a208107" - "6a2082076a2083076a2084076a2085076a2086076a2087076a2088076a2089076a208a076a208b076a208c076a208d" - "076a208e076a208f076a" - "2090076a2091076a2092076a2093076a2094076a2095076a2096076a2097076a2098076a2099076a209a076a209b07" - "6a209c076a209d076a20" - "9e076a209f076a20a0076a20a1076a20a2076a20a3076a20a4076a20a5076a20a6076a20a7076a20a8076a20a9076a" - "20aa076a20ab076a20ac" - "076a20ad076a20ae076a20af076a20b0076a20b1076a20b2076a20b3076a20b4076a20b5076a20b6076a20b7076a20" - "b8076a20b9076a20ba07" - "6a20bb076a20bc076a20bd076a20be076a20bf076a20c0076a20c1076a20c2076a20c3076a20c4076a20c5076a20c6" - "076a20c7076a20c8076a" - "20c9076a20ca076a20cb076a20cc076a20cd076a20ce076a20cf076a20d0076a20d1076a20d2076a20d3076a20d407" - "6a20d5076a20d6076a20" - "d7076a20d8076a20d9076a20da076a20db076a20dc076a20dd076a20de076a20df076a20e0076a20e1076a20e2076a" - "20e3076a20e4076a20e5" - "076a20e6076a20e7076a0b007f0970726f647563657273010c70726f6365737365642d62790105636c616e675f3139" - "2e312e352d776173692d" - "73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235" - "61326462353832393538" - "616631656533303861373930636664623432626432343732302900490f7461726765745f6665617475726573042b0f" - "6d757461626c652d676c" - "6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; + "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" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f7f7f7f7f7f7f7f" "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f" - "7f7f7f017f0303020001" - "05030100020638097f004180080b7f004180080b7f004180080b7f00418088040b7f004180080b7f00418088040b7f" - "00418080080b7f004100" - "0b7f0041010b07a8010c066d656d6f72790200115f5f7761736d5f63616c6c5f63746f72730000047465737400010c" - "5f5f64736f5f68616e64" - "6c6503000a5f5f646174615f656e6403010b5f5f737461636b5f6c6f7703020c5f5f737461636b5f6869676803030d" - "5f5f676c6f62616c5f62" - "61736503040b5f5f686561705f6261736503050a5f5f686561705f656e6403060d5f5f6d656d6f72795f6261736503" - "070c5f5f7461626c655f" - "6261736503080aab1e0202000ba51e00200020016a20026a20036a20046a20056a20066a20076a20086a20096a200a" - "6a200b6a200c6a200d6a" - "200e6a200f6a20106a20116a20126a20136a20146a20156a20166a20176a20186a20196a201a6a201b6a201c6a201d" - "6a201e6a201f6a20206a" - "20216a20226a20236a20246a20256a20266a20276a20286a20296a202a6a202b6a202c6a202d6a202e6a202f6a2030" - "6a20316a20326a20336a" - "20346a20356a20366a20376a20386a20396a203a6a203b6a203c6a203d6a203e6a203f6a20406a20416a20426a2043" - "6a20446a20456a20466a" - "20476a20486a20496a204a6a204b6a204c6a204d6a204e6a204f6a20506a20516a20526a20536a20546a20556a2056" - "6a20576a20586a20596a" - "205a6a205b6a205c6a205d6a205e6a205f6a20606a20616a20626a20636a20646a20656a20666a20676a20686a2069" - "6a206a6a206b6a206c6a" - "206d6a206e6a206f6a20706a20716a20726a20736a20746a20756a20766a20776a20786a20796a207a6a207b6a207c" - "6a207d6a207e6a207f6a" - "2080016a2081016a2082016a2083016a2084016a2085016a2086016a2087016a2088016a2089016a208a016a208b01" - "6a208c016a208d016a20" - "8e016a208f016a2090016a2091016a2092016a2093016a2094016a2095016a2096016a2097016a2098016a2099016a" - "209a016a209b016a209c" - "016a209d016a209e016a209f016a20a0016a20a1016a20a2016a20a3016a20a4016a20a5016a20a6016a20a7016a20" - "a8016a20a9016a20aa01" - "6a20ab016a20ac016a20ad016a20ae016a20af016a20b0016a20b1016a20b2016a20b3016a20b4016a20b5016a20b6" - "016a20b7016a20b8016a" - "20b9016a20ba016a20bb016a20bc016a20bd016a20be016a20bf016a20c0016a20c1016a20c2016a20c3016a20c401" - "6a20c5016a20c6016a20" - "c7016a20c8016a20c9016a20ca016a20cb016a20cc016a20cd016a20ce016a20cf016a20d0016a20d1016a20d2016a" - "20d3016a20d4016a20d5" - "016a20d6016a20d7016a20d8016a20d9016a20da016a20db016a20dc016a20dd016a20de016a20df016a20e0016a20" - "e1016a20e2016a20e301" - "6a20e4016a20e5016a20e6016a20e7016a20e8016a20e9016a20ea016a20eb016a20ec016a20ed016a20ee016a20ef" - "016a20f0016a20f1016a" - "20f2016a20f3016a20f4016a20f5016a20f6016a20f7016a20f8016a20f9016a20fa016a20fb016a20fc016a20fd01" - "6a20fe016a20ff016a20" - "80026a2081026a2082026a2083026a2084026a2085026a2086026a2087026a2088026a2089026a208a026a208b026a" - "208c026a208d026a208e" - "026a208f026a2090026a2091026a2092026a2093026a2094026a2095026a2096026a2097026a2098026a2099026a20" - "9a026a209b026a209c02" - "6a209d026a209e026a209f026a20a0026a20a1026a20a2026a20a3026a20a4026a20a5026a20a6026a20a7026a20a8" - "026a20a9026a20aa026a" - "20ab026a20ac026a20ad026a20ae026a20af026a20b0026a20b1026a20b2026a20b3026a20b4026a20b5026a20b602" - "6a20b7026a20b8026a20" - "b9026a20ba026a20bb026a20bc026a20bd026a20be026a20bf026a20c0026a20c1026a20c2026a20c3026a20c4026a" - "20c5026a20c6026a20c7" - "026a20c8026a20c9026a20ca026a20cb026a20cc026a20cd026a20ce026a20cf026a20d0026a20d1026a20d2026a20" - "d3026a20d4026a20d502" - "6a20d6026a20d7026a20d8026a20d9026a20da026a20db026a20dc026a20dd026a20de026a20df026a20e0026a20e1" - "026a20e2026a20e3026a" - "20e4026a20e5026a20e6026a20e7026a20e8026a20e9026a20ea026a20eb026a20ec026a20ed026a20ee026a20ef02" - "6a20f0026a20f1026a20" - "f2026a20f3026a20f4026a20f5026a20f6026a20f7026a20f8026a20f9026a20fa026a20fb026a20fc026a20fd026a" - "20fe026a20ff026a2080" + "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" - "8c036a208d036a208e03" - "6a208f036a2090036a2091036a2092036a2093036a2094036a2095036a2096036a2097036a2098036a2099036a209a" - "036a209b036a209c036a" - "209d036a209e036a209f036a20a0036a20a1036a20a2036a20a3036a20a4036a20a5036a20a6036a20a7036a20a803" - "6a20a9036a20aa036a20" - "ab036a20ac036a20ad036a20ae036a20af036a20b0036a20b1036a20b2036a20b3036a20b4036a20b5036a20b6036a" - "20b7036a20b8036a20b9" - "036a20ba036a20bb036a20bc036a20bd036a20be036a20bf036a20c0036a20c1036a20c2036a20c3036a20c4036a20" - "c5036a20c6036a20c703" - "6a20c8036a20c9036a20ca036a20cb036a20cc036a20cd036a20ce036a20cf036a20d0036a20d1036a20d2036a20d3" - "036a20d4036a20d5036a" - "20d6036a20d7036a20d8036a20d9036a20da036a20db036a20dc036a20dd036a20de036a20df036a20e0036a20e103" - "6a20e2036a20e3036a20" - "e4036a20e5036a20e6036a20e7036a20e8036a20e9036a20ea036a20eb036a20ec036a20ed036a20ee036a20ef036a" - "20f0036a20f1036a20f2" - "036a20f3036a20f4036a20f5036a20f6036a20f7036a20f8036a20f9036a20fa036a20fb036a20fc036a20fd036a20" - "fe036a20ff036a208004" - "6a2081046a2082046a2083046a2084046a2085046a2086046a2087046a2088046a2089046a208a046a208b046a208c" - "046a208d046a208e046a" - "208f046a2090046a2091046a2092046a2093046a2094046a2095046a2096046a2097046a2098046a2099046a209a04" - "6a209b046a209c046a20" - "9d046a209e046a209f046a20a0046a20a1046a20a2046a20a3046a20a4046a20a5046a20a6046a20a7046a20a8046a" - "20a9046a20aa046a20ab" - "046a20ac046a20ad046a20ae046a20af046a20b0046a20b1046a20b2046a20b3046a20b4046a20b5046a20b6046a20" - "b7046a20b8046a20b904" - "6a20ba046a20bb046a20bc046a20bd046a20be046a20bf046a20c0046a20c1046a20c2046a20c3046a20c4046a20c5" - "046a20c6046a20c7046a" - "20c8046a20c9046a20ca046a20cb046a20cc046a20cd046a20ce046a20cf046a20d0046a20d1046a20d2046a20d304" - "6a20d4046a20d5046a20" - "d6046a20d7046a20d8046a20d9046a20da046a20db046a20dc046a20dd046a20de046a20df046a20e0046a20e1046a" - "20e2046a20e3046a20e4" - "046a20e5046a20e6046a20e7046a20e8046a20e9046a20ea046a20eb046a20ec046a20ed046a20ee046a20ef046a20" - "f0046a20f1046a20f204" - "6a20f3046a20f4046a20f5046a20f6046a20f7046a20f8046a20f9046a20fa046a20fb046a20fc046a20fd046a20fe" - "046a20ff046a2080056a" - "2081056a2082056a2083056a2084056a2085056a2086056a2087056a2088056a2089056a208a056a208b056a208c05" - "6a208d056a208e056a20" - "8f056a2090056a2091056a2092056a2093056a2094056a2095056a2096056a2097056a2098056a2099056a209a056a" - "209b056a209c056a209d" - "056a209e056a209f056a20a0056a20a1056a20a2056a20a3056a20a4056a20a5056a20a6056a20a7056a20a8056a20" - "a9056a20aa056a20ab05" - "6a20ac056a20ad056a20ae056a20af056a20b0056a20b1056a20b2056a20b3056a20b4056a20b5056a20b6056a20b7" - "056a20b8056a20b9056a" - "20ba056a20bb056a20bc056a20bd056a20be056a20bf056a20c0056a20c1056a20c2056a20c3056a20c4056a20c505" - "6a20c6056a20c7056a20" - "c8056a20c9056a20ca056a20cb056a20cc056a20cd056a20ce056a20cf056a20d0056a20d1056a20d2056a20d3056a" - "20d4056a20d5056a20d6" - "056a20d7056a20d8056a20d9056a20da056a20db056a20dc056a20dd056a20de056a20df056a20e0056a20e1056a20" - "e2056a20e3056a20e405" - "6a20e5056a20e6056a20e7056a20e8056a20e9056a20ea056a20eb056a20ec056a20ed056a20ee056a20ef056a20f0" - "056a20f1056a20f2056a" - "20f3056a20f4056a20f5056a20f6056a20f7056a20f8056a20f9056a20fa056a20fb056a20fc056a20fd056a20fe05" - "6a20ff056a2080066a20" - "81066a2082066a2083066a2084066a2085066a2086066a2087066a2088066a2089066a208a066a208b066a208c066a" - "208d066a208e066a208f" - "066a2090066a2091066a2092066a2093066a2094066a2095066a2096066a2097066a2098066a2099066a209a066a20" - "9b066a209c066a209d06" - "6a209e066a209f066a20a0066a20a1066a20a2066a20a3066a20a4066a20a5066a20a6066a20a7066a20a8066a20a9" - "066a20aa066a20ab066a" - "20ac066a20ad066a20ae066a20af066a20b0066a20b1066a20b2066a20b3066a20b4066a20b5066a20b6066a20b706" - "6a20b8066a20b9066a20" - "ba066a20bb066a20bc066a20bd066a20be066a20bf066a20c0066a20c1066a20c2066a20c3066a20c4066a20c5066a" - "20c6066a20c7066a20c8" - "066a20c9066a20ca066a20cb066a20cc066a20cd066a20ce066a20cf066a20d0066a20d1066a20d2066a20d3066a20" - "d4066a20d5066a20d606" - "6a20d7066a20d8066a20d9066a20da066a20db066a20dc066a20dd066a20de066a20df066a20e0066a20e1066a20e2" - "066a20e3066a20e4066a" - "20e5066a20e6066a20e7066a20e8066a20e9066a20ea066a20eb066a20ec066a20ed066a20ee066a20ef066a20f006" - "6a20f1066a20f2066a20" - "f3066a20f4066a20f5066a20f6066a20f7066a20f8066a20f9066a20fa066a20fb066a20fc066a20fd066a20fe066a" - "20ff066a2080076a2081" - "076a2082076a2083076a2084076a2085076a2086076a2087076a2088076a2089076a208a076a208b076a208c076a20" - "8d076a208e076a208f07" - "6a2090076a2091076a2092076a2093076a2094076a2095076a2096076a2097076a2098076a2099076a209a076a209b" - "076a209c076a209d076a" - "209e076a209f076a20a0076a20a1076a20a2076a20a3076a20a4076a20a5076a20a6076a20a7076a20a8076a20a907" - "6a20aa076a20ab076a20" - "ac076a20ad076a20ae076a20af076a20b0076a20b1076a20b2076a20b3076a20b4076a20b5076a20b6076a20b7076a" - "20b8076a20b9076a20ba" - "076a20bb076a20bc076a20bd076a20be076a20bf076a20c0076a20c1076a20c2076a20c3076a20c4076a20c5076a20" - "c6076a20c7076a20c807" - "6a20c9076a20ca076a20cb076a20cc076a20cd076a20ce076a20cf076a20d0076a20d1076a20d2076a20d3076a20d4" - "076a20d5076a20d6076a" - "20d7076a20d8076a20d9076a20da076a20db076a20dc076a20dd076a20de076a20df076a20e0076a20e1076a20e207" - "6a20e3076a20e4076a20" - "e5076a20e6076a20e7076a20e8076a0b007f0970726f647563657273010c70726f6365737365642d62790105636c61" - "6e675f31392e312e352d" - "776173692d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a65637420" - "61623462356132646235" - "3832393538616631656533303861373930636664623432626432343732302900490f7461726765745f666561747572" - "6573042b0f6d75746162" - "6c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c" - "7565"; + "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" - "6a2102200120026a2103" - "200220036a2104200320046a2105200420056a2106200520066a2107200620076a2108200720086a2109200820096a" - "210a2009200a6a210b20" - "0a200b6a210c200b200c6a210d200c200d6a210e200d200e6a210f200e200f6a2110200f20106a2111201020116a21" - "12201120126a21132012" - "20136a2114201320146a2115201420156a2116201520166a2117201620176a2118201720186a2119201820196a211a" - "2019201a6a211b201a20" - "1b6a211c201b201c6a211d201c201d6a211e201d201e6a211f201e201f6a2120201f20206a2121202020216a212220" - "2120226a212320222023" - "6a2124202320246a2125202420256a2126202520266a2127202620276a2128202720286a2129202820296a212a2029" - "202a6a212b202a202b6a" - "212c202b202c6a212d202c202d6a212e202d202e6a212f202e202f6a2130202f20306a2131203020316a2132203120" - "326a2133203220336a21" - "34203320346a2135203420356a2136203520366a2137203620376a2138203720386a2139203820396a213a2039203a" - "6a213b203a203b6a213c" - "203b203c6a213d203c203d6a213e203d203e6a213f203e203f6a2140203f20406a2141204020416a2142204120426a" - "2143204220436a214420" - "4320446a2145204420456a2146204520466a2147204620476a2148204720486a2149204820496a214a2049204a6a21" - "4b204a204b6a214c204b" - "204c6a214d204c204d6a214e204d204e6a214f204e204f6a2150204f20506a2151205020516a2152205120526a2153" - "205220536a2154205320" - "546a2155205420556a2156205520566a2157205620576a2158205720586a2159205820596a215a2059205a6a215b20" - "5a205b6a215c205b205c" - "6a215d205c205d6a215e205d205e6a215f205e205f6a2160205f20606a2161206020616a2162206120626a21632062" - "20636a2164206320646a" - "2165206420656a2166206520666a2167206620676a2168206720686a2169206820696a216a2069206a6a216b206a20" - "6b6a216c206b206c6a21" - "6d206c206d6a216e206d206e6a216f206e206f6a2170206f20706a2171207020716a2172207120726a217320722073" - "6a2174207320746a2175" - "207420756a2176207520766a2177207620776a2178207720786a2179207820796a217a2079207a6a217b207a207b6a" - "217c207b207c6a217d20" - "7c207d6a217e207d207e6a217f207e207f6a218001207f2080016a2181012080012081016a2182012081012082016a" - "2183012082012083016a" - "2184012083012084016a2185012084012085016a2186012085012086016a2187012086012087016a21880120870120" - "88016a21890120880120" - "89016a218a01208901208a016a218b01208a01208b016a218c01208b01208c016a218d01208c01208d016a218e0120" - "8d01208e016a218f0120" - "8e01208f016a219001208f012090016a2191012090012091016a2192012091012092016a2193012092012093016a21" - "94012093012094016a21" - "95012094012095016a2196012095012096016a2197012096012097016a2198012097012098016a2199012098012099" - "016a219a01209901209a" - "016a219b01209a01209b016a219c01209b01209c016a219d01209c01209d016a219e01209d01209e016a219f01209e" - "01209f016a21a001209f" - "0120a0016a21a10120a00120a1016a21a20120a10120a2016a21a30120a20120a3016a21a40120a30120a4016a21a5" - "0120a40120a5016a21a6" - "0120a50120a6016a21a70120a60120a7016a21a80120a70120a8016a21a90120a80120a9016a21aa0120a90120aa01" - "6a21ab0120aa0120ab01" - "6a21ac0120ab0120ac016a21ad0120ac0120ad016a21ae0120ad0120ae016a21af0120ae0120af016a21b00120af01" - "20b0016a21b10120b001" - "20b1016a21b20120b10120b2016a21b30120b20120b3016a21b40120b30120b4016a21b50120b40120b5016a21b601" - "20b50120b6016a21b701" - "20b60120b7016a21b80120b70120b8016a21b90120b80120b9016a21ba0120b90120ba016a21bb0120ba0120bb016a" - "21bc0120bb0120bc016a" - "21bd0120bc0120bd016a21be0120bd0120be016a21bf0120be0120bf016a21c00120bf0120c0016a21c10120c00120" - "c1016a21c20120c10120" - "c2016a21c30120c20120c3016a21c40120c30120c4016a21c50120c40120c5016a21c60120c50120c6016a21c70120" - "c60120c7016a21c80120" - "c70120c8016a21c90120c80120c9016a21ca0120c90120ca016a21cb0120ca0120cb016a21cc0120cb0120cc016a21" - "cd0120cc0120cd016a21" - "ce0120cd0120ce016a21cf0120ce0120cf016a21d00120cf0120d0016a21d10120d00120d1016a21d20120d10120d2" - "016a21d30120d20120d3" - "016a21d40120d30120d4016a21d50120d40120d5016a21d60120d50120d6016a21d70120d60120d7016a21d80120d7" - "0120d8016a21d90120d8" - "0120d9016a21da0120d90120da016a21db0120da0120db016a21dc0120db0120dc016a21dd0120dc0120dd016a21de" - "0120dd0120de016a21df" - "0120de0120df016a21e00120df0120e0016a21e10120e00120e1016a21e20120e10120e2016a21e30120e20120e301" - "6a21e40120e30120e401" - "6a21e50120e40120e5016a21e60120e50120e6016a21e70120e60120e7016a21e80120e70120e8016a21e90120e801" - "20e9016a21ea0120e901" - "20ea016a21eb0120ea0120eb016a21ec0120eb0120ec016a21ed0120ec0120ed016a21ee0120ed0120ee016a21ef01" - "20ee0120ef016a21f001" - "20ef0120f0016a21f10120f00120f1016a21f20120f10120f2016a21f30120f20120f3016a21f40120f30120f4016a" - "21f50120f40120f5016a" - "21f60120f50120f6016a21f70120f60120f7016a21f80120f70120f8016a21f90120f80120f9016a21fa0120f90120" - "fa016a21fb0120fa0120" - "fb016a21fc0120fb0120fc016a21fd0120fc0120fd016a21fe0120fd0120fe016a21ff0120fe0120ff016a21800220" - "ff012080026a21810220" - "80022081026a2182022081022082026a2183022082022083026a2184022083022084026a2185022084022085026a21" - "86022085022086026a21" - "87022086022087026a2188022087022088026a2189022088022089026a218a02208902208a026a218b02208a02208b" - "026a218c02208b02208c" - "026a218d02208c02208d026a218e02208d02208e026a218f02208e02208f026a219002208f022090026a2191022090" - "022091026a2192022091" - "022092026a2193022092022093026a2194022093022094026a2195022094022095026a2196022095022096026a2197" - "022096022097026a2198" - "022097022098026a2199022098022099026a219a02209902209a026a219b02209a02209b026a219c02209b02209c02" - "6a219d02209c02209d02" - "6a219e02209d02209e026a219f02209e02209f026a21a002209f0220a0026a21a10220a00220a1026a21a20220a102" - "20a2026a21a30220a202" - "20a3026a21a40220a30220a4026a21a50220a40220a5026a21a60220a50220a6026a21a70220a60220a7026a21a802" - "20a70220a8026a21a902" - "20a80220a9026a21aa0220a90220aa026a21ab0220aa0220ab026a21ac0220ab0220ac026a21ad0220ac0220ad026a" - "21ae0220ad0220ae026a" + "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" - "b3026a21b40220b30220" - "b4026a21b50220b40220b5026a21b60220b50220b6026a21b70220b60220b7026a21b80220b70220b8026a21b90220" - "b80220b9026a21ba0220" - "b90220ba026a21bb0220ba0220bb026a21bc0220bb0220bc026a21bd0220bc0220bd026a21be0220bd0220be026a21" - "bf0220be0220bf026a21" - "c00220bf0220c0026a21c10220c00220c1026a21c20220c10220c2026a21c30220c20220c3026a21c40220c30220c4" - "026a21c50220c40220c5" - "026a21c60220c50220c6026a21c70220c60220c7026a21c80220c70220c8026a21c90220c80220c9026a21ca0220c9" - "0220ca026a21cb0220ca" - "0220cb026a21cc0220cb0220cc026a21cd0220cc0220cd026a21ce0220cd0220ce026a21cf0220ce0220cf026a21d0" - "0220cf0220d0026a21d1" - "0220d00220d1026a21d20220d10220d2026a21d30220d20220d3026a21d40220d30220d4026a21d50220d40220d502" - "6a21d60220d50220d602" - "6a21d70220d60220d7026a21d80220d70220d8026a21d90220d80220d9026a21da0220d90220da026a21db0220da02" - "20db026a21dc0220db02" - "20dc026a21dd0220dc0220dd026a21de0220dd0220de026a21df0220de0220df026a21e00220df0220e0026a21e102" - "20e00220e1026a21e202" - "20e10220e2026a21e30220e20220e3026a21e40220e30220e4026a21e50220e40220e5026a21e60220e50220e6026a" - "21e70220e60220e7026a" - "21e80220e70220e8026a21e90220e80220e9026a21ea0220e90220ea026a21eb0220ea0220eb026a21ec0220eb0220" - "ec026a21ed0220ec0220" - "ed026a21ee0220ed0220ee026a21ef0220ee0220ef026a21f00220ef0220f0026a21f10220f00220f1026a21f20220" - "f10220f2026a21f30220" - "f20220f3026a21f40220f30220f4026a21f50220f40220f5026a21f60220f50220f6026a21f70220f60220f7026a21" - "f80220f70220f8026a21" - "f90220f80220f9026a21fa0220f90220fa026a21fb0220fa0220fb026a21fc0220fb0220fc026a21fd0220fc0220fd" - "026a21fe0220fd0220fe" - "026a21ff0220fe0220ff026a21800320ff022080036a2181032080032081036a2182032081032082036a2183032082" - "032083036a2184032083" - "032084036a2185032084032085036a2186032085032086036a2187032086032087036a2188032087032088036a2189" - "032088032089036a218a" - "03208903208a036a218b03208a03208b036a218c03208b03208c036a218d03208c03208d036a218e03208d03208e03" - "6a218f03208e03208f03" - "6a219003208f032090036a2191032090032091036a2192032091032092036a2193032092032093036a219403209303" - "2094036a219503209403" - "2095036a2196032095032096036a2197032096032097036a2198032097032098036a2199032098032099036a219a03" - "209903209a036a219b03" - "209a03209b036a219c03209b03209c036a219d03209c03209d036a219e03209d03209e036a219f03209e03209f036a" - "21a003209f0320a0036a" - "21a10320a00320a1036a21a20320a10320a2036a21a30320a20320a3036a21a40320a30320a4036a21a50320a40320" - "a5036a21a60320a50320" - "a6036a21a70320a60320a7036a21a80320a70320a8036a21a90320a80320a9036a21aa0320a90320aa036a21ab0320" - "aa0320ab036a21ac0320" - "ab0320ac036a21ad0320ac0320ad036a21ae0320ad0320ae036a21af0320ae0320af036a21b00320af0320b0036a21" - "b10320b00320b1036a21" - "b20320b10320b2036a21b30320b20320b3036a21b40320b30320b4036a21b50320b40320b5036a21b60320b50320b6" - "036a21b70320b60320b7" - "036a21b80320b70320b8036a21b90320b80320b9036a21ba0320b90320ba036a21bb0320ba0320bb036a21bc0320bb" - "0320bc036a21bd0320bc" - "0320bd036a21be0320bd0320be036a21bf0320be0320bf036a21c00320bf0320c0036a21c10320c00320c1036a21c2" - "0320c10320c2036a21c3" - "0320c20320c3036a21c40320c30320c4036a21c50320c40320c5036a21c60320c50320c6036a21c70320c60320c703" - "6a21c80320c70320c803" - "6a21c90320c80320c9036a21ca0320c90320ca036a21cb0320ca0320cb036a21cc0320cb0320cc036a21cd0320cc03" - "20cd036a21ce0320cd03" - "20ce036a21cf0320ce0320cf036a21d00320cf0320d0036a21d10320d00320d1036a21d20320d10320d2036a21d303" - "20d20320d3036a21d403" - "20d30320d4036a21d50320d40320d5036a21d60320d50320d6036a21d70320d60320d7036a21d80320d70320d8036a" - "21d90320d80320d9036a" - "21da0320d90320da036a21db0320da0320db036a21dc0320db0320dc036a21dd0320dc0320dd036a21de0320dd0320" - "de036a21df0320de0320" - "df036a21e00320df0320e0036a21e10320e00320e1036a21e20320e10320e2036a21e30320e20320e3036a21e40320" - "e30320e4036a21e50320" - "e40320e5036a21e60320e50320e6036a21e70320e60320e7036a21e80320e70320e8036a21e90320e80320e9036a21" - "ea0320e90320ea036a21" - "eb0320ea0320eb036a21ec0320eb0320ec036a21ed0320ec0320ed036a21ee0320ed0320ee036a21ef0320ee0320ef" - "036a21f00320ef0320f0" - "036a21f10320f00320f1036a21f20320f10320f2036a21f30320f20320f3036a21f40320f30320f4036a21f50320f4" - "0320f5036a21f60320f5" - "0320f6036a21f70320f60320f7036a21f80320f70320f8036a21f90320f80320f9036a21fa0320f90320fa036a21fb" - "0320fa0320fb036a21fc" - "0320fb0320fc036a21fd0320fc0320fd036a21fe0320fd0320fe036a21ff0320fe0320ff036a21800420ff03208004" - "6a218104208004208104" - "6a2182042081042082046a2183042082042083046a2184042083042084046a2185042084042085046a218604208504" - "2086046a218704208604" - "2087046a2188042087042088046a2189042088042089046a218a04208904208a046a218b04208a04208b046a218c04" - "208b04208c046a218d04" - "208c04208d046a218e04208d04208e046a218f04208e04208f046a219004208f042090046a2191042090042091046a" - "2192042091042092046a" - "2193042092042093046a2194042093042094046a2195042094042095046a2196042095042096046a21970420960420" - "97046a21980420970420" - "98046a2199042098042099046a219a04209904209a046a219b04209a04209b046a219c04209b04209c046a219d0420" - "9c04209d046a219e0420" - "9d04209e046a219f04209e04209f046a21a004209f0420a0046a21a10420a00420a1046a21a20420a10420a2046a21" - "a30420a20420a3046a21" - "a40420a30420a4046a21a50420a40420a5046a21a60420a50420a6046a21a70420a60420a7046a21a80420a70420a8" - "046a21a90420a80420a9" - "046a21aa0420a90420aa046a21ab0420aa0420ab046a21ac0420ab0420ac046a21ad0420ac0420ad046a21ae0420ad" - "0420ae046a21af0420ae" - "0420af046a21b00420af0420b0046a21b10420b00420b1046a21b20420b10420b2046a21b30420b20420b3046a21b4" - "0420b30420b4046a21b5" - "0420b40420b5046a21b60420b50420b6046a21b70420b60420b7046a21b80420b70420b8046a21b90420b80420b904" - "6a21ba0420b90420ba04" + "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" - "20bf046a21c00420bf04" - "20c0046a21c10420c00420c1046a21c20420c10420c2046a21c30420c20420c3046a21c40420c30420c4046a21c504" - "20c40420c5046a21c604" - "20c50420c6046a21c70420c60420c7046a21c80420c70420c8046a21c90420c80420c9046a21ca0420c90420ca046a" - "21cb0420ca0420cb046a" - "21cc0420cb0420cc046a21cd0420cc0420cd046a21ce0420cd0420ce046a21cf0420ce0420cf046a21d00420cf0420" - "d0046a21d10420d00420" - "d1046a21d20420d10420d2046a21d30420d20420d3046a21d40420d30420d4046a21d50420d40420d5046a21d60420" - "d50420d6046a21d70420" - "d60420d7046a21d80420d70420d8046a21d90420d80420d9046a21da0420d90420da046a21db0420da0420db046a21" - "dc0420db0420dc046a21" - "dd0420dc0420dd046a21de0420dd0420de046a21df0420de0420df046a21e00420df0420e0046a21e10420e00420e1" - "046a21e20420e10420e2" - "046a21e30420e20420e3046a21e40420e30420e4046a21e50420e40420e5046a21e60420e50420e6046a21e70420e6" - "0420e7046a21e80420e7" - "0420e8046a21e90420e80420e9046a21ea0420e90420ea046a21eb0420ea0420eb046a21ec0420eb0420ec046a21ed" - "0420ec0420ed046a21ee" - "0420ed0420ee046a21ef0420ee0420ef046a21f00420ef0420f0046a21f10420f00420f1046a21f20420f10420f204" - "6a21f30420f20420f304" - "6a21f40420f30420f4046a21f50420f40420f5046a21f60420f50420f6046a21f70420f60420f7046a21f80420f704" - "20f8046a21f90420f804" - "20f9046a21fa0420f90420fa046a21fb0420fa0420fb046a21fc0420fb0420fc046a21fd0420fc0420fd046a21fe04" - "20fd0420fe046a21ff04" - "20fe0420ff046a21800520ff042080056a2181052080052081056a2182052081052082056a2183052082052083056a" - "2184052083052084056a" - "2185052084052085056a2186052085052086056a2187052086052087056a2188052087052088056a21890520880520" - "89056a218a0520890520" - "8a056a218b05208a05208b056a218c05208b05208c056a218d05208c05208d056a218e05208d05208e056a218f0520" - "8e05208f056a21900520" - "8f052090056a2191052090052091056a2192052091052092056a2193052092052093056a2194052093052094056a21" - "95052094052095056a21" - "96052095052096056a2197052096052097056a2198052097052098056a2199052098052099056a219a05209905209a" - "056a219b05209a05209b" - "056a219c05209b05209c056a219d05209c05209d056a219e05209d05209e056a219f05209e05209f056a21a005209f" - "0520a0056a21a10520a0" - "0520a1056a21a20520a10520a2056a21a30520a20520a3056a21a40520a30520a4056a21a50520a40520a5056a21a6" - "0520a50520a6056a21a7" - "0520a60520a7056a21a80520a70520a8056a21a90520a80520a9056a21aa0520a90520aa056a21ab0520aa0520ab05" - "6a21ac0520ab0520ac05" - "6a21ad0520ac0520ad056a21ae0520ad0520ae056a21af0520ae0520af056a21b00520af0520b0056a21b10520b005" - "20b1056a21b20520b105" - "20b2056a21b30520b20520b3056a21b40520b30520b4056a21b50520b40520b5056a21b60520b50520b6056a21b705" - "20b60520b7056a21b805" - "20b70520b8056a21b90520b80520b9056a21ba0520b90520ba056a21bb0520ba0520bb056a21bc0520bb0520bc056a" - "21bd0520bc0520bd056a" - "21be0520bd0520be056a21bf0520be0520bf056a21c00520bf0520c0056a21c10520c00520c1056a21c20520c10520" - "c2056a21c30520c20520" - "c3056a21c40520c30520c4056a21c50520c40520c5056a21c60520c50520c6056a21c70520c60520c7056a21c80520" - "c70520c8056a21c90520" - "c80520c9056a21ca0520c90520ca056a21cb0520ca0520cb056a21cc0520cb0520cc056a21cd0520cc0520cd056a21" - "ce0520cd0520ce056a21" - "cf0520ce0520cf056a21d00520cf0520d0056a21d10520d00520d1056a21d20520d10520d2056a21d30520d20520d3" - "056a21d40520d30520d4" - "056a21d50520d40520d5056a21d60520d50520d6056a21d70520d60520d7056a21d80520d70520d8056a21d90520d8" - "0520d9056a21da0520d9" - "0520da056a21db0520da0520db056a21dc0520db0520dc056a21dd0520dc0520dd056a21de0520dd0520de056a21df" - "0520de0520df056a21e0" - "0520df0520e0056a21e10520e00520e1056a21e20520e10520e2056a21e30520e20520e3056a21e40520e30520e405" - "6a21e50520e40520e505" - "6a21e60520e50520e6056a21e70520e60520e7056a21e80520e70520e8056a21e90520e80520e9056a21ea0520e905" - "20ea056a21eb0520ea05" - "20eb056a21ec0520eb0520ec056a21ed0520ec0520ed056a21ee0520ed0520ee056a21ef0520ee0520ef056a21f005" - "20ef0520f0056a21f105" - "20f00520f1056a21f20520f10520f2056a21f30520f20520f3056a21f40520f30520f4056a21f50520f40520f5056a" - "21f60520f50520f6056a" - "21f70520f60520f7056a21f80520f70520f8056a21f90520f80520f9056a21fa0520f90520fa056a21fb0520fa0520" - "fb056a21fc0520fb0520" - "fc056a21fd0520fc0520fd056a21fe0520fd0520fe056a21ff0520fe0520ff056a21800620ff052080066a21810620" - "80062081066a21820620" - "81062082066a2183062082062083066a2184062083062084066a2185062084062085066a2186062085062086066a21" - "87062086062087066a21" - "88062087062088066a2189062088062089066a218a06208906208a066a218b06208a06208b066a218c06208b06208c" - "066a218d06208c06208d" - "066a218e06208d06208e066a218f06208e06208f066a219006208f062090066a2191062090062091066a2192062091" - "062092066a2193062092" - "062093066a2194062093062094066a2195062094062095066a2196062095062096066a2197062096062097066a2198" - "062097062098066a2199" - "062098062099066a219a06209906209a066a219b06209a06209b066a219c06209b06209c066a219d06209c06209d06" - "6a219e06209d06209e06" - "6a219f06209e06209f066a21a006209f0620a0066a21a10620a00620a1066a21a20620a10620a2066a21a30620a206" - "20a3066a21a40620a306" - "20a4066a21a50620a40620a5066a21a60620a50620a6066a21a70620a60620a7066a21a80620a70620a8066a21a906" - "20a80620a9066a21aa06" - "20a90620aa066a21ab0620aa0620ab066a21ac0620ab0620ac066a21ad0620ac0620ad066a21ae0620ad0620ae066a" - "21af0620ae0620af066a" - "21b00620af0620b0066a21b10620b00620b1066a21b20620b10620b2066a21b30620b20620b3066a21b40620b30620" - "b4066a21b50620b40620" - "b5066a21b60620b50620b6066a21b70620b60620b7066a21b80620b70620b8066a21b90620b80620b9066a21ba0620" - "b90620ba066a21bb0620" - "ba0620bb066a21bc0620bb0620bc066a21bd0620bc0620bd066a21be0620bd0620be066a21bf0620be0620bf066a21" - "c00620bf0620c0066a21" - "c10620c00620c1066a21c20620c10620c2066a21c30620c20620c3066a21c40620c30620c4066a21c50620c40620c5" - "066a21c60620c50620c6" + "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" - "0620cb066a21cc0620cb" - "0620cc066a21cd0620cc0620cd066a21ce0620cd0620ce066a21cf0620ce0620cf066a21d00620cf0620d0066a21d1" - "0620d00620d1066a21d2" - "0620d10620d2066a21d30620d20620d3066a21d40620d30620d4066a21d50620d40620d5066a21d60620d50620d606" - "6a21d70620d60620d706" - "6a21d80620d70620d8066a21d90620d80620d9066a21da0620d90620da066a21db0620da0620db066a21dc0620db06" - "20dc066a21dd0620dc06" - "20dd066a21de0620dd0620de066a21df0620de0620df066a21e00620df0620e0066a21e10620e00620e1066a21e206" - "20e10620e2066a21e306" - "20e20620e3066a21e40620e30620e4066a21e50620e40620e5066a21e60620e50620e6066a21e70620e60620e7066a" - "21e80620e70620e8066a" - "21e90620e80620e9066a21ea0620e90620ea066a21eb0620ea0620eb066a21ec0620eb0620ec066a21ed0620ec0620" - "ed066a21ee0620ed0620" - "ee066a21ef0620ee0620ef066a21f00620ef0620f0066a21f10620f00620f1066a21f20620f10620f2066a21f30620" - "f20620f3066a21f40620" - "f30620f4066a21f50620f40620f5066a21f60620f50620f6066a21f70620f60620f7066a21f80620f70620f8066a21" - "f90620f80620f9066a21" - "fa0620f90620fa066a21fb0620fa0620fb066a21fc0620fb0620fc066a21fd0620fc0620fd066a21fe0620fd0620fe" - "066a21ff0620fe0620ff" - "066a21800720ff062080076a2181072080072081076a2182072081072082076a2183072082072083076a2184072083" - "072084076a2185072084" - "072085076a2186072085072086076a2187072086072087076a2188072087072088076a2189072088072089076a218a" - "07208907208a076a218b" - "07208a07208b076a218c07208b07208c076a218d07208c07208d076a218e07208d07208e076a218f07208e07208f07" - "6a219007208f07209007" - "6a2191072090072091076a2192072091072092076a2193072092072093076a2194072093072094076a219507209407" - "2095076a219607209507" - "2096076a2197072096072097076a2198072097072098076a2199072098072099076a219a07209907209a076a219b07" - "209a07209b076a219c07" - "209b07209c076a219d07209c07209d076a219e07209d07209e076a219f07209e07209f076a21a007209f0720a0076a" - "21a10720a00720a1076a" - "21a20720a10720a2076a21a30720a20720a3076a21a40720a30720a4076a21a50720a40720a5076a21a60720a50720" - "a6076a21a70720a60720" - "a7076a21a80720a70720a8076a21a90720a80720a9076a21aa0720a90720aa076a21ab0720aa0720ab076a21ac0720" - "ab0720ac076a21ad0720" - "ac0720ad076a21ae0720ad0720ae076a21af0720ae0720af076a21b00720af0720b0076a21b10720b00720b1076a21" - "b20720b10720b2076a21" - "b30720b20720b3076a21b40720b30720b4076a21b50720b40720b5076a21b60720b50720b6076a21b70720b60720b7" - "076a21b80720b70720b8" - "076a21b90720b80720b9076a21ba0720b90720ba076a21bb0720ba0720bb076a21bc0720bb0720bc076a21bd0720bc" - "0720bd076a21be0720bd" - "0720be076a21bf0720be0720bf076a21c00720bf0720c0076a21c10720c00720c1076a21c20720c10720c2076a21c3" - "0720c20720c3076a21c4" - "0720c30720c4076a21c50720c40720c5076a21c60720c50720c6076a21c70720c60720c7076a21c80720c70720c807" - "6a21c90720c80720c907" - "6a21ca0720c90720ca076a21cb0720ca0720cb076a21cc0720cb0720cc076a21cd0720cc0720cd076a21ce0720cd07" - "20ce076a21cf0720ce07" - "20cf076a21d00720cf0720d0076a21d10720d00720d1076a21d20720d10720d2076a21d30720d20720d3076a21d407" - "20d30720d4076a21d507" - "20d40720d5076a21d60720d50720d6076a21d70720d60720d7076a21d80720d70720d8076a21d90720d80720d9076a" - "21da0720d90720da076a" - "21db0720da0720db076a21dc0720db0720dc076a21dd0720dc0720dd076a21de0720dd0720de076a21df0720de0720" - "df076a21e00720df0720" - "e0076a21e10720e00720e1076a21e20720e10720e2076a21e30720e20720e3076a21e40720e30720e4076a21e50720" - "e40720e5076a21e60720" - "e50720e6076a21e70720e60720e7076a21e80720e70720e8076a21e90720e80720e9076a21ea0720e90720ea076a21" - "eb0720ea0720eb076a21" - "ec0720eb0720ec076a21ed0720ec0720ed076a21ee0720ed0720ee076a21ef0720ee0720ef076a21f00720ef0720f0" - "076a21f10720f00720f1" - "076a21f20720f10720f2076a21f30720f20720f3076a21f40720f30720f4076a21f50720f40720f5076a21f60720f5" - "0720f6076a21f70720f6" - "0720f7076a21f80720f70720f8076a21f90720f80720f9076a21fa0720f90720fa076a21fb0720fa0720fb076a21fc" - "0720fb0720fc076a21fd" - "0720fc0720fd076a21fe0720fd0720fe076a21ff0720fe0720ff076a21800820ff072080086a218108208008208108" - "6a218208208108208208" - "6a2183082082082083086a2184082083082084086a2185082084082085086a2186082085082086086a218708208608" - "2087086a218808208708" - "2088086a2189082088082089086a218a08208908208a086a218b08208a08208b086a218c08208b08208c086a218d08" - "208c08208d086a218e08" - "208d08208e086a218f08208e08208f086a219008208f082090086a2191082090082091086a2192082091082092086a" - "2193082092082093086a" - "2194082093082094086a2195082094082095086a2196082095082096086a2197082096082097086a21980820970820" - "98086a21990820980820" - "99086a219a08209908209a086a219b08209a08209b086a219c08209b08209c086a219d08209c08209d086a219e0820" - "9d08209e086a219f0820" - "9e08209f086a21a008209f0820a0086a21a10820a00820a1086a21a20820a10820a2086a21a30820a20820a3086a21" - "a40820a30820a4086a21" - "a50820a40820a5086a21a60820a50820a6086a21a70820a60820a7086a21a80820a70820a8086a21a90820a80820a9" - "086a21aa0820a90820aa" - "086a21ab0820aa0820ab086a21ac0820ab0820ac086a21ad0820ac0820ad086a21ae0820ad0820ae086a21af0820ae" - "0820af086a21b00820af" - "0820b0086a21b10820b00820b1086a21b20820b10820b2086a21b30820b20820b3086a21b40820b30820b4086a21b5" - "0820b40820b5086a21b6" - "0820b50820b6086a21b70820b60820b7086a21b80820b70820b8086a21b90820b80820b9086a21ba0820b90820ba08" - "6a21bb0820ba0820bb08" - "6a21bc0820bb0820bc086a21bd0820bc0820bd086a21be0820bd0820be086a21bf0820be0820bf086a21c00820bf08" - "20c0086a21c10820c008" - "20c1086a21c20820c10820c2086a21c30820c20820c3086a21c40820c30820c4086a21c50820c40820c5086a21c608" - "20c50820c6086a21c708" - "20c60820c7086a21c80820c70820c8086a21c90820c80820c9086a21ca0820c90820ca086a21cb0820ca0820cb086a" - "21cc0820cb0820cc086a" - "21cd0820cc0820cd086a21ce0820cd0820ce086a21cf0820ce0820cf086a21d00820cf0820d0086a21d10820d00820" - "d1086a21d20820d10820" + "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" - "d60820d7086a21d80820" - "d70820d8086a21d90820d80820d9086a21da0820d90820da086a21db0820da0820db086a21dc0820db0820dc086a21" - "dd0820dc0820dd086a21" - "de0820dd0820de086a21df0820de0820df086a21e00820df0820e0086a21e10820e00820e1086a21e20820e10820e2" - "086a21e30820e20820e3" - "086a21e40820e30820e4086a21e50820e40820e5086a21e60820e50820e6086a21e70820e60820e7086a21e80820e7" - "0820e8086a21e90820e8" - "0820e9086a21ea0820e90820ea086a21eb0820ea0820eb086a21ec0820eb0820ec086a21ed0820ec0820ed086a21ee" - "0820ed0820ee086a21ef" - "0820ee0820ef086a21f00820ef0820f0086a21f10820f00820f1086a21f20820f10820f2086a21f30820f20820f308" - "6a21f40820f30820f408" - "6a21f50820f40820f5086a21f60820f50820f6086a21f70820f60820f7086a21f80820f70820f8086a21f90820f808" - "20f9086a21fa0820f908" - "20fa086a21fb0820fa0820fb086a21fc0820fb0820fc086a21fd0820fc0820fd086a21fe0820fd0820fe086a21ff08" - "20fe0820ff086a218009" - "20ff082080096a2181092080092081096a2182092081092082096a2183092082092083096a2184092083092084096a" - "2185092084092085096a" - "2186092085092086096a2187092086092087096a2188092087092088096a2189092088092089096a218a0920890920" - "8a096a218b09208a0920" - "8b096a218c09208b09208c096a218d09208c09208d096a218e09208d09208e096a218f09208e09208f096a21900920" - "8f092090096a21910920" - "90092091096a2192092091092092096a2193092092092093096a2194092093092094096a2195092094092095096a21" - "96092095092096096a21" - "97092096092097096a2198092097092098096a2199092098092099096a219a09209909209a096a219b09209a09209b" - "096a219c09209b09209c" - "096a219d09209c09209d096a219e09209d09209e096a219f09209e09209f096a21a009209f0920a0096a21a10920a0" - "0920a1096a21a20920a1" - "0920a2096a21a30920a20920a3096a21a40920a30920a4096a21a50920a40920a5096a21a60920a50920a6096a21a7" - "0920a60920a7096a21a8" - "0920a70920a8096a21a90920a80920a9096a21aa0920a90920aa096a21ab0920aa0920ab096a21ac0920ab0920ac09" - "6a21ad0920ac0920ad09" - "6a21ae0920ad0920ae096a21af0920ae0920af096a21b00920af0920b0096a21b10920b00920b1096a21b20920b109" - "20b2096a21b30920b209" - "20b3096a21b40920b30920b4096a21b50920b40920b5096a21b60920b50920b6096a21b70920b60920b7096a21b809" - "20b70920b8096a21b909" - "20b80920b9096a21ba0920b90920ba096a21bb0920ba0920bb096a21bc0920bb0920bc096a21bd0920bc0920bd096a" - "21be0920bd0920be096a" - "21bf0920be0920bf096a21c00920bf0920c0096a21c10920c00920c1096a21c20920c10920c2096a21c30920c20920" - "c3096a21c40920c30920" - "c4096a21c50920c40920c5096a21c60920c50920c6096a21c70920c60920c7096a21c80920c70920c8096a21c90920" - "c80920c9096a21ca0920" - "c90920ca096a21cb0920ca0920cb096a21cc0920cb0920cc096a21cd0920cc0920cd096a21ce0920cd0920ce096a21" - "cf0920ce0920cf096a21" - "d00920cf0920d0096a21d10920d00920d1096a21d20920d10920d2096a21d30920d20920d3096a21d40920d30920d4" - "096a21d50920d40920d5" - "096a21d60920d50920d6096a21d70920d60920d7096a21d80920d70920d8096a21d90920d80920d9096a21da0920d9" - "0920da096a21db0920da" - "0920db096a21dc0920db0920dc096a21dd0920dc0920dd096a21de0920dd0920de096a21df0920de0920df096a21e0" - "0920df0920e0096a21e1" - "0920e00920e1096a21e20920e10920e2096a21e30920e20920e3096a21e40920e30920e4096a21e50920e40920e509" - "6a21e60920e50920e609" - "6a21e70920e60920e7096a21e80920e70920e8096a21e90920e80920e9096a21ea0920e90920ea096a21eb0920ea09" - "20eb096a21ec0920eb09" - "20ec096a21ed0920ec0920ed096a21ee0920ed0920ee096a21ef0920ee0920ef096a21f00920ef0920f0096a21f109" - "20f00920f1096a21f209" - "20f10920f2096a21f30920f20920f3096a21f40920f30920f4096a21f50920f40920f5096a21f60920f50920f6096a" - "21f70920f60920f7096a" - "21f80920f70920f8096a21f90920f80920f9096a21fa0920f90920fa096a21fb0920fa0920fb096a21fc0920fb0920" - "fc096a21fd0920fc0920" - "fd096a21fe0920fd0920fe096a21ff0920fe0920ff096a21800a20ff0920800a6a21810a20800a20810a6a21820a20" - "810a20820a6a21830a20" - "820a20830a6a21840a20830a20840a6a21850a20840a20850a6a21860a20850a20860a6a21870a20860a20870a6a21" - "880a20870a20880a6a21" - "890a20880a20890a6a218a0a20890a208a0a6a218b0a208a0a208b0a6a218c0a208b0a208c0a6a218d0a208c0a208d" - "0a6a218e0a208d0a208e" - "0a6a218f0a208e0a208f0a6a21900a208f0a20900a6a21910a20900a20910a6a21920a20910a20920a6a21930a2092" - "0a20930a6a21940a2093" - "0a20940a6a21950a20940a20950a6a21960a20950a20960a6a21970a20960a20970a6a21980a20970a20980a6a2199" - "0a20980a20990a6a219a" - "0a20990a209a0a6a219b0a209a0a209b0a6a219c0a209b0a209c0a6a219d0a209c0a209d0a6a219e0a209d0a209e0a" - "6a219f0a209e0a209f0a" - "6a21a00a209f0a20a00a6a21a10a20a00a20a10a6a21a20a20a10a20a20a6a21a30a20a20a20a30a6a21a40a20a30a" - "20a40a6a21a50a20a40a" - "20a50a6a21a60a20a50a20a60a6a21a70a20a60a20a70a6a21a80a20a70a20a80a6a21a90a20a80a20a90a6a21aa0a" - "20a90a20aa0a6a21ab0a" - "20aa0a20ab0a6a21ac0a20ab0a20ac0a6a21ad0a20ac0a20ad0a6a21ae0a20ad0a20ae0a6a21af0a20ae0a20af0a6a" - "21b00a20af0a20b00a6a" - "21b10a20b00a20b10a6a21b20a20b10a20b20a6a21b30a20b20a20b30a6a21b40a20b30a20b40a6a21b50a20b40a20" - "b50a6a21b60a20b50a20" - "b60a6a21b70a20b60a20b70a6a21b80a20b70a20b80a6a21b90a20b80a20b90a6a21ba0a20b90a20ba0a6a21bb0a20" - "ba0a20bb0a6a21bc0a20" - "bb0a20bc0a6a21bd0a20bc0a20bd0a6a21be0a20bd0a20be0a6a21bf0a20be0a20bf0a6a21c00a20bf0a20c00a6a21" - "c10a20c00a20c10a6a21" - "c20a20c10a20c20a6a21c30a20c20a20c30a6a21c40a20c30a20c40a6a21c50a20c40a20c50a6a21c60a20c50a20c6" - "0a6a21c70a20c60a20c7" - "0a6a21c80a20c70a20c80a6a21c90a20c80a20c90a6a21ca0a20c90a20ca0a6a21cb0a20ca0a20cb0a6a21cc0a20cb" - "0a20cc0a6a21cd0a20cc" - "0a20cd0a6a21ce0a20cd0a20ce0a6a21cf0a20ce0a20cf0a6a21d00a20cf0a20d00a6a21d10a20d00a20d10a6a21d2" - "0a20d10a20d20a6a21d3" - "0a20d20a20d30a6a21d40a20d30a20d40a6a21d50a20d40a20d50a6a21d60a20d50a20d60a6a21d70a20d60a20d70a" - "6a21d80a20d70a20d80a" - "6a21d90a20d80a20d90a6a21da0a20d90a20da0a6a21db0a20da0a20db0a6a21dc0a20db0a20dc0a6a21dd0a20dc0a" - "20dd0a6a21de0a20dd0a" + "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" - "20e20a20e30a6a21e40a" - "20e30a20e40a6a21e50a20e40a20e50a6a21e60a20e50a20e60a6a21e70a20e60a20e70a6a21e80a20e70a20e80a6a" - "21e90a20e80a20e90a6a" - "21ea0a20e90a20ea0a6a21eb0a20ea0a20eb0a6a21ec0a20eb0a20ec0a6a21ed0a20ec0a20ed0a6a21ee0a20ed0a20" - "ee0a6a21ef0a20ee0a20" - "ef0a6a21f00a20ef0a20f00a6a21f10a20f00a20f10a6a21f20a20f10a20f20a6a21f30a20f20a20f30a6a21f40a20" - "f30a20f40a6a21f50a20" - "f40a20f50a6a21f60a20f50a20f60a6a21f70a20f60a20f70a6a21f80a20f70a20f80a6a21f90a20f80a20f90a6a21" - "fa0a20f90a20fa0a6a21" - "fb0a20fa0a20fb0a6a21fc0a20fb0a20fc0a6a21fd0a20fc0a20fd0a6a21fe0a20fd0a20fe0a6a21ff0a20fe0a20ff" - "0a6a21800b20ff0a2080" - "0b6a21810b20800b20810b6a21820b20810b20820b6a21830b20820b20830b6a21840b20830b20840b6a21850b2084" - "0b20850b6a21860b2085" - "0b20860b6a21870b20860b20870b6a21880b20870b20880b6a21890b20880b20890b6a218a0b20890b208a0b6a218b" - "0b208a0b208b0b6a218c" - "0b208b0b208c0b6a218d0b208c0b208d0b6a218e0b208d0b208e0b6a218f0b208e0b208f0b6a21900b208f0b20900b" - "6a21910b20900b20910b" - "6a21920b20910b20920b6a21930b20920b20930b6a21940b20930b20940b6a21950b20940b20950b6a21960b20950b" - "20960b6a21970b20960b" - "20970b6a21980b20970b20980b6a21990b20980b20990b6a219a0b20990b209a0b6a219b0b209a0b209b0b6a219c0b" - "209b0b209c0b6a219d0b" - "209c0b209d0b6a219e0b209d0b209e0b6a219f0b209e0b209f0b6a21a00b209f0b20a00b6a21a10b20a00b20a10b6a" - "21a20b20a10b20a20b6a" - "21a30b20a20b20a30b6a21a40b20a30b20a40b6a21a50b20a40b20a50b6a21a60b20a50b20a60b6a21a70b20a60b20" - "a70b6a21a80b20a70b20" - "a80b6a21a90b20a80b20a90b6a21aa0b20a90b20aa0b6a21ab0b20aa0b20ab0b6a21ac0b20ab0b20ac0b6a21ad0b20" - "ac0b20ad0b6a21ae0b20" - "ad0b20ae0b6a21af0b20ae0b20af0b6a21b00b20af0b20b00b6a21b10b20b00b20b10b6a21b20b20b10b20b20b6a21" - "b30b20b20b20b30b6a21" - "b40b20b30b20b40b6a21b50b20b40b20b50b6a21b60b20b50b20b60b6a21b70b20b60b20b70b6a21b80b20b70b20b8" - "0b6a21b90b20b80b20b9" - "0b6a21ba0b20b90b20ba0b6a21bb0b20ba0b20bb0b6a21bc0b20bb0b20bc0b6a21bd0b20bc0b20bd0b6a21be0b20bd" - "0b20be0b6a21bf0b20be" - "0b20bf0b6a21c00b20bf0b20c00b6a21c10b20c00b20c10b6a21c20b20c10b20c20b6a21c30b20c20b20c30b6a21c4" - "0b20c30b20c40b6a21c5" - "0b20c40b20c50b6a21c60b20c50b20c60b6a21c70b20c60b20c70b6a21c80b20c70b20c80b6a21c90b20c80b20c90b" - "6a21ca0b20c90b20ca0b" - "6a21cb0b20ca0b20cb0b6a21cc0b20cb0b20cc0b6a21cd0b20cc0b20cd0b6a21ce0b20cd0b20ce0b6a21cf0b20ce0b" - "20cf0b6a21d00b20cf0b" - "20d00b6a21d10b20d00b20d10b6a21d20b20d10b20d20b6a21d30b20d20b20d30b6a21d40b20d30b20d40b6a21d50b" - "20d40b20d50b6a21d60b" - "20d50b20d60b6a21d70b20d60b20d70b6a21d80b20d70b20d80b6a21d90b20d80b20d90b6a21da0b20d90b20da0b6a" - "21db0b20da0b20db0b6a" - "21dc0b20db0b20dc0b6a21dd0b20dc0b20dd0b6a21de0b20dd0b20de0b6a21df0b20de0b20df0b6a21e00b20df0b20" - "e00b6a21e10b20e00b20" - "e10b6a21e20b20e10b20e20b6a21e30b20e20b20e30b6a21e40b20e30b20e40b6a21e50b20e40b20e50b6a21e60b20" - "e50b20e60b6a21e70b20" - "e60b20e70b6a21e80b20e70b20e80b6a21e90b20e80b20e90b6a21ea0b20e90b20ea0b6a21eb0b20ea0b20eb0b6a21" - "ec0b20eb0b20ec0b6a21" - "ed0b20ec0b20ed0b6a21ee0b20ed0b20ee0b6a21ef0b20ee0b20ef0b6a21f00b20ef0b20f00b6a21f10b20f00b20f1" - "0b6a21f20b20f10b20f2" - "0b6a21f30b20f20b20f30b6a21f40b20f30b20f40b6a21f50b20f40b20f50b6a21f60b20f50b20f60b6a21f70b20f6" - "0b20f70b6a21f80b20f7" - "0b20f80b6a21f90b20f80b20f90b6a21fa0b20f90b20fa0b6a21fb0b20fa0b20fb0b6a21fc0b20fb0b20fc0b6a21fd" - "0b20fc0b20fd0b6a21fe" - "0b20fd0b20fe0b6a21ff0b20fe0b20ff0b6a21800c20ff0b20800c6a21810c20800c20810c6a21820c20810c20820c" - "6a21830c20820c20830c" - "6a21840c20830c20840c6a21850c20840c20850c6a21860c20850c20860c6a21870c20860c20870c6a21880c20870c" - "20880c6a21890c20880c" - "20890c6a218a0c20890c208a0c6a218b0c208a0c208b0c6a218c0c208b0c208c0c6a218d0c208c0c208d0c6a218e0c" - "208d0c208e0c6a218f0c" - "208e0c208f0c6a21900c208f0c20900c6a21910c20900c20910c6a21920c20910c20920c6a21930c20920c20930c6a" - "21940c20930c20940c6a" - "21950c20940c20950c6a21960c20950c20960c6a21970c20960c20970c6a21980c20970c20980c6a21990c20980c20" - "990c6a219a0c20990c20" - "9a0c6a219b0c209a0c209b0c6a219c0c209b0c209c0c6a219d0c209c0c209d0c6a219e0c209d0c209e0c6a219f0c20" - "9e0c209f0c6a21a00c20" - "9f0c20a00c6a21a10c20a00c20a10c6a21a20c20a10c20a20c6a21a30c20a20c20a30c6a21a40c20a30c20a40c6a21" - "a50c20a40c20a50c6a21" - "a60c20a50c20a60c6a21a70c20a60c20a70c6a21a80c20a70c20a80c6a21a90c20a80c20a90c6a21aa0c20a90c20aa" - "0c6a21ab0c20aa0c20ab" - "0c6a21ac0c20ab0c20ac0c6a21ad0c20ac0c20ad0c6a21ae0c20ad0c20ae0c6a21af0c20ae0c20af0c6a21b00c20af" - "0c20b00c6a21b10c20b0" - "0c20b10c6a21b20c20b10c20b20c6a21b30c20b20c20b30c6a21b40c20b30c20b40c6a21b50c20b40c20b50c6a21b6" - "0c20b50c20b60c6a21b7" - "0c20b60c20b70c6a21b80c20b70c20b80c6a21b90c20b80c20b90c6a21ba0c20b90c20ba0c6a21bb0c20ba0c20bb0c" - "6a21bc0c20bb0c20bc0c" - "6a21bd0c20bc0c20bd0c6a21be0c20bd0c20be0c6a21bf0c20be0c20bf0c6a21c00c20bf0c20c00c6a21c10c20c00c" - "20c10c6a21c20c20c10c" - "20c20c6a21c30c20c20c20c30c6a21c40c20c30c20c40c6a21c50c20c40c20c50c6a21c60c20c50c20c60c6a21c70c" - "20c60c20c70c6a21c80c" - "20c70c20c80c6a21c90c20c80c20c90c6a21ca0c20c90c20ca0c6a21cb0c20ca0c20cb0c6a21cc0c20cb0c20cc0c6a" - "21cd0c20cc0c20cd0c6a" - "21ce0c20cd0c20ce0c6a21cf0c20ce0c20cf0c6a21d00c20cf0c20d00c6a21d10c20d00c20d10c6a21d20c20d10c20" - "d20c6a21d30c20d20c20" - "d30c6a21d40c20d30c20d40c6a21d50c20d40c20d50c6a21d60c20d50c20d60c6a21d70c20d60c20d70c6a21d80c20" - "d70c20d80c6a21d90c20" - "d80c20d90c6a21da0c20d90c20da0c6a21db0c20da0c20db0c6a21dc0c20db0c20dc0c6a21dd0c20dc0c20dd0c6a21" - "de0c20dd0c20de0c6a21" - "df0c20de0c20df0c6a21e00c20df0c20e00c6a21e10c20e00c20e10c6a21e20c20e10c20e20c6a21e30c20e20c20e3" - "0c6a21e40c20e30c20e4" - "0c6a21e50c20e40c20e50c6a21e60c20e50c20e60c6a21e70c20e60c20e70c6a21e80c20e70c20e80c6a21e90c20e8" - "0c20e90c6a21ea0c20e9" + "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" - "0c20ee0c20ef0c6a21f0" - "0c20ef0c20f00c6a21f10c20f00c20f10c6a21f20c20f10c20f20c6a21f30c20f20c20f30c6a21f40c20f30c20f40c" - "6a21f50c20f40c20f50c" - "6a21f60c20f50c20f60c6a21f70c20f60c20f70c6a21f80c20f70c20f80c6a21f90c20f80c20f90c6a21fa0c20f90c" - "20fa0c6a21fb0c20fa0c" - "20fb0c6a21fc0c20fb0c20fc0c6a21fd0c20fc0c20fd0c6a21fe0c20fd0c20fe0c6a21ff0c20fe0c20ff0c6a21800d" - "20ff0c20800d6a21810d" - "20800d20810d6a21820d20810d20820d6a21830d20820d20830d6a21840d20830d20840d6a21850d20840d20850d6a" - "21860d20850d20860d6a" - "21870d20860d20870d6a21880d20870d20880d6a21890d20880d20890d6a218a0d20890d208a0d6a218b0d208a0d20" - "8b0d6a218c0d208b0d20" - "8c0d6a218d0d208c0d208d0d6a218e0d208d0d208e0d6a218f0d208e0d208f0d6a21900d208f0d20900d6a21910d20" - "900d20910d6a21920d20" - "910d20920d6a21930d20920d20930d6a21940d20930d20940d6a21950d20940d20950d6a21960d20950d20960d6a21" - "970d20960d20970d6a21" - "980d20970d20980d6a21990d20980d20990d6a219a0d20990d209a0d6a219b0d209a0d209b0d6a219c0d209b0d209c" - "0d6a219d0d209c0d209d" - "0d6a219e0d209d0d209e0d6a219f0d209e0d209f0d6a21a00d209f0d20a00d6a21a10d20a00d20a10d6a21a20d20a1" - "0d20a20d6a21a30d20a2" - "0d20a30d6a21a40d20a30d20a40d6a21a50d20a40d20a50d6a21a60d20a50d20a60d6a21a70d20a60d20a70d6a21a8" - "0d20a70d20a80d6a21a9" - "0d20a80d20a90d6a21aa0d20a90d20aa0d6a21ab0d20aa0d20ab0d6a21ac0d20ab0d20ac0d6a21ad0d20ac0d20ad0d" - "6a21ae0d20ad0d20ae0d" - "6a21af0d20ae0d20af0d6a21b00d20af0d20b00d6a21b10d20b00d20b10d6a21b20d20b10d20b20d6a21b30d20b20d" - "20b30d6a21b40d20b30d" - "20b40d6a21b50d20b40d20b50d6a21b60d20b50d20b60d6a21b70d20b60d20b70d6a21b80d20b70d20b80d6a21b90d" - "20b80d20b90d6a21ba0d" - "20b90d20ba0d6a21bb0d20ba0d20bb0d6a21bc0d20bb0d20bc0d6a21bd0d20bc0d20bd0d6a21be0d20bd0d20be0d6a" - "21bf0d20be0d20bf0d6a" - "21c00d20bf0d20c00d6a21c10d20c00d20c10d6a21c20d20c10d20c20d6a21c30d20c20d20c30d6a21c40d20c30d20" - "c40d6a21c50d20c40d20" - "c50d6a21c60d20c50d20c60d6a21c70d20c60d20c70d6a21c80d20c70d20c80d6a21c90d20c80d20c90d6a21ca0d20" - "c90d20ca0d6a21cb0d20" - "ca0d20cb0d6a21cc0d20cb0d20cc0d6a21cd0d20cc0d20cd0d6a21ce0d20cd0d20ce0d6a21cf0d20ce0d20cf0d6a21" - "d00d20cf0d20d00d6a21" - "d10d20d00d20d10d6a21d20d20d10d20d20d6a21d30d20d20d20d30d6a21d40d20d30d20d40d6a21d50d20d40d20d5" - "0d6a21d60d20d50d20d6" - "0d6a21d70d20d60d20d70d6a21d80d20d70d20d80d6a21d90d20d80d20d90d6a21da0d20d90d20da0d6a21db0d20da" - "0d20db0d6a21dc0d20db" - "0d20dc0d6a21dd0d20dc0d20dd0d6a21de0d20dd0d20de0d6a21df0d20de0d20df0d6a21e00d20df0d20e00d6a21e1" - "0d20e00d20e10d6a21e2" - "0d20e10d20e20d6a21e30d20e20d20e30d6a21e40d20e30d20e40d6a21e50d20e40d20e50d6a21e60d20e50d20e60d" - "6a21e70d20e60d20e70d" - "6a21e80d20e70d20e80d6a21e90d20e80d20e90d6a21ea0d20e90d20ea0d6a21eb0d20ea0d20eb0d6a21ec0d20eb0d" - "20ec0d6a21ed0d20ec0d" - "20ed0d6a21ee0d20ed0d20ee0d6a21ef0d20ee0d20ef0d6a21f00d20ef0d20f00d6a21f10d20f00d20f10d6a21f20d" - "20f10d20f20d6a21f30d" - "20f20d20f30d6a21f40d20f30d20f40d6a21f50d20f40d20f50d6a21f60d20f50d20f60d6a21f70d20f60d20f70d6a" - "21f80d20f70d20f80d6a" - "21f90d20f80d20f90d6a21fa0d20f90d20fa0d6a21fb0d20fa0d20fb0d6a21fc0d20fb0d20fc0d6a21fd0d20fc0d20" - "fd0d6a21fe0d20fd0d20" - "fe0d6a21ff0d20fe0d20ff0d6a21800e20ff0d20800e6a21810e20800e20810e6a21820e20810e20820e6a21830e20" - "820e20830e6a21840e20" - "830e20840e6a21850e20840e20850e6a21860e20850e20860e6a21870e20860e20870e6a21880e20870e20880e6a21" - "890e20880e20890e6a21" - "8a0e20890e208a0e6a218b0e208a0e208b0e6a218c0e208b0e208c0e6a218d0e208c0e208d0e6a218e0e208d0e208e" - "0e6a218f0e208e0e208f" - "0e6a21900e208f0e20900e6a21910e20900e20910e6a21920e20910e20920e6a21930e20920e20930e6a21940e2093" - "0e20940e6a21950e2094" - "0e20950e6a21960e20950e20960e6a21970e20960e20970e6a21980e20970e20980e6a21990e20980e20990e6a219a" - "0e20990e209a0e6a219b" - "0e209a0e209b0e6a219c0e209b0e209c0e6a219d0e209c0e209d0e6a219e0e209d0e209e0e6a219f0e209e0e209f0e" - "6a21a00e209f0e20a00e" - "6a21a10e20a00e20a10e6a21a20e20a10e20a20e6a21a30e20a20e20a30e6a21a40e20a30e20a40e6a21a50e20a40e" - "20a50e6a21a60e20a50e" - "20a60e6a21a70e20a60e20a70e6a21a80e20a70e20a80e6a21a90e20a80e20a90e6a21aa0e20a90e20aa0e6a21ab0e" - "20aa0e20ab0e6a21ac0e" - "20ab0e20ac0e6a21ad0e20ac0e20ad0e6a21ae0e20ad0e20ae0e6a21af0e20ae0e20af0e6a21b00e20af0e20b00e6a" - "21b10e20b00e20b10e6a" - "21b20e20b10e20b20e6a21b30e20b20e20b30e6a21b40e20b30e20b40e6a21b50e20b40e20b50e6a21b60e20b50e20" - "b60e6a21b70e20b60e20" - "b70e6a21b80e20b70e20b80e6a21b90e20b80e20b90e6a21ba0e20b90e20ba0e6a21bb0e20ba0e20bb0e6a21bc0e20" - "bb0e20bc0e6a21bd0e20" - "bc0e20bd0e6a21be0e20bd0e20be0e6a21bf0e20be0e20bf0e6a21c00e20bf0e20c00e6a21c10e20c00e20c10e6a21" - "c20e20c10e20c20e6a21" - "c30e20c20e20c30e6a21c40e20c30e20c40e6a21c50e20c40e20c50e6a21c60e20c50e20c60e6a21c70e20c60e20c7" - "0e6a21c80e20c70e20c8" - "0e6a21c90e20c80e20c90e6a21ca0e20c90e20ca0e6a21cb0e20ca0e20cb0e6a21cc0e20cb0e20cc0e6a21cd0e20cc" - "0e20cd0e6a21ce0e20cd" - "0e20ce0e6a21cf0e20ce0e20cf0e6a21d00e20cf0e20d00e6a21d10e20d00e20d10e6a21d20e20d10e20d20e6a21d3" - "0e20d20e20d30e6a21d4" - "0e20d30e20d40e6a21d50e20d40e20d50e6a21d60e20d50e20d60e6a21d70e20d60e20d70e6a21d80e20d70e20d80e" - "6a21d90e20d80e20d90e" - "6a21da0e20d90e20da0e6a21db0e20da0e20db0e6a21dc0e20db0e20dc0e6a21dd0e20dc0e20dd0e6a21de0e20dd0e" - "20de0e6a21df0e20de0e" - "20df0e6a21e00e20df0e20e00e6a21e10e20e00e20e10e6a21e20e20e10e20e20e6a21e30e20e20e20e30e6a21e40e" - "20e30e20e40e6a21e50e" - "20e40e20e50e6a21e60e20e50e20e60e6a21e70e20e60e20e70e6a21e80e20e70e20e80e6a21e90e20e80e20e90e6a" - "21ea0e20e90e20ea0e6a" - "21eb0e20ea0e20eb0e6a21ec0e20eb0e20ec0e6a21ed0e20ec0e20ed0e6a21ee0e20ed0e20ee0e6a21ef0e20ee0e20" - "ef0e6a21f00e20ef0e20" - "f00e6a21f10e20f00e20f10e6a21f20e20f10e20f20e6a21f30e20f20e20f30e6a21f40e20f30e20f40e6a21f50e20" - "f40e20f50e6a21f60e20" + "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" - "fb0e20fa0e20fb0e6a21" - "fc0e20fb0e20fc0e6a21fd0e20fc0e20fd0e6a21fe0e20fd0e20fe0e6a21ff0e20fe0e20ff0e6a21800f20ff0e2080" - "0f6a21810f20800f2081" - "0f6a21820f20810f20820f6a21830f20820f20830f6a21840f20830f20840f6a21850f20840f20850f6a21860f2085" - "0f20860f6a21870f2086" - "0f20870f6a21880f20870f20880f6a21890f20880f20890f6a218a0f20890f208a0f6a218b0f208a0f208b0f6a218c" - "0f208b0f208c0f6a218d" - "0f208c0f208d0f6a218e0f208d0f208e0f6a218f0f208e0f208f0f6a21900f208f0f20900f6a21910f20900f20910f" - "6a21920f20910f20920f" - "6a21930f20920f20930f6a21940f20930f20940f6a21950f20940f20950f6a21960f20950f20960f6a21970f20960f" - "20970f6a21980f20970f" - "20980f6a21990f20980f20990f6a219a0f20990f209a0f6a219b0f209a0f209b0f6a219c0f209b0f209c0f6a219d0f" - "209c0f209d0f6a219e0f" - "209d0f209e0f6a219f0f209e0f209f0f6a21a00f209f0f20a00f6a21a10f20a00f20a10f6a21a20f20a10f20a20f6a" - "21a30f20a20f20a30f6a" - "21a40f20a30f20a40f6a21a50f20a40f20a50f6a21a60f20a50f20a60f6a21a70f20a60f20a70f6a21a80f20a70f20" - "a80f6a21a90f20a80f20" - "a90f6a21aa0f20a90f20aa0f6a21ab0f20aa0f20ab0f6a21ac0f20ab0f20ac0f6a21ad0f20ac0f20ad0f6a21ae0f20" - "ad0f20ae0f6a21af0f20" - "ae0f20af0f6a21b00f20af0f20b00f6a21b10f20b00f20b10f6a21b20f20b10f20b20f6a21b30f20b20f20b30f6a21" - "b40f20b30f20b40f6a21" - "b50f20b40f20b50f6a21b60f20b50f20b60f6a21b70f20b60f20b70f6a21b80f20b70f20b80f6a21b90f20b80f20b9" - "0f6a21ba0f20b90f20ba" - "0f6a21bb0f20ba0f20bb0f6a21bc0f20bb0f20bc0f6a21bd0f20bc0f20bd0f6a21be0f20bd0f20be0f6a21bf0f20be" - "0f20bf0f6a21c00f20bf" - "0f20c00f6a21c10f20c00f20c10f6a21c20f20c10f20c20f6a21c30f20c20f20c30f6a21c40f20c30f20c40f6a21c5" - "0f20c40f20c50f6a21c6" - "0f20c50f20c60f6a21c70f20c60f20c70f6a21c80f20c70f20c80f6a21c90f20c80f20c90f6a21ca0f20c90f20ca0f" - "6a21cb0f20ca0f20cb0f" - "6a21cc0f20cb0f20cc0f6a21cd0f20cc0f20cd0f6a21ce0f20cd0f20ce0f6a21cf0f20ce0f20cf0f6a21d00f20cf0f" - "20d00f6a21d10f20d00f" - "20d10f6a21d20f20d10f20d20f6a21d30f20d20f20d30f6a21d40f20d30f20d40f6a21d50f20d40f20d50f6a21d60f" - "20d50f20d60f6a21d70f" - "20d60f20d70f6a21d80f20d70f20d80f6a21d90f20d80f20d90f6a21da0f20d90f20da0f6a21db0f20da0f20db0f6a" - "21dc0f20db0f20dc0f6a" - "21dd0f20dc0f20dd0f6a21de0f20dd0f20de0f6a21df0f20de0f20df0f6a21e00f20df0f20e00f6a21e10f20e00f20" - "e10f6a21e20f20e10f20" - "e20f6a21e30f20e20f20e30f6a21e40f20e30f20e40f6a21e50f20e40f20e50f6a21e60f20e50f20e60f6a21e70f20" - "e60f20e70f6a21e80f20" - "e70f20e80f6a21e90f20e80f20e90f6a21ea0f20e90f20ea0f6a21eb0f20ea0f20eb0f6a21ec0f20eb0f20ec0f6a21" - "ed0f20ec0f20ed0f6a21" - "ee0f20ed0f20ee0f6a21ef0f20ee0f20ef0f6a21f00f20ef0f20f00f6a21f10f20f00f20f10f6a21f20f20f10f20f2" - "0f6a21f30f20f20f20f3" - "0f6a21f40f20f30f20f40f6a21f50f20f40f20f50f6a21f60f20f50f20f60f6a21f70f20f60f20f70f6a21f80f20f7" - "0f20f80f6a21f90f20f8" - "0f20f90f6a21fa0f20f90f20fa0f6a21fb0f20fa0f20fb0f6a21fc0f20fb0f20fc0f6a21fd0f20fc0f20fd0f6a21fe" - "0f20fd0f20fe0f6a21ff" - "0f20fe0f20ff0f6a21801020ff0f2080106a2181102080102081106a2182102081102082106a218310208210208310" - "6a218410208310208410" - "6a2185102084102085106a2186102085102086106a2187102086102087106a2188102087102088106a218910208810" - "2089106a218a10208910" - "208a106a218b10208a10208b106a218c10208b10208c106a218d10208c10208d106a218e10208d10208e106a218f10" - "208e10208f106a219010" - "208f102090106a2191102090102091106a2192102091102092106a2193102092102093106a2194102093102094106a" - "2195102094102095106a" - "2196102095102096106a2197102096102097106a2198102097102098106a2199102098102099106a219a1020991020" - "9a106a219b10209a1020" - "9b106a219c10209b10209c106a219d10209c10209d106a219e10209d10209e106a219f10209e10209f106a21a01020" - "9f1020a0106a21a11020" - "a01020a1106a21a21020a11020a2106a21a31020a21020a3106a21a41020a31020a4106a21a51020a41020a5106a21" - "a61020a51020a6106a21" - "a71020a61020a7106a21a81020a71020a8106a21a91020a81020a9106a21aa1020a91020aa106a21ab1020aa1020ab" - "106a21ac1020ab1020ac" - "106a21ad1020ac1020ad106a21ae1020ad1020ae106a21af1020ae1020af106a21b01020af1020b0106a21b11020b0" - "1020b1106a21b21020b1" - "1020b2106a21b31020b21020b3106a21b41020b31020b4106a21b51020b41020b5106a21b61020b51020b6106a21b7" - "1020b61020b7106a21b8" - "1020b71020b8106a21b91020b81020b9106a21ba1020b91020ba106a21bb1020ba1020bb106a21bc1020bb1020bc10" - "6a21bd1020bc1020bd10" - "6a21be1020bd1020be106a21bf1020be1020bf106a21c01020bf1020c0106a21c11020c01020c1106a21c21020c110" - "20c2106a21c31020c210" - "20c3106a21c41020c31020c4106a21c51020c41020c5106a21c61020c51020c6106a21c71020c61020c7106a21c810" - "20c71020c8106a21c910" - "20c81020c9106a21ca1020c91020ca106a21cb1020ca1020cb106a21cc1020cb1020cc106a21cd1020cc1020cd106a" - "21ce1020cd1020ce106a" - "21cf1020ce1020cf106a21d01020cf1020d0106a21d11020d01020d1106a21d21020d11020d2106a21d31020d21020" - "d3106a21d41020d31020" - "d4106a21d51020d41020d5106a21d61020d51020d6106a21d71020d61020d7106a21d81020d71020d8106a21d91020" - "d81020d9106a21da1020" - "d91020da106a21db1020da1020db106a21dc1020db1020dc106a21dd1020dc1020dd106a21de1020dd1020de106a21" - "df1020de1020df106a21" - "e01020df1020e0106a21e11020e01020e1106a21e21020e11020e2106a21e31020e21020e3106a21e41020e31020e4" - "106a21e51020e41020e5" - "106a21e61020e51020e6106a21e71020e61020e7106a21e81020e71020e8106a21e91020e81020e9106a21ea1020e9" - "1020ea106a21eb1020ea" - "1020eb106a21ec1020eb1020ec106a21ed1020ec1020ed106a21ee1020ed1020ee106a21ef1020ee1020ef106a21f0" - "1020ef1020f0106a21f1" - "1020f01020f1106a21f21020f11020f2106a21f31020f21020f3106a21f41020f31020f4106a21f51020f41020f510" - "6a21f61020f51020f610" - "6a21f71020f61020f7106a21f81020f71020f8106a21f91020f81020f9106a21fa1020f91020fa106a21fb1020fa10" - "20fb106a21fc1020fb10" - "20fc106a21fd1020fc1020fd106a21fe1020fd1020fe106a21ff1020fe1020ff106a21801120ff102080116a218111" - "2080112081116a218211" + "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" - "2187112086112087116a" - "2188112087112088116a2189112088112089116a218a11208911208a116a218b11208a11208b116a218c11208b1120" - "8c116a218d11208c1120" - "8d116a218e11208d11208e116a218f11208e11208f116a219011208f112090116a2191112090112091116a21921120" - "91112092116a21931120" - "92112093116a2194112093112094116a2195112094112095116a2196112095112096116a2197112096112097116a21" - "98112097112098116a21" - "99112098112099116a219a11209911209a116a219b11209a11209b116a219c11209b11209c116a219d11209c11209d" - "116a219e11209d11209e" - "116a219f11209e11209f116a21a011209f1120a0116a21a11120a01120a1116a21a21120a11120a2116a21a31120a2" - "1120a3116a21a41120a3" - "1120a4116a21a51120a41120a5116a21a61120a51120a6116a21a71120a61120a7116a21a81120a71120a8116a21a9" - "1120a81120a9116a21aa" - "1120a91120aa116a21ab1120aa1120ab116a21ac1120ab1120ac116a21ad1120ac1120ad116a21ae1120ad1120ae11" - "6a21af1120ae1120af11" - "6a21b01120af1120b0116a21b11120b01120b1116a21b21120b11120b2116a21b31120b21120b3116a21b41120b311" - "20b4116a21b51120b411" - "20b5116a21b61120b51120b6116a21b71120b61120b7116a21b81120b71120b8116a21b91120b81120b9116a21ba11" - "20b91120ba116a21bb11" - "20ba1120bb116a21bc1120bb1120bc116a21bd1120bc1120bd116a21be1120bd1120be116a21bf1120be1120bf116a" - "21c01120bf1120c0116a" - "21c11120c01120c1116a21c21120c11120c2116a21c31120c21120c3116a21c41120c31120c4116a21c51120c41120" - "c5116a21c61120c51120" - "c6116a21c71120c61120c7116a21c81120c71120c8116a21c91120c81120c9116a21ca1120c91120ca116a21cb1120" - "ca1120cb116a21cc1120" - "cb1120cc116a21cd1120cc1120cd116a21ce1120cd1120ce116a21cf1120ce1120cf116a21d01120cf1120d0116a21" - "d11120d01120d1116a21" - "d21120d11120d2116a21d31120d21120d3116a21d41120d31120d4116a21d51120d41120d5116a21d61120d51120d6" - "116a21d71120d61120d7" - "116a21d81120d71120d8116a21d91120d81120d9116a21da1120d91120da116a21db1120da1120db116a21dc1120db" - "1120dc116a21dd1120dc" - "1120dd116a21de1120dd1120de116a21df1120de1120df116a21e01120df1120e0116a21e11120e01120e1116a21e2" - "1120e11120e2116a21e3" - "1120e21120e3116a21e41120e31120e4116a21e51120e41120e5116a21e61120e51120e6116a21e71120e61120e711" - "6a21e81120e71120e811" - "6a21e91120e81120e9116a21ea1120e91120ea116a21eb1120ea1120eb116a21ec1120eb1120ec116a21ed1120ec11" - "20ed116a21ee1120ed11" - "20ee116a21ef1120ee1120ef116a21f01120ef1120f0116a21f11120f01120f1116a21f21120f11120f2116a21f311" - "20f21120f3116a21f411" - "20f31120f4116a21f51120f41120f5116a21f61120f51120f6116a21f71120f61120f7116a21f81120f71120f8116a" - "21f91120f81120f9116a" - "21fa1120f91120fa116a21fb1120fa1120fb116a21fc1120fb1120fc116a21fd1120fc1120fd116a21fe1120fd1120" - "fe116a21ff1120fe1120" - "ff116a21801220ff112080126a2181122080122081126a2182122081122082126a2183122082122083126a21841220" - "83122084126a21851220" - "84122085126a2186122085122086126a2187122086122087126a2188122087122088126a2189122088122089126a21" - "8a12208912208a126a21" - "8b12208a12208b126a218c12208b12208c126a218d12208c12208d126a218e12208d12208e126a218f12208e12208f" - "126a219012208f122090" - "126a2191122090122091126a2192122091122092126a2193122092122093126a2194122093122094126a2195122094" - "122095126a2196122095" - "122096126a2197122096122097126a2198122097122098126a2199122098122099126a219a12209912209a126a219b" - "12209a12209b126a219c" - "12209b12209c126a219d12209c12209d126a219e12209d12209e126a219f12209e12209f126a21a012209f1220a012" - "6a21a11220a01220a112" - "6a21a21220a11220a2126a21a31220a21220a3126a21a41220a31220a4126a21a51220a41220a5126a21a61220a512" - "20a6126a21a71220a612" - "20a7126a21a81220a71220a8126a21a91220a81220a9126a21aa1220a91220aa126a21ab1220aa1220ab126a21ac12" - "20ab1220ac126a21ad12" - "20ac1220ad126a21ae1220ad1220ae126a21af1220ae1220af126a21b01220af1220b0126a21b11220b01220b1126a" - "21b21220b11220b2126a" - "21b31220b21220b3126a21b41220b31220b4126a21b51220b41220b5126a21b61220b51220b6126a21b71220b61220" - "b7126a21b81220b71220" - "b8126a21b91220b81220b9126a21ba1220b91220ba126a21bb1220ba1220bb126a21bc1220bb1220bc126a21bd1220" - "bc1220bd126a21be1220" - "bd1220be126a21bf1220be1220bf126a21c01220bf1220c0126a21c11220c01220c1126a21c21220c11220c2126a21" - "c31220c21220c3126a21" - "c41220c31220c4126a21c51220c41220c5126a21c61220c51220c6126a21c71220c61220c7126a21c81220c71220c8" - "126a21c91220c81220c9" - "126a21ca1220c91220ca126a21cb1220ca1220cb126a21cc1220cb1220cc126a21cd1220cc1220cd126a21ce1220cd" - "1220ce126a21cf1220ce" - "1220cf126a21d01220cf1220d0126a21d11220d01220d1126a21d21220d11220d2126a21d31220d21220d3126a21d4" - "1220d31220d4126a21d5" - "1220d41220d5126a21d61220d51220d6126a21d71220d61220d7126a21d81220d71220d8126a21d91220d81220d912" - "6a21da1220d91220da12" - "6a21db1220da1220db126a21dc1220db1220dc126a21dd1220dc1220dd126a21de1220dd1220de126a21df1220de12" - "20df126a21e01220df12" - "20e0126a21e11220e01220e1126a21e21220e11220e2126a21e31220e21220e3126a21e41220e31220e4126a21e512" - "20e41220e5126a21e612" - "20e51220e6126a21e71220e61220e7126a21e81220e71220e8126a21e91220e81220e9126a21ea1220e91220ea126a" - "21eb1220ea1220eb126a" - "21ec1220eb1220ec126a21ed1220ec1220ed126a21ee1220ed1220ee126a21ef1220ee1220ef126a21f01220ef1220" - "f0126a21f11220f01220" - "f1126a21f21220f11220f2126a21f31220f21220f3126a21f41220f31220f4126a21f51220f41220f5126a21f61220" - "f51220f6126a21f71220" - "f61220f7126a21f81220f71220f8126a21f91220f81220f9126a21fa1220f91220fa126a21fb1220fa1220fb126a21" - "fc1220fb1220fc126a21" - "fd1220fc1220fd126a21fe1220fd1220fe126a21ff1220fe1220ff126a21801320ff122080136a2181132080132081" - "136a2182132081132082" - "136a2183132082132083136a2184132083132084136a2185132084132085136a2186132085132086136a2187132086" - "132087136a2188132087" - "132088136a2189132088132089136a218a13208913208a136a218b13208a13208b136a218c13208b13208c136a218d" - "13208c13208d136a218e" + "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" - "6a219313209213209313" - "6a2194132093132094136a2195132094132095136a2196132095132096136a2197132096132097136a219813209713" - "2098136a219913209813" - "2099136a219a13209913209a136a219b13209a13209b136a219c13209b13209c136a219d13209c13209d136a219e13" - "209d13209e136a219f13" - "209e13209f136a21a013209f1320a0136a21a11320a01320a1136a21a21320a11320a2136a21a31320a21320a3136a" - "21a41320a31320a4136a" - "21a51320a41320a5136a21a61320a51320a6136a21a71320a61320a7136a21a81320a71320a8136a21a91320a81320" - "a9136a21aa1320a91320" - "aa136a21ab1320aa1320ab136a21ac1320ab1320ac136a21ad1320ac1320ad136a21ae1320ad1320ae136a21af1320" - "ae1320af136a21b01320" - "af1320b0136a21b11320b01320b1136a21b21320b11320b2136a21b31320b21320b3136a21b41320b31320b4136a21" - "b51320b41320b5136a21" - "b61320b51320b6136a21b71320b61320b7136a21b81320b71320b8136a21b91320b81320b9136a21ba1320b91320ba" - "136a21bb1320ba1320bb" - "136a21bc1320bb1320bc136a21bd1320bc1320bd136a21be1320bd1320be136a21bf1320be1320bf136a21c01320bf" - "1320c0136a21c11320c0" - "1320c1136a21c21320c11320c2136a21c31320c21320c3136a21c41320c31320c4136a21c51320c41320c5136a21c6" - "1320c51320c6136a21c7" - "1320c61320c7136a21c81320c71320c8136a21c91320c81320c9136a21ca1320c91320ca136a21cb1320ca1320cb13" - "6a21cc1320cb1320cc13" - "6a21cd1320cc1320cd136a21ce1320cd1320ce136a21cf1320ce1320cf136a21d01320cf1320d0136a21d11320d013" - "20d1136a21d21320d113" - "20d2136a21d31320d21320d3136a21d41320d31320d4136a21d51320d41320d5136a21d61320d51320d6136a21d713" - "20d61320d7136a21d813" - "20d71320d8136a21d91320d81320d9136a21da1320d91320da136a21db1320da1320db136a21dc1320db1320dc136a" - "21dd1320dc1320dd136a" - "21de1320dd1320de136a21df1320de1320df136a21e01320df1320e0136a21e11320e01320e1136a21e21320e11320" - "e2136a21e31320e21320" - "e3136a21e41320e31320e4136a21e51320e41320e5136a21e61320e51320e6136a21e71320e61320e7136a21e81320" - "e71320e8136a21e91320" - "e81320e9136a21ea1320e91320ea136a21eb1320ea1320eb136a21ec1320eb1320ec136a21ed1320ec1320ed136a21" - "ee1320ed1320ee136a21" - "ef1320ee1320ef136a21f01320ef1320f0136a21f11320f01320f1136a21f21320f11320f2136a21f31320f21320f3" - "136a21f41320f31320f4" - "136a21f51320f41320f5136a21f61320f51320f6136a21f71320f61320f7136a21f81320f71320f8136a21f91320f8" - "1320f9136a21fa1320f9" - "1320fa136a21fb1320fa1320fb136a21fc1320fb1320fc136a21fd1320fc1320fd136a21fe1320fd1320fe136a21ff" - "1320fe1320ff136a2180" - "1420ff132080146a2181142080142081146a2182142081142082146a2183142082142083146a218414208314208414" - "6a218514208414208514" - "6a2186142085142086146a2187142086142087146a2188142087142088146a2189142088142089146a218a14208914" - "208a146a218b14208a14" - "208b146a218c14208b14208c146a218d14208c14208d146a218e14208d14208e146a218f14208e14208f146a219014" - "208f142090146a219114" - "2090142091146a2192142091142092146a2193142092142093146a2194142093142094146a2195142094142095146a" - "2196142095142096146a" - "2197142096142097146a2198142097142098146a2199142098142099146a219a14209914209a146a219b14209a1420" - "9b146a219c14209b1420" - "9c146a219d14209c14209d146a219e14209d14209e146a219f14209e14209f146a21a014209f1420a0146a21a11420" - "a01420a1146a21a21420" - "a11420a2146a21a31420a21420a3146a21a41420a31420a4146a21a51420a41420a5146a21a61420a51420a6146a21" - "a71420a61420a7146a21" - "a81420a71420a8146a21a91420a81420a9146a21aa1420a91420aa146a21ab1420aa1420ab146a21ac1420ab1420ac" - "146a21ad1420ac1420ad" - "146a21ae1420ad1420ae146a21af1420ae1420af146a21b01420af1420b0146a21b11420b01420b1146a21b21420b1" - "1420b2146a21b31420b2" - "1420b3146a21b41420b31420b4146a21b51420b41420b5146a21b61420b51420b6146a21b71420b61420b7146a21b8" - "1420b71420b8146a21b9" - "1420b81420b9146a21ba1420b91420ba146a21bb1420ba1420bb146a21bc1420bb1420bc146a21bd1420bc1420bd14" - "6a21be1420bd1420be14" - "6a21bf1420be1420bf146a21c01420bf1420c0146a21c11420c01420c1146a21c21420c11420c2146a21c31420c214" - "20c3146a21c41420c314" - "20c4146a21c51420c41420c5146a21c61420c51420c6146a21c71420c61420c7146a21c81420c71420c8146a21c914" - "20c81420c9146a21ca14" - "20c91420ca146a21cb1420ca1420cb146a21cc1420cb1420cc146a21cd1420cc1420cd146a21ce1420cd1420ce146a" - "21cf1420ce1420cf146a" - "21d01420cf1420d0146a21d11420d01420d1146a21d21420d11420d2146a21d31420d21420d3146a21d41420d31420" - "d4146a21d51420d41420" - "d5146a21d61420d51420d6146a21d71420d61420d7146a21d81420d71420d8146a21d91420d81420d9146a21da1420" - "d91420da146a21db1420" - "da1420db146a21dc1420db1420dc146a21dd1420dc1420dd146a21de1420dd1420de146a21df1420de1420df146a21" - "e01420df1420e0146a21" - "e11420e01420e1146a21e21420e11420e2146a21e31420e21420e3146a21e41420e31420e4146a21e51420e41420e5" - "146a21e61420e51420e6" - "146a21e71420e61420e7146a21e81420e71420e8146a21e91420e81420e9146a21ea1420e91420ea146a21eb1420ea" - "1420eb146a21ec1420eb" - "1420ec146a21ed1420ec1420ed146a21ee1420ed1420ee146a21ef1420ee1420ef146a21f01420ef1420f0146a21f1" - "1420f01420f1146a21f2" - "1420f11420f2146a21f31420f21420f3146a21f41420f31420f4146a21f51420f41420f5146a21f61420f51420f614" - "6a21f71420f61420f714" - "6a21f81420f71420f8146a21f91420f81420f9146a21fa1420f91420fa146a21fb1420fa1420fb146a21fc1420fb14" - "20fc146a21fd1420fc14" - "20fd146a21fe1420fd1420fe146a21ff1420fe1420ff146a21801520ff142080156a2181152080152081156a218215" - "2081152082156a218315" - "2082152083156a2184152083152084156a2185152084152085156a2186152085152086156a2187152086152087156a" - "2188152087152088156a" - "2189152088152089156a218a15208915208a156a218b15208a15208b156a218c15208b15208c156a218d15208c1520" - "8d156a218e15208d1520" - "8e156a218f15208e15208f156a219015208f152090156a2191152090152091156a2192152091152092156a21931520" - "92152093156a21941520" - "93152094156a2195152094152095156a2196152095152096156a2197152096152097156a2198152097152098156a21" - "99152098152099156a21" + "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" - "156a219f15209e15209f" - "156a21a015209f1520a0156a21a11520a01520a1156a21a21520a11520a2156a21a31520a21520a3156a21a41520a3" - "1520a4156a21a51520a4" - "1520a5156a21a61520a51520a6156a21a71520a61520a7156a21a81520a71520a8156a21a91520a81520a9156a21aa" - "1520a91520aa156a21ab" - "1520aa1520ab156a21ac1520ab1520ac156a21ad1520ac1520ad156a21ae1520ad1520ae156a21af1520ae1520af15" - "6a21b01520af1520b015" - "6a21b11520b01520b1156a21b21520b11520b2156a21b31520b21520b3156a21b41520b31520b4156a21b51520b415" - "20b5156a21b61520b515" - "20b6156a21b71520b61520b7156a21b81520b71520b8156a21b91520b81520b9156a21ba1520b91520ba156a21bb15" - "20ba1520bb156a21bc15" - "20bb1520bc156a21bd1520bc1520bd156a21be1520bd1520be156a21bf1520be1520bf156a21c01520bf1520c0156a" - "21c11520c01520c1156a" - "21c21520c11520c2156a21c31520c21520c3156a21c41520c31520c4156a21c51520c41520c5156a21c61520c51520" - "c6156a21c71520c61520" - "c7156a21c81520c71520c8156a21c91520c81520c9156a21ca1520c91520ca156a21cb1520ca1520cb156a21cc1520" - "cb1520cc156a21cd1520" - "cc1520cd156a21ce1520cd1520ce156a21cf1520ce1520cf156a21d01520cf1520d0156a21d11520d01520d1156a21" - "d21520d11520d2156a21" - "d31520d21520d3156a21d41520d31520d4156a21d51520d41520d5156a21d61520d51520d6156a21d71520d61520d7" - "156a21d81520d71520d8" - "156a21d91520d81520d9156a21da1520d91520da156a21db1520da1520db156a21dc1520db1520dc156a21dd1520dc" - "1520dd156a21de1520dd" - "1520de156a21df1520de1520df156a21e01520df1520e0156a21e11520e01520e1156a21e21520e11520e2156a21e3" - "1520e21520e3156a21e4" - "1520e31520e4156a21e51520e41520e5156a21e61520e51520e6156a21e71520e61520e7156a21e81520e71520e815" - "6a21e91520e81520e915" - "6a21ea1520e91520ea156a21eb1520ea1520eb156a21ec1520eb1520ec156a21ed1520ec1520ed156a21ee1520ed15" - "20ee156a21ef1520ee15" - "20ef156a21f01520ef1520f0156a21f11520f01520f1156a21f21520f11520f2156a21f31520f21520f3156a21f415" - "20f31520f4156a21f515" - "20f41520f5156a21f61520f51520f6156a21f71520f61520f7156a21f81520f71520f8156a21f91520f81520f9156a" - "21fa1520f91520fa156a" - "21fb1520fa1520fb156a21fc1520fb1520fc156a21fd1520fc1520fd156a21fe1520fd1520fe156a21ff1520fe1520" - "ff156a21801620ff1520" - "80166a2181162080162081166a2182162081162082166a2183162082162083166a2184162083162084166a21851620" - "84162085166a21861620" - "85162086166a2187162086162087166a2188162087162088166a2189162088162089166a218a16208916208a166a21" - "8b16208a16208b166a21" - "8c16208b16208c166a218d16208c16208d166a218e16208d16208e166a218f16208e16208f166a219016208f162090" - "166a2191162090162091" - "166a2192162091162092166a2193162092162093166a2194162093162094166a2195162094162095166a2196162095" - "162096166a2197162096" - "162097166a2198162097162098166a2199162098162099166a219a16209916209a166a219b16209a16209b166a219c" - "16209b16209c166a219d" - "16209c16209d166a219e16209d16209e166a219f16209e16209f166a21a016209f1620a0166a21a11620a01620a116" - "6a21a21620a11620a216" - "6a21a31620a21620a3166a21a41620a31620a4166a21a51620a41620a5166a21a61620a51620a6166a21a71620a616" - "20a7166a21a81620a716" - "20a8166a21a91620a81620a9166a21aa1620a91620aa166a21ab1620aa1620ab166a21ac1620ab1620ac166a21ad16" - "20ac1620ad166a21ae16" - "20ad1620ae166a21af1620ae1620af166a21b01620af1620b0166a21b11620b01620b1166a21b21620b11620b2166a" - "21b31620b21620b3166a" - "21b41620b31620b4166a21b51620b41620b5166a21b61620b51620b6166a21b71620b61620b7166a21b81620b71620" - "b8166a21b91620b81620" - "b9166a21ba1620b91620ba166a21bb1620ba1620bb166a21bc1620bb1620bc166a21bd1620bc1620bd166a21be1620" - "bd1620be166a21bf1620" - "be1620bf166a21c01620bf1620c0166a21c11620c01620c1166a21c21620c11620c2166a21c31620c21620c3166a21" - "c41620c31620c4166a21" - "c51620c41620c5166a21c61620c51620c6166a21c71620c61620c7166a21c81620c71620c8166a21c91620c81620c9" - "166a21ca1620c91620ca" - "166a21cb1620ca1620cb166a21cc1620cb1620cc166a21cd1620cc1620cd166a21ce1620cd1620ce166a21cf1620ce" - "1620cf166a21d01620cf" - "1620d0166a21d11620d01620d1166a21d21620d11620d2166a21d31620d21620d3166a21d41620d31620d4166a21d5" - "1620d41620d5166a21d6" - "1620d51620d6166a21d71620d61620d7166a21d81620d71620d8166a21d91620d81620d9166a21da1620d91620da16" - "6a21db1620da1620db16" - "6a21dc1620db1620dc166a21dd1620dc1620dd166a21de1620dd1620de166a21df1620de1620df166a21e01620df16" - "20e0166a21e11620e016" - "20e1166a21e21620e11620e2166a21e31620e21620e3166a21e41620e31620e4166a21e51620e41620e5166a21e616" - "20e51620e6166a21e716" - "20e61620e7166a21e81620e71620e8166a21e91620e81620e9166a21ea1620e91620ea166a21eb1620ea1620eb166a" - "21ec1620eb1620ec166a" - "21ed1620ec1620ed166a21ee1620ed1620ee166a21ef1620ee1620ef166a21f01620ef1620f0166a21f11620f01620" - "f1166a21f21620f11620" - "f2166a21f31620f21620f3166a21f41620f31620f4166a21f51620f41620f5166a21f61620f51620f6166a21f71620" - "f61620f7166a21f81620" - "f71620f8166a21f91620f81620f9166a21fa1620f91620fa166a21fb1620fa1620fb166a21fc1620fb1620fc166a21" - "fd1620fc1620fd166a21" - "fe1620fd1620fe166a21ff1620fe1620ff166a21801720ff162080176a2181172080172081176a2182172081172082" - "176a2183172082172083" - "176a2184172083172084176a2185172084172085176a2186172085172086176a2187172086172087176a2188172087" - "172088176a2189172088" - "172089176a218a17208917208a176a218b17208a17208b176a218c17208b17208c176a218d17208c17208d176a218e" - "17208d17208e176a218f" - "17208e17208f176a219017208f172090176a2191172090172091176a2192172091172092176a219317209217209317" - "6a219417209317209417" - "6a2195172094172095176a2196172095172096176a2197172096172097176a2198172097172098176a219917209817" - "2099176a219a17209917" - "209a176a219b17209a17209b176a219c17209b17209c176a219d17209c17209d176a219e17209d17209e176a219f17" - "209e17209f176a21a017" - "209f1720a0176a21a11720a01720a1176a21a21720a11720a2176a21a31720a21720a3176a21a41720a31720a4176a" - "21a51720a41720a5176a" + "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" - "aa176a21ab1720aa1720" - "ab176a21ac1720ab1720ac176a21ad1720ac1720ad176a21ae1720ad1720ae176a21af1720ae1720af176a21b01720" - "af1720b0176a21b11720" - "b01720b1176a21b21720b11720b2176a21b31720b21720b3176a21b41720b31720b4176a21b51720b41720b5176a21" - "b61720b51720b6176a21" - "b71720b61720b7176a21b81720b71720b8176a21b91720b81720b9176a21ba1720b91720ba176a21bb1720ba1720bb" - "176a21bc1720bb1720bc" - "176a21bd1720bc1720bd176a21be1720bd1720be176a21bf1720be1720bf176a21c01720bf1720c0176a21c11720c0" - "1720c1176a21c21720c1" - "1720c2176a21c31720c21720c3176a21c41720c31720c4176a21c51720c41720c5176a21c61720c51720c6176a21c7" - "1720c61720c7176a21c8" - "1720c71720c8176a21c91720c81720c9176a21ca1720c91720ca176a21cb1720ca1720cb176a21cc1720cb1720cc17" - "6a21cd1720cc1720cd17" - "6a21ce1720cd1720ce176a21cf1720ce1720cf176a21d01720cf1720d0176a21d11720d01720d1176a21d21720d117" - "20d2176a21d31720d217" - "20d3176a21d41720d31720d4176a21d51720d41720d5176a21d61720d51720d6176a21d71720d61720d7176a21d817" - "20d71720d8176a21d917" - "20d81720d9176a21da1720d91720da176a21db1720da1720db176a21dc1720db1720dc176a21dd1720dc1720dd176a" - "21de1720dd1720de176a" - "21df1720de1720df176a21e01720df1720e0176a21e11720e01720e1176a21e21720e11720e2176a21e31720e21720" - "e3176a21e41720e31720" - "e4176a21e51720e41720e5176a21e61720e51720e6176a21e71720e61720e7176a21e81720e71720e8176a21e91720" - "e81720e9176a21ea1720" - "e91720ea176a21eb1720ea1720eb176a21ec1720eb1720ec176a21ed1720ec1720ed176a21ee1720ed1720ee176a21" - "ef1720ee1720ef176a21" - "f01720ef1720f0176a21f11720f01720f1176a21f21720f11720f2176a21f31720f21720f3176a21f41720f31720f4" - "176a21f51720f41720f5" - "176a21f61720f51720f6176a21f71720f61720f7176a21f81720f71720f8176a21f91720f81720f9176a21fa1720f9" - "1720fa176a21fb1720fa" - "1720fb176a21fc1720fb1720fc176a21fd1720fc1720fd176a21fe1720fd1720fe176a21ff1720fe1720ff176a2180" - "1820ff172080186a2181" - "182080182081186a2182182081182082186a2183182082182083186a2184182083182084186a218518208418208518" - "6a218618208518208618" - "6a2187182086182087186a2188182087182088186a2189182088182089186a218a18208918208a186a218b18208a18" - "208b186a218c18208b18" - "208c186a218d18208c18208d186a218e18208d18208e186a218f18208e18208f186a219018208f182090186a219118" - "2090182091186a219218" - "2091182092186a2193182092182093186a2194182093182094186a2195182094182095186a2196182095182096186a" - "2197182096182097186a" - "2198182097182098186a2199182098182099186a219a18209918209a186a219b18209a18209b186a219c18209b1820" - "9c186a219d18209c1820" - "9d186a219e18209d18209e186a219f18209e18209f186a21a018209f1820a0186a21a11820a01820a1186a21a21820" - "a11820a2186a21a31820" - "a21820a3186a21a41820a31820a4186a21a51820a41820a5186a21a61820a51820a6186a21a71820a61820a7186a21" - "a81820a71820a8186a21" - "a91820a81820a9186a21aa1820a91820aa186a21ab1820aa1820ab186a21ac1820ab1820ac186a21ad1820ac1820ad" - "186a21ae1820ad1820ae" - "186a21af1820ae1820af186a21b01820af1820b0186a21b11820b01820b1186a21b21820b11820b2186a21b31820b2" - "1820b3186a21b41820b3" - "1820b4186a21b51820b41820b5186a21b61820b51820b6186a21b71820b61820b7186a21b81820b71820b8186a21b9" - "1820b81820b9186a21ba" - "1820b91820ba186a21bb1820ba1820bb186a21bc1820bb1820bc186a21bd1820bc1820bd186a21be1820bd1820be18" - "6a21bf1820be1820bf18" - "6a21c01820bf1820c0186a21c11820c01820c1186a21c21820c11820c2186a21c31820c21820c3186a21c41820c318" - "20c4186a21c51820c418" - "20c5186a21c61820c51820c6186a21c71820c61820c7186a21c81820c71820c8186a21c91820c81820c9186a21ca18" - "20c91820ca186a21cb18" - "20ca1820cb186a21cc1820cb1820cc186a21cd1820cc1820cd186a21ce1820cd1820ce186a21cf1820ce1820cf186a" - "21d01820cf1820d0186a" - "21d11820d01820d1186a21d21820d11820d2186a21d31820d21820d3186a21d41820d31820d4186a21d51820d41820" - "d5186a21d61820d51820" - "d6186a21d71820d61820d7186a21d81820d71820d8186a21d91820d81820d9186a21da1820d91820da186a21db1820" - "da1820db186a21dc1820" - "db1820dc186a21dd1820dc1820dd186a21de1820dd1820de186a21df1820de1820df186a21e01820df1820e0186a21" - "e11820e01820e1186a21" - "e21820e11820e2186a21e31820e21820e3186a21e41820e31820e4186a21e51820e41820e5186a21e61820e51820e6" - "186a21e71820e61820e7" - "186a21e81820e71820e8186a21e91820e81820e9186a21ea1820e91820ea186a21eb1820ea1820eb186a21ec1820eb" - "1820ec186a21ed1820ec" - "1820ed186a21ee1820ed1820ee186a21ef1820ee1820ef186a21f01820ef1820f0186a21f11820f01820f1186a21f2" - "1820f11820f2186a21f3" - "1820f21820f3186a21f41820f31820f4186a21f51820f41820f5186a21f61820f51820f6186a21f71820f61820f718" - "6a21f81820f71820f818" - "6a21f91820f81820f9186a21fa1820f91820fa186a21fb1820fa1820fb186a21fc1820fb1820fc186a21fd1820fc18" - "20fd186a21fe1820fd18" - "20fe186a21ff1820fe1820ff186a21801920ff182080196a2181192080192081196a2182192081192082196a218319" - "2082192083196a218419" - "2083192084196a2185192084192085196a2186192085192086196a2187192086192087196a2188192087192088196a" - "2189192088192089196a" - "218a19208919208a196a218b19208a19208b196a218c19208b19208c196a218d19208c19208d196a218e19208d1920" - "8e196a218f19208e1920" - "8f196a219019208f192090196a2191192090192091196a2192192091192092196a2193192092192093196a21941920" - "93192094196a21951920" - "94192095196a2196192095192096196a2197192096192097196a2198192097192098196a2199192098192099196a21" - "9a19209919209a196a21" - "9b19209a19209b196a219c19209b19209c196a219d19209c19209d196a219e19209d19209e196a219f19209e19209f" - "196a21a019209f1920a0" - "196a21a11920a01920a1196a21a21920a11920a2196a21a31920a21920a3196a21a41920a31920a4196a21a51920a4" - "1920a5196a21a61920a5" - "1920a6196a21a71920a61920a7196a21a81920a71920a8196a21a91920a81920a9196a21aa1920a91920aa196a21ab" - "1920aa1920ab196a21ac" - "1920ab1920ac196a21ad1920ac1920ad196a21ae1920ad1920ae196a21af1920ae1920af196a21b01920af1920b019" - "6a21b11920b01920b119" + "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" - "20b6196a21b71920b619" - "20b7196a21b81920b71920b8196a21b91920b81920b9196a21ba1920b91920ba196a21bb1920ba1920bb196a21bc19" - "20bb1920bc196a21bd19" - "20bc1920bd196a21be1920bd1920be196a21bf1920be1920bf196a21c01920bf1920c0196a21c11920c01920c1196a" - "21c21920c11920c2196a" - "21c31920c21920c3196a21c41920c31920c4196a21c51920c41920c5196a21c61920c51920c6196a21c71920c61920" - "c7196a21c81920c71920" - "c8196a21c91920c81920c9196a21ca1920c91920ca196a21cb1920ca1920cb196a21cc1920cb1920cc196a21cd1920" - "cc1920cd196a21ce1920" - "cd1920ce196a21cf1920ce1920cf196a21d01920cf1920d0196a21d11920d01920d1196a21d21920d11920d2196a21" - "d31920d21920d3196a21" - "d41920d31920d4196a21d51920d41920d5196a21d61920d51920d6196a21d71920d61920d7196a21d81920d71920d8" - "196a21d91920d81920d9" - "196a21da1920d91920da196a21db1920da1920db196a21dc1920db1920dc196a21dd1920dc1920dd196a21de1920dd" - "1920de196a21df1920de" - "1920df196a21e01920df1920e0196a21e11920e01920e1196a21e21920e11920e2196a21e31920e21920e3196a21e4" - "1920e31920e4196a21e5" - "1920e41920e5196a21e61920e51920e6196a21e71920e61920e7196a21e81920e71920e8196a21e91920e81920e919" - "6a21ea1920e91920ea19" - "6a21eb1920ea1920eb196a21ec1920eb1920ec196a21ed1920ec1920ed196a21ee1920ed1920ee196a21ef1920ee19" - "20ef196a21f01920ef19" - "20f0196a21f11920f01920f1196a21f21920f11920f2196a21f31920f21920f3196a21f41920f31920f4196a21f519" - "20f41920f5196a21f619" - "20f51920f6196a21f71920f61920f7196a21f81920f71920f8196a21f91920f81920f9196a21fa1920f91920fa196a" - "21fb1920fa1920fb196a" - "21fc1920fb1920fc196a21fd1920fc1920fd196a21fe1920fd1920fe196a21ff1920fe1920ff196a21801a20ff1920" - "801a6a21811a20801a20" - "811a6a21821a20811a20821a6a21831a20821a20831a6a21841a20831a20841a6a21851a20841a20851a6a21861a20" - "851a20861a6a21871a20" - "861a20871a6a21881a20871a20881a6a21891a20881a20891a6a218a1a20891a208a1a6a218b1a208a1a208b1a6a21" - "8c1a208b1a208c1a6a21" - "8d1a208c1a208d1a6a218e1a208d1a208e1a6a218f1a208e1a208f1a6a21901a208f1a20901a6a21911a20901a2091" - "1a6a21921a20911a2092" - "1a6a21931a20921a20931a6a21941a20931a20941a6a21951a20941a20951a6a21961a20951a20961a6a21971a2096" - "1a20971a6a21981a2097" - "1a20981a6a21991a20981a20991a6a219a1a20991a209a1a6a219b1a209a1a209b1a6a219c1a209b1a209c1a6a219d" - "1a209c1a209d1a6a219e" - "1a209d1a209e1a6a219f1a209e1a209f1a6a21a01a209f1a20a01a6a21a11a20a01a20a11a6a21a21a20a11a20a21a" - "6a21a31a20a21a20a31a" - "6a21a41a20a31a20a41a6a21a51a20a41a20a51a6a21a61a20a51a20a61a6a21a71a20a61a20a71a6a21a81a20a71a" - "20a81a6a21a91a20a81a" - "20a91a6a21aa1a20a91a20aa1a6a21ab1a20aa1a20ab1a6a21ac1a20ab1a20ac1a6a21ad1a20ac1a20ad1a6a21ae1a" - "20ad1a20ae1a6a21af1a" - "20ae1a20af1a6a21b01a20af1a20b01a6a21b11a20b01a20b11a6a21b21a20b11a20b21a6a21b31a20b21a20b31a6a" - "21b41a20b31a20b41a6a" - "21b51a20b41a20b51a6a21b61a20b51a20b61a6a21b71a20b61a20b71a6a21b81a20b71a20b81a6a21b91a20b81a20" - "b91a6a21ba1a20b91a20" - "ba1a6a21bb1a20ba1a20bb1a6a21bc1a20bb1a20bc1a6a21bd1a20bc1a20bd1a6a21be1a20bd1a20be1a6a21bf1a20" - "be1a20bf1a6a21c01a20" - "bf1a20c01a6a21c11a20c01a20c11a6a21c21a20c11a20c21a6a21c31a20c21a20c31a6a21c41a20c31a20c41a6a21" - "c51a20c41a20c51a6a21" - "c61a20c51a20c61a6a21c71a20c61a20c71a6a21c81a20c71a20c81a6a21c91a20c81a20c91a6a21ca1a20c91a20ca" - "1a6a21cb1a20ca1a20cb" - "1a6a21cc1a20cb1a20cc1a6a21cd1a20cc1a20cd1a6a21ce1a20cd1a20ce1a6a21cf1a20ce1a20cf1a6a21d01a20cf" - "1a20d01a6a21d11a20d0" - "1a20d11a6a21d21a20d11a20d21a6a21d31a20d21a20d31a6a21d41a20d31a20d41a6a21d51a20d41a20d51a6a21d6" - "1a20d51a20d61a6a21d7" - "1a20d61a20d71a6a21d81a20d71a20d81a6a21d91a20d81a20d91a6a21da1a20d91a20da1a6a21db1a20da1a20db1a" - "6a21dc1a20db1a20dc1a" - "6a21dd1a20dc1a20dd1a6a21de1a20dd1a20de1a6a21df1a20de1a20df1a6a21e01a20df1a20e01a6a21e11a20e01a" - "20e11a6a21e21a20e11a" - "20e21a6a21e31a20e21a20e31a6a21e41a20e31a20e41a6a21e51a20e41a20e51a6a21e61a20e51a20e61a6a21e71a" - "20e61a20e71a6a21e81a" - "20e71a20e81a6a21e91a20e81a20e91a6a21ea1a20e91a20ea1a6a21eb1a20ea1a20eb1a6a21ec1a20eb1a20ec1a6a" - "21ed1a20ec1a20ed1a6a" - "21ee1a20ed1a20ee1a6a21ef1a20ee1a20ef1a6a21f01a20ef1a20f01a6a21f11a20f01a20f11a6a21f21a20f11a20" - "f21a6a21f31a20f21a20" - "f31a6a21f41a20f31a20f41a6a21f51a20f41a20f51a6a21f61a20f51a20f61a6a21f71a20f61a20f71a6a21f81a20" - "f71a20f81a6a21f91a20" - "f81a20f91a6a21fa1a20f91a20fa1a6a21fb1a20fa1a20fb1a6a21fc1a20fb1a20fc1a6a21fd1a20fc1a20fd1a6a21" - "fe1a20fd1a20fe1a6a21" - "ff1a20fe1a20ff1a6a21801b20ff1a20801b6a21811b20801b20811b6a21821b20811b20821b6a21831b20821b2083" - "1b6a21841b20831b2084" - "1b6a21851b20841b20851b6a21861b20851b20861b6a21871b20861b20871b6a21881b20871b20881b6a21891b2088" - "1b20891b6a218a1b2089" - "1b208a1b6a218b1b208a1b208b1b6a218c1b208b1b208c1b6a218d1b208c1b208d1b6a218e1b208d1b208e1b6a218f" - "1b208e1b208f1b6a2190" - "1b208f1b20901b6a21911b20901b20911b6a21921b20911b20921b6a21931b20921b20931b6a21941b20931b20941b" - "6a21951b20941b20951b" - "6a21961b20951b20961b6a21971b20961b20971b6a21981b20971b20981b6a21991b20981b20991b6a219a1b20991b" - "209a1b6a219b1b209a1b" - "209b1b6a219c1b209b1b209c1b6a219d1b209c1b209d1b6a219e1b209d1b209e1b6a219f1b209e1b209f1b6a21a01b" - "209f1b20a01b6a21a11b" - "20a01b20a11b6a21a21b20a11b20a21b6a21a31b20a21b20a31b6a21a41b20a31b20a41b6a21a51b20a41b20a51b6a" - "21a61b20a51b20a61b6a" - "21a71b20a61b20a71b6a21a81b20a71b20a81b6a21a91b20a81b20a91b6a21aa1b20a91b20aa1b6a21ab1b20aa1b20" - "ab1b6a21ac1b20ab1b20" - "ac1b6a21ad1b20ac1b20ad1b6a21ae1b20ad1b20ae1b6a21af1b20ae1b20af1b6a21b01b20af1b20b01b6a21b11b20" - "b01b20b11b6a21b21b20" - "b11b20b21b6a21b31b20b21b20b31b6a21b41b20b31b20b41b6a21b51b20b41b20b51b6a21b61b20b51b20b61b6a21" - "b71b20b61b20b71b6a21" - "b81b20b71b20b81b6a21b91b20b81b20b91b6a21ba1b20b91b20ba1b6a21bb1b20ba1b20bb1b6a21bc1b20bb1b20bc" - "1b6a21bd1b20bc1b20bd" + "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" - "1b20c21b6a21c31b20c2" - "1b20c31b6a21c41b20c31b20c41b6a21c51b20c41b20c51b6a21c61b20c51b20c61b6a21c71b20c61b20c71b6a21c8" - "1b20c71b20c81b6a21c9" - "1b20c81b20c91b6a21ca1b20c91b20ca1b6a21cb1b20ca1b20cb1b6a21cc1b20cb1b20cc1b6a21cd1b20cc1b20cd1b" - "6a21ce1b20cd1b20ce1b" - "6a21cf1b20ce1b20cf1b6a21d01b20cf1b20d01b6a21d11b20d01b20d11b6a21d21b20d11b20d21b6a21d31b20d21b" - "20d31b6a21d41b20d31b" - "20d41b6a21d51b20d41b20d51b6a21d61b20d51b20d61b6a21d71b20d61b20d71b6a21d81b20d71b20d81b6a21d91b" - "20d81b20d91b6a21da1b" - "20d91b20da1b6a21db1b20da1b20db1b6a21dc1b20db1b20dc1b6a21dd1b20dc1b20dd1b6a21de1b20dd1b20de1b6a" - "21df1b20de1b20df1b6a" - "21e01b20df1b20e01b6a21e11b20e01b20e11b6a21e21b20e11b20e21b6a21e31b20e21b20e31b6a21e41b20e31b20" - "e41b6a21e51b20e41b20" - "e51b6a21e61b20e51b20e61b6a21e71b20e61b20e71b6a21e81b20e71b20e81b6a21e91b20e81b20e91b6a21ea1b20" - "e91b20ea1b6a21eb1b20" - "ea1b20eb1b6a21ec1b20eb1b20ec1b6a21ed1b20ec1b20ed1b6a21ee1b20ed1b20ee1b6a21ef1b20ee1b20ef1b6a21" - "f01b20ef1b20f01b6a21" - "f11b20f01b20f11b6a21f21b20f11b20f21b6a21f31b20f21b20f31b6a21f41b20f31b20f41b6a21f51b20f41b20f5" - "1b6a21f61b20f51b20f6" - "1b6a21f71b20f61b20f71b6a21f81b20f71b20f81b6a21f91b20f81b20f91b6a21fa1b20f91b20fa1b6a21fb1b20fa" - "1b20fb1b6a21fc1b20fb" - "1b20fc1b6a21fd1b20fc1b20fd1b6a21fe1b20fd1b20fe1b6a21ff1b20fe1b20ff1b6a21801c20ff1b20801c6a2181" - "1c20801c20811c6a2182" - "1c20811c20821c6a21831c20821c20831c6a21841c20831c20841c6a21851c20841c20851c6a21861c20851c20861c" - "6a21871c20861c20871c" - "6a21881c20871c20881c6a21891c20881c20891c6a218a1c20891c208a1c6a218b1c208a1c208b1c6a218c1c208b1c" - "208c1c6a218d1c208c1c" - "208d1c6a218e1c208d1c208e1c6a218f1c208e1c208f1c6a21901c208f1c20901c6a21911c20901c20911c6a21921c" - "20911c20921c6a21931c" - "20921c20931c6a21941c20931c20941c6a21951c20941c20951c6a21961c20951c20961c6a21971c20961c20971c6a" - "21981c20971c20981c6a" - "21991c20981c20991c6a219a1c20991c209a1c6a219b1c209a1c209b1c6a219c1c209b1c209c1c6a219d1c209c1c20" - "9d1c6a219e1c209d1c20" - "9e1c6a219f1c209e1c209f1c6a21a01c209f1c20a01c6a21a11c20a01c20a11c6a21a21c20a11c20a21c6a21a31c20" - "a21c20a31c6a21a41c20" - "a31c20a41c6a21a51c20a41c20a51c6a21a61c20a51c20a61c6a21a71c20a61c20a71c6a21a81c20a71c20a81c6a21" - "a91c20a81c20a91c6a21" - "aa1c20a91c20aa1c6a21ab1c20aa1c20ab1c6a21ac1c20ab1c20ac1c6a21ad1c20ac1c20ad1c6a21ae1c20ad1c20ae" - "1c6a21af1c20ae1c20af" - "1c6a21b01c20af1c20b01c6a21b11c20b01c20b11c6a21b21c20b11c20b21c6a21b31c20b21c20b31c6a21b41c20b3" - "1c20b41c6a21b51c20b4" - "1c20b51c6a21b61c20b51c20b61c6a21b71c20b61c20b71c6a21b81c20b71c20b81c6a21b91c20b81c20b91c6a21ba" - "1c20b91c20ba1c6a21bb" - "1c20ba1c20bb1c6a21bc1c20bb1c20bc1c6a21bd1c20bc1c20bd1c6a21be1c20bd1c20be1c6a21bf1c20be1c20bf1c" - "6a21c01c20bf1c20c01c" - "6a21c11c20c01c20c11c6a21c21c20c11c20c21c6a21c31c20c21c20c31c6a21c41c20c31c20c41c6a21c51c20c41c" - "20c51c6a21c61c20c51c" - "20c61c6a21c71c20c61c20c71c6a21c81c20c71c20c81c6a21c91c20c81c20c91c6a21ca1c20c91c20ca1c6a21cb1c" - "20ca1c20cb1c6a21cc1c" - "20cb1c20cc1c6a21cd1c20cc1c20cd1c6a21ce1c20cd1c20ce1c6a21cf1c20ce1c20cf1c6a21d01c20cf1c20d01c6a" - "21d11c20d01c20d11c6a" - "21d21c20d11c20d21c6a21d31c20d21c20d31c6a21d41c20d31c20d41c6a21d51c20d41c20d51c6a21d61c20d51c20" - "d61c6a21d71c20d61c20" - "d71c6a21d81c20d71c20d81c6a21d91c20d81c20d91c6a21da1c20d91c20da1c6a21db1c20da1c20db1c6a21dc1c20" - "db1c20dc1c6a21dd1c20" - "dc1c20dd1c6a21de1c20dd1c20de1c6a21df1c20de1c20df1c6a21e01c20df1c20e01c6a21e11c20e01c20e11c6a21" - "e21c20e11c20e21c6a21" - "e31c20e21c20e31c6a21e41c20e31c20e41c6a21e51c20e41c20e51c6a21e61c20e51c20e61c6a21e71c20e61c20e7" - "1c6a21e81c20e71c20e8" - "1c6a21e91c20e81c20e91c6a21ea1c20e91c20ea1c6a21eb1c20ea1c20eb1c6a21ec1c20eb1c20ec1c6a21ed1c20ec" - "1c20ed1c6a21ee1c20ed" - "1c20ee1c6a21ef1c20ee1c20ef1c6a21f01c20ef1c20f01c6a21f11c20f01c20f11c6a21f21c20f11c20f21c6a21f3" - "1c20f21c20f31c6a21f4" - "1c20f31c20f41c6a21f51c20f41c20f51c6a21f61c20f51c20f61c6a21f71c20f61c20f71c6a21f81c20f71c20f81c" - "6a21f91c20f81c20f91c" - "6a21fa1c20f91c20fa1c6a21fb1c20fa1c20fb1c6a21fc1c20fb1c20fc1c6a21fd1c20fc1c20fd1c6a21fe1c20fd1c" - "20fe1c6a21ff1c20fe1c" - "20ff1c6a21801d20ff1c20801d6a21811d20801d20811d6a21821d20811d20821d6a21831d20821d20831d6a21841d" - "20831d20841d6a21851d" - "20841d20851d6a21861d20851d20861d6a21871d20861d20871d6a21881d20871d20881d6a21891d20881d20891d6a" - "218a1d20891d208a1d6a" - "218b1d208a1d208b1d6a218c1d208b1d208c1d6a218d1d208c1d208d1d6a218e1d208d1d208e1d6a218f1d208e1d20" - "8f1d6a21901d208f1d20" - "901d6a21911d20901d20911d6a21921d20911d20921d6a21931d20921d20931d6a21941d20931d20941d6a21951d20" - "941d20951d6a21961d20" - "951d20961d6a21971d20961d20971d6a21981d20971d20981d6a21991d20981d20991d6a219a1d20991d209a1d6a21" - "9b1d209a1d209b1d6a21" - "9c1d209b1d209c1d6a219d1d209c1d209d1d6a219e1d209d1d209e1d6a219f1d209e1d209f1d6a21a01d209f1d20a0" - "1d6a21a11d20a01d20a1" - "1d6a21a21d20a11d20a21d6a21a31d20a21d20a31d6a21a41d20a31d20a41d6a21a51d20a41d20a51d6a21a61d20a5" - "1d20a61d6a21a71d20a6" - "1d20a71d6a21a81d20a71d20a81d6a21a91d20a81d20a91d6a21aa1d20a91d20aa1d6a21ab1d20aa1d20ab1d6a21ac" - "1d20ab1d20ac1d6a21ad" - "1d20ac1d20ad1d6a21ae1d20ad1d20ae1d6a21af1d20ae1d20af1d6a21b01d20af1d20b01d6a21b11d20b01d20b11d" - "6a21b21d20b11d20b21d" - "6a21b31d20b21d20b31d6a21b41d20b31d20b41d6a21b51d20b41d20b51d6a21b61d20b51d20b61d6a21b71d20b61d" - "20b71d6a21b81d20b71d" - "20b81d6a21b91d20b81d20b91d6a21ba1d20b91d20ba1d6a21bb1d20ba1d20bb1d6a21bc1d20bb1d20bc1d6a21bd1d" - "20bc1d20bd1d6a21be1d" - "20bd1d20be1d6a21bf1d20be1d20bf1d6a21c01d20bf1d20c01d6a21c11d20c01d20c11d6a21c21d20c11d20c21d6a" - "21c31d20c21d20c31d6a" - "21c41d20c31d20c41d6a21c51d20c41d20c51d6a21c61d20c51d20c61d6a21c71d20c61d20c71d6a21c81d20c71d20" - "c81d6a21c91d20c81d20" + "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" - "cd1d20ce1d6a21cf1d20" - "ce1d20cf1d6a21d01d20cf1d20d01d6a21d11d20d01d20d11d6a21d21d20d11d20d21d6a21d31d20d21d20d31d6a21" - "d41d20d31d20d41d6a21" - "d51d20d41d20d51d6a21d61d20d51d20d61d6a21d71d20d61d20d71d6a21d81d20d71d20d81d6a21d91d20d81d20d9" - "1d6a21da1d20d91d20da" - "1d6a21db1d20da1d20db1d6a21dc1d20db1d20dc1d6a21dd1d20dc1d20dd1d6a21de1d20dd1d20de1d6a21df1d20de" - "1d20df1d6a21e01d20df" - "1d20e01d6a21e11d20e01d20e11d6a21e21d20e11d20e21d6a21e31d20e21d20e31d6a21e41d20e31d20e41d6a21e5" - "1d20e41d20e51d6a21e6" - "1d20e51d20e61d6a21e71d20e61d20e71d6a21e81d20e71d20e81d6a21e91d20e81d20e91d6a21ea1d20e91d20ea1d" - "6a21eb1d20ea1d20eb1d" - "6a21ec1d20eb1d20ec1d6a21ed1d20ec1d20ed1d6a21ee1d20ed1d20ee1d6a21ef1d20ee1d20ef1d6a21f01d20ef1d" - "20f01d6a21f11d20f01d" - "20f11d6a21f21d20f11d20f21d6a21f31d20f21d20f31d6a21f41d20f31d20f41d6a21f51d20f41d20f51d6a21f61d" - "20f51d20f61d6a21f71d" - "20f61d20f71d6a21f81d20f71d20f81d6a21f91d20f81d20f91d6a21fa1d20f91d20fa1d6a21fb1d20fa1d20fb1d6a" - "21fc1d20fb1d20fc1d6a" - "21fd1d20fc1d20fd1d6a21fe1d20fd1d20fe1d6a21ff1d20fe1d20ff1d6a21801e20ff1d20801e6a21811e20801e20" - "811e6a21821e20811e20" - "821e6a21831e20821e20831e6a21841e20831e20841e6a21851e20841e20851e6a21861e20851e20861e6a21871e20" - "861e20871e6a21881e20" - "871e20881e6a21891e20881e20891e6a218a1e20891e208a1e6a218b1e208a1e208b1e6a218c1e208b1e208c1e6a21" - "8d1e208c1e208d1e6a21" - "8e1e208d1e208e1e6a218f1e208e1e208f1e6a21901e208f1e20901e6a21911e20901e20911e6a21921e20911e2092" - "1e6a21931e20921e2093" - "1e6a21941e20931e20941e6a21951e20941e20951e6a21961e20951e20961e6a21971e20961e20971e6a21981e2097" - "1e20981e6a21991e2098" - "1e20991e6a219a1e20991e209a1e6a219b1e209a1e209b1e6a219c1e209b1e209c1e6a219d1e209c1e209d1e6a219e" - "1e209d1e209e1e6a219f" - "1e209e1e209f1e6a21a01e209f1e20a01e6a21a11e20a01e20a11e6a21a21e20a11e20a21e6a21a31e20a21e20a31e" - "6a21a41e20a31e20a41e" - "6a21a51e20a41e20a51e6a21a61e20a51e20a61e6a21a71e20a61e20a71e6a21a81e20a71e20a81e6a21a91e20a81e" - "20a91e6a21aa1e20a91e" - "20aa1e6a21ab1e20aa1e20ab1e6a21ac1e20ab1e20ac1e6a21ad1e20ac1e20ad1e6a21ae1e20ad1e20ae1e6a21af1e" - "20ae1e20af1e6a21b01e" - "20af1e20b01e6a21b11e20b01e20b11e6a21b21e20b11e20b21e6a21b31e20b21e20b31e6a21b41e20b31e20b41e6a" - "21b51e20b41e20b51e6a" - "21b61e20b51e20b61e6a21b71e20b61e20b71e6a21b81e20b71e20b81e6a21b91e20b81e20b91e6a21ba1e20b91e20" - "ba1e6a21bb1e20ba1e20" - "bb1e6a21bc1e20bb1e20bc1e6a21bd1e20bc1e20bd1e6a21be1e20bd1e20be1e6a21bf1e20be1e20bf1e6a21c01e20" - "bf1e20c01e6a21c11e20" - "c01e20c11e6a21c21e20c11e20c21e6a21c31e20c21e20c31e6a21c41e20c31e20c41e6a21c51e20c41e20c51e6a21" - "c61e20c51e20c61e6a21" - "c71e20c61e20c71e6a21c81e20c71e20c81e6a21c91e20c81e20c91e6a21ca1e20c91e20ca1e6a21cb1e20ca1e20cb" - "1e6a21cc1e20cb1e20cc" - "1e6a21cd1e20cc1e20cd1e6a21ce1e20cd1e20ce1e6a21cf1e20ce1e20cf1e6a21d01e20cf1e20d01e6a21d11e20d0" - "1e20d11e6a21d21e20d1" - "1e20d21e6a21d31e20d21e20d31e6a21d41e20d31e20d41e6a21d51e20d41e20d51e6a21d61e20d51e20d61e6a21d7" - "1e20d61e20d71e6a21d8" - "1e20d71e20d81e6a21d91e20d81e20d91e6a21da1e20d91e20da1e6a21db1e20da1e20db1e6a21dc1e20db1e20dc1e" - "6a21dd1e20dc1e20dd1e" - "6a21de1e20dd1e20de1e6a21df1e20de1e20df1e6a21e01e20df1e20e01e6a21e11e20e01e20e11e6a21e21e20e11e" - "20e21e6a21e31e20e21e" - "20e31e6a21e41e20e31e20e41e6a21e51e20e41e20e51e6a21e61e20e51e20e61e6a21e71e20e61e20e71e6a21e81e" - "20e71e20e81e6a21e91e" - "20e81e20e91e6a21ea1e20e91e20ea1e6a21eb1e20ea1e20eb1e6a21ec1e20eb1e20ec1e6a21ed1e20ec1e20ed1e6a" - "21ee1e20ed1e20ee1e6a" - "21ef1e20ee1e20ef1e6a21f01e20ef1e20f01e6a21f11e20f01e20f11e6a21f21e20f11e20f21e6a21f31e20f21e20" - "f31e6a21f41e20f31e20" - "f41e6a21f51e20f41e20f51e6a21f61e20f51e20f61e6a21f71e20f61e20f71e6a21f81e20f71e20f81e6a21f91e20" - "f81e20f91e6a21fa1e20" - "f91e20fa1e6a21fb1e20fa1e20fb1e6a21fc1e20fb1e20fc1e6a21fd1e20fc1e20fd1e6a21fe1e20fd1e20fe1e6a21" - "ff1e20fe1e20ff1e6a21" - "801f20ff1e20801f6a21811f20801f20811f6a21821f20811f20821f6a21831f20821f20831f6a21841f20831f2084" - "1f6a21851f20841f2085" - "1f6a21861f20851f20861f6a21871f20861f20871f6a21881f20871f20881f6a21891f20881f20891f6a218a1f2089" - "1f208a1f6a218b1f208a" - "1f208b1f6a218c1f208b1f208c1f6a218d1f208c1f208d1f6a218e1f208d1f208e1f6a218f1f208e1f208f1f6a2190" - "1f208f1f20901f6a2191" - "1f20901f20911f6a21921f20911f20921f6a21931f20921f20931f6a21941f20931f20941f6a21951f20941f20951f" - "6a21961f20951f20961f" - "6a21971f20961f20971f6a21981f20971f20981f6a21991f20981f20991f6a219a1f20991f209a1f6a219b1f209a1f" - "209b1f6a219c1f209b1f" - "209c1f6a219d1f209c1f209d1f6a219e1f209d1f209e1f6a219f1f209e1f209f1f6a21a01f209f1f20a01f6a21a11f" - "20a01f20a11f6a21a21f" - "20a11f20a21f6a21a31f20a21f20a31f6a21a41f20a31f20a41f6a21a51f20a41f20a51f6a21a61f20a51f20a61f6a" - "21a71f20a61f20a71f6a" - "21a81f20a71f20a81f6a21a91f20a81f20a91f6a21aa1f20a91f20aa1f6a21ab1f20aa1f20ab1f6a21ac1f20ab1f20" - "ac1f6a21ad1f20ac1f20" - "ad1f6a21ae1f20ad1f20ae1f6a21af1f20ae1f20af1f6a21b01f20af1f20b01f6a21b11f20b01f20b11f6a21b21f20" - "b11f20b21f6a21b31f20" - "b21f20b31f6a21b41f20b31f20b41f6a21b51f20b41f20b51f6a21b61f20b51f20b61f6a21b71f20b61f20b71f6a21" - "b81f20b71f20b81f6a21" - "b91f20b81f20b91f6a21ba1f20b91f20ba1f6a21bb1f20ba1f20bb1f6a21bc1f20bb1f20bc1f6a21bd1f20bc1f20bd" - "1f6a21be1f20bd1f20be" - "1f6a21bf1f20be1f20bf1f6a21c01f20bf1f20c01f6a21c11f20c01f20c11f6a21c21f20c11f20c21f6a21c31f20c2" - "1f20c31f6a21c41f20c3" - "1f20c41f6a21c51f20c41f20c51f6a21c61f20c51f20c61f6a21c71f20c61f20c71f6a21c81f20c71f20c81f6a21c9" - "1f20c81f20c91f6a21ca" - "1f20c91f20ca1f6a21cb1f20ca1f20cb1f6a21cc1f20cb1f20cc1f6a21cd1f20cc1f20cd1f6a21ce1f20cd1f20ce1f" - "6a21cf1f20ce1f20cf1f" - "6a21d01f20cf1f20d01f6a21d11f20d01f20d11f6a21d21f20d11f20d21f6a21d31f20d21f20d31f6a21d41f20d31f" - "20d41f6a21d51f20d41f" + "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" - "20d91f20da1f6a21db1f" - "20da1f20db1f6a21dc1f20db1f20dc1f6a21dd1f20dc1f20dd1f6a21de1f20dd1f20de1f6a21df1f20de1f20df1f6a" - "21e01f20df1f20e01f6a" - "21e11f20e01f20e11f6a21e21f20e11f20e21f6a21e31f20e21f20e31f6a21e41f20e31f20e41f6a21e51f20e41f20" - "e51f6a21e61f20e51f20" - "e61f6a21e71f20e61f20e71f6a21e81f20e71f20e81f6a21e91f20e81f20e91f6a21ea1f20e91f20ea1f6a21eb1f20" - "ea1f20eb1f6a21ec1f20" - "eb1f20ec1f6a21ed1f20ec1f20ed1f6a21ee1f20ed1f20ee1f6a21ef1f20ee1f20ef1f6a21f01f20ef1f20f01f6a21" - "f11f20f01f20f11f6a21" - "f21f20f11f20f21f6a21f31f20f21f20f31f6a21f41f20f31f20f41f6a21f51f20f41f20f51f6a21f61f20f51f20f6" - "1f6a21f71f20f61f20f7" - "1f6a21f81f20f71f20f81f6a21f91f20f81f20f91f6a21fa1f20f91f20fa1f6a21fb1f20fa1f20fb1f6a21fc1f20fb" - "1f20fc1f6a21fd1f20fc" - "1f20fd1f6a21fe1f20fd1f20fe1f6a21ff1f20fe1f20ff1f6a21802020ff1f2080206a2181202080202081206a2182" - "202081202082206a2183" - "202082202083206a2184202083202084206a2185202084202085206a2186202085202086206a218720208620208720" - "6a218820208720208820" - "6a2189202088202089206a218a20208920208a206a218b20208a20208b206a218c20208b20208c206a218d20208c20" - "208d206a218e20208d20" - "208e206a218f20208e20208f206a219020208f202090206a2191202090202091206a2192202091202092206a219320" - "2092202093206a219420" - "2093202094206a2195202094202095206a2196202095202096206a2197202096202097206a2198202097202098206a" - "2199202098202099206a" - "219a20209920209a206a219b20209a20209b206a219c20209b20209c206a219d20209c20209d206a219e20209d2020" - "9e206a219f20209e2020" - "9f206a21a020209f2020a0206a21a12020a02020a1206a21a22020a12020a2206a21a32020a22020a3206a21a42020" - "a32020a4206a21a52020" - "a42020a5206a21a62020a52020a6206a21a72020a62020a7206a21a82020a72020a8206a21a92020a82020a9206a21" - "aa2020a92020aa206a21" - "ab2020aa2020ab206a21ac2020ab2020ac206a21ad2020ac2020ad206a21ae2020ad2020ae206a21af2020ae2020af" - "206a21b02020af2020b0" - "206a21b12020b02020b1206a21b22020b12020b2206a21b32020b22020b3206a21b42020b32020b4206a21b52020b4" - "2020b5206a21b62020b5" - "2020b6206a21b72020b62020b7206a21b82020b72020b8206a21b92020b82020b9206a21ba2020b92020ba206a21bb" - "2020ba2020bb206a21bc" - "2020bb2020bc206a21bd2020bc2020bd206a21be2020bd2020be206a21bf2020be2020bf206a21c02020bf2020c020" - "6a21c12020c02020c120" - "6a21c22020c12020c2206a21c32020c22020c3206a21c42020c32020c4206a21c52020c42020c5206a21c62020c520" - "20c6206a21c72020c620" - "20c7206a21c82020c72020c8206a21c92020c82020c9206a21ca2020c92020ca206a21cb2020ca2020cb206a21cc20" - "20cb2020cc206a21cd20" - "20cc2020cd206a21ce2020cd2020ce206a21cf2020ce2020cf206a21d02020cf2020d0206a21d12020d02020d1206a" - "21d22020d12020d2206a" - "21d32020d22020d3206a21d42020d32020d4206a21d52020d42020d5206a21d62020d52020d6206a21d72020d62020" - "d7206a21d82020d72020" - "d8206a21d92020d82020d9206a21da2020d92020da206a21db2020da2020db206a21dc2020db2020dc206a21dd2020" - "dc2020dd206a21de2020" - "dd2020de206a21df2020de2020df206a21e02020df2020e0206a21e12020e02020e1206a21e22020e12020e2206a21" - "e32020e22020e3206a21" - "e42020e32020e4206a21e52020e42020e5206a21e62020e52020e6206a21e72020e62020e7206a21e82020e72020e8" - "206a21e92020e82020e9" - "206a21ea2020e92020ea206a21eb2020ea2020eb206a21ec2020eb2020ec206a21ed2020ec2020ed206a21ee2020ed" - "2020ee206a21ef2020ee" - "2020ef206a21f02020ef2020f0206a21f12020f02020f1206a21f22020f12020f2206a21f32020f22020f3206a21f4" - "2020f32020f4206a21f5" - "2020f42020f5206a21f62020f52020f6206a21f72020f62020f7206a21f82020f72020f8206a21f92020f82020f920" - "6a21fa2020f92020fa20" - "6a21fb2020fa2020fb206a21fc2020fb2020fc206a21fd2020fc2020fd206a21fe2020fd2020fe206a21ff2020fe20" - "20ff206a21802120ff20" - "2080216a2181212080212081216a2182212081212082216a2183212082212083216a2184212083212084216a218521" - "2084212085216a218621" - "2085212086216a2187212086212087216a2188212087212088216a2189212088212089216a218a21208921208a216a" - "218b21208a21208b216a" - "218c21208b21208c216a218d21208c21208d216a218e21208d21208e216a218f21208e21208f216a219021208f2120" - "90216a21912120902120" - "91216a2192212091212092216a2193212092212093216a2194212093212094216a2195212094212095216a21962120" - "95212096216a21972120" - "96212097216a2198212097212098216a2199212098212099216a219a21209921209a216a219b21209a21209b216a21" - "9c21209b21209c216a21" - "9d21209c21209d216a219e21209d21209e216a219f21209e21209f216a21a021209f2120a0216a21a12120a02120a1" - "216a21a22120a12120a2" - "216a21a32120a22120a3216a21a42120a32120a4216a21a52120a42120a5216a21a62120a52120a6216a21a72120a6" - "2120a7216a21a82120a7" - "2120a8216a21a92120a82120a9216a21aa2120a92120aa216a21ab2120aa2120ab216a21ac2120ab2120ac216a21ad" - "2120ac2120ad216a21ae" - "2120ad2120ae216a21af2120ae2120af216a21b02120af2120b0216a21b12120b02120b1216a21b22120b12120b221" - "6a21b32120b22120b321" - "6a21b42120b32120b4216a21b52120b42120b5216a21b62120b52120b6216a21b72120b62120b7216a21b82120b721" - "20b8216a21b92120b821" - "20b9216a21ba2120b92120ba216a21bb2120ba2120bb216a21bc2120bb2120bc216a21bd2120bc2120bd216a21be21" - "20bd2120be216a21bf21" - "20be2120bf216a21c02120bf2120c0216a21c12120c02120c1216a21c22120c12120c2216a21c32120c22120c3216a" - "21c42120c32120c4216a" - "21c52120c42120c5216a21c62120c52120c6216a21c72120c62120c7216a21c82120c72120c8216a21c92120c82120" - "c9216a21ca2120c92120" - "ca216a21cb2120ca2120cb216a21cc2120cb2120cc216a21cd2120cc2120cd216a21ce2120cd2120ce216a21cf2120" - "ce2120cf216a21d02120" - "cf2120d0216a21d12120d02120d1216a21d22120d12120d2216a21d32120d22120d3216a21d42120d32120d4216a21" - "d52120d42120d5216a21" - "d62120d52120d6216a21d72120d62120d7216a21d82120d72120d8216a21d92120d82120d9216a21da2120d92120da" - "216a21db2120da2120db" - "216a21dc2120db2120dc216a21dd2120dc2120dd216a21de2120dd2120de216a21df2120de2120df216a21e02120df" - "2120e0216a21e12120e0" + "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" - "2120e52120e6216a21e7" - "2120e62120e7216a21e82120e72120e8216a21e92120e82120e9216a21ea2120e92120ea216a21eb2120ea2120eb21" - "6a21ec2120eb2120ec21" - "6a21ed2120ec2120ed216a21ee2120ed2120ee216a21ef2120ee2120ef216a21f02120ef2120f0216a21f12120f021" - "20f1216a21f22120f121" - "20f2216a21f32120f22120f3216a21f42120f32120f4216a21f52120f42120f5216a21f62120f52120f6216a21f721" - "20f62120f7216a21f821" - "20f72120f8216a21f92120f82120f9216a21fa2120f92120fa216a21fb2120fa2120fb216a21fc2120fb2120fc216a" - "21fd2120fc2120fd216a" - "21fe2120fd2120fe216a21ff2120fe2120ff216a21802220ff212080226a2181222080222081226a21822220812220" - "82226a21832220822220" - "83226a2184222083222084226a2185222084222085226a2186222085222086226a2187222086222087226a21882220" - "87222088226a21892220" - "88222089226a218a22208922208a226a218b22208a22208b226a218c22208b22208c226a218d22208c22208d226a21" - "8e22208d22208e226a21" - "8f22208e22208f226a219022208f222090226a2191222090222091226a2192222091222092226a2193222092222093" - "226a2194222093222094" - "226a2195222094222095226a2196222095222096226a2197222096222097226a2198222097222098226a2199222098" - "222099226a219a222099" - "22209a226a219b22209a22209b226a219c22209b22209c226a219d22209c22209d226a219e22209d22209e226a219f" - "22209e22209f226a21a0" - "22209f2220a0226a21a12220a02220a1226a21a22220a12220a2226a21a32220a22220a3226a21a42220a32220a422" - "6a21a52220a42220a522" - "6a21a62220a52220a6226a21a72220a62220a7226a21a82220a72220a8226a21a92220a82220a9226a21aa2220a922" - "20aa226a21ab2220aa22" - "20ab226a21ac2220ab2220ac226a21ad2220ac2220ad226a21ae2220ad2220ae226a21af2220ae2220af226a21b022" - "20af2220b0226a21b122" - "20b02220b1226a21b22220b12220b2226a21b32220b22220b3226a21b42220b32220b4226a21b52220b42220b5226a" - "21b62220b52220b6226a" - "21b72220b62220b7226a21b82220b72220b8226a21b92220b82220b9226a21ba2220b92220ba226a21bb2220ba2220" - "bb226a21bc2220bb2220" - "bc226a21bd2220bc2220bd226a21be2220bd2220be226a21bf2220be2220bf226a21c02220bf2220c0226a21c12220" - "c02220c1226a21c22220" - "c12220c2226a21c32220c22220c3226a21c42220c32220c4226a21c52220c42220c5226a21c62220c52220c6226a21" - "c72220c62220c7226a21" - "c82220c72220c8226a21c92220c82220c9226a21ca2220c92220ca226a21cb2220ca2220cb226a21cc2220cb2220cc" - "226a21cd2220cc2220cd" - "226a21ce2220cd2220ce226a21cf2220ce2220cf226a21d02220cf2220d0226a21d12220d02220d1226a21d22220d1" - "2220d2226a21d32220d2" - "2220d3226a21d42220d32220d4226a21d52220d42220d5226a21d62220d52220d6226a21d72220d62220d7226a21d8" - "2220d72220d8226a21d9" - "2220d82220d9226a21da2220d92220da226a21db2220da2220db226a21dc2220db2220dc226a21dd2220dc2220dd22" - "6a21de2220dd2220de22" - "6a21df2220de2220df226a21e02220df2220e0226a21e12220e02220e1226a21e22220e12220e2226a21e32220e222" - "20e3226a21e42220e322" - "20e4226a21e52220e42220e5226a21e62220e52220e6226a21e72220e62220e7226a21e82220e72220e8226a21e922" - "20e82220e9226a21ea22" - "20e92220ea226a21eb2220ea2220eb226a21ec2220eb2220ec226a21ed2220ec2220ed226a21ee2220ed2220ee226a" - "21ef2220ee2220ef226a" - "21f02220ef2220f0226a21f12220f02220f1226a21f22220f12220f2226a21f32220f22220f3226a21f42220f32220" - "f4226a21f52220f42220" - "f5226a21f62220f52220f6226a21f72220f62220f7226a21f82220f72220f8226a21f92220f82220f9226a21fa2220" - "f92220fa226a21fb2220" - "fa2220fb226a21fc2220fb2220fc226a21fd2220fc2220fd226a21fe2220fd2220fe226a21ff2220fe2220ff226a21" - "802320ff222080236a21" - "81232080232081236a2182232081232082236a2183232082232083236a2184232083232084236a2185232084232085" - "236a2186232085232086" - "236a2187232086232087236a2188232087232088236a2189232088232089236a218a23208923208a236a218b23208a" - "23208b236a218c23208b" - "23208c236a218d23208c23208d236a218e23208d23208e236a218f23208e23208f236a219023208f232090236a2191" - "232090232091236a2192" - "232091232092236a2193232092232093236a2194232093232094236a2195232094232095236a219623209523209623" - "6a219723209623209723" - "6a2198232097232098236a2199232098232099236a219a23209923209a236a219b23209a23209b236a219c23209b23" - "209c236a219d23209c23" - "209d236a219e23209d23209e236a219f23209e23209f236a21a023209f2320a0236a21a12320a02320a1236a21a223" - "20a12320a2236a21a323" - "20a22320a3236a21a42320a32320a4236a21a52320a42320a5236a21a62320a52320a6236a21a72320a62320a7236a" - "21a82320a72320a8236a" - "21a92320a82320a9236a21aa2320a92320aa236a21ab2320aa2320ab236a21ac2320ab2320ac236a21ad2320ac2320" - "ad236a21ae2320ad2320" - "ae236a21af2320ae2320af236a21b02320af2320b0236a21b12320b02320b1236a21b22320b12320b2236a21b32320" - "b22320b3236a21b42320" - "b32320b4236a21b52320b42320b5236a21b62320b52320b6236a21b72320b62320b7236a21b82320b72320b8236a21" - "b92320b82320b9236a21" - "ba2320b92320ba236a21bb2320ba2320bb236a21bc2320bb2320bc236a21bd2320bc2320bd236a21be2320bd2320be" - "236a21bf2320be2320bf" - "236a21c02320bf2320c0236a21c12320c02320c1236a21c22320c12320c2236a21c32320c22320c3236a21c42320c3" - "2320c4236a21c52320c4" - "2320c5236a21c62320c52320c6236a21c72320c62320c7236a21c82320c72320c8236a21c92320c82320c9236a21ca" - "2320c92320ca236a21cb" - "2320ca2320cb236a21cc2320cb2320cc236a21cd2320cc2320cd236a21ce2320cd2320ce236a21cf2320ce2320cf23" - "6a21d02320cf2320d023" - "6a21d12320d02320d1236a21d22320d12320d2236a21d32320d22320d3236a21d42320d32320d4236a21d52320d423" - "20d5236a21d62320d523" - "20d6236a21d72320d62320d7236a21d82320d72320d8236a21d92320d82320d9236a21da2320d92320da236a21db23" - "20da2320db236a21dc23" - "20db2320dc236a21dd2320dc2320dd236a21de2320dd2320de236a21df2320de2320df236a21e02320df2320e0236a" - "21e12320e02320e1236a" - "21e22320e12320e2236a21e32320e22320e3236a21e42320e32320e4236a21e52320e42320e5236a21e62320e52320" - "e6236a21e72320e62320" - "e7236a21e82320e72320e8236a21e92320e82320e9236a21ea2320e92320ea236a21eb2320ea2320eb236a21ec2320" - "eb2320ec236a21ed2320" + "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" - "f22320f12320f2236a21" - "f32320f22320f3236a21f42320f32320f4236a21f52320f42320f5236a21f62320f52320f6236a21f72320f62320f7" - "236a21f82320f72320f8" - "236a21f92320f82320f9236a21fa2320f92320fa236a21fb2320fa2320fb236a21fc2320fb2320fc236a21fd2320fc" - "2320fd236a21fe2320fd" - "2320fe236a21ff2320fe2320ff236a21802420ff232080246a2181242080242081246a2182242081242082246a2183" - "242082242083246a2184" - "242083242084246a2185242084242085246a2186242085242086246a2187242086242087246a218824208724208824" - "6a218924208824208924" - "6a218a24208924208a246a218b24208a24208b246a218c24208b24208c246a218d24208c24208d246a218e24208d24" - "208e246a218f24208e24" - "208f246a219024208f242090246a2191242090242091246a2192242091242092246a2193242092242093246a219424" - "2093242094246a219524" - "2094242095246a2196242095242096246a2197242096242097246a2198242097242098246a2199242098242099246a" - "219a24209924209a246a" - "219b24209a24209b246a219c24209b24209c246a219d24209c24209d246a219e24209d24209e246a219f24209e2420" - "9f246a21a024209f2420" - "a0246a21a12420a02420a1246a21a22420a12420a2246a21a32420a22420a3246a21a42420a32420a4246a21a52420" - "a42420a5246a21a62420" - "a52420a6246a21a72420a62420a7246a21a82420a72420a8246a21a92420a82420a9246a21aa2420a92420aa246a21" - "ab2420aa2420ab246a21" - "ac2420ab2420ac246a21ad2420ac2420ad246a21ae2420ad2420ae246a21af2420ae2420af246a21b02420af2420b0" - "246a21b12420b02420b1" - "246a21b22420b12420b2246a21b32420b22420b3246a21b42420b32420b4246a21b52420b42420b5246a21b62420b5" - "2420b6246a21b72420b6" - "2420b7246a21b82420b72420b8246a21b92420b82420b9246a21ba2420b92420ba246a21bb2420ba2420bb246a21bc" - "2420bb2420bc246a21bd" - "2420bc2420bd246a21be2420bd2420be246a21bf2420be2420bf246a21c02420bf2420c0246a21c12420c02420c124" - "6a21c22420c12420c224" - "6a21c32420c22420c3246a21c42420c32420c4246a21c52420c42420c5246a21c62420c52420c6246a21c72420c624" - "20c7246a21c82420c724" - "20c8246a21c92420c82420c9246a21ca2420c92420ca246a21cb2420ca2420cb246a21cc2420cb2420cc246a21cd24" - "20cc2420cd246a21ce24" - "20cd2420ce246a21cf2420ce2420cf246a21d02420cf2420d0246a21d12420d02420d1246a21d22420d12420d2246a" - "21d32420d22420d3246a" - "21d42420d32420d4246a21d52420d42420d5246a21d62420d52420d6246a21d72420d62420d7246a21d82420d72420" - "d8246a21d92420d82420" - "d9246a21da2420d92420da246a21db2420da2420db246a21dc2420db2420dc246a21dd2420dc2420dd246a21de2420" - "dd2420de246a21df2420" - "de2420df246a21e02420df2420e0246a21e12420e02420e1246a21e22420e12420e2246a21e32420e22420e3246a21" - "e42420e32420e4246a21" - "e52420e42420e5246a21e62420e52420e6246a21e72420e62420e7246a21e82420e72420e8246a21e92420e82420e9" - "246a21ea2420e92420ea" - "246a21eb2420ea2420eb246a21ec2420eb2420ec246a21ed2420ec2420ed246a21ee2420ed2420ee246a21ef2420ee" - "2420ef246a21f02420ef" - "2420f0246a21f12420f02420f1246a21f22420f12420f2246a21f32420f22420f3246a21f42420f32420f4246a21f5" - "2420f42420f5246a21f6" - "2420f52420f6246a21f72420f62420f7246a21f82420f72420f8246a21f92420f82420f9246a21fa2420f92420fa24" - "6a21fb2420fa2420fb24" - "6a21fc2420fb2420fc246a21fd2420fc2420fd246a21fe2420fd2420fe246a21ff2420fe2420ff246a21802520ff24" - "2080256a218125208025" - "2081256a2182252081252082256a2183252082252083256a2184252083252084256a2185252084252085256a218625" - "2085252086256a218725" - "2086252087256a2188252087252088256a2189252088252089256a218a25208925208a256a218b25208a25208b256a" - "218c25208b25208c256a" - "218d25208c25208d256a218e25208d25208e256a218f25208e25208f256a219025208f252090256a21912520902520" - "91256a21922520912520" - "92256a2193252092252093256a2194252093252094256a2195252094252095256a2196252095252096256a21972520" - "96252097256a21982520" - "97252098256a2199252098252099256a219a25209925209a256a219b25209a25209b256a219c25209b25209c256a21" - "9d25209c25209d256a21" - "9e25209d25209e256a219f25209e25209f256a21a025209f2520a0256a21a12520a02520a1256a21a22520a12520a2" - "256a21a32520a22520a3" - "256a21a42520a32520a4256a21a52520a42520a5256a21a62520a52520a6256a21a72520a62520a7256a21a82520a7" - "2520a8256a21a92520a8" - "2520a9256a21aa2520a92520aa256a21ab2520aa2520ab256a21ac2520ab2520ac256a21ad2520ac2520ad256a21ae" - "2520ad2520ae256a21af" - "2520ae2520af256a21b02520af2520b0256a21b12520b02520b1256a21b22520b12520b2256a21b32520b22520b325" - "6a21b42520b32520b425" - "6a21b52520b42520b5256a21b62520b52520b6256a21b72520b62520b7256a21b82520b72520b8256a21b92520b825" - "20b9256a21ba2520b925" - "20ba256a21bb2520ba2520bb256a21bc2520bb2520bc256a21bd2520bc2520bd256a21be2520bd2520be256a21bf25" - "20be2520bf256a21c025" - "20bf2520c0256a21c12520c02520c1256a21c22520c12520c2256a21c32520c22520c3256a21c42520c32520c4256a" - "21c52520c42520c5256a" - "21c62520c52520c6256a21c72520c62520c7256a21c82520c72520c8256a21c92520c82520c9256a21ca2520c92520" - "ca256a21cb2520ca2520" - "cb256a21cc2520cb2520cc256a21cd2520cc2520cd256a21ce2520cd2520ce256a21cf2520ce2520cf256a21d02520" - "cf2520d0256a21d12520" - "d02520d1256a21d22520d12520d2256a21d32520d22520d3256a21d42520d32520d4256a21d52520d42520d5256a21" - "d62520d52520d6256a21" - "d72520d62520d7256a21d82520d72520d8256a21d92520d82520d9256a21da2520d92520da256a21db2520da2520db" - "256a21dc2520db2520dc" - "256a21dd2520dc2520dd256a21de2520dd2520de256a21df2520de2520df256a21e02520df2520e0256a21e12520e0" - "2520e1256a21e22520e1" - "2520e2256a21e32520e22520e3256a21e42520e32520e4256a21e52520e42520e5256a21e62520e52520e6256a21e7" - "2520e62520e7256a21e8" - "2520e72520e8256a21e92520e82520e9256a21ea2520e92520ea256a21eb2520ea2520eb256a21ec2520eb2520ec25" - "6a21ed2520ec2520ed25" - "6a21ee2520ed2520ee256a21ef2520ee2520ef256a21f02520ef2520f0256a21f12520f02520f1256a21f22520f125" - "20f2256a21f32520f225" - "20f3256a21f42520f32520f4256a21f52520f42520f5256a21f62520f52520f6256a21f72520f62520f7256a21f825" - "20f72520f8256a21f925" + "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" - "21fe2520fd2520fe256a" - "21ff2520fe2520ff256a21802620ff252080266a2181262080262081266a2182262081262082266a21832620822620" - "83266a21842620832620" - "84266a2185262084262085266a2186262085262086266a2187262086262087266a2188262087262088266a21892620" - "88262089266a218a2620" - "8926208a266a218b26208a26208b266a218c26208b26208c266a218d26208c26208d266a218e26208d26208e266a21" - "8f26208e26208f266a21" - "9026208f262090266a2191262090262091266a2192262091262092266a2193262092262093266a2194262093262094" - "266a2195262094262095" - "266a2196262095262096266a2197262096262097266a2198262097262098266a2199262098262099266a219a262099" - "26209a266a219b26209a" - "26209b266a219c26209b26209c266a219d26209c26209d266a219e26209d26209e266a219f26209e26209f266a21a0" - "26209f2620a0266a21a1" - "2620a02620a1266a21a22620a12620a2266a21a32620a22620a3266a21a42620a32620a4266a21a52620a42620a526" - "6a21a62620a52620a626" - "6a21a72620a62620a7266a21a82620a72620a8266a21a92620a82620a9266a21aa2620a92620aa266a21ab2620aa26" - "20ab266a21ac2620ab26" - "20ac266a21ad2620ac2620ad266a21ae2620ad2620ae266a21af2620ae2620af266a21b02620af2620b0266a21b126" - "20b02620b1266a21b226" - "20b12620b2266a21b32620b22620b3266a21b42620b32620b4266a21b52620b42620b5266a21b62620b52620b6266a" - "21b72620b62620b7266a" - "21b82620b72620b8266a21b92620b82620b9266a21ba2620b92620ba266a21bb2620ba2620bb266a21bc2620bb2620" - "bc266a21bd2620bc2620" - "bd266a21be2620bd2620be266a21bf2620be2620bf266a21c02620bf2620c0266a21c12620c02620c1266a21c22620" - "c12620c2266a21c32620" - "c22620c3266a21c42620c32620c4266a21c52620c42620c5266a21c62620c52620c6266a21c72620c62620c7266a21" - "c82620c72620c8266a21" - "c92620c82620c9266a21ca2620c92620ca266a21cb2620ca2620cb266a21cc2620cb2620cc266a21cd2620cc2620cd" - "266a21ce2620cd2620ce" - "266a21cf2620ce2620cf266a21d02620cf2620d0266a21d12620d02620d1266a21d22620d12620d2266a21d32620d2" - "2620d3266a21d42620d3" - "2620d4266a21d52620d42620d5266a21d62620d52620d6266a21d72620d62620d7266a21d82620d72620d8266a21d9" - "2620d82620d9266a21da" - "2620d92620da266a21db2620da2620db266a21dc2620db2620dc266a21dd2620dc2620dd266a21de2620dd2620de26" - "6a21df2620de2620df26" - "6a21e02620df2620e0266a21e12620e02620e1266a21e22620e12620e2266a21e32620e22620e3266a21e42620e326" - "20e4266a21e52620e426" - "20e5266a21e62620e52620e6266a21e72620e62620e7266a21e82620e72620e8266a21e92620e82620e9266a21ea26" - "20e92620ea266a21eb26" - "20ea2620eb266a21ec2620eb2620ec266a21ed2620ec2620ed266a21ee2620ed2620ee266a21ef2620ee2620ef266a" - "21f02620ef2620f0266a" - "21f12620f02620f1266a21f22620f12620f2266a21f32620f22620f3266a21f42620f32620f4266a21f52620f42620" - "f5266a21f62620f52620" - "f6266a21f72620f62620f7266a21f82620f72620f8266a21f92620f82620f9266a21fa2620f92620fa266a21fb2620" - "fa2620fb266a21fc2620" - "fb2620fc266a21fd2620fc2620fd266a21fe2620fd2620fe266a21ff2620fe2620ff266a21802720ff262080276a21" - "81272080272081276a21" - "82272081272082276a2183272082272083276a2184272083272084276a2185272084272085276a2186272085272086" - "276a2187272086272087" - "276a2188272087272088276a2189272088272089276a218a27208927208a276a218b27208a27208b276a218c27208b" - "27208c276a218d27208c" - "27208d276a218e27208d27208e276a218f27208e27208f276a219027208f272090276a2191272090272091276a2192" - "272091272092276a2193" - "272092272093276a2194272093272094276a2195272094272095276a2196272095272096276a219727209627209727" - "6a219827209727209827" - "6a2199272098272099276a219a27209927209a276a219b27209a27209b276a219c27209b27209c276a219d27209c27" - "209d276a219e27209d27" - "209e276a219f27209e27209f276a21a027209f2720a0276a21a12720a02720a1276a21a22720a12720a2276a21a327" - "20a22720a3276a21a427" - "20a32720a4276a21a52720a42720a5276a21a62720a52720a6276a21a72720a62720a7276a21a82720a72720a8276a" - "21a92720a82720a9276a" - "21aa2720a92720aa276a21ab2720aa2720ab276a21ac2720ab2720ac276a21ad2720ac2720ad276a21ae2720ad2720" - "ae276a21af2720ae2720" - "af276a21b02720af2720b0276a21b12720b02720b1276a21b22720b12720b2276a21b32720b22720b3276a21b42720" - "b32720b4276a21b52720" - "b42720b5276a21b62720b52720b6276a21b72720b62720b7276a21b82720b72720b8276a21b92720b82720b9276a21" - "ba2720b92720ba276a21" - "bb2720ba2720bb276a21bc2720bb2720bc276a21bd2720bc2720bd276a21be2720bd2720be276a21bf2720be2720bf" - "276a21c02720bf2720c0" - "276a21c12720c02720c1276a21c22720c12720c2276a21c32720c22720c3276a21c42720c32720c4276a21c52720c4" - "2720c5276a21c62720c5" - "2720c6276a21c72720c62720c7276a21c82720c72720c8276a21c92720c82720c9276a21ca2720c92720ca276a21cb" - "2720ca2720cb276a21cc" - "2720cb2720cc276a21cd2720cc2720cd276a21ce2720cd2720ce276a21cf2720ce2720cf276a21d02720cf2720d027" - "6a21d12720d02720d127" - "6a21d22720d12720d2276a21d32720d22720d3276a21d42720d32720d4276a21d52720d42720d5276a21d62720d527" - "20d6276a21d72720d627" - "20d7276a21d82720d72720d8276a21d92720d82720d9276a21da2720d92720da276a21db2720da2720db276a21dc27" - "20db2720dc276a21dd27" - "20dc2720dd276a21de2720dd2720de276a21df2720de2720df276a21e02720df2720e0276a21e12720e02720e1276a" - "21e22720e12720e2276a" - "21e32720e22720e3276a21e42720e32720e4276a21e52720e42720e5276a21e62720e52720e6276a21e72720e62720" - "e7276a21e82720e72720" - "e8276a21e92720e82720e9276a21ea2720e92720ea276a21eb2720ea2720eb276a21ec2720eb2720ec276a21ed2720" - "ec2720ed276a21ee2720" - "ed2720ee276a21ef2720ee2720ef276a21f02720ef2720f0276a21f12720f02720f1276a21f22720f12720f2276a21" - "f32720f22720f3276a21" - "f42720f32720f4276a21f52720f42720f5276a21f62720f52720f6276a21f72720f62720f7276a21f82720f72720f8" - "276a21f92720f82720f9" - "276a21fa2720f92720fa276a21fb2720fa2720fb276a21fc2720fb2720fc276a21fd2720fc2720fd276a21fe2720fd" - "2720fe276a21ff2720fe" - "2720ff276a21802820ff272080286a2181282080282081286a2182282081282082286a2183282082282083286a2184" - "282083282084286a2185" + "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" - "6a218a28208928208a28" - "6a218b28208a28208b286a218c28208b28208c286a218d28208c28208d286a218e28208d28208e286a218f28208e28" - "208f286a219028208f28" - "2090286a2191282090282091286a2192282091282092286a2193282092282093286a2194282093282094286a219528" - "2094282095286a219628" - "2095282096286a2197282096282097286a2198282097282098286a2199282098282099286a219a28209928209a286a" - "219b28209a28209b286a" - "219c28209b28209c286a219d28209c28209d286a219e28209d28209e286a219f28209e28209f286a21a028209f2820" - "a0286a21a12820a02820" - "a1286a21a22820a12820a2286a21a32820a22820a3286a21a42820a32820a4286a21a52820a42820a5286a21a62820" - "a52820a6286a21a72820" - "a62820a7286a21a82820a72820a8286a21a92820a82820a9286a21aa2820a92820aa286a21ab2820aa2820ab286a21" - "ac2820ab2820ac286a21" - "ad2820ac2820ad286a21ae2820ad2820ae286a21af2820ae2820af286a21b02820af2820b0286a21b12820b02820b1" - "286a21b22820b12820b2" - "286a21b32820b22820b3286a21b42820b32820b4286a21b52820b42820b5286a21b62820b52820b6286a21b72820b6" - "2820b7286a21b82820b7" - "2820b8286a21b92820b82820b9286a21ba2820b92820ba286a21bb2820ba2820bb286a21bc2820bb2820bc286a21bd" - "2820bc2820bd286a21be" - "2820bd2820be286a21bf2820be2820bf286a21c02820bf2820c0286a21c12820c02820c1286a21c22820c12820c228" - "6a21c32820c22820c328" - "6a21c42820c32820c4286a21c52820c42820c5286a21c62820c52820c6286a21c72820c62820c7286a21c82820c728" - "20c8286a21c92820c828" - "20c9286a21ca2820c92820ca286a21cb2820ca2820cb286a21cc2820cb2820cc286a21cd2820cc2820cd286a21ce28" - "20cd2820ce286a21cf28" - "20ce2820cf286a21d02820cf2820d0286a21d12820d02820d1286a21d22820d12820d2286a21d32820d22820d3286a" - "21d42820d32820d4286a" - "21d52820d42820d5286a21d62820d52820d6286a21d72820d62820d7286a21d82820d72820d8286a21d92820d82820" - "d9286a21da2820d92820" - "da286a21db2820da2820db286a21dc2820db2820dc286a21dd2820dc2820dd286a21de2820dd2820de286a21df2820" - "de2820df286a21e02820" - "df2820e0286a21e12820e02820e1286a21e22820e12820e2286a21e32820e22820e3286a21e42820e32820e4286a21" - "e52820e42820e5286a21" - "e62820e52820e6286a21e72820e62820e7286a21e82820e72820e8286a21e92820e82820e9286a21ea2820e92820ea" - "286a21eb2820ea2820eb" - "286a21ec2820eb2820ec286a21ed2820ec2820ed286a21ee2820ed2820ee286a21ef2820ee2820ef286a21f02820ef" - "2820f0286a21f12820f0" - "2820f1286a21f22820f12820f2286a21f32820f22820f3286a21f42820f32820f4286a21f52820f42820f5286a21f6" - "2820f52820f6286a21f7" - "2820f62820f7286a21f82820f72820f8286a21f92820f82820f9286a21fa2820f92820fa286a21fb2820fa2820fb28" - "6a21fc2820fb2820fc28" - "6a21fd2820fc2820fd286a21fe2820fd2820fe286a21ff2820fe2820ff286a21802920ff282080296a218129208029" - "2081296a218229208129" - "2082296a2183292082292083296a2184292083292084296a2185292084292085296a2186292085292086296a218729" - "2086292087296a218829" - "2087292088296a2189292088292089296a218a29208929208a296a218b29208a29208b296a218c29208b29208c296a" - "218d29208c29208d296a" - "218e29208d29208e296a218f29208e29208f296a219029208f292090296a2191292090292091296a21922920912920" - "92296a21932920922920" - "93296a2194292093292094296a2195292094292095296a2196292095292096296a2197292096292097296a21982920" - "97292098296a21992920" - "98292099296a219a29209929209a296a219b29209a29209b296a219c29209b29209c296a219d29209c29209d296a21" - "9e29209d29209e296a21" - "9f29209e29209f296a21a029209f2920a0296a21a12920a02920a1296a21a22920a12920a2296a21a32920a22920a3" - "296a21a42920a32920a4" - "296a21a52920a42920a5296a21a62920a52920a6296a21a72920a62920a7296a21a82920a72920a8296a21a92920a8" - "2920a9296a21aa2920a9" - "2920aa296a21ab2920aa2920ab296a21ac2920ab2920ac296a21ad2920ac2920ad296a21ae2920ad2920ae296a21af" - "2920ae2920af296a21b0" - "2920af2920b0296a21b12920b02920b1296a21b22920b12920b2296a21b32920b22920b3296a21b42920b32920b429" - "6a21b52920b42920b529" - "6a21b62920b52920b6296a21b72920b62920b7296a21b82920b72920b8296a21b92920b82920b9296a21ba2920b929" - "20ba296a21bb2920ba29" - "20bb296a21bc2920bb2920bc296a21bd2920bc2920bd296a21be2920bd2920be296a21bf2920be2920bf296a21c029" - "20bf2920c0296a21c129" - "20c02920c1296a21c22920c12920c2296a21c32920c22920c3296a21c42920c32920c4296a21c52920c42920c5296a" - "21c62920c52920c6296a" - "21c72920c62920c7296a21c82920c72920c8296a21c92920c82920c9296a21ca2920c92920ca296a21cb2920ca2920" - "cb296a21cc2920cb2920" - "cc296a21cd2920cc2920cd296a21ce2920cd2920ce296a21cf2920ce2920cf296a21d02920cf2920d0296a21d12920" - "d02920d1296a21d22920" - "d12920d2296a21d32920d22920d3296a21d42920d32920d4296a21d52920d42920d5296a21d62920d52920d6296a21" - "d72920d62920d7296a21" - "d82920d72920d8296a21d92920d82920d9296a21da2920d92920da296a21db2920da2920db296a21dc2920db2920dc" - "296a21dd2920dc2920dd" - "296a21de2920dd2920de296a21df2920de2920df296a21e02920df2920e0296a21e12920e02920e1296a21e22920e1" - "2920e2296a21e32920e2" - "2920e3296a21e42920e32920e4296a21e52920e42920e5296a21e62920e52920e6296a21e72920e62920e7296a21e8" - "2920e72920e8296a21e9" - "2920e82920e9296a21ea2920e92920ea296a21eb2920ea2920eb296a21ec2920eb2920ec296a21ed2920ec2920ed29" - "6a21ee2920ed2920ee29" - "6a21ef2920ee2920ef296a21f02920ef2920f0296a21f12920f02920f1296a21f22920f12920f2296a21f32920f229" - "20f3296a21f42920f329" - "20f4296a21f52920f42920f5296a21f62920f52920f6296a21f72920f62920f7296a21f82920f72920f8296a21f929" - "20f82920f9296a21fa29" - "20f92920fa296a21fb2920fa2920fb296a21fc2920fb2920fc296a21fd2920fc2920fd296a21fe2920fd2920fe296a" - "21ff2920fe2920ff296a" - "21802a20ff2920802a6a21812a20802a20812a6a21822a20812a20822a6a21832a20822a20832a6a21842a20832a20" - "842a6a21852a20842a20" - "852a6a21862a20852a20862a6a21872a20862a20872a6a21882a20872a20882a6a21892a20882a20892a6a218a2a20" - "892a208a2a6a218b2a20" - "8a2a208b2a6a218c2a208b2a208c2a6a218d2a208c2a208d2a6a218e2a208d2a208e2a6a218f2a208e2a208f2a6a21" - "902a208f2a20902a6a21" + "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" - "2a6a21962a20952a2096" - "2a6a21972a20962a20972a6a21982a20972a20982a6a21992a20982a20992a6a219a2a20992a209a2a6a219b2a209a" - "2a209b2a6a219c2a209b" - "2a209c2a6a219d2a209c2a209d2a6a219e2a209d2a209e2a6a219f2a209e2a209f2a6a21a02a209f2a20a02a6a21a1" - "2a20a02a20a12a6a21a2" - "2a20a12a20a22a6a21a32a20a22a20a32a6a21a42a20a32a20a42a6a21a52a20a42a20a52a6a21a62a20a52a20a62a" - "6a21a72a20a62a20a72a" - "6a21a82a20a72a20a82a6a21a92a20a82a20a92a6a21aa2a20a92a20aa2a6a21ab2a20aa2a20ab2a6a21ac2a20ab2a" - "20ac2a6a21ad2a20ac2a" - "20ad2a6a21ae2a20ad2a20ae2a6a21af2a20ae2a20af2a6a21b02a20af2a20b02a6a21b12a20b02a20b12a6a21b22a" - "20b12a20b22a6a21b32a" - "20b22a20b32a6a21b42a20b32a20b42a6a21b52a20b42a20b52a6a21b62a20b52a20b62a6a21b72a20b62a20b72a6a" - "21b82a20b72a20b82a6a" - "21b92a20b82a20b92a6a21ba2a20b92a20ba2a6a21bb2a20ba2a20bb2a6a21bc2a20bb2a20bc2a6a21bd2a20bc2a20" - "bd2a6a21be2a20bd2a20" - "be2a6a21bf2a20be2a20bf2a6a21c02a20bf2a20c02a6a21c12a20c02a20c12a6a21c22a20c12a20c22a6a21c32a20" - "c22a20c32a6a21c42a20" - "c32a20c42a6a21c52a20c42a20c52a6a21c62a20c52a20c62a6a21c72a20c62a20c72a6a21c82a20c72a20c82a6a21" - "c92a20c82a20c92a6a21" - "ca2a20c92a20ca2a6a21cb2a20ca2a20cb2a6a21cc2a20cb2a20cc2a6a21cd2a20cc2a20cd2a6a21ce2a20cd2a20ce" - "2a6a21cf2a20ce2a20cf" - "2a6a21d02a20cf2a20d02a6a21d12a20d02a20d12a6a21d22a20d12a20d22a6a21d32a20d22a20d32a6a21d42a20d3" - "2a20d42a6a21d52a20d4" - "2a20d52a6a21d62a20d52a20d62a6a21d72a20d62a20d72a6a21d82a20d72a20d82a6a21d92a20d82a20d92a6a21da" - "2a20d92a20da2a6a21db" - "2a20da2a20db2a6a21dc2a20db2a20dc2a6a21dd2a20dc2a20dd2a6a21de2a20dd2a20de2a6a21df2a20de2a20df2a" - "6a21e02a20df2a20e02a" - "6a21e12a20e02a20e12a6a21e22a20e12a20e22a6a21e32a20e22a20e32a6a21e42a20e32a20e42a6a21e52a20e42a" - "20e52a6a21e62a20e52a" - "20e62a6a21e72a20e62a20e72a6a21e82a20e72a20e82a6a21e92a20e82a20e92a6a21ea2a20e92a20ea2a6a21eb2a" - "20ea2a20eb2a6a21ec2a" - "20eb2a20ec2a6a21ed2a20ec2a20ed2a6a21ee2a20ed2a20ee2a6a21ef2a20ee2a20ef2a6a21f02a20ef2a20f02a6a" - "21f12a20f02a20f12a6a" - "21f22a20f12a20f22a6a21f32a20f22a20f32a6a21f42a20f32a20f42a6a21f52a20f42a20f52a6a21f62a20f52a20" - "f62a6a21f72a20f62a20" - "f72a6a21f82a20f72a20f82a6a21f92a20f82a20f92a6a21fa2a20f92a20fa2a6a21fb2a20fa2a20fb2a6a21fc2a20" - "fb2a20fc2a6a21fd2a20" - "fc2a20fd2a6a21fe2a20fd2a20fe2a6a21ff2a20fe2a20ff2a6a21802b20ff2a20802b6a21812b20802b20812b6a21" - "822b20812b20822b6a21" - "832b20822b20832b6a21842b20832b20842b6a21852b20842b20852b6a21862b20852b20862b6a21872b20862b2087" - "2b6a21882b20872b2088" - "2b6a21892b20882b20892b6a218a2b20892b208a2b6a218b2b208a2b208b2b6a218c2b208b2b208c2b6a218d2b208c" - "2b208d2b6a218e2b208d" - "2b208e2b6a218f2b208e2b208f2b6a21902b208f2b20902b6a21912b20902b20912b6a21922b20912b20922b6a2193" - "2b20922b20932b6a2194" - "2b20932b20942b6a21952b20942b20952b6a21962b20952b20962b6a21972b20962b20972b6a21982b20972b20982b" - "6a21992b20982b20992b" - "6a219a2b20992b209a2b6a219b2b209a2b209b2b6a219c2b209b2b209c2b6a219d2b209c2b209d2b6a219e2b209d2b" - "209e2b6a219f2b209e2b" - "209f2b6a21a02b209f2b20a02b6a21a12b20a02b20a12b6a21a22b20a12b20a22b6a21a32b20a22b20a32b6a21a42b" - "20a32b20a42b6a21a52b" - "20a42b20a52b6a21a62b20a52b20a62b6a21a72b20a62b20a72b6a21a82b20a72b20a82b6a21a92b20a82b20a92b6a" - "21aa2b20a92b20aa2b6a" - "21ab2b20aa2b20ab2b6a21ac2b20ab2b20ac2b6a21ad2b20ac2b20ad2b6a21ae2b20ad2b20ae2b6a21af2b20ae2b20" - "af2b6a21b02b20af2b20" - "b02b6a21b12b20b02b20b12b6a21b22b20b12b20b22b6a21b32b20b22b20b32b6a21b42b20b32b20b42b6a21b52b20" - "b42b20b52b6a21b62b20" - "b52b20b62b6a21b72b20b62b20b72b6a21b82b20b72b20b82b6a21b92b20b82b20b92b6a21ba2b20b92b20ba2b6a21" - "bb2b20ba2b20bb2b6a21" - "bc2b20bb2b20bc2b6a21bd2b20bc2b20bd2b6a21be2b20bd2b20be2b6a21bf2b20be2b20bf2b6a21c02b20bf2b20c0" - "2b6a21c12b20c02b20c1" - "2b6a21c22b20c12b20c22b6a21c32b20c22b20c32b6a21c42b20c32b20c42b6a21c52b20c42b20c52b6a21c62b20c5" - "2b20c62b6a21c72b20c6" - "2b20c72b6a21c82b20c72b20c82b6a21c92b20c82b20c92b6a21ca2b20c92b20ca2b6a21cb2b20ca2b20cb2b6a21cc" - "2b20cb2b20cc2b6a21cd" - "2b20cc2b20cd2b6a21ce2b20cd2b20ce2b6a21cf2b20ce2b20cf2b6a21d02b20cf2b20d02b6a21d12b20d02b20d12b" - "6a21d22b20d12b20d22b" - "6a21d32b20d22b20d32b6a21d42b20d32b20d42b6a21d52b20d42b20d52b6a21d62b20d52b20d62b6a21d72b20d62b" - "20d72b6a21d82b20d72b" - "20d82b6a21d92b20d82b20d92b6a21da2b20d92b20da2b6a21db2b20da2b20db2b6a21dc2b20db2b20dc2b6a21dd2b" - "20dc2b20dd2b6a21de2b" - "20dd2b20de2b6a21df2b20de2b20df2b6a21e02b20df2b20e02b6a21e12b20e02b20e12b6a21e22b20e12b20e22b6a" - "21e32b20e22b20e32b6a" - "21e42b20e32b20e42b6a21e52b20e42b20e52b6a21e62b20e52b20e62b6a21e72b20e62b20e72b6a21e82b20e72b20" - "e82b6a21e92b20e82b20" - "e92b6a21ea2b20e92b20ea2b6a21eb2b20ea2b20eb2b6a21ec2b20eb2b20ec2b6a21ed2b20ec2b20ed2b6a21ee2b20" - "ed2b20ee2b6a21ef2b20" - "ee2b20ef2b6a21f02b20ef2b20f02b6a21f12b20f02b20f12b6a21f22b20f12b20f22b6a21f32b20f22b20f32b6a21" - "f42b20f32b20f42b6a21" - "f52b20f42b20f52b6a21f62b20f52b20f62b6a21f72b20f62b20f72b6a21f82b20f72b20f82b6a21f92b20f82b20f9" - "2b6a21fa2b20f92b20fa" - "2b6a21fb2b20fa2b20fb2b6a21fc2b20fb2b20fc2b6a21fd2b20fc2b20fd2b6a21fe2b20fd2b20fe2b6a21ff2b20fe" - "2b20ff2b6a21802c20ff" - "2b20802c6a21812c20802c20812c6a21822c20812c20822c6a21832c20822c20832c6a21842c20832c20842c6a2185" - "2c20842c20852c6a2186" - "2c20852c20862c6a21872c20862c20872c6a21882c20872c20882c6a21892c20882c20892c6a218a2c20892c208a2c" - "6a218b2c208a2c208b2c" - "6a218c2c208b2c208c2c6a218d2c208c2c208d2c6a218e2c208d2c208e2c6a218f2c208e2c208f2c6a21902c208f2c" - "20902c6a21912c20902c" - "20912c6a21922c20912c20922c6a21932c20922c20932c6a21942c20932c20942c6a21952c20942c20952c6a21962c" - "20952c20962c6a21972c" - "20962c20972c6a21982c20972c20982c6a21992c20982c20992c6a219a2c20992c209a2c6a219b2c209a2c209b2c6a" - "219c2c209b2c209c2c6a" + "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" - "a12c6a21a22c20a12c20" - "a22c6a21a32c20a22c20a32c6a21a42c20a32c20a42c6a21a52c20a42c20a52c6a21a62c20a52c20a62c6a21a72c20" - "a62c20a72c6a21a82c20" - "a72c20a82c6a21a92c20a82c20a92c6a21aa2c20a92c20aa2c6a21ab2c20aa2c20ab2c6a21ac2c20ab2c20ac2c6a21" - "ad2c20ac2c20ad2c6a21" - "ae2c20ad2c20ae2c6a21af2c20ae2c20af2c6a21b02c20af2c20b02c6a21b12c20b02c20b12c6a21b22c20b12c20b2" - "2c6a21b32c20b22c20b3" - "2c6a21b42c20b32c20b42c6a21b52c20b42c20b52c6a21b62c20b52c20b62c6a21b72c20b62c20b72c6a21b82c20b7" - "2c20b82c6a21b92c20b8" - "2c20b92c6a21ba2c20b92c20ba2c6a21bb2c20ba2c20bb2c6a21bc2c20bb2c20bc2c6a21bd2c20bc2c20bd2c6a21be" - "2c20bd2c20be2c6a21bf" - "2c20be2c20bf2c6a21c02c20bf2c20c02c6a21c12c20c02c20c12c6a21c22c20c12c20c22c6a21c32c20c22c20c32c" - "6a21c42c20c32c20c42c" - "6a21c52c20c42c20c52c6a21c62c20c52c20c62c6a21c72c20c62c20c72c6a21c82c20c72c20c82c6a21c92c20c82c" - "20c92c6a21ca2c20c92c" - "20ca2c6a21cb2c20ca2c20cb2c6a21cc2c20cb2c20cc2c6a21cd2c20cc2c20cd2c6a21ce2c20cd2c20ce2c6a21cf2c" - "20ce2c20cf2c6a21d02c" - "20cf2c20d02c6a21d12c20d02c20d12c6a21d22c20d12c20d22c6a21d32c20d22c20d32c6a21d42c20d32c20d42c6a" - "21d52c20d42c20d52c6a" - "21d62c20d52c20d62c6a21d72c20d62c20d72c6a21d82c20d72c20d82c6a21d92c20d82c20d92c6a21da2c20d92c20" - "da2c6a21db2c20da2c20" - "db2c6a21dc2c20db2c20dc2c6a21dd2c20dc2c20dd2c6a21de2c20dd2c20de2c6a21df2c20de2c20df2c6a21e02c20" - "df2c20e02c6a21e12c20" - "e02c20e12c6a21e22c20e12c20e22c6a21e32c20e22c20e32c6a21e42c20e32c20e42c6a21e52c20e42c20e52c6a21" - "e62c20e52c20e62c6a21" - "e72c20e62c20e72c6a21e82c20e72c20e82c6a21e92c20e82c20e92c6a21ea2c20e92c20ea2c6a21eb2c20ea2c20eb" - "2c6a21ec2c20eb2c20ec" - "2c6a21ed2c20ec2c20ed2c6a21ee2c20ed2c20ee2c6a21ef2c20ee2c20ef2c6a21f02c20ef2c20f02c6a21f12c20f0" - "2c20f12c6a21f22c20f1" - "2c20f22c6a21f32c20f22c20f32c6a21f42c20f32c20f42c6a21f52c20f42c20f52c6a21f62c20f52c20f62c6a21f7" - "2c20f62c20f72c6a21f8" - "2c20f72c20f82c6a21f92c20f82c20f92c6a21fa2c20f92c20fa2c6a21fb2c20fa2c20fb2c6a21fc2c20fb2c20fc2c" - "6a21fd2c20fc2c20fd2c" - "6a21fe2c20fd2c20fe2c6a21ff2c20fe2c20ff2c6a21802d20ff2c20802d6a21812d20802d20812d6a21822d20812d" - "20822d6a21832d20822d" - "20832d6a21842d20832d20842d6a21852d20842d20852d6a21862d20852d20862d6a21872d20862d20872d6a21882d" - "20872d20882d6a21892d" - "20882d20892d6a218a2d20892d208a2d6a218b2d208a2d208b2d6a218c2d208b2d208c2d6a218d2d208c2d208d2d6a" - "218e2d208d2d208e2d6a" - "218f2d208e2d208f2d6a21902d208f2d20902d6a21912d20902d20912d6a21922d20912d20922d6a21932d20922d20" - "932d6a21942d20932d20" - "942d6a21952d20942d20952d6a21962d20952d20962d6a21972d20962d20972d6a21982d20972d20982d6a21992d20" - "982d20992d6a219a2d20" - "992d209a2d6a219b2d209a2d209b2d6a219c2d209b2d209c2d6a219d2d209c2d209d2d6a219e2d209d2d209e2d6a21" - "9f2d209e2d209f2d6a21" - "a02d209f2d20a02d6a21a12d20a02d20a12d6a21a22d20a12d20a22d6a21a32d20a22d20a32d6a21a42d20a32d20a4" - "2d6a21a52d20a42d20a5" - "2d6a21a62d20a52d20a62d6a21a72d20a62d20a72d6a21a82d20a72d20a82d6a21a92d20a82d20a92d6a21aa2d20a9" - "2d20aa2d6a21ab2d20aa" - "2d20ab2d6a21ac2d20ab2d20ac2d6a21ad2d20ac2d20ad2d6a21ae2d20ad2d20ae2d6a21af2d20ae2d20af2d6a21b0" - "2d20af2d20b02d6a21b1" - "2d20b02d20b12d6a21b22d20b12d20b22d6a21b32d20b22d20b32d6a21b42d20b32d20b42d6a21b52d20b42d20b52d" - "6a21b62d20b52d20b62d" - "6a21b72d20b62d20b72d6a21b82d20b72d20b82d6a21b92d20b82d20b92d6a21ba2d20b92d20ba2d6a21bb2d20ba2d" - "20bb2d6a21bc2d20bb2d" - "20bc2d6a21bd2d20bc2d20bd2d6a21be2d20bd2d20be2d6a21bf2d20be2d20bf2d6a21c02d20bf2d20c02d6a21c12d" - "20c02d20c12d6a21c22d" - "20c12d20c22d6a21c32d20c22d20c32d6a21c42d20c32d20c42d6a21c52d20c42d20c52d6a21c62d20c52d20c62d6a" - "21c72d20c62d20c72d6a" - "21c82d20c72d20c82d6a21c92d20c82d20c92d6a21ca2d20c92d20ca2d6a21cb2d20ca2d20cb2d6a21cc2d20cb2d20" - "cc2d6a21cd2d20cc2d20" - "cd2d6a21ce2d20cd2d20ce2d6a21cf2d20ce2d20cf2d6a21d02d20cf2d20d02d6a21d12d20d02d20d12d6a21d22d20" - "d12d20d22d6a21d32d20" - "d22d20d32d6a21d42d20d32d20d42d6a21d52d20d42d20d52d6a21d62d20d52d20d62d6a21d72d20d62d20d72d6a21" - "d82d20d72d20d82d6a21" - "d92d20d82d20d92d6a21da2d20d92d20da2d6a21db2d20da2d20db2d6a21dc2d20db2d20dc2d6a21dd2d20dc2d20dd" - "2d6a21de2d20dd2d20de" - "2d6a21df2d20de2d20df2d6a21e02d20df2d20e02d6a21e12d20e02d20e12d6a21e22d20e12d20e22d6a21e32d20e2" - "2d20e32d6a21e42d20e3" - "2d20e42d6a21e52d20e42d20e52d6a21e62d20e52d20e62d6a21e72d20e62d20e72d6a21e82d20e72d20e82d6a21e9" - "2d20e82d20e92d6a21ea" - "2d20e92d20ea2d6a21eb2d20ea2d20eb2d6a21ec2d20eb2d20ec2d6a21ed2d20ec2d20ed2d6a21ee2d20ed2d20ee2d" - "6a21ef2d20ee2d20ef2d" - "6a21f02d20ef2d20f02d6a21f12d20f02d20f12d6a21f22d20f12d20f22d6a21f32d20f22d20f32d6a21f42d20f32d" - "20f42d6a21f52d20f42d" - "20f52d6a21f62d20f52d20f62d6a21f72d20f62d20f72d6a21f82d20f72d20f82d6a21f92d20f82d20f92d6a21fa2d" - "20f92d20fa2d6a21fb2d" - "20fa2d20fb2d6a21fc2d20fb2d20fc2d6a21fd2d20fc2d20fd2d6a21fe2d20fd2d20fe2d6a21ff2d20fe2d20ff2d6a" - "21802e20ff2d20802e6a" - "21812e20802e20812e6a21822e20812e20822e6a21832e20822e20832e6a21842e20832e20842e6a21852e20842e20" - "852e6a21862e20852e20" - "862e6a21872e20862e20872e6a21882e20872e20882e6a21892e20882e20892e6a218a2e20892e208a2e6a218b2e20" - "8a2e208b2e6a218c2e20" - "8b2e208c2e6a218d2e208c2e208d2e6a218e2e208d2e208e2e6a218f2e208e2e208f2e6a21902e208f2e20902e6a21" - "912e20902e20912e6a21" - "922e20912e20922e6a21932e20922e20932e6a21942e20932e20942e6a21952e20942e20952e6a21962e20952e2096" - "2e6a21972e20962e2097" - "2e6a21982e20972e20982e6a21992e20982e20992e6a219a2e20992e209a2e6a219b2e209a2e209b2e6a219c2e209b" - "2e209c2e6a219d2e209c" - "2e209d2e6a219e2e209d2e209e2e6a219f2e209e2e209f2e6a21a02e209f2e20a02e6a21a12e20a02e20a12e6a21a2" - "2e20a12e20a22e6a21a3" - "2e20a22e20a32e6a21a42e20a32e20a42e6a21a52e20a42e20a52e6a21a62e20a52e20a62e6a21a72e20a62e20a72e" - "6a21a82e20a72e20a82e" + "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" - "20ad2e6a21ae2e20ad2e" - "20ae2e6a21af2e20ae2e20af2e6a21b02e20af2e20b02e6a21b12e20b02e20b12e6a21b22e20b12e20b22e6a21b32e" - "20b22e20b32e6a21b42e" - "20b32e20b42e6a21b52e20b42e20b52e6a21b62e20b52e20b62e6a21b72e20b62e20b72e6a21b82e20b72e20b82e6a" - "21b92e20b82e20b92e6a" - "21ba2e20b92e20ba2e6a21bb2e20ba2e20bb2e6a21bc2e20bb2e20bc2e6a21bd2e20bc2e20bd2e6a21be2e20bd2e20" - "be2e6a21bf2e20be2e20" - "bf2e6a21c02e20bf2e20c02e6a21c12e20c02e20c12e6a21c22e20c12e20c22e6a21c32e20c22e20c32e6a21c42e20" - "c32e20c42e6a21c52e20" - "c42e20c52e6a21c62e20c52e20c62e6a21c72e20c62e20c72e6a21c82e20c72e20c82e6a21c92e20c82e20c92e6a21" - "ca2e20c92e20ca2e6a21" - "cb2e20ca2e20cb2e6a21cc2e20cb2e20cc2e6a21cd2e20cc2e20cd2e6a21ce2e20cd2e20ce2e6a21cf2e20ce2e20cf" - "2e6a21d02e20cf2e20d0" - "2e6a21d12e20d02e20d12e6a21d22e20d12e20d22e6a21d32e20d22e20d32e6a21d42e20d32e20d42e6a21d52e20d4" - "2e20d52e6a21d62e20d5" - "2e20d62e6a21d72e20d62e20d72e6a21d82e20d72e20d82e6a21d92e20d82e20d92e6a21da2e20d92e20da2e6a21db" - "2e20da2e20db2e6a21dc" - "2e20db2e20dc2e6a21dd2e20dc2e20dd2e6a21de2e20dd2e20de2e6a21df2e20de2e20df2e6a21e02e20df2e20e02e" - "6a21e12e20e02e20e12e" - "6a21e22e20e12e20e22e6a21e32e20e22e20e32e6a21e42e20e32e20e42e6a21e52e20e42e20e52e6a21e62e20e52e" - "20e62e6a21e72e20e62e" - "20e72e6a21e82e20e72e20e82e6a21e92e20e82e20e92e6a21ea2e20e92e20ea2e6a21eb2e20ea2e20eb2e6a21ec2e" - "20eb2e20ec2e6a21ed2e" - "20ec2e20ed2e6a21ee2e20ed2e20ee2e6a21ef2e20ee2e20ef2e6a21f02e20ef2e20f02e6a21f12e20f02e20f12e6a" - "21f22e20f12e20f22e6a" - "21f32e20f22e20f32e6a21f42e20f32e20f42e6a21f52e20f42e20f52e6a21f62e20f52e20f62e6a21f72e20f62e20" - "f72e6a21f82e20f72e20" - "f82e6a21f92e20f82e20f92e6a21fa2e20f92e20fa2e6a21fb2e20fa2e20fb2e6a21fc2e20fb2e20fc2e6a21fd2e20" - "fc2e20fd2e6a21fe2e20" - "fd2e20fe2e6a21ff2e20fe2e20ff2e6a21802f20ff2e20802f6a21812f20802f20812f6a21822f20812f20822f6a21" - "832f20822f20832f6a21" - "842f20832f20842f6a21852f20842f20852f6a21862f20852f20862f6a21872f20862f20872f6a21882f20872f2088" - "2f6a21892f20882f2089" - "2f6a218a2f20892f208a2f6a218b2f208a2f208b2f6a218c2f208b2f208c2f6a218d2f208c2f208d2f6a218e2f208d" - "2f208e2f6a218f2f208e" - "2f208f2f6a21902f208f2f20902f6a21912f20902f20912f6a21922f20912f20922f6a21932f20922f20932f6a2194" - "2f20932f20942f6a2195" - "2f20942f20952f6a21962f20952f20962f6a21972f20962f20972f6a21982f20972f20982f6a21992f20982f20992f" - "6a219a2f20992f209a2f" - "6a219b2f209a2f209b2f6a219c2f209b2f209c2f6a219d2f209c2f209d2f6a219e2f209d2f209e2f6a219f2f209e2f" - "209f2f6a21a02f209f2f" - "20a02f6a21a12f20a02f20a12f6a21a22f20a12f20a22f6a21a32f20a22f20a32f6a21a42f20a32f20a42f6a21a52f" - "20a42f20a52f6a21a62f" - "20a52f20a62f6a21a72f20a62f20a72f6a21a82f20a72f20a82f6a21a92f20a82f20a92f6a21aa2f20a92f20aa2f6a" - "21ab2f20aa2f20ab2f6a" - "21ac2f20ab2f20ac2f6a21ad2f20ac2f20ad2f6a21ae2f20ad2f20ae2f6a21af2f20ae2f20af2f6a21b02f20af2f20" - "b02f6a21b12f20b02f20" - "b12f6a21b22f20b12f20b22f6a21b32f20b22f20b32f6a21b42f20b32f20b42f6a21b52f20b42f20b52f6a21b62f20" - "b52f20b62f6a21b72f20" - "b62f20b72f6a21b82f20b72f20b82f6a21b92f20b82f20b92f6a21ba2f20b92f20ba2f6a21bb2f20ba2f20bb2f6a21" - "bc2f20bb2f20bc2f6a21" - "bd2f20bc2f20bd2f6a21be2f20bd2f20be2f6a21bf2f20be2f20bf2f6a21c02f20bf2f20c02f6a21c12f20c02f20c1" - "2f6a21c22f20c12f20c2" - "2f6a21c32f20c22f20c32f6a21c42f20c32f20c42f6a21c52f20c42f20c52f6a21c62f20c52f20c62f6a21c72f20c6" - "2f20c72f6a21c82f20c7" - "2f20c82f6a21c92f20c82f20c92f6a21ca2f20c92f20ca2f6a21cb2f20ca2f20cb2f6a21cc2f20cb2f20cc2f6a21cd" - "2f20cc2f20cd2f6a21ce" - "2f20cd2f20ce2f6a21cf2f20ce2f20cf2f6a21d02f20cf2f20d02f6a21d12f20d02f20d12f6a21d22f20d12f20d22f" - "6a21d32f20d22f20d32f" - "6a21d42f20d32f20d42f6a21d52f20d42f20d52f6a21d62f20d52f20d62f6a21d72f20d62f20d72f6a21d82f20d72f" - "20d82f6a21d92f20d82f" - "20d92f6a21da2f20d92f20da2f6a21db2f20da2f20db2f6a21dc2f20db2f20dc2f6a21dd2f20dc2f20dd2f6a21de2f" - "20dd2f20de2f6a21df2f" - "20de2f20df2f6a21e02f20df2f20e02f6a21e12f20e02f20e12f6a21e22f20e12f20e22f6a21e32f20e22f20e32f6a" - "21e42f20e32f20e42f6a" - "21e52f20e42f20e52f6a21e62f20e52f20e62f6a21e72f20e62f20e72f6a21e82f20e72f20e82f6a21e92f20e82f20" - "e92f6a21ea2f20e92f20" - "ea2f6a21eb2f20ea2f20eb2f6a21ec2f20eb2f20ec2f6a21ed2f20ec2f20ed2f6a21ee2f20ed2f20ee2f6a21ef2f20" - "ee2f20ef2f6a21f02f20" - "ef2f20f02f6a21f12f20f02f20f12f6a21f22f20f12f20f22f6a21f32f20f22f20f32f6a21f42f20f32f20f42f6a21" - "f52f20f42f20f52f6a21" - "f62f20f52f20f62f6a21f72f20f62f20f72f6a21f82f20f72f20f82f6a21f92f20f82f20f92f6a21fa2f20f92f20fa" - "2f6a21fb2f20fa2f20fb" - "2f6a21fc2f20fb2f20fc2f6a21fd2f20fc2f20fd2f6a21fe2f20fd2f20fe2f6a21ff2f20fe2f20ff2f6a21803020ff" - "2f2080306a2181302080" - "302081306a2182302081302082306a2183302082302083306a2184302083302084306a2185302084302085306a2186" - "302085302086306a2187" - "302086302087306a2188302087302088306a2189302088302089306a218a30208930208a306a218b30208a30208b30" - "6a218c30208b30208c30" - "6a218d30208c30208d306a218e30208d30208e306a218f30208e30208f306a219030208f302090306a219130209030" - "2091306a219230209130" - "2092306a2193302092302093306a2194302093302094306a2195302094302095306a2196302095302096306a219730" - "2096302097306a219830" - "2097302098306a2199302098302099306a219a30209930209a306a219b30209a30209b306a219c30209b30209c306a" - "219d30209c30209d306a" - "219e30209d30209e306a219f30209e30209f306a21a030209f3020a0306a21a13020a03020a1306a21a23020a13020" - "a2306a21a33020a23020" - "a3306a21a43020a33020a4306a21a53020a43020a5306a21a63020a53020a6306a21a73020a63020a7306a21a83020" - "a73020a8306a21a93020" - "a83020a9306a21aa3020a93020aa306a21ab3020aa3020ab306a21ac3020ab3020ac306a21ad3020ac3020ad306a21" - "ae3020ad3020ae306a21" - "af3020ae3020af306a21b03020af3020b0306a21b13020b03020b1306a21b23020b13020b2306a21b33020b23020b3" - "306a21b43020b33020b4" + "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" - "3020b9306a21ba3020b9" - "3020ba306a21bb3020ba3020bb306a21bc3020bb3020bc306a21bd3020bc3020bd306a21be3020bd3020be306a21bf" - "3020be3020bf306a21c0" - "3020bf3020c0306a21c13020c03020c1306a21c23020c13020c2306a21c33020c23020c3306a21c43020c33020c430" - "6a21c53020c43020c530" - "6a21c63020c53020c6306a21c73020c63020c7306a21c83020c73020c8306a21c93020c83020c9306a21ca3020c930" - "20ca306a21cb3020ca30" - "20cb306a21cc3020cb3020cc306a21cd3020cc3020cd306a21ce3020cd3020ce306a21cf3020ce3020cf306a21d030" - "20cf3020d0306a21d130" - "20d03020d1306a21d23020d13020d2306a21d33020d23020d3306a21d43020d33020d4306a21d53020d43020d5306a" - "21d63020d53020d6306a" - "21d73020d63020d7306a21d83020d73020d8306a21d93020d83020d9306a21da3020d93020da306a21db3020da3020" - "db306a21dc3020db3020" - "dc306a21dd3020dc3020dd306a21de3020dd3020de306a21df3020de3020df306a21e03020df3020e0306a21e13020" - "e03020e1306a21e23020" - "e13020e2306a21e33020e23020e3306a21e43020e33020e4306a21e53020e43020e5306a21e63020e53020e6306a21" - "e73020e63020e7306a21" - "e83020e73020e8306a21e93020e83020e9306a21ea3020e93020ea306a21eb3020ea3020eb306a21ec3020eb3020ec" - "306a21ed3020ec3020ed" - "306a21ee3020ed3020ee306a21ef3020ee3020ef306a21f03020ef3020f0306a21f13020f03020f1306a21f23020f1" - "3020f2306a21f33020f2" - "3020f3306a21f43020f33020f4306a21f53020f43020f5306a21f63020f53020f6306a21f73020f63020f7306a21f8" - "3020f73020f8306a21f9" - "3020f83020f9306a21fa3020f93020fa306a21fb3020fa3020fb306a21fc3020fb3020fc306a21fd3020fc3020fd30" - "6a21fe3020fd3020fe30" - "6a21ff3020fe3020ff306a21803120ff302080316a2181312080312081316a2182312081312082316a218331208231" - "2083316a218431208331" - "2084316a2185312084312085316a2186312085312086316a2187312086312087316a2188312087312088316a218931" - "2088312089316a218a31" - "208931208a316a218b31208a31208b316a218c31208b31208c316a218d31208c31208d316a218e31208d31208e316a" - "218f31208e31208f316a" - "219031208f312090316a2191312090312091316a2192312091312092316a2193312092312093316a21943120933120" - "94316a21953120943120" - "95316a2196312095312096316a2197312096312097316a2198312097312098316a2199312098312099316a219a3120" - "9931209a316a219b3120" - "9a31209b316a219c31209b31209c316a219d31209c31209d316a219e31209d31209e316a219f31209e31209f316a21" - "a031209f3120a0316a21" - "a13120a03120a1316a21a23120a13120a2316a21a33120a23120a3316a21a43120a33120a4316a21a53120a43120a5" - "316a21a63120a53120a6" - "316a21a73120a63120a7316a21a83120a73120a8316a21a93120a83120a9316a21aa3120a93120aa316a21ab3120aa" - "3120ab316a21ac3120ab" - "3120ac316a21ad3120ac3120ad316a21ae3120ad3120ae316a21af3120ae3120af316a21b03120af3120b0316a21b1" - "3120b03120b1316a21b2" - "3120b13120b2316a21b33120b23120b3316a21b43120b33120b4316a21b53120b43120b5316a21b63120b53120b631" - "6a21b73120b63120b731" - "6a21b83120b73120b8316a21b93120b83120b9316a21ba3120b93120ba316a21bb3120ba3120bb316a21bc3120bb31" - "20bc316a21bd3120bc31" - "20bd316a21be3120bd3120be316a21bf3120be3120bf316a21c03120bf3120c0316a21c13120c03120c1316a21c231" - "20c13120c2316a21c331" - "20c23120c3316a21c43120c33120c4316a21c53120c43120c5316a21c63120c53120c6316a21c73120c63120c7316a" - "21c83120c73120c8316a" - "21c93120c83120c9316a21ca3120c93120ca316a21cb3120ca3120cb316a21cc3120cb3120cc316a21cd3120cc3120" - "cd316a21ce3120cd3120" - "ce316a21cf3120ce3120cf316a21d03120cf3120d0316a21d13120d03120d1316a21d23120d13120d2316a21d33120" - "d23120d3316a21d43120" - "d33120d4316a21d53120d43120d5316a21d63120d53120d6316a21d73120d63120d7316a21d83120d73120d8316a21" - "d93120d83120d9316a21" - "da3120d93120da316a21db3120da3120db316a21dc3120db3120dc316a21dd3120dc3120dd316a21de3120dd3120de" - "316a21df3120de3120df" - "316a21e03120df3120e0316a21e13120e03120e1316a21e23120e13120e2316a21e33120e23120e3316a21e43120e3" - "3120e4316a21e53120e4" - "3120e5316a21e63120e53120e6316a21e73120e63120e7316a21e83120e73120e8316a21e93120e83120e9316a21ea" - "3120e93120ea316a21eb" - "3120ea3120eb316a21ec3120eb3120ec316a21ed3120ec3120ed316a21ee3120ed3120ee316a21ef3120ee3120ef31" - "6a21f03120ef3120f031" - "6a21f13120f03120f1316a21f23120f13120f2316a21f33120f23120f3316a21f43120f33120f4316a21f53120f431" - "20f5316a21f63120f531" - "20f6316a21f73120f63120f7316a21f83120f73120f8316a21f93120f83120f9316a21fa3120f93120fa316a21fb31" - "20fa3120fb316a21fc31" - "20fb3120fc316a21fd3120fc3120fd316a21fe3120fd3120fe316a21ff3120fe3120ff316a21803220ff312080326a" - "2181322080322081326a" - "2182322081322082326a2183322082322083326a2184322083322084326a2185322084322085326a21863220853220" - "86326a21873220863220" - "87326a2188322087322088326a2189322088322089326a218a32208932208a326a218b32208a32208b326a218c3220" - "8b32208c326a218d3220" - "8c32208d326a218e32208d32208e326a218f32208e32208f326a219032208f322090326a2191322090322091326a21" - "92322091322092326a21" - "93322092322093326a2194322093322094326a2195322094322095326a2196322095322096326a2197322096322097" - "326a2198322097322098" - "326a2199322098322099326a219a32209932209a326a219b32209a32209b326a219c32209b32209c326a219d32209c" - "32209d326a219e32209d" - "32209e326a219f32209e32209f326a21a032209f3220a0326a21a13220a03220a1326a21a23220a13220a2326a21a3" - "3220a23220a3326a21a4" - "3220a33220a4326a21a53220a43220a5326a21a63220a53220a6326a21a73220a63220a7326a21a83220a73220a832" - "6a21a93220a83220a932" - "6a21aa3220a93220aa326a21ab3220aa3220ab326a21ac3220ab3220ac326a21ad3220ac3220ad326a21ae3220ad32" - "20ae326a21af3220ae32" - "20af326a21b03220af3220b0326a21b13220b03220b1326a21b23220b13220b2326a21b33220b23220b3326a21b432" - "20b33220b4326a21b532" - "20b43220b5326a21b63220b53220b6326a21b73220b63220b7326a21b83220b73220b8326a21b93220b83220b9326a" - "21ba3220b93220ba326a" - "21bb3220ba3220bb326a21bc3220bb3220bc326a21bd3220bc3220bd326a21be3220bd3220be326a21bf3220be3220" - "bf326a21c03220bf3220" + "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" - "c43220c5326a21c63220" - "c53220c6326a21c73220c63220c7326a21c83220c73220c8326a21c93220c83220c9326a21ca3220c93220ca326a21" - "cb3220ca3220cb326a21" - "cc3220cb3220cc326a21cd3220cc3220cd326a21ce3220cd3220ce326a21cf3220ce3220cf326a21d03220cf3220d0" - "326a21d13220d03220d1" - "326a21d23220d13220d2326a21d33220d23220d3326a21d43220d33220d4326a21d53220d43220d5326a21d63220d5" - "3220d6326a21d73220d6" - "3220d7326a21d83220d73220d8326a21d93220d83220d9326a21da3220d93220da326a21db3220da3220db326a21dc" - "3220db3220dc326a21dd" - "3220dc3220dd326a21de3220dd3220de326a21df3220de3220df326a21e03220df3220e0326a21e13220e03220e132" - "6a21e23220e13220e232" - "6a21e33220e23220e3326a21e43220e33220e4326a21e53220e43220e5326a21e63220e53220e6326a21e73220e632" - "20e7326a21e83220e732" - "20e8326a21e93220e83220e9326a21ea3220e93220ea326a21eb3220ea3220eb326a21ec3220eb3220ec326a21ed32" - "20ec3220ed326a21ee32" - "20ed3220ee326a21ef3220ee3220ef326a21f03220ef3220f0326a21f13220f03220f1326a21f23220f13220f2326a" - "21f33220f23220f3326a" - "21f43220f33220f4326a21f53220f43220f5326a21f63220f53220f6326a21f73220f63220f7326a21f83220f73220" - "f8326a21f93220f83220" - "f9326a21fa3220f93220fa326a21fb3220fa3220fb326a21fc3220fb3220fc326a21fd3220fc3220fd326a21fe3220" - "fd3220fe326a21ff3220" - "fe3220ff326a21803320ff322080336a2181332080332081336a2182332081332082336a2183332082332083336a21" - "84332083332084336a21" - "85332084332085336a2186332085332086336a2187332086332087336a2188332087332088336a2189332088332089" - "336a218a33208933208a" - "336a218b33208a33208b336a218c33208b33208c336a218d33208c33208d336a218e33208d33208e336a218f33208e" - "33208f336a219033208f" - "332090336a2191332090332091336a2192332091332092336a2193332092332093336a2194332093332094336a2195" - "332094332095336a2196" - "332095332096336a2197332096332097336a2198332097332098336a2199332098332099336a219a33209933209a33" - "6a219b33209a33209b33" - "6a219c33209b33209c336a219d33209c33209d336a219e33209d33209e336a219f33209e33209f336a21a033209f33" - "20a0336a21a13320a033" - "20a1336a21a23320a13320a2336a21a33320a23320a3336a21a43320a33320a4336a21a53320a43320a5336a21a633" - "20a53320a6336a21a733" - "20a63320a7336a21a83320a73320a8336a21a93320a83320a9336a21aa3320a93320aa336a21ab3320aa3320ab336a" - "21ac3320ab3320ac336a" - "21ad3320ac3320ad336a21ae3320ad3320ae336a21af3320ae3320af336a21b03320af3320b0336a21b13320b03320" - "b1336a21b23320b13320" - "b2336a21b33320b23320b3336a21b43320b33320b4336a21b53320b43320b5336a21b63320b53320b6336a21b73320" - "b63320b7336a21b83320" - "b73320b8336a21b93320b83320b9336a21ba3320b93320ba336a21bb3320ba3320bb336a21bc3320bb3320bc336a21" - "bd3320bc3320bd336a21" - "be3320bd3320be336a21bf3320be3320bf336a21c03320bf3320c0336a21c13320c03320c1336a21c23320c13320c2" - "336a21c33320c23320c3" - "336a21c43320c33320c4336a21c53320c43320c5336a21c63320c53320c6336a21c73320c63320c7336a21c83320c7" - "3320c8336a21c93320c8" - "3320c9336a21ca3320c93320ca336a21cb3320ca3320cb336a21cc3320cb3320cc336a21cd3320cc3320cd336a21ce" - "3320cd3320ce336a21cf" - "3320ce3320cf336a21d03320cf3320d0336a21d13320d03320d1336a21d23320d13320d2336a21d33320d23320d333" - "6a21d43320d33320d433" - "6a21d53320d43320d5336a21d63320d53320d6336a21d73320d63320d7336a21d83320d73320d8336a21d93320d833" - "20d9336a21da3320d933" - "20da336a21db3320da3320db336a21dc3320db3320dc336a21dd3320dc3320dd336a21de3320dd3320de336a21df33" - "20de3320df336a21e033" - "20df3320e0336a21e13320e03320e1336a21e23320e13320e2336a21e33320e23320e3336a21e43320e33320e4336a" - "21e53320e43320e5336a" - "21e63320e53320e6336a21e73320e63320e7336a21e83320e73320e8336a21e93320e83320e9336a21ea3320e93320" - "ea336a21eb3320ea3320" - "eb336a21ec3320eb3320ec336a21ed3320ec3320ed336a21ee3320ed3320ee336a21ef3320ee3320ef336a21f03320" - "ef3320f0336a21f13320" - "f03320f1336a21f23320f13320f2336a21f33320f23320f3336a21f43320f33320f4336a21f53320f43320f5336a21" - "f63320f53320f6336a21" - "f73320f63320f7336a21f83320f73320f8336a21f93320f83320f9336a21fa3320f93320fa336a21fb3320fa3320fb" - "336a21fc3320fb3320fc" - "336a21fd3320fc3320fd336a21fe3320fd3320fe336a21ff3320fe3320ff336a21803420ff332080346a2181342080" - "342081346a2182342081" - "342082346a2183342082342083346a2184342083342084346a2185342084342085346a2186342085342086346a2187" - "342086342087346a2188" - "342087342088346a2189342088342089346a218a34208934208a346a218b34208a34208b346a218c34208b34208c34" - "6a218d34208c34208d34" - "6a218e34208d34208e346a218f34208e34208f346a219034208f342090346a2191342090342091346a219234209134" - "2092346a219334209234" - "2093346a2194342093342094346a2195342094342095346a2196342095342096346a2197342096342097346a219834" - "2097342098346a219934" - "2098342099346a219a34209934209a346a219b34209a34209b346a219c34209b34209c346a219d34209c34209d346a" - "219e34209d34209e346a" - "219f34209e34209f346a21a034209f3420a0346a21a13420a03420a1346a21a23420a13420a2346a21a33420a23420" - "a3346a21a43420a33420" - "a4346a21a53420a43420a5346a21a63420a53420a6346a21a73420a63420a7346a21a83420a73420a8346a21a93420" - "a83420a9346a21aa3420" - "a93420aa346a21ab3420aa3420ab346a21ac3420ab3420ac346a21ad3420ac3420ad346a21ae3420ad3420ae346a21" - "af3420ae3420af346a21" - "b03420af3420b0346a21b13420b03420b1346a21b23420b13420b2346a21b33420b23420b3346a21b43420b33420b4" - "346a21b53420b43420b5" - "346a21b63420b53420b6346a21b73420b63420b7346a21b83420b73420b8346a21b93420b83420b9346a21ba3420b9" - "3420ba346a21bb3420ba" - "3420bb346a21bc3420bb3420bc346a21bd3420bc3420bd346a21be3420bd3420be346a21bf3420be3420bf346a21c0" - "3420bf3420c0346a21c1" - "3420c03420c1346a21c23420c13420c2346a21c33420c23420c3346a21c43420c33420c4346a21c53420c43420c534" - "6a21c63420c53420c634" - "6a21c73420c63420c7346a21c83420c73420c8346a21c93420c83420c9346a21ca3420c93420ca346a21cb3420ca34" - "20cb346a21cc3420cb34" + "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" - "20d03420d1346a21d234" - "20d13420d2346a21d33420d23420d3346a21d43420d33420d4346a21d53420d43420d5346a21d63420d53420d6346a" - "21d73420d63420d7346a" - "21d83420d73420d8346a21d93420d83420d9346a21da3420d93420da346a21db3420da3420db346a21dc3420db3420" - "dc346a21dd3420dc3420" - "dd346a21de3420dd3420de346a21df3420de3420df346a21e03420df3420e0346a21e13420e03420e1346a21e23420" - "e13420e2346a21e33420" - "e23420e3346a21e43420e33420e4346a21e53420e43420e5346a21e63420e53420e6346a21e73420e63420e7346a21" - "e83420e73420e8346a21" - "e93420e83420e9346a21ea3420e93420ea346a21eb3420ea3420eb346a21ec3420eb3420ec346a21ed3420ec3420ed" - "346a21ee3420ed3420ee" - "346a21ef3420ee3420ef346a21f03420ef3420f0346a21f13420f03420f1346a21f23420f13420f2346a21f33420f2" - "3420f3346a21f43420f3" - "3420f4346a21f53420f43420f5346a21f63420f53420f6346a21f73420f63420f7346a21f83420f73420f8346a21f9" - "3420f83420f9346a21fa" - "3420f93420fa346a21fb3420fa3420fb346a21fc3420fb3420fc346a21fd3420fc3420fd346a21fe3420fd3420fe34" - "6a21ff3420fe3420ff34" - "6a21803520ff342080356a2181352080352081356a2182352081352082356a2183352082352083356a218435208335" - "2084356a218535208435" - "2085356a2186352085352086356a2187352086352087356a2188352087352088356a2189352088352089356a218a35" - "208935208a356a218b35" - "208a35208b356a218c35208b35208c356a218d35208c35208d356a218e35208d35208e356a218f35208e35208f356a" - "219035208f352090356a" - "2191352090352091356a2192352091352092356a2193352092352093356a2194352093352094356a21953520943520" - "95356a21963520953520" - "96356a2197352096352097356a2198352097352098356a2199352098352099356a219a35209935209a356a219b3520" - "9a35209b356a219c3520" - "9b35209c356a219d35209c35209d356a219e35209d35209e356a219f35209e35209f356a21a035209f3520a0356a21" - "a13520a03520a1356a21" - "a23520a13520a2356a21a33520a23520a3356a21a43520a33520a4356a21a53520a43520a5356a21a63520a53520a6" - "356a21a73520a63520a7" - "356a21a83520a73520a8356a21a93520a83520a9356a21aa3520a93520aa356a21ab3520aa3520ab356a21ac3520ab" - "3520ac356a21ad3520ac" - "3520ad356a21ae3520ad3520ae356a21af3520ae3520af356a21b03520af3520b0356a21b13520b03520b1356a21b2" - "3520b13520b2356a21b3" - "3520b23520b3356a21b43520b33520b4356a21b53520b43520b5356a21b63520b53520b6356a21b73520b63520b735" - "6a21b83520b73520b835" - "6a21b93520b83520b9356a21ba3520b93520ba356a21bb3520ba3520bb356a21bc3520bb3520bc356a21bd3520bc35" - "20bd356a21be3520bd35" - "20be356a21bf3520be3520bf356a21c03520bf3520c0356a21c13520c03520c1356a21c23520c13520c2356a21c335" - "20c23520c3356a21c435" - "20c33520c4356a21c53520c43520c5356a21c63520c53520c6356a21c73520c63520c7356a21c83520c73520c8356a" - "21c93520c83520c9356a" - "21ca3520c93520ca356a21cb3520ca3520cb356a21cc3520cb3520cc356a21cd3520cc3520cd356a21ce3520cd3520" - "ce356a21cf3520ce3520" - "cf356a21d03520cf3520d0356a21d13520d03520d1356a21d23520d13520d2356a21d33520d23520d3356a21d43520" - "d33520d4356a21d53520" - "d43520d5356a21d63520d53520d6356a21d73520d63520d7356a21d83520d73520d8356a21d93520d83520d9356a21" - "da3520d93520da356a21" - "db3520da3520db356a21dc3520db3520dc356a21dd3520dc3520dd356a21de3520dd3520de356a21df3520de3520df" - "356a21e03520df3520e0" - "356a21e13520e03520e1356a21e23520e13520e2356a21e33520e23520e3356a21e43520e33520e4356a21e53520e4" - "3520e5356a21e63520e5" - "3520e6356a21e73520e63520e7356a21e83520e73520e8356a21e93520e83520e9356a21ea3520e93520ea356a21eb" - "3520ea3520eb356a21ec" - "3520eb3520ec356a21ed3520ec3520ed356a21ee3520ed3520ee356a21ef3520ee3520ef356a21f03520ef3520f035" - "6a21f13520f03520f135" - "6a21f23520f13520f2356a21f33520f23520f3356a21f43520f33520f4356a21f53520f43520f5356a21f63520f535" - "20f6356a21f73520f635" - "20f7356a21f83520f73520f8356a21f93520f83520f9356a21fa3520f93520fa356a21fb3520fa3520fb356a21fc35" - "20fb3520fc356a21fd35" - "20fc3520fd356a21fe3520fd3520fe356a21ff3520fe3520ff356a21803620ff352080366a2181362080362081366a" - "2182362081362082366a" - "2183362082362083366a2184362083362084366a2185362084362085366a2186362085362086366a21873620863620" - "87366a21883620873620" - "88366a2189362088362089366a218a36208936208a366a218b36208a36208b366a218c36208b36208c366a218d3620" - "8c36208d366a218e3620" - "8d36208e366a218f36208e36208f366a219036208f362090366a2191362090362091366a2192362091362092366a21" - "93362092362093366a21" - "94362093362094366a2195362094362095366a2196362095362096366a2197362096362097366a2198362097362098" - "366a2199362098362099" - "366a219a36209936209a366a219b36209a36209b366a219c36209b36209c366a219d36209c36209d366a219e36209d" - "36209e366a219f36209e" - "36209f366a21a036209f3620a0366a21a13620a03620a1366a21a23620a13620a2366a21a33620a23620a3366a21a4" - "3620a33620a4366a21a5" - "3620a43620a5366a21a63620a53620a6366a21a73620a63620a7366a21a83620a73620a8366a21a93620a83620a936" - "6a21aa3620a93620aa36" - "6a21ab3620aa3620ab366a21ac3620ab3620ac366a21ad3620ac3620ad366a21ae3620ad3620ae366a21af3620ae36" - "20af366a21b03620af36" - "20b0366a21b13620b03620b1366a21b23620b13620b2366a21b33620b23620b3366a21b43620b33620b4366a21b536" - "20b43620b5366a21b636" - "20b53620b6366a21b73620b63620b7366a21b83620b73620b8366a21b93620b83620b9366a21ba3620b93620ba366a" - "21bb3620ba3620bb366a" - "21bc3620bb3620bc366a21bd3620bc3620bd366a21be3620bd3620be366a21bf3620be3620bf366a21c03620bf3620" - "c0366a21c13620c03620" - "c1366a21c23620c13620c2366a21c33620c23620c3366a21c43620c33620c4366a21c53620c43620c5366a21c63620" - "c53620c6366a21c73620" - "c63620c7366a21c83620c73620c8366a21c93620c83620c9366a21ca3620c93620ca366a21cb3620ca3620cb366a21" - "cc3620cb3620cc366a21" - "cd3620cc3620cd366a21ce3620cd3620ce366a21cf3620ce3620cf366a21d03620cf3620d0366a21d13620d03620d1" - "366a21d23620d13620d2" - "366a21d33620d23620d3366a21d43620d33620d4366a21d53620d43620d5366a21d63620d53620d6366a21d73620d6" - "3620d7366a21d83620d7" + "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" - "3620dc3620dd366a21de" - "3620dd3620de366a21df3620de3620df366a21e03620df3620e0366a21e13620e03620e1366a21e23620e13620e236" - "6a21e33620e23620e336" - "6a21e43620e33620e4366a21e53620e43620e5366a21e63620e53620e6366a21e73620e63620e7366a21e83620e736" - "20e8366a21e93620e836" - "20e9366a21ea3620e93620ea366a21eb3620ea3620eb366a21ec3620eb3620ec366a21ed3620ec3620ed366a21ee36" - "20ed3620ee366a21ef36" - "20ee3620ef366a21f03620ef3620f0366a21f13620f03620f1366a21f23620f13620f2366a21f33620f23620f3366a" - "21f43620f33620f4366a" - "21f53620f43620f5366a21f63620f53620f6366a21f73620f63620f7366a21f83620f73620f8366a21f93620f83620" - "f9366a21fa3620f93620" - "fa366a21fb3620fa3620fb366a21fc3620fb3620fc366a21fd3620fc3620fd366a21fe3620fd3620fe366a21ff3620" - "fe3620ff366a21803720" - "ff362080376a2181372080372081376a2182372081372082376a2183372082372083376a2184372083372084376a21" - "85372084372085376a21" - "86372085372086376a2187372086372087376a2188372087372088376a2189372088372089376a218a37208937208a" - "376a218b37208a37208b" - "376a218c37208b37208c376a218d37208c37208d376a218e37208d37208e376a218f37208e37208f376a219037208f" - "372090376a2191372090" - "372091376a2192372091372092376a2193372092372093376a2194372093372094376a2195372094372095376a2196" - "372095372096376a2197" - "372096372097376a2198372097372098376a2199372098372099376a219a37209937209a376a219b37209a37209b37" - "6a219c37209b37209c37" - "6a219d37209c37209d376a219e37209d37209e376a219f37209e37209f376a21a037209f3720a0376a21a13720a037" - "20a1376a21a23720a137" - "20a2376a21a33720a23720a3376a21a43720a33720a4376a21a53720a43720a5376a21a63720a53720a6376a21a737" - "20a63720a7376a21a837" - "20a73720a8376a21a93720a83720a9376a21aa3720a93720aa376a21ab3720aa3720ab376a21ac3720ab3720ac376a" - "21ad3720ac3720ad376a" - "21ae3720ad3720ae376a21af3720ae3720af376a21b03720af3720b0376a21b13720b03720b1376a21b23720b13720" - "b2376a21b33720b23720" - "b3376a21b43720b33720b4376a21b53720b43720b5376a21b63720b53720b6376a21b73720b63720b7376a21b83720" - "b73720b8376a21b93720" - "b83720b9376a21ba3720b93720ba376a21bb3720ba3720bb376a21bc3720bb3720bc376a21bd3720bc3720bd376a21" - "be3720bd3720be376a21" - "bf3720be3720bf376a21c03720bf3720c0376a21c13720c03720c1376a21c23720c13720c2376a21c33720c23720c3" - "376a21c43720c33720c4" - "376a21c53720c43720c5376a21c63720c53720c6376a21c73720c63720c7376a21c83720c73720c8376a21c93720c8" - "3720c9376a21ca3720c9" - "3720ca376a21cb3720ca3720cb376a21cc3720cb3720cc376a21cd3720cc3720cd376a21ce3720cd3720ce376a21cf" - "3720ce3720cf376a21d0" - "3720cf3720d0376a21d13720d03720d1376a21d23720d13720d2376a21d33720d23720d3376a21d43720d33720d437" - "6a21d53720d43720d537" - "6a21d63720d53720d6376a21d73720d63720d7376a21d83720d73720d8376a21d93720d83720d9376a21da3720d937" - "20da376a21db3720da37" - "20db376a21dc3720db3720dc376a21dd3720dc3720dd376a21de3720dd3720de376a21df3720de3720df376a21e037" - "20df3720e0376a21e137" - "20e03720e1376a21e23720e13720e2376a21e33720e23720e3376a21e43720e33720e4376a21e53720e43720e5376a" - "21e63720e53720e6376a" - "21e73720e63720e7376a21e83720e73720e8376a21e93720e83720e9376a21ea3720e93720ea376a21eb3720ea3720" - "eb376a21ec3720eb3720" - "ec376a21ed3720ec3720ed376a21ee3720ed3720ee376a21ef3720ee3720ef376a21f03720ef3720f0376a21f13720" - "f03720f1376a21f23720" - "f13720f2376a21f33720f23720f3376a21f43720f33720f4376a21f53720f43720f5376a21f63720f53720f6376a21" - "f73720f63720f7376a21" - "f83720f73720f8376a21f93720f83720f9376a21fa3720f93720fa376a21fb3720fa3720fb376a21fc3720fb3720fc" - "376a21fd3720fc3720fd" - "376a21fe3720fd3720fe376a21ff3720fe3720ff376a21803820ff372080386a2181382080382081386a2182382081" - "382082386a2183382082" - "382083386a2184382083382084386a2185382084382085386a2186382085382086386a2187382086382087386a2188" - "382087382088386a2189" - "382088382089386a218a38208938208a386a218b38208a38208b386a218c38208b38208c386a218d38208c38208d38" - "6a218e38208d38208e38" - "6a218f38208e38208f386a219038208f382090386a2191382090382091386a2192382091382092386a219338209238" - "2093386a219438209338" - "2094386a2195382094382095386a2196382095382096386a2197382096382097386a2198382097382098386a219938" - "2098382099386a219a38" - "209938209a386a219b38209a38209b386a219c38209b38209c386a219d38209c38209d386a219e38209d38209e386a" - "219f38209e38209f386a" - "21a038209f3820a0386a21a13820a03820a1386a21a23820a13820a2386a21a33820a23820a3386a21a43820a33820" - "a4386a21a53820a43820" - "a5386a21a63820a53820a6386a21a73820a63820a7386a21a83820a73820a8386a21a93820a83820a9386a21aa3820" - "a93820aa386a21ab3820" - "aa3820ab386a21ac3820ab3820ac386a21ad3820ac3820ad386a21ae3820ad3820ae386a21af3820ae3820af386a21" - "b03820af3820b0386a21" - "b13820b03820b1386a21b23820b13820b2386a21b33820b23820b3386a21b43820b33820b4386a21b53820b43820b5" - "386a21b63820b53820b6" - "386a21b73820b63820b7386a21b83820b73820b8386a21b93820b83820b9386a21ba3820b93820ba386a21bb3820ba" - "3820bb386a21bc3820bb" - "3820bc386a21bd3820bc3820bd386a21be3820bd3820be386a21bf3820be3820bf386a21c03820bf3820c0386a21c1" - "3820c03820c1386a21c2" - "3820c13820c2386a21c33820c23820c3386a21c43820c33820c4386a21c53820c43820c5386a21c63820c53820c638" - "6a21c73820c63820c738" - "6a21c83820c73820c8386a21c93820c83820c9386a21ca3820c93820ca386a21cb3820ca3820cb386a21cc3820cb38" - "20cc386a21cd3820cc38" - "20cd386a21ce3820cd3820ce386a21cf3820ce3820cf386a21d03820cf3820d0386a21d13820d03820d1386a21d238" - "20d13820d2386a21d338" - "20d23820d3386a21d43820d33820d4386a21d53820d43820d5386a21d63820d53820d6386a21d73820d63820d7386a" - "21d83820d73820d8386a" - "21d93820d83820d9386a21da3820d93820da386a21db3820da3820db386a21dc3820db3820dc386a21dd3820dc3820" - "dd386a21de3820dd3820" - "de386a21df3820de3820df386a21e03820df3820e0386a21e13820e03820e1386a21e23820e13820e2386a21e33820" - "e23820e3386a21e43820" + "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" - "e93820e83820e9386a21" - "ea3820e93820ea386a21eb3820ea3820eb386a21ec3820eb3820ec386a21ed3820ec3820ed386a21ee3820ed3820ee" - "386a21ef3820ee3820ef" - "386a21f03820ef3820f0386a21f13820f03820f1386a21f23820f13820f2386a21f33820f23820f3386a21f43820f3" - "3820f4386a21f53820f4" - "3820f5386a21f63820f53820f6386a21f73820f63820f7386a21f83820f73820f8386a21f93820f83820f9386a21fa" - "3820f93820fa386a21fb" - "3820fa3820fb386a21fc3820fb3820fc386a21fd3820fc3820fd386a21fe3820fd3820fe386a21ff3820fe3820ff38" - "6a21803920ff38208039" - "6a2181392080392081396a2182392081392082396a2183392082392083396a2184392083392084396a218539208439" - "2085396a218639208539" - "2086396a2187392086392087396a2188392087392088396a2189392088392089396a218a39208939208a396a218b39" - "208a39208b396a218c39" - "208b39208c396a218d39208c39208d396a218e39208d39208e396a218f39208e39208f396a219039208f392090396a" - "2191392090392091396a" - "2192392091392092396a2193392092392093396a2194392093392094396a2195392094392095396a21963920953920" - "96396a21973920963920" - "97396a2198392097392098396a2199392098392099396a219a39209939209a396a219b39209a39209b396a219c3920" - "9b39209c396a219d3920" - "9c39209d396a219e39209d39209e396a219f39209e39209f396a21a039209f3920a0396a21a13920a03920a1396a21" - "a23920a13920a2396a21" - "a33920a23920a3396a21a43920a33920a4396a21a53920a43920a5396a21a63920a53920a6396a21a73920a63920a7" - "396a21a83920a73920a8" - "396a21a93920a83920a9396a21aa3920a93920aa396a21ab3920aa3920ab396a21ac3920ab3920ac396a21ad3920ac" - "3920ad396a21ae3920ad" - "3920ae396a21af3920ae3920af396a21b03920af3920b0396a21b13920b03920b1396a21b23920b13920b2396a21b3" - "3920b23920b3396a21b4" - "3920b33920b4396a21b53920b43920b5396a21b63920b53920b6396a21b73920b63920b7396a21b83920b73920b839" - "6a21b93920b83920b939" - "6a21ba3920b93920ba396a21bb3920ba3920bb396a21bc3920bb3920bc396a21bd3920bc3920bd396a21be3920bd39" - "20be396a21bf3920be39" - "20bf396a21c03920bf3920c0396a21c13920c03920c1396a21c23920c13920c2396a21c33920c23920c3396a21c439" - "20c33920c4396a21c539" - "20c43920c5396a21c63920c53920c6396a21c73920c63920c7396a21c83920c73920c8396a21c93920c83920c9396a" - "21ca3920c93920ca396a" - "21cb3920ca3920cb396a21cc3920cb3920cc396a21cd3920cc3920cd396a21ce3920cd3920ce396a21cf3920ce3920" - "cf396a21d03920cf3920" - "d0396a21d13920d03920d1396a21d23920d13920d2396a21d33920d23920d3396a21d43920d33920d4396a21d53920" - "d43920d5396a21d63920" - "d53920d6396a21d73920d63920d7396a21d83920d73920d8396a21d93920d83920d9396a21da3920d93920da396a21" - "db3920da3920db396a21" - "dc3920db3920dc396a21dd3920dc3920dd396a21de3920dd3920de396a21df3920de3920df396a21e03920df3920e0" - "396a21e13920e03920e1" - "396a21e23920e13920e2396a21e33920e23920e3396a21e43920e33920e4396a21e53920e43920e5396a21e63920e5" - "3920e6396a21e73920e6" - "3920e7396a21e83920e73920e8396a21e93920e83920e9396a21ea3920e93920ea396a21eb3920ea3920eb396a21ec" - "3920eb3920ec396a21ed" - "3920ec3920ed396a21ee3920ed3920ee396a21ef3920ee3920ef396a21f03920ef3920f0396a21f13920f03920f139" - "6a21f23920f13920f239" - "6a21f33920f23920f3396a21f43920f33920f4396a21f53920f43920f5396a21f63920f53920f6396a21f73920f639" - "20f7396a21f83920f739" - "20f8396a21f93920f83920f9396a21fa3920f93920fa396a21fb3920fa3920fb396a21fc3920fb3920fc396a21fd39" - "20fc3920fd396a21fe39" - "20fd3920fe396a21ff3920fe3920ff396a21803a20ff3920803a6a21813a20803a20813a6a21823a20813a20823a6a" - "21833a20823a20833a6a" - "21843a20833a20843a6a21853a20843a20853a6a21863a20853a20863a6a21873a20863a20873a6a21883a20873a20" - "883a6a21893a20883a20" - "893a6a218a3a20893a208a3a6a218b3a208a3a208b3a6a218c3a208b3a208c3a6a218d3a208c3a208d3a6a218e3a20" - "8d3a208e3a6a218f3a20" - "8e3a208f3a6a21903a208f3a20903a6a21913a20903a20913a6a21923a20913a20923a6a21933a20923a20933a6a21" - "943a20933a20943a6a21" - "953a20943a20953a6a21963a20953a20963a6a21973a20963a20973a6a21983a20973a20983a6a21993a20983a2099" - "3a6a219a3a20993a209a" - "3a6a219b3a209a3a209b3a6a219c3a209b3a209c3a6a219d3a209c3a209d3a6a219e3a209d3a209e3a6a219f3a209e" - "3a209f3a6a21a03a209f" - "3a20a03a6a21a13a20a03a20a13a6a21a23a20a13a20a23a6a21a33a20a23a20a33a6a21a43a20a33a20a43a6a21a5" - "3a20a43a20a53a6a21a6" - "3a20a53a20a63a6a21a73a20a63a20a73a6a21a83a20a73a20a83a6a21a93a20a83a20a93a6a21aa3a20a93a20aa3a" - "6a21ab3a20aa3a20ab3a" - "6a21ac3a20ab3a20ac3a6a21ad3a20ac3a20ad3a6a21ae3a20ad3a20ae3a6a21af3a20ae3a20af3a6a21b03a20af3a" - "20b03a6a21b13a20b03a" - "20b13a6a21b23a20b13a20b23a6a21b33a20b23a20b33a6a21b43a20b33a20b43a6a21b53a20b43a20b53a6a21b63a" - "20b53a20b63a6a21b73a" - "20b63a20b73a6a21b83a20b73a20b83a6a21b93a20b83a20b93a6a21ba3a20b93a20ba3a6a21bb3a20ba3a20bb3a6a" - "21bc3a20bb3a20bc3a6a" - "21bd3a20bc3a20bd3a6a21be3a20bd3a20be3a6a21bf3a20be3a20bf3a6a21c03a20bf3a20c03a6a21c13a20c03a20" - "c13a6a21c23a20c13a20" - "c23a6a21c33a20c23a20c33a6a21c43a20c33a20c43a6a21c53a20c43a20c53a6a21c63a20c53a20c63a6a21c73a20" - "c63a20c73a6a21c83a20" - "c73a20c83a6a21c93a20c83a20c93a6a21ca3a20c93a20ca3a6a21cb3a20ca3a20cb3a6a21cc3a20cb3a20cc3a6a21" - "cd3a20cc3a20cd3a6a21" - "ce3a20cd3a20ce3a6a21cf3a20ce3a20cf3a6a21d03a20cf3a20d03a6a21d13a20d03a20d13a6a21d23a20d13a20d2" - "3a6a21d33a20d23a20d3" - "3a6a21d43a20d33a20d43a6a21d53a20d43a20d53a6a21d63a20d53a20d63a6a21d73a20d63a20d73a6a21d83a20d7" - "3a20d83a6a21d93a20d8" - "3a20d93a6a21da3a20d93a20da3a6a21db3a20da3a20db3a6a21dc3a20db3a20dc3a6a21dd3a20dc3a20dd3a6a21de" - "3a20dd3a20de3a6a21df" - "3a20de3a20df3a6a21e03a20df3a20e03a6a21e13a20e03a20e13a6a21e23a20e13a20e23a6a21e33a20e23a20e33a" - "6a21e43a20e33a20e43a" - "6a21e53a20e43a20e53a6a21e63a20e53a20e63a6a21e73a20e63a20e73a6a21e83a20e73a20e83a6a21e93a20e83a" - "20e93a6a21ea3a20e93a" - "20ea3a6a21eb3a20ea3a20eb3a6a21ec3a20eb3a20ec3a6a21ed3a20ec3a20ed3a6a21ee3a20ed3a20ee3a6a21ef3a" - "20ee3a20ef3a6a21f03a" + "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" - "21f53a20f43a20f53a6a" - "21f63a20f53a20f63a6a21f73a20f63a20f73a6a21f83a20f73a20f83a6a21f93a20f83a20f93a6a21fa3a20f93a20" - "fa3a6a21fb3a20fa3a20" - "fb3a6a21fc3a20fb3a20fc3a6a21fd3a20fc3a20fd3a6a21fe3a20fd3a20fe3a6a21ff3a20fe3a20ff3a6a21803b20" - "ff3a20803b6a21813b20" - "803b20813b6a21823b20813b20823b6a21833b20823b20833b6a21843b20833b20843b6a21853b20843b20853b6a21" - "863b20853b20863b6a21" - "873b20863b20873b6a21883b20873b20883b6a21893b20883b20893b6a218a3b20893b208a3b6a218b3b208a3b208b" - "3b6a218c3b208b3b208c" - "3b6a218d3b208c3b208d3b6a218e3b208d3b208e3b6a218f3b208e3b208f3b6a21903b208f3b20903b6a21913b2090" - "3b20913b6a21923b2091" - "3b20923b6a21933b20923b20933b6a21943b20933b20943b6a21953b20943b20953b6a21963b20953b20963b6a2197" - "3b20963b20973b6a2198" - "3b20973b20983b6a21993b20983b20993b6a219a3b20993b209a3b6a219b3b209a3b209b3b6a219c3b209b3b209c3b" - "6a219d3b209c3b209d3b" - "6a219e3b209d3b209e3b6a219f3b209e3b209f3b6a21a03b209f3b20a03b6a21a13b20a03b20a13b6a21a23b20a13b" - "20a23b6a21a33b20a23b" - "20a33b6a21a43b20a33b20a43b6a21a53b20a43b20a53b6a21a63b20a53b20a63b6a21a73b20a63b20a73b6a21a83b" - "20a73b20a83b6a21a93b" - "20a83b20a93b6a21aa3b20a93b20aa3b6a21ab3b20aa3b20ab3b6a21ac3b20ab3b20ac3b6a21ad3b20ac3b20ad3b6a" - "21ae3b20ad3b20ae3b6a" - "21af3b20ae3b20af3b6a21b03b20af3b20b03b6a21b13b20b03b20b13b6a21b23b20b13b20b23b6a21b33b20b23b20" - "b33b6a21b43b20b33b20" - "b43b6a21b53b20b43b20b53b6a21b63b20b53b20b63b6a21b73b20b63b20b73b6a21b83b20b73b20b83b6a21b93b20" - "b83b20b93b6a21ba3b20" - "b93b20ba3b6a21bb3b20ba3b20bb3b6a21bc3b20bb3b20bc3b6a21bd3b20bc3b20bd3b6a21be3b20bd3b20be3b6a21" - "bf3b20be3b20bf3b6a21" - "c03b20bf3b20c03b6a21c13b20c03b20c13b6a21c23b20c13b20c23b6a21c33b20c23b20c33b6a21c43b20c33b20c4" - "3b6a21c53b20c43b20c5" - "3b6a21c63b20c53b20c63b6a21c73b20c63b20c73b6a21c83b20c73b20c83b6a21c93b20c83b20c93b6a21ca3b20c9" - "3b20ca3b6a21cb3b20ca" - "3b20cb3b6a21cc3b20cb3b20cc3b6a21cd3b20cc3b20cd3b6a21ce3b20cd3b20ce3b6a21cf3b20ce3b20cf3b6a21d0" - "3b20cf3b20d03b6a21d1" - "3b20d03b20d13b6a21d23b20d13b20d23b6a21d33b20d23b20d33b6a21d43b20d33b20d43b6a21d53b20d43b20d53b" - "6a21d63b20d53b20d63b" - "6a21d73b20d63b20d73b6a21d83b20d73b20d83b6a21d93b20d83b20d93b6a21da3b20d93b20da3b6a21db3b20da3b" - "20db3b6a21dc3b20db3b" - "20dc3b6a21dd3b20dc3b20dd3b6a21de3b20dd3b20de3b6a21df3b20de3b20df3b6a21e03b20df3b20e03b6a21e13b" - "20e03b20e13b6a21e23b" - "20e13b20e23b6a21e33b20e23b20e33b6a21e43b20e33b20e43b6a21e53b20e43b20e53b6a21e63b20e53b20e63b6a" - "21e73b20e63b20e73b6a" - "21e83b20e73b20e83b6a21e93b20e83b20e93b6a21ea3b20e93b20ea3b6a21eb3b20ea3b20eb3b6a21ec3b20eb3b20" - "ec3b6a21ed3b20ec3b20" - "ed3b6a21ee3b20ed3b20ee3b6a21ef3b20ee3b20ef3b6a21f03b20ef3b20f03b6a21f13b20f03b20f13b6a21f23b20" - "f13b20f23b6a21f33b20" - "f23b20f33b6a21f43b20f33b20f43b6a21f53b20f43b20f53b6a21f63b20f53b20f63b6a21f73b20f63b20f73b6a21" - "f83b20f73b20f83b6a21" - "f93b20f83b20f93b6a21fa3b20f93b20fa3b6a21fb3b20fa3b20fb3b6a21fc3b20fb3b20fc3b6a21fd3b20fc3b20fd" - "3b6a21fe3b20fd3b20fe" - "3b6a21ff3b20fe3b20ff3b6a21803c20ff3b20803c6a21813c20803c20813c6a21823c20813c20823c6a21833c2082" - "3c20833c6a21843c2083" - "3c20843c6a21853c20843c20853c6a21863c20853c20863c6a21873c20863c20873c6a21883c20873c20883c6a2189" - "3c20883c20893c6a218a" - "3c20893c208a3c6a218b3c208a3c208b3c6a218c3c208b3c208c3c6a218d3c208c3c208d3c6a218e3c208d3c208e3c" - "6a218f3c208e3c208f3c" - "6a21903c208f3c20903c6a21913c20903c20913c6a21923c20913c20923c6a21933c20923c20933c6a21943c20933c" - "20943c6a21953c20943c" - "20953c6a21963c20953c20963c6a21973c20963c20973c6a21983c20973c20983c6a21993c20983c20993c6a219a3c" - "20993c209a3c6a219b3c" - "209a3c209b3c6a219c3c209b3c209c3c6a219d3c209c3c209d3c6a219e3c209d3c209e3c6a219f3c209e3c209f3c6a" - "21a03c209f3c20a03c6a" - "21a13c20a03c20a13c6a21a23c20a13c20a23c6a21a33c20a23c20a33c6a21a43c20a33c20a43c6a21a53c20a43c20" - "a53c6a21a63c20a53c20" - "a63c6a21a73c20a63c20a73c6a21a83c20a73c20a83c6a21a93c20a83c20a93c6a21aa3c20a93c20aa3c6a21ab3c20" - "aa3c20ab3c6a21ac3c20" - "ab3c20ac3c6a21ad3c20ac3c20ad3c6a21ae3c20ad3c20ae3c6a21af3c20ae3c20af3c6a21b03c20af3c20b03c6a21" - "b13c20b03c20b13c6a21" - "b23c20b13c20b23c6a21b33c20b23c20b33c6a21b43c20b33c20b43c6a21b53c20b43c20b53c6a21b63c20b53c20b6" - "3c6a21b73c20b63c20b7" - "3c6a21b83c20b73c20b83c6a21b93c20b83c20b93c6a21ba3c20b93c20ba3c6a21bb3c20ba3c20bb3c6a21bc3c20bb" - "3c20bc3c6a21bd3c20bc" - "3c20bd3c6a21be3c20bd3c20be3c6a21bf3c20be3c20bf3c6a21c03c20bf3c20c03c6a21c13c20c03c20c13c6a21c2" - "3c20c13c20c23c6a21c3" - "3c20c23c20c33c6a21c43c20c33c20c43c6a21c53c20c43c20c53c6a21c63c20c53c20c63c6a21c73c20c63c20c73c" - "6a21c83c20c73c20c83c" - "6a21c93c20c83c20c93c6a21ca3c20c93c20ca3c6a21cb3c20ca3c20cb3c6a21cc3c20cb3c20cc3c6a21cd3c20cc3c" - "20cd3c6a21ce3c20cd3c" - "20ce3c6a21cf3c20ce3c20cf3c6a21d03c20cf3c20d03c6a21d13c20d03c20d13c6a21d23c20d13c20d23c6a21d33c" - "20d23c20d33c6a21d43c" - "20d33c20d43c6a21d53c20d43c20d53c6a21d63c20d53c20d63c6a21d73c20d63c20d73c6a21d83c20d73c20d83c6a" - "21d93c20d83c20d93c6a" - "21da3c20d93c20da3c6a21db3c20da3c20db3c6a21dc3c20db3c20dc3c6a21dd3c20dc3c20dd3c6a21de3c20dd3c20" - "de3c6a21df3c20de3c20" - "df3c6a21e03c20df3c20e03c6a21e13c20e03c20e13c6a21e23c20e13c20e23c6a21e33c20e23c20e33c6a21e43c20" - "e33c20e43c6a21e53c20" - "e43c20e53c6a21e63c20e53c20e63c6a21e73c20e63c20e73c6a21e83c20e73c20e83c6a21e93c20e83c20e93c6a21" - "ea3c20e93c20ea3c6a21" - "eb3c20ea3c20eb3c6a21ec3c20eb3c20ec3c6a21ed3c20ec3c20ed3c6a21ee3c20ed3c20ee3c6a21ef3c20ee3c20ef" - "3c6a21f03c20ef3c20f0" - "3c6a21f13c20f03c20f13c6a21f23c20f13c20f23c6a21f33c20f23c20f33c6a21f43c20f33c20f43c6a21f53c20f4" - "3c20f53c6a21f63c20f5" - "3c20f63c6a21f73c20f63c20f73c6a21f83c20f73c20f83c6a21f93c20f83c20f93c6a21fa3c20f93c20fa3c6a21fb" - "3c20fa3c20fb3c6a21fc" + "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" - "6a21813d20803d20813d" - "6a21823d20813d20823d6a21833d20823d20833d6a21843d20833d20843d6a21853d20843d20853d6a21863d20853d" - "20863d6a21873d20863d" - "20873d6a21883d20873d20883d6a21893d20883d20893d6a218a3d20893d208a3d6a218b3d208a3d208b3d6a218c3d" - "208b3d208c3d6a218d3d" - "208c3d208d3d6a218e3d208d3d208e3d6a218f3d208e3d208f3d6a21903d208f3d20903d6a21913d20903d20913d6a" - "21923d20913d20923d6a" - "21933d20923d20933d6a21943d20933d20943d6a21953d20943d20953d6a21963d20953d20963d6a21973d20963d20" - "973d6a21983d20973d20" - "983d6a21993d20983d20993d6a219a3d20993d209a3d6a219b3d209a3d209b3d6a219c3d209b3d209c3d6a219d3d20" - "9c3d209d3d6a219e3d20" - "9d3d209e3d6a219f3d209e3d209f3d6a21a03d209f3d20a03d6a21a13d20a03d20a13d6a21a23d20a13d20a23d6a21" - "a33d20a23d20a33d6a21" - "a43d20a33d20a43d6a21a53d20a43d20a53d6a21a63d20a53d20a63d6a21a73d20a63d20a73d6a21a83d20a73d20a8" - "3d6a21a93d20a83d20a9" - "3d6a21aa3d20a93d20aa3d6a21ab3d20aa3d20ab3d6a21ac3d20ab3d20ac3d6a21ad3d20ac3d20ad3d6a21ae3d20ad" - "3d20ae3d6a21af3d20ae" - "3d20af3d6a21b03d20af3d20b03d6a21b13d20b03d20b13d6a21b23d20b13d20b23d6a21b33d20b23d20b33d6a21b4" - "3d20b33d20b43d6a21b5" - "3d20b43d20b53d6a21b63d20b53d20b63d6a21b73d20b63d20b73d6a21b83d20b73d20b83d6a21b93d20b83d20b93d" - "6a21ba3d20b93d20ba3d" - "6a21bb3d20ba3d20bb3d6a21bc3d20bb3d20bc3d6a21bd3d20bc3d20bd3d6a21be3d20bd3d20be3d6a21bf3d20be3d" - "20bf3d6a21c03d20bf3d" - "20c03d6a21c13d20c03d20c13d6a21c23d20c13d20c23d6a21c33d20c23d20c33d6a21c43d20c33d20c43d6a21c53d" - "20c43d20c53d6a21c63d" - "20c53d20c63d6a21c73d20c63d20c73d6a21c83d20c73d20c83d6a21c93d20c83d20c93d6a21ca3d20c93d20ca3d6a" - "21cb3d20ca3d20cb3d6a" - "21cc3d20cb3d20cc3d6a21cd3d20cc3d20cd3d6a21ce3d20cd3d20ce3d6a21cf3d20ce3d20cf3d6a21d03d20cf3d20" - "d03d6a21d13d20d03d20" - "d13d6a21d23d20d13d20d23d6a21d33d20d23d20d33d6a21d43d20d33d20d43d6a21d53d20d43d20d53d6a21d63d20" - "d53d20d63d6a21d73d20" - "d63d20d73d6a21d83d20d73d20d83d6a21d93d20d83d20d93d6a21da3d20d93d20da3d6a21db3d20da3d20db3d6a21" - "dc3d20db3d20dc3d6a21" - "dd3d20dc3d20dd3d6a21de3d20dd3d20de3d6a21df3d20de3d20df3d6a21e03d20df3d20e03d6a21e13d20e03d20e1" - "3d6a21e23d20e13d20e2" - "3d6a21e33d20e23d20e33d6a21e43d20e33d20e43d6a21e53d20e43d20e53d6a21e63d20e53d20e63d6a21e73d20e6" - "3d20e73d6a21e83d20e7" - "3d20e83d6a21e93d20e83d20e93d6a21ea3d20e93d20ea3d6a21eb3d20ea3d20eb3d6a21ec3d20eb3d20ec3d6a21ed" - "3d20ec3d20ed3d6a21ee" - "3d20ed3d20ee3d6a21ef3d20ee3d20ef3d6a21f03d20ef3d20f03d6a21f13d20f03d20f13d6a21f23d20f13d20f23d" - "6a21f33d20f23d20f33d" - "6a21f43d20f33d20f43d6a21f53d20f43d20f53d6a21f63d20f53d20f63d6a21f73d20f63d20f73d6a21f83d20f73d" - "20f83d6a21f93d20f83d" - "20f93d6a21fa3d20f93d20fa3d6a21fb3d20fa3d20fb3d6a21fc3d20fb3d20fc3d6a21fd3d20fc3d20fd3d6a21fe3d" - "20fd3d20fe3d6a21ff3d" - "20fe3d20ff3d6a21803e20ff3d20803e6a21813e20803e20813e6a21823e20813e20823e6a21833e20823e20833e6a" - "21843e20833e20843e6a" - "21853e20843e20853e6a21863e20853e20863e6a21873e20863e20873e6a21883e20873e20883e6a21893e20883e20" - "893e6a218a3e20893e20" - "8a3e6a218b3e208a3e208b3e6a218c3e208b3e208c3e6a218d3e208c3e208d3e6a218e3e208d3e208e3e6a218f3e20" - "8e3e208f3e6a21903e20" - "8f3e20903e6a21913e20903e20913e6a21923e20913e20923e6a21933e20923e20933e6a21943e20933e20943e6a21" - "953e20943e20953e6a21" - "963e20953e20963e6a21973e20963e20973e6a21983e20973e20983e6a21993e20983e20993e6a219a3e20993e209a" - "3e6a219b3e209a3e209b" - "3e6a219c3e209b3e209c3e6a219d3e209c3e209d3e6a219e3e209d3e209e3e6a219f3e209e3e209f3e6a21a03e209f" - "3e20a03e6a21a13e20a0" - "3e20a13e6a21a23e20a13e20a23e6a21a33e20a23e20a33e6a21a43e20a33e20a43e6a21a53e20a43e20a53e6a21a6" - "3e20a53e20a63e6a21a7" - "3e20a63e20a73e6a21a83e20a73e20a83e6a21a93e20a83e20a93e6a21aa3e20a93e20aa3e6a21ab3e20aa3e20ab3e" - "6a21ac3e20ab3e20ac3e" - "6a21ad3e20ac3e20ad3e6a21ae3e20ad3e20ae3e6a21af3e20ae3e20af3e6a21b03e20af3e20b03e6a21b13e20b03e" - "20b13e6a21b23e20b13e" - "20b23e6a21b33e20b23e20b33e6a21b43e20b33e20b43e6a21b53e20b43e20b53e6a21b63e20b53e20b63e6a21b73e" - "20b63e20b73e6a21b83e" - "20b73e20b83e6a21b93e20b83e20b93e6a21ba3e20b93e20ba3e6a21bb3e20ba3e20bb3e6a21bc3e20bb3e20bc3e6a" - "21bd3e20bc3e20bd3e6a" - "21be3e20bd3e20be3e6a21bf3e20be3e20bf3e6a21c03e20bf3e20c03e6a21c13e20c03e20c13e6a21c23e20c13e20" - "c23e6a21c33e20c23e20" - "c33e6a21c43e20c33e20c43e6a21c53e20c43e20c53e6a21c63e20c53e20c63e6a21c73e20c63e20c73e6a21c83e20" - "c73e20c83e6a21c93e20" - "c83e20c93e6a21ca3e20c93e20ca3e6a21cb3e20ca3e20cb3e6a21cc3e20cb3e20cc3e6a21cd3e20cc3e20cd3e6a21" - "ce3e20cd3e20ce3e6a21" - "cf3e20ce3e20cf3e6a21d03e20cf3e20d03e6a21d13e20d03e20d13e6a21d23e20d13e20d23e6a21d33e20d23e20d3" - "3e6a21d43e20d33e20d4" - "3e6a21d53e20d43e20d53e6a21d63e20d53e20d63e6a21d73e20d63e20d73e6a21d83e20d73e20d83e6a21d93e20d8" - "3e20d93e6a21da3e20d9" - "3e20da3e6a21db3e20da3e20db3e6a21dc3e20db3e20dc3e6a21dd3e20dc3e20dd3e6a21de3e20dd3e20de3e6a21df" - "3e20de3e20df3e6a21e0" - "3e20df3e20e03e6a21e13e20e03e20e13e6a21e23e20e13e20e23e6a21e33e20e23e20e33e6a21e43e20e33e20e43e" - "6a21e53e20e43e20e53e" - "6a21e63e20e53e20e63e6a21e73e20e63e20e73e6a21e83e20e73e20e83e6a21e93e20e83e20e93e6a21ea3e20e93e" - "20ea3e6a21eb3e20ea3e" - "20eb3e6a21ec3e20eb3e20ec3e6a21ed3e20ec3e20ed3e6a21ee3e20ed3e20ee3e6a21ef3e20ee3e20ef3e6a21f03e" - "20ef3e20f03e6a21f13e" - "20f03e20f13e6a21f23e20f13e20f23e6a21f33e20f23e20f33e6a21f43e20f33e20f43e6a21f53e20f43e20f53e6a" - "21f63e20f53e20f63e6a" - "21f73e20f63e20f73e6a21f83e20f73e20f83e6a21f93e20f83e20f93e6a21fa3e20f93e20fa3e6a21fb3e20fa3e20" - "fb3e6a21fc3e20fb3e20" - "fc3e6a21fd3e20fc3e20fd3e6a21fe3e20fd3e20fe3e6a21ff3e20fe3e20ff3e6a21803f20ff3e20803f6a21813f20" - "803f20813f6a21823f20" - "813f20823f6a21833f20823f20833f6a21843f20833f20843f6a21853f20843f20853f6a21863f20853f20863f6a21" - "873f20863f20873f6a21" + "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" - "3f6a218d3f208c3f208d" - "3f6a218e3f208d3f208e3f6a218f3f208e3f208f3f6a21903f208f3f20903f6a21913f20903f20913f6a21923f2091" - "3f20923f6a21933f2092" - "3f20933f6a21943f20933f20943f6a21953f20943f20953f6a21963f20953f20963f6a21973f20963f20973f6a2198" - "3f20973f20983f6a2199" - "3f20983f20993f6a219a3f20993f209a3f6a219b3f209a3f209b3f6a219c3f209b3f209c3f6a219d3f209c3f209d3f" - "6a219e3f209d3f209e3f" - "6a219f3f209e3f209f3f6a21a03f209f3f20a03f6a21a13f20a03f20a13f6a21a23f20a13f20a23f6a21a33f20a23f" - "20a33f6a21a43f20a33f" - "20a43f6a21a53f20a43f20a53f6a21a63f20a53f20a63f6a21a73f20a63f20a73f6a21a83f20a73f20a83f6a21a93f" - "20a83f20a93f6a21aa3f" - "20a93f20aa3f6a21ab3f20aa3f20ab3f6a21ac3f20ab3f20ac3f6a21ad3f20ac3f20ad3f6a21ae3f20ad3f20ae3f6a" - "21af3f20ae3f20af3f6a" - "21b03f20af3f20b03f6a21b13f20b03f20b13f6a21b23f20b13f20b23f6a21b33f20b23f20b33f6a21b43f20b33f20" - "b43f6a21b53f20b43f20" - "b53f6a21b63f20b53f20b63f6a21b73f20b63f20b73f6a21b83f20b73f20b83f6a21b93f20b83f20b93f6a21ba3f20" - "b93f20ba3f6a21bb3f20" - "ba3f20bb3f6a21bc3f20bb3f20bc3f6a21bd3f20bc3f20bd3f6a21be3f20bd3f20be3f6a21bf3f20be3f20bf3f6a21" - "c03f20bf3f20c03f6a21" - "c13f20c03f20c13f6a21c23f20c13f20c23f6a21c33f20c23f20c33f6a21c43f20c33f20c43f6a21c53f20c43f20c5" - "3f6a21c63f20c53f20c6" - "3f6a21c73f20c63f20c73f6a21c83f20c73f20c83f6a21c93f20c83f20c93f6a21ca3f20c93f20ca3f6a21cb3f20ca" - "3f20cb3f6a21cc3f20cb" - "3f20cc3f6a21cd3f20cc3f20cd3f6a21ce3f20cd3f20ce3f6a21cf3f20ce3f20cf3f6a21d03f20cf3f20d03f6a21d1" - "3f20d03f20d13f6a21d2" - "3f20d13f20d23f6a21d33f20d23f20d33f6a21d43f20d33f20d43f6a21d53f20d43f20d53f6a21d63f20d53f20d63f" - "6a21d73f20d63f20d73f" - "6a21d83f20d73f20d83f6a21d93f20d83f20d93f6a21da3f20d93f20da3f6a21db3f20da3f20db3f6a21dc3f20db3f" - "20dc3f6a21dd3f20dc3f" - "20dd3f6a21de3f20dd3f20de3f6a21df3f20de3f20df3f6a21e03f20df3f20e03f6a21e13f20e03f20e13f6a21e23f" - "20e13f20e23f6a21e33f" - "20e23f20e33f6a21e43f20e33f20e43f6a21e53f20e43f20e53f6a21e63f20e53f20e63f6a21e73f20e63f20e73f6a" - "21e83f20e73f20e83f6a" - "21e93f20e83f20e93f6a21ea3f20e93f20ea3f6a21eb3f20ea3f20eb3f6a21ec3f20eb3f20ec3f6a21ed3f20ec3f20" - "ed3f6a21ee3f20ed3f20" - "ee3f6a21ef3f20ee3f20ef3f6a21f03f20ef3f20f03f6a21f13f20f03f20f13f6a21f23f20f13f20f23f6a21f33f20" - "f23f20f33f6a21f43f20" - "f33f20f43f6a21f53f20f43f20f53f6a21f63f20f53f20f63f6a21f73f20f63f20f73f6a21f83f20f73f20f83f6a21" - "f93f20f83f20f93f6a21" - "fa3f20f93f20fa3f6a21fb3f20fa3f20fb3f6a21fc3f20fb3f20fc3f6a21fd3f20fc3f20fd3f6a21fe3f20fd3f20fe" - "3f6a21ff3f20fe3f20ff" - "3f6a21804020ff3f2080406a2181402080402081406a2182402081402082406a2183402082402083406a2184402083" - "402084406a2185402084" - "402085406a2186402085402086406a2187402086402087406a2188402087402088406a2189402088402089406a218a" - "40208940208a406a218b" - "40208a40208b406a218c40208b40208c406a218d40208c40208d406a218e40208d40208e406a218f40208e40208f40" - "6a219040208f40209040" - "6a2191402090402091406a2192402091402092406a2193402092402093406a2194402093402094406a219540209440" - "2095406a219640209540" - "2096406a2197402096402097406a2198402097402098406a2199402098402099406a219a40209940209a406a219b40" - "209a40209b406a219c40" - "209b40209c406a219d40209c40209d406a219e40209d40209e406a219f40209e40209f406a21a040209f4020a0406a" - "21a14020a04020a1406a" - "21a24020a14020a2406a21a34020a24020a3406a21a44020a34020a4406a21a54020a44020a5406a21a64020a54020" - "a6406a21a74020a64020" - "a7406a21a84020a74020a8406a21a94020a84020a9406a21aa4020a94020aa406a21ab4020aa4020ab406a21ac4020" - "ab4020ac406a21ad4020" - "ac4020ad406a21ae4020ad4020ae406a21af4020ae4020af406a21b04020af4020b0406a21b14020b04020b1406a21" - "b24020b14020b2406a21" - "b34020b24020b3406a21b44020b34020b4406a21b54020b44020b5406a21b64020b54020b6406a21b74020b64020b7" - "406a21b84020b74020b8" - "406a21b94020b84020b9406a21ba4020b94020ba406a21bb4020ba4020bb406a21bc4020bb4020bc406a21bd4020bc" - "4020bd406a21be4020bd" - "4020be406a21bf4020be4020bf406a21c04020bf4020c0406a21c14020c04020c1406a21c24020c14020c2406a21c3" - "4020c24020c3406a21c4" - "4020c34020c4406a21c54020c44020c5406a21c64020c54020c6406a21c74020c64020c7406a21c84020c74020c840" - "6a21c94020c84020c940" - "6a21ca4020c94020ca406a21cb4020ca4020cb406a21cc4020cb4020cc406a21cd4020cc4020cd406a21ce4020cd40" - "20ce406a21cf4020ce40" - "20cf406a21d04020cf4020d0406a21d14020d04020d1406a21d24020d14020d2406a21d34020d24020d3406a21d440" - "20d34020d4406a21d540" - "20d44020d5406a21d64020d54020d6406a21d74020d64020d7406a21d84020d74020d8406a21d94020d84020d9406a" - "21da4020d94020da406a" - "21db4020da4020db406a21dc4020db4020dc406a21dd4020dc4020dd406a21de4020dd4020de406a21df4020de4020" - "df406a21e04020df4020" - "e0406a21e14020e04020e1406a21e24020e14020e2406a21e34020e24020e3406a21e44020e34020e4406a21e54020" - "e44020e5406a21e64020" - "e54020e6406a21e74020e64020e7406a21e84020e74020e8406a21e94020e84020e9406a21ea4020e94020ea406a21" - "eb4020ea4020eb406a21" - "ec4020eb4020ec406a21ed4020ec4020ed406a21ee4020ed4020ee406a21ef4020ee4020ef406a21f04020ef4020f0" - "406a21f14020f04020f1" - "406a21f24020f14020f2406a21f34020f24020f3406a21f44020f34020f4406a21f54020f44020f5406a21f64020f5" - "4020f6406a21f74020f6" - "4020f7406a21f84020f74020f8406a21f94020f84020f9406a21fa4020f94020fa406a21fb4020fa4020fb406a21fc" - "4020fb4020fc406a21fd" - "4020fc4020fd406a21fe4020fd4020fe406a21ff4020fe4020ff406a21804120ff402080416a218141208041208141" - "6a218241208141208241" - "6a2183412082412083416a2184412083412084416a2185412084412085416a2186412085412086416a218741208641" - "2087416a218841208741" - "2088416a2189412088412089416a218a41208941208a416a218b41208a41208b416a218c41208b41208c416a218d41" - "208c41208d416a218e41" - "208d41208e416a218f41208e41208f416a219041208f412090416a2191412090412091416a2192412091412092416a" - "2193412092412093416a" + "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" - "98416a21994120984120" - "99416a219a41209941209a416a219b41209a41209b416a219c41209b41209c416a219d41209c41209d416a219e4120" - "9d41209e416a219f4120" - "9e41209f416a21a041209f4120a0416a21a14120a04120a1416a21a24120a14120a2416a21a34120a24120a3416a21" - "a44120a34120a4416a21" - "a54120a44120a5416a21a64120a54120a6416a21a74120a64120a7416a21a84120a74120a8416a21a94120a84120a9" - "416a21aa4120a94120aa" - "416a21ab4120aa4120ab416a21ac4120ab4120ac416a21ad4120ac4120ad416a21ae4120ad4120ae416a21af4120ae" - "4120af416a21b04120af" - "4120b0416a21b14120b04120b1416a21b24120b14120b2416a21b34120b24120b3416a21b44120b34120b4416a21b5" - "4120b44120b5416a21b6" - "4120b54120b6416a21b74120b64120b7416a21b84120b74120b8416a21b94120b84120b9416a21ba4120b94120ba41" - "6a21bb4120ba4120bb41" - "6a21bc4120bb4120bc416a21bd4120bc4120bd416a21be4120bd4120be416a21bf4120be4120bf416a21c04120bf41" - "20c0416a21c14120c041" - "20c1416a21c24120c14120c2416a21c34120c24120c3416a21c44120c34120c4416a21c54120c44120c5416a21c641" - "20c54120c6416a21c741" - "20c64120c7416a21c84120c74120c8416a21c94120c84120c9416a21ca4120c94120ca416a21cb4120ca4120cb416a" - "21cc4120cb4120cc416a" - "21cd4120cc4120cd416a21ce4120cd4120ce416a21cf4120ce4120cf416a21d04120cf4120d0416a21d14120d04120" - "d1416a21d24120d14120" - "d2416a21d34120d24120d3416a21d44120d34120d4416a21d54120d44120d5416a21d64120d54120d6416a21d74120" - "d64120d7416a21d84120" - "d74120d8416a21d94120d84120d9416a21da4120d94120da416a21db4120da4120db416a21dc4120db4120dc416a21" - "dd4120dc4120dd416a21" - "de4120dd4120de416a21df4120de4120df416a21e04120df4120e0416a21e14120e04120e1416a21e24120e14120e2" - "416a21e34120e24120e3" - "416a21e44120e34120e4416a21e54120e44120e5416a21e64120e54120e6416a21e74120e64120e7416a21e84120e7" - "4120e8416a21e94120e8" - "4120e9416a21ea4120e94120ea416a21eb4120ea4120eb416a21ec4120eb4120ec416a21ed4120ec4120ed416a21ee" - "4120ed4120ee416a21ef" - "4120ee4120ef416a21f04120ef4120f0416a21f14120f04120f1416a21f24120f14120f2416a21f34120f24120f341" - "6a21f44120f34120f441" - "6a21f54120f44120f5416a21f64120f54120f6416a21f74120f64120f7416a21f84120f74120f8416a21f94120f841" - "20f9416a21fa4120f941" - "20fa416a21fb4120fa4120fb416a21fc4120fb4120fc416a21fd4120fc4120fd416a21fe4120fd4120fe416a21ff41" - "20fe4120ff416a218042" - "20ff412080426a2181422080422081426a2182422081422082426a2183422082422083426a2184422083422084426a" - "2185422084422085426a" - "2186422085422086426a2187422086422087426a2188422087422088426a2189422088422089426a218a4220894220" - "8a426a218b42208a4220" - "8b426a218c42208b42208c426a218d42208c42208d426a218e42208d42208e426a218f42208e42208f426a21904220" - "8f422090426a21914220" - "90422091426a2192422091422092426a2193422092422093426a2194422093422094426a2195422094422095426a21" - "96422095422096426a21" - "97422096422097426a2198422097422098426a2199422098422099426a219a42209942209a426a219b42209a42209b" - "426a219c42209b42209c" - "426a219d42209c42209d426a219e42209d42209e426a219f42209e42209f426a21a042209f4220a0426a21a14220a0" - "4220a1426a21a24220a1" - "4220a2426a21a34220a24220a3426a21a44220a34220a4426a21a54220a44220a5426a21a64220a54220a6426a21a7" - "4220a64220a7426a21a8" - "4220a74220a8426a21a94220a84220a9426a21aa4220a94220aa426a21ab4220aa4220ab426a21ac4220ab4220ac42" - "6a21ad4220ac4220ad42" - "6a21ae4220ad4220ae426a21af4220ae4220af426a21b04220af4220b0426a21b14220b04220b1426a21b24220b142" - "20b2426a21b34220b242" - "20b3426a21b44220b34220b4426a21b54220b44220b5426a21b64220b54220b6426a21b74220b64220b7426a21b842" - "20b74220b8426a21b942" - "20b84220b9426a21ba4220b94220ba426a21bb4220ba4220bb426a21bc4220bb4220bc426a21bd4220bc4220bd426a" - "21be4220bd4220be426a" - "21bf4220be4220bf426a21c04220bf4220c0426a21c14220c04220c1426a21c24220c14220c2426a21c34220c24220" - "c3426a21c44220c34220" - "c4426a21c54220c44220c5426a21c64220c54220c6426a21c74220c64220c7426a21c84220c74220c8426a21c94220" - "c84220c9426a21ca4220" - "c94220ca426a21cb4220ca4220cb426a21cc4220cb4220cc426a21cd4220cc4220cd426a21ce4220cd4220ce426a21" - "cf4220ce4220cf426a21" - "d04220cf4220d0426a21d14220d04220d1426a21d24220d14220d2426a21d34220d24220d3426a21d44220d34220d4" - "426a21d54220d44220d5" - "426a21d64220d54220d6426a21d74220d64220d7426a21d84220d74220d8426a21d94220d84220d9426a21da4220d9" - "4220da426a21db4220da" - "4220db426a21dc4220db4220dc426a21dd4220dc4220dd426a21de4220dd4220de426a21df4220de4220df426a21e0" - "4220df4220e0426a21e1" - "4220e04220e1426a21e24220e14220e2426a21e34220e24220e3426a21e44220e34220e4426a21e54220e44220e542" - "6a21e64220e54220e642" - "6a21e74220e64220e7426a21e84220e74220e8426a21e94220e84220e9426a21ea4220e94220ea426a21eb4220ea42" - "20eb426a21ec4220eb42" - "20ec426a21ed4220ec4220ed426a21ee4220ed4220ee426a21ef4220ee4220ef426a21f04220ef4220f0426a21f142" - "20f04220f1426a21f242" - "20f14220f2426a21f34220f24220f3426a21f44220f34220f4426a21f54220f44220f5426a21f64220f54220f6426a" - "21f74220f64220f7426a" - "21f84220f74220f8426a21f94220f84220f9426a21fa4220f94220fa426a21fb4220fa4220fb426a21fc4220fb4220" - "fc426a21fd4220fc4220" - "fd426a21fe4220fd4220fe426a21ff4220fe4220ff426a21804320ff422080436a2181432080432081436a21824320" - "81432082436a21834320" - "82432083436a2184432083432084436a2185432084432085436a2186432085432086436a2187432086432087436a21" - "88432087432088436a21" - "89432088432089436a218a43208943208a436a218b43208a43208b436a218c43208b43208c436a218d43208c43208d" - "436a218e43208d43208e" - "436a218f43208e43208f436a219043208f432090436a2191432090432091436a2192432091432092436a2193432092" - "432093436a2194432093" - "432094436a2195432094432095436a2196432095432096436a2197432096432097436a2198432097432098436a2199" - "432098432099436a219a" - "43209943209a436a219b43209a43209b436a219c43209b43209c436a219d43209c43209d436a219e43209d43209e43" - "6a219f43209e43209f43" + "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" - "20a4436a21a54320a443" - "20a5436a21a64320a54320a6436a21a74320a64320a7436a21a84320a74320a8436a21a94320a84320a9436a21aa43" - "20a94320aa436a21ab43" - "20aa4320ab436a21ac4320ab4320ac436a21ad4320ac4320ad436a21ae4320ad4320ae436a21af4320ae4320af436a" - "21b04320af4320b0436a" - "21b14320b04320b1436a21b24320b14320b2436a21b34320b24320b3436a21b44320b34320b4436a21b54320b44320" - "b5436a21b64320b54320" - "b6436a21b74320b64320b7436a21b84320b74320b8436a21b94320b84320b9436a21ba4320b94320ba436a21bb4320" - "ba4320bb436a21bc4320" - "bb4320bc436a21bd4320bc4320bd436a21be4320bd4320be436a21bf4320be4320bf436a21c04320bf4320c0436a21" - "c14320c04320c1436a21" - "c24320c14320c2436a21c34320c24320c3436a21c44320c34320c4436a21c54320c44320c5436a21c64320c54320c6" - "436a21c74320c64320c7" - "436a21c84320c74320c8436a21c94320c84320c9436a21ca4320c94320ca436a21cb4320ca4320cb436a21cc4320cb" - "4320cc436a21cd4320cc" - "4320cd436a21ce4320cd4320ce436a21cf4320ce4320cf436a21d04320cf4320d0436a21d14320d04320d1436a21d2" - "4320d14320d2436a21d3" - "4320d24320d3436a21d44320d34320d4436a21d54320d44320d5436a21d64320d54320d6436a21d74320d64320d743" - "6a21d84320d74320d843" - "6a21d94320d84320d9436a21da4320d94320da436a21db4320da4320db436a21dc4320db4320dc436a21dd4320dc43" - "20dd436a21de4320dd43" - "20de436a21df4320de4320df436a21e04320df4320e0436a21e14320e04320e1436a21e24320e14320e2436a21e343" - "20e24320e3436a21e443" - "20e34320e4436a21e54320e44320e5436a21e64320e54320e6436a21e74320e64320e7436a21e84320e74320e8436a" - "21e94320e84320e9436a" - "21ea4320e94320ea436a21eb4320ea4320eb436a21ec4320eb4320ec436a21ed4320ec4320ed436a21ee4320ed4320" - "ee436a21ef4320ee4320" - "ef436a21f04320ef4320f0436a21f14320f04320f1436a21f24320f14320f2436a21f34320f24320f3436a21f44320" - "f34320f4436a21f54320" - "f44320f5436a21f64320f54320f6436a21f74320f64320f7436a21f84320f74320f8436a21f94320f84320f9436a21" - "fa4320f94320fa436a21" - "fb4320fa4320fb436a21fc4320fb4320fc436a21fd4320fc4320fd436a21fe4320fd4320fe436a21ff4320fe4320ff" - "436a21804420ff432080" - "446a2181442080442081446a2182442081442082446a2183442082442083446a2184442083442084446a2185442084" - "442085446a2186442085" - "442086446a2187442086442087446a2188442087442088446a2189442088442089446a218a44208944208a446a218b" - "44208a44208b446a218c" - "44208b44208c446a218d44208c44208d446a218e44208d44208e446a218f44208e44208f446a219044208f44209044" - "6a219144209044209144" - "6a2192442091442092446a2193442092442093446a2194442093442094446a2195442094442095446a219644209544" - "2096446a219744209644" - "2097446a2198442097442098446a2199442098442099446a219a44209944209a446a219b44209a44209b446a219c44" - "209b44209c446a219d44" - "209c44209d446a219e44209d44209e446a219f44209e44209f446a21a044209f4420a0446a21a14420a04420a1446a" - "21a24420a14420a2446a" - "21a34420a24420a3446a21a44420a34420a4446a21a54420a44420a5446a21a64420a54420a6446a21a74420a64420" - "a7446a21a84420a74420" - "a8446a21a94420a84420a9446a21aa4420a94420aa446a21ab4420aa4420ab446a21ac4420ab4420ac446a21ad4420" - "ac4420ad446a21ae4420" - "ad4420ae446a21af4420ae4420af446a21b04420af4420b0446a21b14420b04420b1446a21b24420b14420b2446a21" - "b34420b24420b3446a21" - "b44420b34420b4446a21b54420b44420b5446a21b64420b54420b6446a21b74420b64420b7446a21b84420b74420b8" - "446a21b94420b84420b9" - "446a21ba4420b94420ba446a21bb4420ba4420bb446a21bc4420bb4420bc446a21bd4420bc4420bd446a21be4420bd" - "4420be446a21bf4420be" - "4420bf446a21c04420bf4420c0446a21c14420c04420c1446a21c24420c14420c2446a21c34420c24420c3446a21c4" - "4420c34420c4446a21c5" - "4420c44420c5446a21c64420c54420c6446a21c74420c64420c7446a21c84420c74420c8446a21c94420c84420c944" - "6a21ca4420c94420ca44" - "6a21cb4420ca4420cb446a21cc4420cb4420cc446a21cd4420cc4420cd446a21ce4420cd4420ce446a21cf4420ce44" - "20cf446a21d04420cf44" - "20d0446a21d14420d04420d1446a21d24420d14420d2446a21d34420d24420d3446a21d44420d34420d4446a21d544" - "20d44420d5446a21d644" - "20d54420d6446a21d74420d64420d7446a21d84420d74420d8446a21d94420d84420d9446a21da4420d94420da446a" - "21db4420da4420db446a" - "21dc4420db4420dc446a21dd4420dc4420dd446a21de4420dd4420de446a21df4420de4420df446a21e04420df4420" - "e0446a21e14420e04420" - "e1446a21e24420e14420e2446a21e34420e24420e3446a21e44420e34420e4446a21e54420e44420e5446a21e64420" - "e54420e6446a21e74420" - "e64420e7446a21e84420e74420e8446a21e94420e84420e9446a21ea4420e94420ea446a21eb4420ea4420eb446a21" - "ec4420eb4420ec446a21" - "ed4420ec4420ed446a21ee4420ed4420ee446a21ef4420ee4420ef446a21f04420ef4420f0446a21f14420f04420f1" - "446a21f24420f14420f2" - "446a21f34420f24420f3446a21f44420f34420f4446a21f54420f44420f5446a21f64420f54420f6446a21f74420f6" - "4420f7446a21f84420f7" - "4420f8446a21f94420f84420f9446a21fa4420f94420fa446a21fb4420fa4420fb446a21fc4420fb4420fc446a21fd" - "4420fc4420fd446a21fe" - "4420fd4420fe446a21ff4420fe4420ff446a21804520ff442080456a2181452080452081456a218245208145208245" - "6a218345208245208345" - "6a2184452083452084456a2185452084452085456a2186452085452086456a2187452086452087456a218845208745" - "2088456a218945208845" - "2089456a218a45208945208a456a218b45208a45208b456a218c45208b45208c456a218d45208c45208d456a218e45" - "208d45208e456a218f45" - "208e45208f456a219045208f452090456a2191452090452091456a2192452091452092456a2193452092452093456a" - "2194452093452094456a" - "2195452094452095456a2196452095452096456a2197452096452097456a2198452097452098456a21994520984520" - "99456a219a4520994520" - "9a456a219b45209a45209b456a219c45209b45209c456a219d45209c45209d456a219e45209d45209e456a219f4520" - "9e45209f456a21a04520" - "9f4520a0456a21a14520a04520a1456a21a24520a14520a2456a21a34520a24520a3456a21a44520a34520a4456a21" - "a54520a44520a5456a21" - "a64520a54520a6456a21a74520a64520a7456a21a84520a74520a8456a21a94520a84520a9456a21aa4520a94520aa" - "456a21ab4520aa4520ab" + "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" - "4520b0456a21b14520b0" - "4520b1456a21b24520b14520b2456a21b34520b24520b3456a21b44520b34520b4456a21b54520b44520b5456a21b6" - "4520b54520b6456a21b7" - "4520b64520b7456a21b84520b74520b8456a21b94520b84520b9456a21ba4520b94520ba456a21bb4520ba4520bb45" - "6a21bc4520bb4520bc45" - "6a21bd4520bc4520bd456a21be4520bd4520be456a21bf4520be4520bf456a21c04520bf4520c0456a21c14520c045" - "20c1456a21c24520c145" - "20c2456a21c34520c24520c3456a21c44520c34520c4456a21c54520c44520c5456a21c64520c54520c6456a21c745" - "20c64520c7456a21c845" - "20c74520c8456a21c94520c84520c9456a21ca4520c94520ca456a21cb4520ca4520cb456a21cc4520cb4520cc456a" - "21cd4520cc4520cd456a" - "21ce4520cd4520ce456a21cf4520ce4520cf456a21d04520cf4520d0456a21d14520d04520d1456a21d24520d14520" - "d2456a21d34520d24520" - "d3456a21d44520d34520d4456a21d54520d44520d5456a21d64520d54520d6456a21d74520d64520d7456a21d84520" - "d74520d8456a21d94520" - "d84520d9456a21da4520d94520da456a21db4520da4520db456a21dc4520db4520dc456a21dd4520dc4520dd456a21" - "de4520dd4520de456a21" - "df4520de4520df456a21e04520df4520e0456a21e14520e04520e1456a21e24520e14520e2456a21e34520e24520e3" - "456a21e44520e34520e4" - "456a21e54520e44520e5456a21e64520e54520e6456a21e74520e64520e7456a21e84520e74520e8456a21e94520e8" - "4520e9456a21ea4520e9" - "4520ea456a21eb4520ea4520eb456a21ec4520eb4520ec456a21ed4520ec4520ed456a21ee4520ed4520ee456a21ef" - "4520ee4520ef456a21f0" - "4520ef4520f0456a21f14520f04520f1456a21f24520f14520f2456a21f34520f24520f3456a21f44520f34520f445" - "6a21f54520f44520f545" - "6a21f64520f54520f6456a21f74520f64520f7456a21f84520f74520f8456a21f94520f84520f9456a21fa4520f945" - "20fa456a21fb4520fa45" - "20fb456a21fc4520fb4520fc456a21fd4520fc4520fd456a21fe4520fd4520fe456a21ff4520fe4520ff456a218046" - "20ff452080466a218146" - "2080462081466a2182462081462082466a2183462082462083466a2184462083462084466a2185462084462085466a" - "2186462085462086466a" - "2187462086462087466a2188462087462088466a2189462088462089466a218a46208946208a466a218b46208a4620" - "8b466a218c46208b4620" - "8c466a218d46208c46208d466a218e46208d46208e466a218f46208e46208f466a219046208f462090466a21914620" - "90462091466a21924620" - "91462092466a2193462092462093466a2194462093462094466a2195462094462095466a2196462095462096466a21" - "97462096462097466a21" - "98462097462098466a2199462098462099466a219a46209946209a466a219b46209a46209b466a219c46209b46209c" - "466a219d46209c46209d" - "466a219e46209d46209e466a219f46209e46209f466a21a046209f4620a0466a21a14620a04620a1466a21a24620a1" - "4620a2466a21a34620a2" - "4620a3466a21a44620a34620a4466a21a54620a44620a5466a21a64620a54620a6466a21a74620a64620a7466a21a8" - "4620a74620a8466a21a9" - "4620a84620a9466a21aa4620a94620aa466a21ab4620aa4620ab466a21ac4620ab4620ac466a21ad4620ac4620ad46" - "6a21ae4620ad4620ae46" - "6a21af4620ae4620af466a21b04620af4620b0466a21b14620b04620b1466a21b24620b14620b2466a21b34620b246" - "20b3466a21b44620b346" - "20b4466a21b54620b44620b5466a21b64620b54620b6466a21b74620b64620b7466a21b84620b74620b8466a21b946" - "20b84620b9466a21ba46" - "20b94620ba466a21bb4620ba4620bb466a21bc4620bb4620bc466a21bd4620bc4620bd466a21be4620bd4620be466a" - "21bf4620be4620bf466a" - "21c04620bf4620c0466a21c14620c04620c1466a21c24620c14620c2466a21c34620c24620c3466a21c44620c34620" - "c4466a21c54620c44620" - "c5466a21c64620c54620c6466a21c74620c64620c7466a21c84620c74620c8466a21c94620c84620c9466a21ca4620" - "c94620ca466a21cb4620" - "ca4620cb466a21cc4620cb4620cc466a21cd4620cc4620cd466a21ce4620cd4620ce466a21cf4620ce4620cf466a21" - "d04620cf4620d0466a21" - "d14620d04620d1466a21d24620d14620d2466a21d34620d24620d3466a21d44620d34620d4466a21d54620d44620d5" - "466a21d64620d54620d6" - "466a21d74620d64620d7466a21d84620d74620d8466a21d94620d84620d9466a21da4620d94620da466a21db4620da" - "4620db466a21dc4620db" - "4620dc466a21dd4620dc4620dd466a21de4620dd4620de466a21df4620de4620df466a21e04620df4620e0466a21e1" - "4620e04620e1466a21e2" - "4620e14620e2466a21e34620e24620e3466a21e44620e34620e4466a21e54620e44620e5466a21e64620e54620e646" - "6a21e74620e64620e746" - "6a21e84620e74620e8466a21e94620e84620e9466a21ea4620e94620ea466a21eb4620ea4620eb466a21ec4620eb46" - "20ec466a21ed4620ec46" - "20ed466a21ee4620ed4620ee466a21ef4620ee4620ef466a21f04620ef4620f0466a21f14620f04620f1466a21f246" - "20f14620f2466a21f346" - "20f24620f3466a21f44620f34620f4466a21f54620f44620f5466a21f64620f54620f6466a21f74620f64620f7466a" - "21f84620f74620f8466a" - "21f94620f84620f9466a21fa4620f94620fa466a21fb4620fa4620fb466a21fc4620fb4620fc466a21fd4620fc4620" - "fd466a21fe4620fd4620" - "fe466a21ff4620fe4620ff466a21804720ff462080476a2181472080472081476a2182472081472082476a21834720" - "82472083476a21844720" - "83472084476a2185472084472085476a2186472085472086476a2187472086472087476a2188472087472088476a21" - "89472088472089476a21" - "8a47208947208a476a218b47208a47208b476a218c47208b47208c476a218d47208c47208d476a218e47208d47208e" - "476a218f47208e47208f" - "476a219047208f472090476a2191472090472091476a2192472091472092476a2193472092472093476a2194472093" - "472094476a2195472094" - "472095476a2196472095472096476a2197472096472097476a2198472097472098476a2199472098472099476a219a" - "47209947209a476a219b" - "47209a47209b476a219c47209b47209c476a219d47209c47209d476a219e47209d47209e476a219f47209e47209f47" - "6a21a047209f4720a047" - "6a21a14720a04720a1476a21a24720a14720a2476a21a34720a24720a3476a21a44720a34720a4476a21a54720a447" - "20a5476a21a64720a547" - "20a6476a21a74720a64720a7476a21a84720a74720a8476a21a94720a84720a9476a21aa4720a94720aa476a21ab47" - "20aa4720ab476a21ac47" - "20ab4720ac476a21ad4720ac4720ad476a21ae4720ad4720ae476a21af4720ae4720af476a21b04720af4720b0476a" - "21b14720b04720b1476a" - "21b24720b14720b2476a21b34720b24720b3476a21b44720b34720b4476a21b54720b44720b5476a21b64720b54720" - "b6476a21b74720b64720" + "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" - "bb4720bc476a21bd4720" - "bc4720bd476a21be4720bd4720be476a21bf4720be4720bf476a21c04720bf4720c0476a21c14720c04720c1476a21" - "c24720c14720c2476a21" - "c34720c24720c3476a21c44720c34720c4476a21c54720c44720c5476a21c64720c54720c6476a21c74720c64720c7" - "476a21c84720c74720c8" - "476a21c94720c84720c9476a21ca4720c94720ca476a21cb4720ca4720cb476a21cc4720cb4720cc476a21cd4720cc" - "4720cd476a21ce4720cd" - "4720ce476a21cf4720ce4720cf476a21d04720cf4720d0476a21d14720d04720d1476a21d24720d14720d2476a21d3" - "4720d24720d3476a21d4" - "4720d34720d4476a21d54720d44720d5476a21d64720d54720d6476a21d74720d64720d7476a21d84720d74720d847" - "6a21d94720d84720d947" - "6a21da4720d94720da476a21db4720da4720db476a21dc4720db4720dc476a21dd4720dc4720dd476a21de4720dd47" - "20de476a21df4720de47" - "20df476a21e04720df4720e0476a21e14720e04720e1476a21e24720e14720e2476a21e34720e24720e3476a21e447" - "20e34720e4476a21e547" - "20e44720e5476a21e64720e54720e6476a21e74720e64720e7476a21e84720e74720e8476a21e94720e84720e9476a" - "21ea4720e94720ea476a" - "21eb4720ea4720eb476a21ec4720eb4720ec476a21ed4720ec4720ed476a21ee4720ed4720ee476a21ef4720ee4720" - "ef476a21f04720ef4720" - "f0476a21f14720f04720f1476a21f24720f14720f2476a21f34720f24720f3476a21f44720f34720f4476a21f54720" - "f44720f5476a21f64720" - "f54720f6476a21f74720f64720f7476a21f84720f74720f8476a21f94720f84720f9476a21fa4720f94720fa476a21" - "fb4720fa4720fb476a21" - "fc4720fb4720fc476a21fd4720fc4720fd476a21fe4720fd4720fe476a21ff4720fe4720ff476a21804820ff472080" - "486a2181482080482081" - "486a2182482081482082486a2183482082482083486a2184482083482084486a2185482084482085486a2186482085" - "482086486a2187482086" - "482087486a2188482087482088486a2189482088482089486a218a48208948208a486a218b48208a48208b486a218c" - "48208b48208c486a218d" - "48208c48208d486a218e48208d48208e486a218f48208e48208f486a219048208f482090486a219148209048209148" - "6a219248209148209248" - "6a2193482092482093486a2194482093482094486a2195482094482095486a2196482095482096486a219748209648" - "2097486a219848209748" - "2098486a2199482098482099486a219a48209948209a486a219b48209a48209b486a219c48209b48209c486a219d48" - "209c48209d486a219e48" - "209d48209e486a219f48209e48209f486a21a048209f4820a0486a21a14820a04820a1486a21a24820a14820a2486a" - "21a34820a24820a3486a" - "21a44820a34820a4486a21a54820a44820a5486a21a64820a54820a6486a21a74820a64820a7486a21a84820a74820" - "a8486a21a94820a84820" - "a9486a21aa4820a94820aa486a21ab4820aa4820ab486a21ac4820ab4820ac486a21ad4820ac4820ad486a21ae4820" - "ad4820ae486a21af4820" - "ae4820af486a21b04820af4820b0486a21b14820b04820b1486a21b24820b14820b2486a21b34820b24820b3486a21" - "b44820b34820b4486a21" - "b54820b44820b5486a21b64820b54820b6486a21b74820b64820b7486a21b84820b74820b8486a21b94820b84820b9" - "486a21ba4820b94820ba" - "486a21bb4820ba4820bb486a21bc4820bb4820bc486a21bd4820bc4820bd486a21be4820bd4820be486a21bf4820be" - "4820bf486a21c04820bf" - "4820c0486a21c14820c04820c1486a21c24820c14820c2486a21c34820c24820c3486a21c44820c34820c4486a21c5" - "4820c44820c5486a21c6" - "4820c54820c6486a21c74820c64820c7486a21c84820c74820c8486a21c94820c84820c9486a21ca4820c94820ca48" - "6a21cb4820ca4820cb48" - "6a21cc4820cb4820cc486a21cd4820cc4820cd486a21ce4820cd4820ce486a21cf4820ce4820cf486a21d04820cf48" - "20d0486a21d14820d048" - "20d1486a21d24820d14820d2486a21d34820d24820d3486a21d44820d34820d4486a21d54820d44820d5486a21d648" - "20d54820d6486a21d748" - "20d64820d7486a21d84820d74820d8486a21d94820d84820d9486a21da4820d94820da486a21db4820da4820db486a" - "21dc4820db4820dc486a" - "21dd4820dc4820dd486a21de4820dd4820de486a21df4820de4820df486a21e04820df4820e0486a21e14820e04820" - "e1486a21e24820e14820" - "e2486a21e34820e24820e3486a21e44820e34820e4486a21e54820e44820e5486a21e64820e54820e6486a21e74820" - "e64820e7486a21e84820" - "e74820e8486a21e94820e84820e9486a21ea4820e94820ea486a21eb4820ea4820eb486a21ec4820eb4820ec486a21" - "ed4820ec4820ed486a21" - "ee4820ed4820ee486a21ef4820ee4820ef486a21f04820ef4820f0486a21f14820f04820f1486a21f24820f14820f2" - "486a21f34820f24820f3" - "486a21f44820f34820f4486a21f54820f44820f5486a21f64820f54820f6486a21f74820f64820f7486a21f84820f7" - "4820f8486a21f94820f8" - "4820f9486a21fa4820f94820fa486a21fb4820fa4820fb486a21fc4820fb4820fc486a21fd4820fc4820fd486a21fe" - "4820fd4820fe486a21ff" - "4820fe4820ff486a21804920ff482080496a2181492080492081496a2182492081492082496a218349208249208349" - "6a218449208349208449" - "6a2185492084492085496a2186492085492086496a2187492086492087496a2188492087492088496a218949208849" - "2089496a218a49208949" - "208a496a218b49208a49208b496a218c49208b49208c496a218d49208c49208d496a218e49208d49208e496a218f49" - "208e49208f496a219049" - "208f492090496a2191492090492091496a2192492091492092496a2193492092492093496a2194492093492094496a" - "2195492094492095496a" - "2196492095492096496a2197492096492097496a2198492097492098496a2199492098492099496a219a4920994920" - "9a496a219b49209a4920" - "9b496a219c49209b49209c496a219d49209c49209d496a219e49209d49209e496a219f49209e49209f496a21a04920" - "9f4920a0496a21a14920" - "a04920a1496a21a24920a14920a2496a21a34920a24920a3496a21a44920a34920a4496a21a54920a44920a5496a21" - "a64920a54920a6496a21" - "a74920a64920a7496a21a84920a74920a8496a21a94920a84920a9496a21aa4920a94920aa496a21ab4920aa4920ab" - "496a21ac4920ab4920ac" - "496a21ad4920ac4920ad496a21ae4920ad4920ae496a21af4920ae4920af496a21b04920af4920b0496a21b14920b0" - "4920b1496a21b24920b1" - "4920b2496a21b34920b24920b3496a21b44920b34920b4496a21b54920b44920b5496a21b64920b54920b6496a21b7" - "4920b64920b7496a21b8" - "4920b74920b8496a21b94920b84920b9496a21ba4920b94920ba496a21bb4920ba4920bb496a21bc4920bb4920bc49" - "6a21bd4920bc4920bd49" - "6a21be4920bd4920be496a21bf4920be4920bf496a21c04920bf4920c0496a21c14920c04920c1496a21c24920c149" - "20c2496a21c34920c249" + "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" - "20c74920c8496a21c949" - "20c84920c9496a21ca4920c94920ca496a21cb4920ca4920cb496a21cc4920cb4920cc496a21cd4920cc4920cd496a" - "21ce4920cd4920ce496a" - "21cf4920ce4920cf496a21d04920cf4920d0496a21d14920d04920d1496a21d24920d14920d2496a21d34920d24920" - "d3496a21d44920d34920" - "d4496a21d54920d44920d5496a21d64920d54920d6496a21d74920d64920d7496a21d84920d74920d8496a21d94920" - "d84920d9496a21da4920" - "d94920da496a21db4920da4920db496a21dc4920db4920dc496a21dd4920dc4920dd496a21de4920dd4920de496a21" - "df4920de4920df496a21" - "e04920df4920e0496a21e14920e04920e1496a21e24920e14920e2496a21e34920e24920e3496a21e44920e34920e4" - "496a21e54920e44920e5" - "496a21e64920e54920e6496a21e74920e64920e7496a21e84920e74920e8496a21e94920e84920e9496a21ea4920e9" - "4920ea496a21eb4920ea" - "4920eb496a21ec4920eb4920ec496a21ed4920ec4920ed496a21ee4920ed4920ee496a21ef4920ee4920ef496a21f0" - "4920ef4920f0496a21f1" - "4920f04920f1496a21f24920f14920f2496a21f34920f24920f3496a21f44920f34920f4496a21f54920f44920f549" - "6a21f64920f54920f649" - "6a21f74920f64920f7496a21f84920f74920f8496a21f94920f84920f9496a21fa4920f94920fa496a21fb4920fa49" - "20fb496a21fc4920fb49" - "20fc496a21fd4920fc4920fd496a21fe4920fd4920fe496a21ff4920fe4920ff496a21804a20ff4920804a6a21814a" - "20804a20814a6a21824a" - "20814a20824a6a21834a20824a20834a6a21844a20834a20844a6a21854a20844a20854a6a21864a20854a20864a6a" - "21874a20864a20874a6a" - "21884a20874a20884a6a21894a20884a20894a6a218a4a20894a208a4a6a218b4a208a4a208b4a6a218c4a208b4a20" - "8c4a6a218d4a208c4a20" - "8d4a6a218e4a208d4a208e4a6a218f4a208e4a208f4a6a21904a208f4a20904a6a21914a20904a20914a6a21924a20" - "914a20924a6a21934a20" - "924a20934a6a21944a20934a20944a6a21954a20944a20954a6a21964a20954a20964a6a21974a20964a20974a6a21" - "984a20974a20984a6a21" - "994a20984a20994a6a219a4a20994a209a4a6a219b4a209a4a209b4a6a219c4a209b4a209c4a6a219d4a209c4a209d" - "4a6a219e4a209d4a209e" - "4a6a219f4a209e4a209f4a6a21a04a209f4a20a04a6a21a14a20a04a20a14a6a21a24a20a14a20a24a6a21a34a20a2" - "4a20a34a6a21a44a20a3" - "4a20a44a6a21a54a20a44a20a54a6a21a64a20a54a20a64a6a21a74a20a64a20a74a6a21a84a20a74a20a84a6a21a9" - "4a20a84a20a94a6a21aa" - "4a20a94a20aa4a6a21ab4a20aa4a20ab4a6a21ac4a20ab4a20ac4a6a21ad4a20ac4a20ad4a6a21ae4a20ad4a20ae4a" - "6a21af4a20ae4a20af4a" - "6a21b04a20af4a20b04a6a21b14a20b04a20b14a6a21b24a20b14a20b24a6a21b34a20b24a20b34a6a21b44a20b34a" - "20b44a6a21b54a20b44a" - "20b54a6a21b64a20b54a20b64a6a21b74a20b64a20b74a6a21b84a20b74a20b84a6a21b94a20b84a20b94a6a21ba4a" - "20b94a20ba4a6a21bb4a" - "20ba4a20bb4a6a21bc4a20bb4a20bc4a6a21bd4a20bc4a20bd4a6a21be4a20bd4a20be4a6a21bf4a20be4a20bf4a6a" - "21c04a20bf4a20c04a6a" - "21c14a20c04a20c14a6a21c24a20c14a20c24a6a21c34a20c24a20c34a6a21c44a20c34a20c44a6a21c54a20c44a20" - "c54a6a21c64a20c54a20" - "c64a6a21c74a20c64a20c74a6a21c84a20c74a20c84a6a21c94a20c84a20c94a6a21ca4a20c94a20ca4a6a21cb4a20" - "ca4a20cb4a6a21cc4a20" - "cb4a20cc4a6a21cd4a20cc4a20cd4a6a21ce4a20cd4a20ce4a6a21cf4a20ce4a20cf4a6a21d04a20cf4a20d04a6a21" - "d14a20d04a20d14a6a21" - "d24a20d14a20d24a6a21d34a20d24a20d34a6a21d44a20d34a20d44a6a21d54a20d44a20d54a6a21d64a20d54a20d6" - "4a6a21d74a20d64a20d7" - "4a6a21d84a20d74a20d84a6a21d94a20d84a20d94a6a21da4a20d94a20da4a6a21db4a20da4a20db4a6a21dc4a20db" - "4a20dc4a6a21dd4a20dc" - "4a20dd4a6a21de4a20dd4a20de4a6a21df4a20de4a20df4a6a21e04a20df4a20e04a6a21e14a20e04a20e14a6a21e2" - "4a20e14a20e24a6a21e3" - "4a20e24a20e34a6a21e44a20e34a20e44a6a21e54a20e44a20e54a6a21e64a20e54a20e64a6a21e74a20e64a20e74a" - "6a21e84a20e74a20e84a" - "6a21e94a20e84a20e94a6a21ea4a20e94a20ea4a6a21eb4a20ea4a20eb4a6a21ec4a20eb4a20ec4a6a21ed4a20ec4a" - "20ed4a6a21ee4a20ed4a" - "20ee4a6a21ef4a20ee4a20ef4a6a21f04a20ef4a20f04a6a21f14a20f04a20f14a6a21f24a20f14a20f24a6a21f34a" - "20f24a20f34a6a21f44a" - "20f34a20f44a6a21f54a20f44a20f54a6a21f64a20f54a20f64a6a21f74a20f64a20f74a6a21f84a20f74a20f84a6a" - "21f94a20f84a20f94a6a" - "21fa4a20f94a20fa4a6a21fb4a20fa4a20fb4a6a21fc4a20fb4a20fc4a6a21fd4a20fc4a20fd4a6a21fe4a20fd4a20" - "fe4a6a21ff4a20fe4a20" - "ff4a6a21804b20ff4a20804b6a21814b20804b20814b6a21824b20814b20824b6a21834b20824b20834b6a21844b20" - "834b20844b6a21854b20" - "844b20854b6a21864b20854b20864b6a21874b20864b20874b6a21884b20874b20884b6a21894b20884b20894b6a21" - "8a4b20894b208a4b6a21" - "8b4b208a4b208b4b6a218c4b208b4b208c4b6a218d4b208c4b208d4b6a218e4b208d4b208e4b6a218f4b208e4b208f" - "4b6a21904b208f4b2090" - "4b6a21914b20904b20914b6a21924b20914b20924b6a21934b20924b20934b6a21944b20934b20944b6a21954b2094" - "4b20954b6a21964b2095" - "4b20964b6a21974b20964b20974b6a21984b20974b20984b6a21994b20984b20994b6a219a4b20994b209a4b6a219b" - "4b209a4b209b4b6a219c" - "4b209b4b209c4b6a219d4b209c4b209d4b6a219e4b209d4b209e4b6a219f4b209e4b209f4b6a21a04b209f4b20a04b" - "6a21a14b20a04b20a14b" - "6a21a24b20a14b20a24b6a21a34b20a24b20a34b6a21a44b20a34b20a44b6a21a54b20a44b20a54b6a21a64b20a54b" - "20a64b6a21a74b20a64b" - "20a74b6a21a84b20a74b20a84b6a21a94b20a84b20a94b6a21aa4b20a94b20aa4b6a21ab4b20aa4b20ab4b6a21ac4b" - "20ab4b20ac4b6a21ad4b" - "20ac4b20ad4b6a21ae4b20ad4b20ae4b6a21af4b20ae4b20af4b6a21b04b20af4b20b04b6a21b14b20b04b20b14b6a" - "21b24b20b14b20b24b6a" - "21b34b20b24b20b34b6a21b44b20b34b20b44b6a21b54b20b44b20b54b6a21b64b20b54b20b64b6a21b74b20b64b20" - "b74b6a21b84b20b74b20" - "b84b6a21b94b20b84b20b94b6a21ba4b20b94b20ba4b6a21bb4b20ba4b20bb4b6a21bc4b20bb4b20bc4b6a21bd4b20" - "bc4b20bd4b6a21be4b20" - "bd4b20be4b6a21bf4b20be4b20bf4b6a21c04b20bf4b20c04b6a21c14b20c04b20c14b6a21c24b20c14b20c24b6a21" - "c34b20c24b20c34b6a21" - "c44b20c34b20c44b6a21c54b20c44b20c54b6a21c64b20c54b20c64b6a21c74b20c64b20c74b6a21c84b20c74b20c8" - "4b6a21c94b20c84b20c9" - "4b6a21ca4b20c94b20ca4b6a21cb4b20ca4b20cb4b6a21cc4b20cb4b20cc4b6a21cd4b20cc4b20cd4b6a21ce4b20cd" - "4b20ce4b6a21cf4b20ce" + "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" - "4b20d34b20d44b6a21d5" - "4b20d44b20d54b6a21d64b20d54b20d64b6a21d74b20d64b20d74b6a21d84b20d74b20d84b6a21d94b20d84b20d94b" - "6a21da4b20d94b20da4b" - "6a21db4b20da4b20db4b6a21dc4b20db4b20dc4b6a21dd4b20dc4b20dd4b6a21de4b20dd4b20de4b6a21df4b20de4b" - "20df4b6a21e04b20df4b" - "20e04b6a21e14b20e04b20e14b6a21e24b20e14b20e24b6a21e34b20e24b20e34b6a21e44b20e34b20e44b6a21e54b" - "20e44b20e54b6a21e64b" - "20e54b20e64b6a21e74b20e64b20e74b6a21e84b20e74b20e84b6a21e94b20e84b20e94b6a21ea4b20e94b20ea4b6a" - "21eb4b20ea4b20eb4b6a" - "21ec4b20eb4b20ec4b6a21ed4b20ec4b20ed4b6a21ee4b20ed4b20ee4b6a21ef4b20ee4b20ef4b6a21f04b20ef4b20" - "f04b6a21f14b20f04b20" - "f14b6a21f24b20f14b20f24b6a21f34b20f24b20f34b6a21f44b20f34b20f44b6a21f54b20f44b20f54b6a21f64b20" - "f54b20f64b6a21f74b20" - "f64b20f74b6a21f84b20f74b20f84b6a21f94b20f84b20f94b6a21fa4b20f94b20fa4b6a21fb4b20fa4b20fb4b6a21" - "fc4b20fb4b20fc4b6a21" - "fd4b20fc4b20fd4b6a21fe4b20fd4b20fe4b6a21ff4b20fe4b20ff4b6a21804c20ff4b20804c6a21814c20804c2081" - "4c6a21824c20814c2082" - "4c6a21834c20824c20834c6a21844c20834c20844c6a21854c20844c20854c6a21864c20854c20864c6a21874c2086" - "4c20874c6a21884c2087" - "4c20884c6a21894c20884c20894c6a218a4c20894c208a4c6a218b4c208a4c208b4c6a218c4c208b4c208c4c6a218d" - "4c208c4c208d4c6a218e" - "4c208d4c208e4c6a218f4c208e4c208f4c6a21904c208f4c20904c6a21914c20904c20914c6a21924c20914c20924c" - "6a21934c20924c20934c" - "6a21944c20934c20944c6a21954c20944c20954c6a21964c20954c20964c6a21974c20964c20974c6a21984c20974c" - "20984c6a21994c20984c" - "20994c6a219a4c20994c209a4c6a219b4c209a4c209b4c6a219c4c209b4c209c4c6a219d4c209c4c209d4c6a219e4c" - "209d4c209e4c6a219f4c" - "209e4c209f4c6a21a04c209f4c20a04c6a21a14c20a04c20a14c6a21a24c20a14c20a24c6a21a34c20a24c20a34c6a" - "21a44c20a34c20a44c6a" - "21a54c20a44c20a54c6a21a64c20a54c20a64c6a21a74c20a64c20a74c6a21a84c20a74c20a84c6a21a94c20a84c20" - "a94c6a21aa4c20a94c20" - "aa4c6a21ab4c20aa4c20ab4c6a21ac4c20ab4c20ac4c6a21ad4c20ac4c20ad4c6a21ae4c20ad4c20ae4c6a21af4c20" - "ae4c20af4c6a21b04c20" - "af4c20b04c6a21b14c20b04c20b14c6a21b24c20b14c20b24c6a21b34c20b24c20b34c6a21b44c20b34c20b44c6a21" - "b54c20b44c20b54c6a21" - "b64c20b54c20b64c6a21b74c20b64c20b74c6a21b84c20b74c20b84c6a21b94c20b84c20b94c6a21ba4c20b94c20ba" - "4c6a21bb4c20ba4c20bb" - "4c6a21bc4c20bb4c20bc4c6a21bd4c20bc4c20bd4c6a21be4c20bd4c20be4c6a21bf4c20be4c20bf4c6a21c04c20bf" - "4c20c04c6a21c14c20c0" - "4c20c14c6a21c24c20c14c20c24c6a21c34c20c24c20c34c6a21c44c20c34c20c44c6a21c54c20c44c20c54c6a21c6" - "4c20c54c20c64c6a21c7" - "4c20c64c20c74c6a21c84c20c74c20c84c6a21c94c20c84c20c94c6a21ca4c20c94c20ca4c6a21cb4c20ca4c20cb4c" - "6a21cc4c20cb4c20cc4c" - "6a21cd4c20cc4c20cd4c6a21ce4c20cd4c20ce4c6a21cf4c20ce4c20cf4c6a21d04c20cf4c20d04c6a21d14c20d04c" - "20d14c6a21d24c20d14c" - "20d24c6a21d34c20d24c20d34c6a21d44c20d34c20d44c6a21d54c20d44c20d54c6a21d64c20d54c20d64c6a21d74c" - "20d64c20d74c6a21d84c" - "20d74c20d84c6a21d94c20d84c20d94c6a21da4c20d94c20da4c6a21db4c20da4c20db4c6a21dc4c20db4c20dc4c6a" - "21dd4c20dc4c20dd4c6a" - "21de4c20dd4c20de4c6a21df4c20de4c20df4c6a21e04c20df4c20e04c6a21e14c20e04c20e14c6a21e24c20e14c20" - "e24c6a21e34c20e24c20" - "e34c6a21e44c20e34c20e44c6a21e54c20e44c20e54c6a21e64c20e54c20e64c6a21e74c20e64c20e74c6a21e84c20" - "e74c20e84c6a21e94c20" - "e84c20e94c6a21ea4c20e94c20ea4c6a21eb4c20ea4c20eb4c6a21ec4c20eb4c20ec4c6a21ed4c20ec4c20ed4c6a21" - "ee4c20ed4c20ee4c6a21" - "ef4c20ee4c20ef4c6a21f04c20ef4c20f04c6a21f14c20f04c20f14c6a21f24c20f14c20f24c6a21f34c20f24c20f3" - "4c6a21f44c20f34c20f4" - "4c6a21f54c20f44c20f54c6a21f64c20f54c20f64c6a21f74c20f64c20f74c6a21f84c20f74c20f84c6a21f94c20f8" - "4c20f94c6a21fa4c20f9" - "4c20fa4c6a21fb4c20fa4c20fb4c6a21fc4c20fb4c20fc4c6a21fd4c20fc4c20fd4c6a21fe4c20fd4c20fe4c6a21ff" - "4c20fe4c20ff4c6a2180" - "4d20ff4c20804d6a21814d20804d20814d6a21824d20814d20824d6a21834d20824d20834d6a21844d20834d20844d" - "6a21854d20844d20854d" - "6a21864d20854d20864d6a21874d20864d20874d6a21884d20874d20884d6a21894d20884d20894d6a218a4d20894d" - "208a4d6a218b4d208a4d" - "208b4d6a218c4d208b4d208c4d6a218d4d208c4d208d4d6a218e4d208d4d208e4d6a218f4d208e4d208f4d6a21904d" - "208f4d20904d6a21914d" - "20904d20914d6a21924d20914d20924d6a21934d20924d20934d6a21944d20934d20944d6a21954d20944d20954d6a" - "21964d20954d20964d6a" - "21974d20964d20974d6a21984d20974d20984d6a21994d20984d20994d6a219a4d20994d209a4d6a219b4d209a4d20" - "9b4d6a219c4d209b4d20" - "9c4d6a219d4d209c4d209d4d6a219e4d209d4d209e4d6a219f4d209e4d209f4d6a21a04d209f4d20a04d6a21a14d20" - "a04d20a14d6a21a24d20" - "a14d20a24d6a21a34d20a24d20a34d6a21a44d20a34d20a44d6a21a54d20a44d20a54d6a21a64d20a54d20a64d6a21" - "a74d20a64d20a74d6a21" - "a84d20a74d20a84d6a21a94d20a84d20a94d6a21aa4d20a94d20aa4d6a21ab4d20aa4d20ab4d6a21ac4d20ab4d20ac" - "4d6a21ad4d20ac4d20ad" - "4d6a21ae4d20ad4d20ae4d6a21af4d20ae4d20af4d6a21b04d20af4d20b04d6a21b14d20b04d20b14d6a21b24d20b1" - "4d20b24d6a21b34d20b2" - "4d20b34d6a21b44d20b34d20b44d6a21b54d20b44d20b54d6a21b64d20b54d20b64d6a21b74d20b64d20b74d6a21b8" - "4d20b74d20b84d6a21b9" - "4d20b84d20b94d6a21ba4d20b94d20ba4d6a21bb4d20ba4d20bb4d6a21bc4d20bb4d20bc4d6a21bd4d20bc4d20bd4d" - "6a21be4d20bd4d20be4d" - "6a21bf4d20be4d20bf4d6a21c04d20bf4d20c04d6a21c14d20c04d20c14d6a21c24d20c14d20c24d6a21c34d20c24d" - "20c34d6a21c44d20c34d" - "20c44d6a21c54d20c44d20c54d6a21c64d20c54d20c64d6a21c74d20c64d20c74d6a21c84d20c74d20c84d6a21c94d" - "20c84d20c94d6a21ca4d" - "20c94d20ca4d6a21cb4d20ca4d20cb4d6a21cc4d20cb4d20cc4d6a21cd4d20cc4d20cd4d6a21ce4d20cd4d20ce4d6a" - "21cf4d20ce4d20cf4d6a" - "21d04d20cf4d20d04d6a21d14d20d04d20d14d6a21d24d20d14d20d24d6a21d34d20d24d20d34d6a21d44d20d34d20" - "d44d6a21d54d20d44d20" - "d54d6a21d64d20d54d20d64d6a21d74d20d64d20d74d6a21d84d20d74d20d84d6a21d94d20d84d20d94d6a21da4d20" - "d94d20da4d6a21db4d20" + "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" - "e04d20df4d20e04d6a21" - "e14d20e04d20e14d6a21e24d20e14d20e24d6a21e34d20e24d20e34d6a21e44d20e34d20e44d6a21e54d20e44d20e5" - "4d6a21e64d20e54d20e6" - "4d6a21e74d20e64d20e74d6a21e84d20e74d20e84d6a21e94d20e84d20e94d6a21ea4d20e94d20ea4d6a21eb4d20ea" - "4d20eb4d6a21ec4d20eb" - "4d20ec4d6a21ed4d20ec4d20ed4d6a21ee4d20ed4d20ee4d6a21ef4d20ee4d20ef4d6a21f04d20ef4d20f04d6a21f1" - "4d20f04d20f14d6a21f2" - "4d20f14d20f24d6a21f34d20f24d20f34d6a21f44d20f34d20f44d6a21f54d20f44d20f54d6a21f64d20f54d20f64d" - "6a21f74d20f64d20f74d" - "6a21f84d20f74d20f84d6a21f94d20f84d20f94d6a21fa4d20f94d20fa4d6a21fb4d20fa4d20fb4d6a21fc4d20fb4d" - "20fc4d6a21fd4d20fc4d" - "20fd4d6a21fe4d20fd4d20fe4d6a21ff4d20fe4d20ff4d6a21804e20ff4d20804e6a21814e20804e20814e6a21824e" - "20814e20824e6a21834e" - "20824e20834e6a21844e20834e20844e6a21854e20844e20854e6a21864e20854e20864e6a21874e20864e20874e6a" - "21884e20874e20884e6a" - "21894e20884e20894e6a218a4e20894e208a4e6a218b4e208a4e208b4e6a218c4e208b4e208c4e6a218d4e208c4e20" - "8d4e6a218e4e208d4e20" - "8e4e6a218f4e208f4e0b"; + "e04d20df4d20e04d6a21e14d20e04d20e14d6a21e24d20e14d20e24d6a21e34d20e24d20e34d6a21e44d20e34d20e4" + "4d6a21e54d20e44d20e54d6a21e64d20e54d20e64d6a21e74d20e64d20e74d6a21e84d20e74d20e84d6a21e94d20e8" + "4d20e94d6a21ea4d20e94d20ea4d6a21eb4d20ea4d20eb4d6a21ec4d20eb4d20ec4d6a21ed4d20ec4d20ed4d6a21ee" + "4d20ed4d20ee4d6a21ef4d20ee4d20ef4d6a21f04d20ef4d20f04d6a21f14d20f04d20f14d6a21f24d20f14d20f24d" + "6a21f34d20f24d20f34d6a21f44d20f34d20f44d6a21f54d20f44d20f54d6a21f64d20f54d20f64d6a21f74d20f64d" + "20f74d6a21f84d20f74d20f84d6a21f94d20f84d20f94d6a21fa4d20f94d20fa4d6a21fb4d20fa4d20fb4d6a21fc4d" + "20fb4d20fc4d6a21fd4d20fc4d20fd4d6a21fe4d20fd4d20fe4d6a21ff4d20fe4d20ff4d6a21804e20ff4d20804e6a" + "21814e20804e20814e6a21824e20814e20824e6a21834e20824e20834e6a21844e20834e20844e6a21854e20844e20" + "854e6a21864e20854e20864e6a21874e20864e20874e6a21884e20874e20884e6a21894e20884e20894e6a218a4e20" + "894e208a4e6a218b4e208a4e208b4e6a218c4e208b4e208c4e6a218d4e208c4e208d4e6a218e4e208d4e208e4e6a21" + "8f4e208f4e0b"; extern std::string const functions5kHex = "0061736d0100000001070160027f7f017f038a27882700000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "00000000000000000000" - "00000000000007e2d30388270874657374303030300000087465737430303031000108746573743030303200020874" - "65737430303033000308" - "7465737430303034000408746573743030303500050874657374303030360006087465737430303037000708746573" - "74303030380008087465" - "7374303030390009087465737430303130000a087465737430303131000b087465737430303132000c087465737430" - "303133000d0874657374" - "30303134000e087465737430303135000f087465737430303136001008746573743030313700110874657374303031" - "38001208746573743030" - "3139001308746573743030323000140874657374303032310015087465737430303232001608746573743030323300" - "17087465737430303234" - "00180874657374303032350019087465737430303236001a087465737430303237001b087465737430303238001c08" - "7465737430303239001d" + "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" - "73743030333400220874" - "6573743030333500230874657374303033360024087465737430303337002508746573743030333800260874657374" - "30303339002708746573" - "743030343000280874657374303034310029087465737430303432002a087465737430303433002b08746573743030" - "3434002c087465737430" - "303435002d087465737430303436002e087465737430303437002f0874657374303034380030087465737430303439" - "00310874657374303035" - "3000320874657374303035310033087465737430303532003408746573743030353300350874657374303035340036" - "08746573743030353500" - "3708746573743030353600380874657374303035370039087465737430303538003a087465737430303539003b0874" - "65737430303630003c08" - "7465737430303631003d087465737430303632003e087465737430303633003f087465737430303634004008746573" - "74303036350041087465" - "7374303036360042087465737430303637004308746573743030363800440874657374303036390045087465737430" - "30373000460874657374" - "30303731004708746573743030373200480874657374303037330049087465737430303734004a0874657374303037" - "35004b08746573743030" - "3736004c087465737430303737004d087465737430303738004e087465737430303739004f08746573743030383000" - "50087465737430303831" - "0051087465737430303832005208746573743030383300530874657374303038340054087465737430303835005508" - "74657374303038360056" - "087465737430303837005708746573743030383800580874657374303038390059087465737430303930005a087465" - "737430303931005b0874" - "65737430303932005c087465737430303933005d087465737430303934005e087465737430303935005f0874657374" - "30303936006008746573" - "7430303937006108746573743030393800620874657374303039390063087465737430313030006408746573743031" - "30310065087465737430" - "3130320066087465737430313033006708746573743031303400680874657374303130350069087465737430313036" - "006a0874657374303130" - "37006b087465737430313038006c087465737430313039006d087465737430313130006e087465737430313131006f" - "08746573743031313200" - "7008746573743031313300710874657374303131340072087465737430313135007308746573743031313600740874" - "65737430313137007508" - "7465737430313138007608746573743031313900770874657374303132300078087465737430313231007908746573" - "7430313232007a087465" - "737430313233007b087465737430313234007c087465737430313235007d087465737430313236007e087465737430" - "313237007f0874657374" - "3031323800800108746573743031323900810108746573743031333000820108746573743031333100830108746573" - "74303133320084010874" - "6573743031333300850108746573743031333400860108746573743031333500870108746573743031333600880108" - "74657374303133370089" - "01087465737430313338008a01087465737430313339008b01087465737430313430008c0108746573743031343100" - "8d010874657374303134" - "32008e01087465737430313433008f0108746573743031343400900108746573743031343500910108746573743031" - "34360092010874657374" - "3031343700930108746573743031343800940108746573743031343900950108746573743031353000960108746573" - "74303135310097010874" - "65737430313532009801087465737430313533009901087465737430313534009a01087465737430313535009b0108" - "7465737430313536009c" - "01087465737430313537009d01087465737430313538009e01087465737430313539009f0108746573743031363000" - "a0010874657374303136" - "3100a10108746573743031363200a20108746573743031363300a30108746573743031363400a40108746573743031" - "363500a5010874657374" - "3031363600a60108746573743031363700a70108746573743031363800a80108746573743031363900a90108746573" - "743031373000aa010874" - "6573743031373100ab0108746573743031373200ac0108746573743031373300ad0108746573743031373400ae0108" - "746573743031373500af" - "0108746573743031373600b00108746573743031373700b10108746573743031373800b20108746573743031373900" - "b3010874657374303138" - "3000b40108746573743031383100b50108746573743031383200b60108746573743031383300b70108746573743031" - "383400b8010874657374" - "3031383500b90108746573743031383600ba0108746573743031383700bb0108746573743031383800bc0108746573" - "743031383900bd010874" - "6573743031393000be0108746573743031393100bf0108746573743031393200c00108746573743031393300c10108" - "746573743031393400c2" - "0108746573743031393500c30108746573743031393600c40108746573743031393700c50108746573743031393800" - "c6010874657374303139" - "3900c70108746573743032303000c80108746573743032303100c90108746573743032303200ca0108746573743032" - "303300cb010874657374" - "3032303400cc0108746573743032303500cd0108746573743032303600ce0108746573743032303700cf0108746573" - "743032303800d0010874" - "6573743032303900d10108746573743032313000d20108746573743032313100d30108746573743032313200d40108" - "746573743032313300d5" - "0108746573743032313400d60108746573743032313500d70108746573743032313600d80108746573743032313700" - "d9010874657374303231" - "3800da0108746573743032313900db0108746573743032323000dc0108746573743032323100dd0108746573743032" - "323200de010874657374" - "3032323300df0108746573743032323400e00108746573743032323500e10108746573743032323600e20108746573" - "743032323700e3010874" - "6573743032323800e40108746573743032323900e50108746573743032333000e60108746573743032333100e70108" - "746573743032333200e8" - "0108746573743032333300e90108746573743032333400ea0108746573743032333500eb0108746573743032333600" - "ec010874657374303233" - "3700ed0108746573743032333800ee0108746573743032333900ef0108746573743032343000f00108746573743032" - "343100f1010874657374" - "3032343200f20108746573743032343300f30108746573743032343400f40108746573743032343500f50108746573" - "743032343600f6010874" - "6573743032343700f70108746573743032343800f80108746573743032343900f90108746573743032353000fa0108" - "746573743032353100fb" - "0108746573743032353200fc0108746573743032353300fd0108746573743032353400fe0108746573743032353500" - "ff010874657374303235" - "3600800208746573743032353700810208746573743032353800820208746573743032353900830208746573743032" - "36300084020874657374" + "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" - "74303236350089020874" - "65737430323636008a02087465737430323637008b02087465737430323638008c02087465737430323639008d0208" - "7465737430323730008e" - "02087465737430323731008f0208746573743032373200900208746573743032373300910208746573743032373400" - "92020874657374303237" - "3500930208746573743032373600940208746573743032373700950208746573743032373800960208746573743032" - "37390097020874657374" - "30323830009802087465737430323831009902087465737430323832009a02087465737430323833009b0208746573" - "7430323834009c020874" - "65737430323835009d02087465737430323836009e02087465737430323837009f0208746573743032383800a00208" - "746573743032383900a1" - "0208746573743032393000a20208746573743032393100a30208746573743032393200a40208746573743032393300" - "a5020874657374303239" - "3400a60208746573743032393500a70208746573743032393600a80208746573743032393700a90208746573743032" - "393800aa020874657374" - "3032393900ab0208746573743033303000ac0208746573743033303100ad0208746573743033303200ae0208746573" - "743033303300af020874" - "6573743033303400b00208746573743033303500b10208746573743033303600b20208746573743033303700b30208" - "746573743033303800b4" - "0208746573743033303900b50208746573743033313000b60208746573743033313100b70208746573743033313200" - "b8020874657374303331" - "3300b90208746573743033313400ba0208746573743033313500bb0208746573743033313600bc0208746573743033" - "313700bd020874657374" - "3033313800be0208746573743033313900bf0208746573743033323000c00208746573743033323100c10208746573" - "743033323200c2020874" - "6573743033323300c30208746573743033323400c40208746573743033323500c50208746573743033323600c60208" - "746573743033323700c7" - "0208746573743033323800c80208746573743033323900c90208746573743033333000ca0208746573743033333100" - "cb020874657374303333" - "3200cc0208746573743033333300cd0208746573743033333400ce0208746573743033333500cf0208746573743033" - "333600d0020874657374" - "3033333700d10208746573743033333800d20208746573743033333900d30208746573743033343000d40208746573" - "743033343100d5020874" - "6573743033343200d60208746573743033343300d70208746573743033343400d80208746573743033343500d90208" - "746573743033343600da" - "0208746573743033343700db0208746573743033343800dc0208746573743033343900dd0208746573743033353000" - "de020874657374303335" - "3100df0208746573743033353200e00208746573743033353300e10208746573743033353400e20208746573743033" - "353500e3020874657374" - "3033353600e40208746573743033353700e50208746573743033353800e60208746573743033353900e70208746573" - "743033363000e8020874" - "6573743033363100e90208746573743033363200ea0208746573743033363300eb0208746573743033363400ec0208" - "746573743033363500ed" - "0208746573743033363600ee0208746573743033363700ef0208746573743033363800f00208746573743033363900" - "f1020874657374303337" - "3000f20208746573743033373100f30208746573743033373200f40208746573743033373300f50208746573743033" - "373400f6020874657374" - "3033373500f70208746573743033373600f80208746573743033373700f90208746573743033373800fa0208746573" - "743033373900fb020874" - "6573743033383000fc0208746573743033383100fd0208746573743033383200fe0208746573743033383300ff0208" - "74657374303338340080" - "0308746573743033383500810308746573743033383600820308746573743033383700830308746573743033383800" - "84030874657374303338" - "3900850308746573743033393000860308746573743033393100870308746573743033393200880308746573743033" - "39330089030874657374" - "30333934008a03087465737430333935008b03087465737430333936008c03087465737430333937008d0308746573" - "7430333938008e030874" - "65737430333939008f0308746573743034303000900308746573743034303100910308746573743034303200920308" - "74657374303430330093" - "0308746573743034303400940308746573743034303500950308746573743034303600960308746573743034303700" - "97030874657374303430" - "38009803087465737430343039009903087465737430343130009a03087465737430343131009b0308746573743034" - "3132009c030874657374" - "30343133009d03087465737430343134009e03087465737430343135009f0308746573743034313600a00308746573" - "743034313700a1030874" - "6573743034313800a20308746573743034313900a30308746573743034323000a40308746573743034323100a50308" - "746573743034323200a6" - "0308746573743034323300a70308746573743034323400a80308746573743034323500a90308746573743034323600" - "aa030874657374303432" - "3700ab0308746573743034323800ac0308746573743034323900ad0308746573743034333000ae0308746573743034" - "333100af030874657374" - "3034333200b00308746573743034333300b10308746573743034333400b20308746573743034333500b30308746573" - "743034333600b4030874" - "6573743034333700b50308746573743034333800b60308746573743034333900b70308746573743034343000b80308" - "746573743034343100b9" - "0308746573743034343200ba0308746573743034343300bb0308746573743034343400bc0308746573743034343500" - "bd030874657374303434" - "3600be0308746573743034343700bf0308746573743034343800c00308746573743034343900c10308746573743034" - "353000c2030874657374" - "3034353100c30308746573743034353200c40308746573743034353300c50308746573743034353400c60308746573" - "743034353500c7030874" - "6573743034353600c80308746573743034353700c90308746573743034353800ca0308746573743034353900cb0308" - "746573743034363000cc" - "0308746573743034363100cd0308746573743034363200ce0308746573743034363300cf0308746573743034363400" - "d0030874657374303436" - "3500d10308746573743034363600d20308746573743034363700d30308746573743034363800d40308746573743034" - "363900d5030874657374" - "3034373000d60308746573743034373100d70308746573743034373200d80308746573743034373300d90308746573" - "743034373400da030874" - "6573743034373500db0308746573743034373600dc0308746573743034373700dd0308746573743034373800de0308" - "746573743034373900df" - "0308746573743034383000e00308746573743034383100e10308746573743034383200e20308746573743034383300" - "e3030874657374303438" + "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" - "383800e8030874657374" - "3034383900e90308746573743034393000ea0308746573743034393100eb0308746573743034393200ec0308746573" - "743034393300ed030874" - "6573743034393400ee0308746573743034393500ef0308746573743034393600f00308746573743034393700f10308" - "746573743034393800f2" - "0308746573743034393900f30308746573743035303000f40308746573743035303100f50308746573743035303200" - "f6030874657374303530" - "3300f70308746573743035303400f80308746573743035303500f90308746573743035303600fa0308746573743035" - "303700fb030874657374" - "3035303800fc0308746573743035303900fd0308746573743035313000fe0308746573743035313100ff0308746573" - "74303531320080040874" - "6573743035313300810408746573743035313400820408746573743035313500830408746573743035313600840408" - "74657374303531370085" - "0408746573743035313800860408746573743035313900870408746573743035323000880408746573743035323100" - "89040874657374303532" - "32008a04087465737430353233008b04087465737430353234008c04087465737430353235008d0408746573743035" - "3236008e040874657374" - "30353237008f0408746573743035323800900408746573743035323900910408746573743035333000920408746573" - "74303533310093040874" - "6573743035333200940408746573743035333300950408746573743035333400960408746573743035333500970408" - "74657374303533360098" - "04087465737430353337009904087465737430353338009a04087465737430353339009b0408746573743035343000" - "9c040874657374303534" - "31009d04087465737430353432009e04087465737430353433009f0408746573743035343400a00408746573743035" - "343500a1040874657374" - "3035343600a20408746573743035343700a30408746573743035343800a40408746573743035343900a50408746573" - "743035353000a6040874" - "6573743035353100a70408746573743035353200a80408746573743035353300a90408746573743035353400aa0408" - "746573743035353500ab" - "0408746573743035353600ac0408746573743035353700ad0408746573743035353800ae0408746573743035353900" - "af040874657374303536" - "3000b00408746573743035363100b10408746573743035363200b20408746573743035363300b30408746573743035" - "363400b4040874657374" - "3035363500b50408746573743035363600b60408746573743035363700b70408746573743035363800b80408746573" - "743035363900b9040874" - "6573743035373000ba0408746573743035373100bb0408746573743035373200bc0408746573743035373300bd0408" - "746573743035373400be" - "0408746573743035373500bf0408746573743035373600c00408746573743035373700c10408746573743035373800" - "c2040874657374303537" - "3900c30408746573743035383000c40408746573743035383100c50408746573743035383200c60408746573743035" - "383300c7040874657374" - "3035383400c80408746573743035383500c90408746573743035383600ca0408746573743035383700cb0408746573" - "743035383800cc040874" - "6573743035383900cd0408746573743035393000ce0408746573743035393100cf0408746573743035393200d00408" - "746573743035393300d1" - "0408746573743035393400d20408746573743035393500d30408746573743035393600d40408746573743035393700" - "d5040874657374303539" - "3800d60408746573743035393900d70408746573743036303000d80408746573743036303100d90408746573743036" - "303200da040874657374" - "3036303300db0408746573743036303400dc0408746573743036303500dd0408746573743036303600de0408746573" - "743036303700df040874" - "6573743036303800e00408746573743036303900e10408746573743036313000e20408746573743036313100e30408" - "746573743036313200e4" - "0408746573743036313300e50408746573743036313400e60408746573743036313500e70408746573743036313600" - "e8040874657374303631" - "3700e90408746573743036313800ea0408746573743036313900eb0408746573743036323000ec0408746573743036" - "323100ed040874657374" - "3036323200ee0408746573743036323300ef0408746573743036323400f00408746573743036323500f10408746573" - "743036323600f2040874" - "6573743036323700f30408746573743036323800f40408746573743036323900f50408746573743036333000f60408" - "746573743036333100f7" - "0408746573743036333200f80408746573743036333300f90408746573743036333400fa0408746573743036333500" - "fb040874657374303633" - "3600fc0408746573743036333700fd0408746573743036333800fe0408746573743036333900ff0408746573743036" - "34300080050874657374" - "3036343100810508746573743036343200820508746573743036343300830508746573743036343400840508746573" - "74303634350085050874" - "6573743036343600860508746573743036343700870508746573743036343800880508746573743036343900890508" - "7465737430363530008a" - "05087465737430363531008b05087465737430363532008c05087465737430363533008d0508746573743036353400" - "8e050874657374303635" - "35008f0508746573743036353600900508746573743036353700910508746573743036353800920508746573743036" - "35390093050874657374" - "3036363000940508746573743036363100950508746573743036363200960508746573743036363300970508746573" - "74303636340098050874" - "65737430363635009905087465737430363636009a05087465737430363637009b05087465737430363638009c0508" - "7465737430363639009d" - "05087465737430363730009e05087465737430363731009f0508746573743036373200a00508746573743036373300" - "a1050874657374303637" - "3400a20508746573743036373500a30508746573743036373600a40508746573743036373700a50508746573743036" - "373800a6050874657374" - "3036373900a70508746573743036383000a80508746573743036383100a90508746573743036383200aa0508746573" - "743036383300ab050874" - "6573743036383400ac0508746573743036383500ad0508746573743036383600ae0508746573743036383700af0508" - "746573743036383800b0" - "0508746573743036383900b10508746573743036393000b20508746573743036393100b30508746573743036393200" - "b4050874657374303639" - "3300b50508746573743036393400b60508746573743036393500b70508746573743036393600b80508746573743036" - "393700b9050874657374" - "3036393800ba0508746573743036393900bb0508746573743037303000bc0508746573743037303100bd0508746573" - "743037303200be050874" - "6573743037303300bf0508746573743037303400c00508746573743037303500c10508746573743037303600c20508" - "746573743037303700c3" + "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" - "c7050874657374303731" - "3200c80508746573743037313300c90508746573743037313400ca0508746573743037313500cb0508746573743037" - "313600cc050874657374" - "3037313700cd0508746573743037313800ce0508746573743037313900cf0508746573743037323000d00508746573" - "743037323100d1050874" - "6573743037323200d20508746573743037323300d30508746573743037323400d40508746573743037323500d50508" - "746573743037323600d6" - "0508746573743037323700d70508746573743037323800d80508746573743037323900d90508746573743037333000" - "da050874657374303733" - "3100db0508746573743037333200dc0508746573743037333300dd0508746573743037333400de0508746573743037" - "333500df050874657374" - "3037333600e00508746573743037333700e10508746573743037333800e20508746573743037333900e30508746573" - "743037343000e4050874" - "6573743037343100e50508746573743037343200e60508746573743037343300e70508746573743037343400e80508" - "746573743037343500e9" - "0508746573743037343600ea0508746573743037343700eb0508746573743037343800ec0508746573743037343900" - "ed050874657374303735" - "3000ee0508746573743037353100ef0508746573743037353200f00508746573743037353300f10508746573743037" - "353400f2050874657374" - "3037353500f30508746573743037353600f40508746573743037353700f50508746573743037353800f60508746573" - "743037353900f7050874" - "6573743037363000f80508746573743037363100f90508746573743037363200fa0508746573743037363300fb0508" - "746573743037363400fc" - "0508746573743037363500fd0508746573743037363600fe0508746573743037363700ff0508746573743037363800" - "80060874657374303736" - "3900810608746573743037373000820608746573743037373100830608746573743037373200840608746573743037" - "37330085060874657374" - "3037373400860608746573743037373500870608746573743037373600880608746573743037373700890608746573" - "7430373738008a060874" - "65737430373739008b06087465737430373830008c06087465737430373831008d06087465737430373832008e0608" - "7465737430373833008f" - "0608746573743037383400900608746573743037383500910608746573743037383600920608746573743037383700" - "93060874657374303738" - "3800940608746573743037383900950608746573743037393000960608746573743037393100970608746573743037" - "39320098060874657374" - "30373933009906087465737430373934009a06087465737430373935009b06087465737430373936009c0608746573" - "7430373937009d060874" - "65737430373938009e06087465737430373939009f0608746573743038303000a00608746573743038303100a10608" - "746573743038303200a2" - "0608746573743038303300a30608746573743038303400a40608746573743038303500a50608746573743038303600" - "a6060874657374303830" - "3700a70608746573743038303800a80608746573743038303900a90608746573743038313000aa0608746573743038" - "313100ab060874657374" - "3038313200ac0608746573743038313300ad0608746573743038313400ae0608746573743038313500af0608746573" - "743038313600b0060874" - "6573743038313700b10608746573743038313800b20608746573743038313900b30608746573743038323000b40608" - "746573743038323100b5" - "0608746573743038323200b60608746573743038323300b70608746573743038323400b80608746573743038323500" - "b9060874657374303832" - "3600ba0608746573743038323700bb0608746573743038323800bc0608746573743038323900bd0608746573743038" - "333000be060874657374" - "3038333100bf0608746573743038333200c00608746573743038333300c10608746573743038333400c20608746573" - "743038333500c3060874" - "6573743038333600c40608746573743038333700c50608746573743038333800c60608746573743038333900c70608" - "746573743038343000c8" - "0608746573743038343100c90608746573743038343200ca0608746573743038343300cb0608746573743038343400" - "cc060874657374303834" - "3500cd0608746573743038343600ce0608746573743038343700cf0608746573743038343800d00608746573743038" - "343900d1060874657374" - "3038353000d20608746573743038353100d30608746573743038353200d40608746573743038353300d50608746573" - "743038353400d6060874" - "6573743038353500d70608746573743038353600d80608746573743038353700d90608746573743038353800da0608" - "746573743038353900db" - "0608746573743038363000dc0608746573743038363100dd0608746573743038363200de0608746573743038363300" - "df060874657374303836" - "3400e00608746573743038363500e10608746573743038363600e20608746573743038363700e30608746573743038" - "363800e4060874657374" - "3038363900e50608746573743038373000e60608746573743038373100e70608746573743038373200e80608746573" - "743038373300e9060874" - "6573743038373400ea0608746573743038373500eb0608746573743038373600ec0608746573743038373700ed0608" - "746573743038373800ee" - "0608746573743038373900ef0608746573743038383000f00608746573743038383100f10608746573743038383200" - "f2060874657374303838" - "3300f30608746573743038383400f40608746573743038383500f50608746573743038383600f60608746573743038" - "383700f7060874657374" - "3038383800f80608746573743038383900f90608746573743038393000fa0608746573743038393100fb0608746573" - "743038393200fc060874" - "6573743038393300fd0608746573743038393400fe0608746573743038393500ff0608746573743038393600800708" - "74657374303839370081" - "0708746573743038393800820708746573743038393900830708746573743039303000840708746573743039303100" - "85070874657374303930" - "3200860708746573743039303300870708746573743039303400880708746573743039303500890708746573743039" - "3036008a070874657374" - "30393037008b07087465737430393038008c07087465737430393039008d07087465737430393130008e0708746573" - "7430393131008f070874" - "6573743039313200900708746573743039313300910708746573743039313400920708746573743039313500930708" - "74657374303931360094" - "0708746573743039313700950708746573743039313800960708746573743039313900970708746573743039323000" - "98070874657374303932" - "31009907087465737430393232009a07087465737430393233009b07087465737430393234009c0708746573743039" - "3235009d070874657374" - "30393236009e07087465737430393237009f0708746573743039323800a00708746573743039323900a10708746573" - "743039333000a2070874" + "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" - "746573743039333500a7" - "0708746573743039333600a80708746573743039333700a90708746573743039333800aa0708746573743039333900" - "ab070874657374303934" - "3000ac0708746573743039343100ad0708746573743039343200ae0708746573743039343300af0708746573743039" - "343400b0070874657374" - "3039343500b10708746573743039343600b20708746573743039343700b30708746573743039343800b40708746573" - "743039343900b5070874" - "6573743039353000b60708746573743039353100b70708746573743039353200b80708746573743039353300b90708" - "746573743039353400ba" - "0708746573743039353500bb0708746573743039353600bc0708746573743039353700bd0708746573743039353800" - "be070874657374303935" - "3900bf0708746573743039363000c00708746573743039363100c10708746573743039363200c20708746573743039" - "363300c3070874657374" - "3039363400c40708746573743039363500c50708746573743039363600c60708746573743039363700c70708746573" - "743039363800c8070874" - "6573743039363900c90708746573743039373000ca0708746573743039373100cb0708746573743039373200cc0708" - "746573743039373300cd" - "0708746573743039373400ce0708746573743039373500cf0708746573743039373600d00708746573743039373700" - "d1070874657374303937" - "3800d20708746573743039373900d30708746573743039383000d40708746573743039383100d50708746573743039" - "383200d6070874657374" - "3039383300d70708746573743039383400d80708746573743039383500d90708746573743039383600da0708746573" - "743039383700db070874" - "6573743039383800dc0708746573743039383900dd0708746573743039393000de0708746573743039393100df0708" - "746573743039393200e0" - "0708746573743039393300e10708746573743039393400e20708746573743039393500e30708746573743039393600" - "e4070874657374303939" - "3700e50708746573743039393800e60708746573743039393900e70708746573743130303000e80708746573743130" - "303100e9070874657374" - "3130303200ea0708746573743130303300eb0708746573743130303400ec0708746573743130303500ed0708746573" - "743130303600ee070874" - "6573743130303700ef0708746573743130303800f00708746573743130303900f10708746573743130313000f20708" - "746573743130313100f3" - "0708746573743130313200f40708746573743130313300f50708746573743130313400f60708746573743130313500" - "f7070874657374313031" - "3600f80708746573743130313700f90708746573743130313800fa0708746573743130313900fb0708746573743130" - "323000fc070874657374" - "3130323100fd0708746573743130323200fe0708746573743130323300ff0708746573743130323400800808746573" - "74313032350081080874" - "6573743130323600820808746573743130323700830808746573743130323800840808746573743130323900850808" - "74657374313033300086" - "0808746573743130333100870808746573743130333200880808746573743130333300890808746573743130333400" - "8a080874657374313033" - "35008b08087465737431303336008c08087465737431303337008d08087465737431303338008e0808746573743130" - "3339008f080874657374" - "3130343000900808746573743130343100910808746573743130343200920808746573743130343300930808746573" - "74313034340094080874" - "6573743130343500950808746573743130343600960808746573743130343700970808746573743130343800980808" - "74657374313034390099" - "08087465737431303530009a08087465737431303531009b08087465737431303532009c0808746573743130353300" - "9d080874657374313035" - "34009e08087465737431303535009f0808746573743130353600a00808746573743130353700a10808746573743130" - "353800a2080874657374" - "3130353900a30808746573743130363000a40808746573743130363100a50808746573743130363200a60808746573" - "743130363300a7080874" - "6573743130363400a80808746573743130363500a90808746573743130363600aa0808746573743130363700ab0808" - "746573743130363800ac" - "0808746573743130363900ad0808746573743130373000ae0808746573743130373100af0808746573743130373200" - "b0080874657374313037" - "3300b10808746573743130373400b20808746573743130373500b30808746573743130373600b40808746573743130" - "373700b5080874657374" - "3130373800b60808746573743130373900b70808746573743130383000b80808746573743130383100b90808746573" - "743130383200ba080874" - "6573743130383300bb0808746573743130383400bc0808746573743130383500bd0808746573743130383600be0808" - "746573743130383700bf" - "0808746573743130383800c00808746573743130383900c10808746573743130393000c20808746573743130393100" - "c3080874657374313039" - "3200c40808746573743130393300c50808746573743130393400c60808746573743130393500c70808746573743130" - "393600c8080874657374" - "3130393700c90808746573743130393800ca0808746573743130393900cb0808746573743131303000cc0808746573" - "743131303100cd080874" - "6573743131303200ce0808746573743131303300cf0808746573743131303400d00808746573743131303500d10808" - "746573743131303600d2" - "0808746573743131303700d30808746573743131303800d40808746573743131303900d50808746573743131313000" - "d6080874657374313131" - "3100d70808746573743131313200d80808746573743131313300d90808746573743131313400da0808746573743131" - "313500db080874657374" - "3131313600dc0808746573743131313700dd0808746573743131313800de0808746573743131313900df0808746573" - "743131323000e0080874" - "6573743131323100e10808746573743131323200e20808746573743131323300e30808746573743131323400e40808" - "746573743131323500e5" - "0808746573743131323600e60808746573743131323700e70808746573743131323800e80808746573743131323900" - "e9080874657374313133" - "3000ea0808746573743131333100eb0808746573743131333200ec0808746573743131333300ed0808746573743131" - "333400ee080874657374" - "3131333500ef0808746573743131333600f00808746573743131333700f10808746573743131333800f20808746573" - "743131333900f3080874" - "6573743131343000f40808746573743131343100f50808746573743131343200f60808746573743131343300f70808" - "746573743131343400f8" - "0808746573743131343500f90808746573743131343600fa0808746573743131343700fb0808746573743131343800" - "fc080874657374313134" - "3900fd0808746573743131353000fe0808746573743131353100ff0808746573743131353200800908746573743131" - "35330081090874657374" + "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" - "74313135380086090874" - "65737431313539008709087465737431313630008809087465737431313631008909087465737431313632008a0908" - "7465737431313633008b" - "09087465737431313634008c09087465737431313635008d09087465737431313636008e0908746573743131363700" - "8f090874657374313136" - "3800900908746573743131363900910908746573743131373000920908746573743131373100930908746573743131" - "37320094090874657374" - "3131373300950908746573743131373400960908746573743131373500970908746573743131373600980908746573" - "74313137370099090874" - "65737431313738009a09087465737431313739009b09087465737431313830009c09087465737431313831009d0908" - "7465737431313832009e" - "09087465737431313833009f0908746573743131383400a00908746573743131383500a10908746573743131383600" - "a2090874657374313138" - "3700a30908746573743131383800a40908746573743131383900a50908746573743131393000a60908746573743131" - "393100a7090874657374" - "3131393200a80908746573743131393300a90908746573743131393400aa0908746573743131393500ab0908746573" - "743131393600ac090874" - "6573743131393700ad0908746573743131393800ae0908746573743131393900af0908746573743132303000b00908" - "746573743132303100b1" - "0908746573743132303200b20908746573743132303300b30908746573743132303400b40908746573743132303500" - "b5090874657374313230" - "3600b60908746573743132303700b70908746573743132303800b80908746573743132303900b90908746573743132" - "313000ba090874657374" - "3132313100bb0908746573743132313200bc0908746573743132313300bd0908746573743132313400be0908746573" - "743132313500bf090874" - "6573743132313600c00908746573743132313700c10908746573743132313800c20908746573743132313900c30908" - "746573743132323000c4" - "0908746573743132323100c50908746573743132323200c60908746573743132323300c70908746573743132323400" - "c8090874657374313232" - "3500c90908746573743132323600ca0908746573743132323700cb0908746573743132323800cc0908746573743132" - "323900cd090874657374" - "3132333000ce0908746573743132333100cf0908746573743132333200d00908746573743132333300d10908746573" - "743132333400d2090874" - "6573743132333500d30908746573743132333600d40908746573743132333700d50908746573743132333800d60908" - "746573743132333900d7" - "0908746573743132343000d80908746573743132343100d90908746573743132343200da0908746573743132343300" - "db090874657374313234" - "3400dc0908746573743132343500dd0908746573743132343600de0908746573743132343700df0908746573743132" - "343800e0090874657374" - "3132343900e10908746573743132353000e20908746573743132353100e30908746573743132353200e40908746573" - "743132353300e5090874" - "6573743132353400e60908746573743132353500e70908746573743132353600e80908746573743132353700e90908" - "746573743132353800ea" - "0908746573743132353900eb0908746573743132363000ec0908746573743132363100ed0908746573743132363200" - "ee090874657374313236" - "3300ef0908746573743132363400f00908746573743132363500f10908746573743132363600f20908746573743132" - "363700f3090874657374" - "3132363800f40908746573743132363900f50908746573743132373000f60908746573743132373100f70908746573" - "743132373200f8090874" - "6573743132373300f90908746573743132373400fa0908746573743132373500fb0908746573743132373600fc0908" - "746573743132373700fd" - "0908746573743132373800fe0908746573743132373900ff0908746573743132383000800a08746573743132383100" - "810a0874657374313238" - "3200820a08746573743132383300830a08746573743132383400840a08746573743132383500850a08746573743132" - "383600860a0874657374" - "3132383700870a08746573743132383800880a08746573743132383900890a087465737431323930008a0a08746573" - "7431323931008b0a0874" - "65737431323932008c0a087465737431323933008d0a087465737431323934008e0a087465737431323935008f0a08" - "74657374313239360090" - "0a08746573743132393700910a08746573743132393800920a08746573743132393900930a08746573743133303000" - "940a0874657374313330" - "3100950a08746573743133303200960a08746573743133303300970a08746573743133303400980a08746573743133" - "303500990a0874657374" - "31333036009a0a087465737431333037009b0a087465737431333038009c0a087465737431333039009d0a08746573" - "7431333130009e0a0874" - "65737431333131009f0a08746573743133313200a00a08746573743133313300a10a08746573743133313400a20a08" - "746573743133313500a3" - "0a08746573743133313600a40a08746573743133313700a50a08746573743133313800a60a08746573743133313900" - "a70a0874657374313332" - "3000a80a08746573743133323100a90a08746573743133323200aa0a08746573743133323300ab0a08746573743133" - "323400ac0a0874657374" - "3133323500ad0a08746573743133323600ae0a08746573743133323700af0a08746573743133323800b00a08746573" - "743133323900b10a0874" - "6573743133333000b20a08746573743133333100b30a08746573743133333200b40a08746573743133333300b50a08" - "746573743133333400b6" - "0a08746573743133333500b70a08746573743133333600b80a08746573743133333700b90a08746573743133333800" - "ba0a0874657374313333" - "3900bb0a08746573743133343000bc0a08746573743133343100bd0a08746573743133343200be0a08746573743133" - "343300bf0a0874657374" - "3133343400c00a08746573743133343500c10a08746573743133343600c20a08746573743133343700c30a08746573" - "743133343800c40a0874" - "6573743133343900c50a08746573743133353000c60a08746573743133353100c70a08746573743133353200c80a08" - "746573743133353300c9" - "0a08746573743133353400ca0a08746573743133353500cb0a08746573743133353600cc0a08746573743133353700" - "cd0a0874657374313335" - "3800ce0a08746573743133353900cf0a08746573743133363000d00a08746573743133363100d10a08746573743133" - "363200d20a0874657374" - "3133363300d30a08746573743133363400d40a08746573743133363500d50a08746573743133363600d60a08746573" - "743133363700d70a0874" - "6573743133363800d80a08746573743133363900d90a08746573743133373000da0a08746573743133373100db0a08" - "746573743133373200dc" - "0a08746573743133373300dd0a08746573743133373400de0a08746573743133373500df0a08746573743133373600" - "e00a0874657374313337" + "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" - "383100e50a0874657374" - "3133383200e60a08746573743133383300e70a08746573743133383400e80a08746573743133383500e90a08746573" - "743133383600ea0a0874" - "6573743133383700eb0a08746573743133383800ec0a08746573743133383900ed0a08746573743133393000ee0a08" - "746573743133393100ef" - "0a08746573743133393200f00a08746573743133393300f10a08746573743133393400f20a08746573743133393500" - "f30a0874657374313339" - "3600f40a08746573743133393700f50a08746573743133393800f60a08746573743133393900f70a08746573743134" - "303000f80a0874657374" - "3134303100f90a08746573743134303200fa0a08746573743134303300fb0a08746573743134303400fc0a08746573" - "743134303500fd0a0874" - "6573743134303600fe0a08746573743134303700ff0a08746573743134303800800b08746573743134303900810b08" - "74657374313431300082" - "0b08746573743134313100830b08746573743134313200840b08746573743134313300850b08746573743134313400" - "860b0874657374313431" - "3500870b08746573743134313600880b08746573743134313700890b087465737431343138008a0b08746573743134" - "3139008b0b0874657374" - "31343230008c0b087465737431343231008d0b087465737431343232008e0b087465737431343233008f0b08746573" - "743134323400900b0874" - "6573743134323500910b08746573743134323600920b08746573743134323700930b08746573743134323800940b08" - "74657374313432390095" - "0b08746573743134333000960b08746573743134333100970b08746573743134333200980b08746573743134333300" - "990b0874657374313433" - "34009a0b087465737431343335009b0b087465737431343336009c0b087465737431343337009d0b08746573743134" - "3338009e0b0874657374" - "31343339009f0b08746573743134343000a00b08746573743134343100a10b08746573743134343200a20b08746573" - "743134343300a30b0874" - "6573743134343400a40b08746573743134343500a50b08746573743134343600a60b08746573743134343700a70b08" - "746573743134343800a8" - "0b08746573743134343900a90b08746573743134353000aa0b08746573743134353100ab0b08746573743134353200" - "ac0b0874657374313435" - "3300ad0b08746573743134353400ae0b08746573743134353500af0b08746573743134353600b00b08746573743134" - "353700b10b0874657374" - "3134353800b20b08746573743134353900b30b08746573743134363000b40b08746573743134363100b50b08746573" - "743134363200b60b0874" - "6573743134363300b70b08746573743134363400b80b08746573743134363500b90b08746573743134363600ba0b08" - "746573743134363700bb" - "0b08746573743134363800bc0b08746573743134363900bd0b08746573743134373000be0b08746573743134373100" - "bf0b0874657374313437" - "3200c00b08746573743134373300c10b08746573743134373400c20b08746573743134373500c30b08746573743134" - "373600c40b0874657374" - "3134373700c50b08746573743134373800c60b08746573743134373900c70b08746573743134383000c80b08746573" - "743134383100c90b0874" - "6573743134383200ca0b08746573743134383300cb0b08746573743134383400cc0b08746573743134383500cd0b08" - "746573743134383600ce" - "0b08746573743134383700cf0b08746573743134383800d00b08746573743134383900d10b08746573743134393000" - "d20b0874657374313439" - "3100d30b08746573743134393200d40b08746573743134393300d50b08746573743134393400d60b08746573743134" - "393500d70b0874657374" - "3134393600d80b08746573743134393700d90b08746573743134393800da0b08746573743134393900db0b08746573" - "743135303000dc0b0874" - "6573743135303100dd0b08746573743135303200de0b08746573743135303300df0b08746573743135303400e00b08" - "746573743135303500e1" - "0b08746573743135303600e20b08746573743135303700e30b08746573743135303800e40b08746573743135303900" - "e50b0874657374313531" - "3000e60b08746573743135313100e70b08746573743135313200e80b08746573743135313300e90b08746573743135" - "313400ea0b0874657374" - "3135313500eb0b08746573743135313600ec0b08746573743135313700ed0b08746573743135313800ee0b08746573" - "743135313900ef0b0874" - "6573743135323000f00b08746573743135323100f10b08746573743135323200f20b08746573743135323300f30b08" - "746573743135323400f4" - "0b08746573743135323500f50b08746573743135323600f60b08746573743135323700f70b08746573743135323800" - "f80b0874657374313532" - "3900f90b08746573743135333000fa0b08746573743135333100fb0b08746573743135333200fc0b08746573743135" - "333300fd0b0874657374" - "3135333400fe0b08746573743135333500ff0b08746573743135333600800c08746573743135333700810c08746573" - "743135333800820c0874" - "6573743135333900830c08746573743135343000840c08746573743135343100850c08746573743135343200860c08" - "74657374313534330087" - "0c08746573743135343400880c08746573743135343500890c087465737431353436008a0c08746573743135343700" - "8b0c0874657374313534" - "38008c0c087465737431353439008d0c087465737431353530008e0c087465737431353531008f0c08746573743135" - "353200900c0874657374" - "3135353300910c08746573743135353400920c08746573743135353500930c08746573743135353600940c08746573" - "743135353700950c0874" - "6573743135353800960c08746573743135353900970c08746573743135363000980c08746573743135363100990c08" - "7465737431353632009a" - "0c087465737431353633009b0c087465737431353634009c0c087465737431353635009d0c08746573743135363600" - "9e0c0874657374313536" - "37009f0c08746573743135363800a00c08746573743135363900a10c08746573743135373000a20c08746573743135" - "373100a30c0874657374" - "3135373200a40c08746573743135373300a50c08746573743135373400a60c08746573743135373500a70c08746573" - "743135373600a80c0874" - "6573743135373700a90c08746573743135373800aa0c08746573743135373900ab0c08746573743135383000ac0c08" - "746573743135383100ad" - "0c08746573743135383200ae0c08746573743135383300af0c08746573743135383400b00c08746573743135383500" - "b10c0874657374313538" - "3600b20c08746573743135383700b30c08746573743135383800b40c08746573743135383900b50c08746573743135" - "393000b60c0874657374" - "3135393100b70c08746573743135393200b80c08746573743135393300b90c08746573743135393400ba0c08746573" - "743135393500bb0c0874" - "6573743135393600bc0c08746573743135393700bd0c08746573743135393800be0c08746573743135393900bf0c08" - "746573743136303000c0" + "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" - "c40c0874657374313630" - "3500c50c08746573743136303600c60c08746573743136303700c70c08746573743136303800c80c08746573743136" - "303900c90c0874657374" - "3136313000ca0c08746573743136313100cb0c08746573743136313200cc0c08746573743136313300cd0c08746573" - "743136313400ce0c0874" - "6573743136313500cf0c08746573743136313600d00c08746573743136313700d10c08746573743136313800d20c08" - "746573743136313900d3" - "0c08746573743136323000d40c08746573743136323100d50c08746573743136323200d60c08746573743136323300" - "d70c0874657374313632" - "3400d80c08746573743136323500d90c08746573743136323600da0c08746573743136323700db0c08746573743136" - "323800dc0c0874657374" - "3136323900dd0c08746573743136333000de0c08746573743136333100df0c08746573743136333200e00c08746573" - "743136333300e10c0874" - "6573743136333400e20c08746573743136333500e30c08746573743136333600e40c08746573743136333700e50c08" - "746573743136333800e6" - "0c08746573743136333900e70c08746573743136343000e80c08746573743136343100e90c08746573743136343200" - "ea0c0874657374313634" - "3300eb0c08746573743136343400ec0c08746573743136343500ed0c08746573743136343600ee0c08746573743136" - "343700ef0c0874657374" - "3136343800f00c08746573743136343900f10c08746573743136353000f20c08746573743136353100f30c08746573" - "743136353200f40c0874" - "6573743136353300f50c08746573743136353400f60c08746573743136353500f70c08746573743136353600f80c08" - "746573743136353700f9" - "0c08746573743136353800fa0c08746573743136353900fb0c08746573743136363000fc0c08746573743136363100" - "fd0c0874657374313636" - "3200fe0c08746573743136363300ff0c08746573743136363400800d08746573743136363500810d08746573743136" - "363600820d0874657374" - "3136363700830d08746573743136363800840d08746573743136363900850d08746573743136373000860d08746573" - "743136373100870d0874" - "6573743136373200880d08746573743136373300890d087465737431363734008a0d087465737431363735008b0d08" - "7465737431363736008c" - "0d087465737431363737008d0d087465737431363738008e0d087465737431363739008f0d08746573743136383000" - "900d0874657374313638" - "3100910d08746573743136383200920d08746573743136383300930d08746573743136383400940d08746573743136" - "383500950d0874657374" - "3136383600960d08746573743136383700970d08746573743136383800980d08746573743136383900990d08746573" - "7431363930009a0d0874" - "65737431363931009b0d087465737431363932009c0d087465737431363933009d0d087465737431363934009e0d08" - "7465737431363935009f" - "0d08746573743136393600a00d08746573743136393700a10d08746573743136393800a20d08746573743136393900" - "a30d0874657374313730" - "3000a40d08746573743137303100a50d08746573743137303200a60d08746573743137303300a70d08746573743137" - "303400a80d0874657374" - "3137303500a90d08746573743137303600aa0d08746573743137303700ab0d08746573743137303800ac0d08746573" - "743137303900ad0d0874" - "6573743137313000ae0d08746573743137313100af0d08746573743137313200b00d08746573743137313300b10d08" - "746573743137313400b2" - "0d08746573743137313500b30d08746573743137313600b40d08746573743137313700b50d08746573743137313800" - "b60d0874657374313731" - "3900b70d08746573743137323000b80d08746573743137323100b90d08746573743137323200ba0d08746573743137" - "323300bb0d0874657374" - "3137323400bc0d08746573743137323500bd0d08746573743137323600be0d08746573743137323700bf0d08746573" - "743137323800c00d0874" - "6573743137323900c10d08746573743137333000c20d08746573743137333100c30d08746573743137333200c40d08" - "746573743137333300c5" - "0d08746573743137333400c60d08746573743137333500c70d08746573743137333600c80d08746573743137333700" - "c90d0874657374313733" - "3800ca0d08746573743137333900cb0d08746573743137343000cc0d08746573743137343100cd0d08746573743137" - "343200ce0d0874657374" - "3137343300cf0d08746573743137343400d00d08746573743137343500d10d08746573743137343600d20d08746573" - "743137343700d30d0874" - "6573743137343800d40d08746573743137343900d50d08746573743137353000d60d08746573743137353100d70d08" - "746573743137353200d8" - "0d08746573743137353300d90d08746573743137353400da0d08746573743137353500db0d08746573743137353600" - "dc0d0874657374313735" - "3700dd0d08746573743137353800de0d08746573743137353900df0d08746573743137363000e00d08746573743137" - "363100e10d0874657374" - "3137363200e20d08746573743137363300e30d08746573743137363400e40d08746573743137363500e50d08746573" - "743137363600e60d0874" - "6573743137363700e70d08746573743137363800e80d08746573743137363900e90d08746573743137373000ea0d08" - "746573743137373100eb" - "0d08746573743137373200ec0d08746573743137373300ed0d08746573743137373400ee0d08746573743137373500" - "ef0d0874657374313737" - "3600f00d08746573743137373700f10d08746573743137373800f20d08746573743137373900f30d08746573743137" - "383000f40d0874657374" - "3137383100f50d08746573743137383200f60d08746573743137383300f70d08746573743137383400f80d08746573" - "743137383500f90d0874" - "6573743137383600fa0d08746573743137383700fb0d08746573743137383800fc0d08746573743137383900fd0d08" - "746573743137393000fe" - "0d08746573743137393100ff0d08746573743137393200800e08746573743137393300810e08746573743137393400" - "820e0874657374313739" - "3500830e08746573743137393600840e08746573743137393700850e08746573743137393800860e08746573743137" - "393900870e0874657374" - "3138303000880e08746573743138303100890e087465737431383032008a0e087465737431383033008b0e08746573" - "7431383034008c0e0874" - "65737431383035008d0e087465737431383036008e0e087465737431383037008f0e08746573743138303800900e08" - "74657374313830390091" - "0e08746573743138313000920e08746573743138313100930e08746573743138313200940e08746573743138313300" - "950e0874657374313831" - "3400960e08746573743138313500970e08746573743138313600980e08746573743138313700990e08746573743138" - "3138009a0e0874657374" - "31383139009b0e087465737431383230009c0e087465737431383231009d0e087465737431383232009e0e08746573" - "7431383233009f0e0874" + "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" - "746573743138323800a4" - "0e08746573743138323900a50e08746573743138333000a60e08746573743138333100a70e08746573743138333200" - "a80e0874657374313833" - "3300a90e08746573743138333400aa0e08746573743138333500ab0e08746573743138333600ac0e08746573743138" - "333700ad0e0874657374" - "3138333800ae0e08746573743138333900af0e08746573743138343000b00e08746573743138343100b10e08746573" - "743138343200b20e0874" - "6573743138343300b30e08746573743138343400b40e08746573743138343500b50e08746573743138343600b60e08" - "746573743138343700b7" - "0e08746573743138343800b80e08746573743138343900b90e08746573743138353000ba0e08746573743138353100" - "bb0e0874657374313835" - "3200bc0e08746573743138353300bd0e08746573743138353400be0e08746573743138353500bf0e08746573743138" - "353600c00e0874657374" - "3138353700c10e08746573743138353800c20e08746573743138353900c30e08746573743138363000c40e08746573" - "743138363100c50e0874" - "6573743138363200c60e08746573743138363300c70e08746573743138363400c80e08746573743138363500c90e08" - "746573743138363600ca" - "0e08746573743138363700cb0e08746573743138363800cc0e08746573743138363900cd0e08746573743138373000" - "ce0e0874657374313837" - "3100cf0e08746573743138373200d00e08746573743138373300d10e08746573743138373400d20e08746573743138" - "373500d30e0874657374" - "3138373600d40e08746573743138373700d50e08746573743138373800d60e08746573743138373900d70e08746573" - "743138383000d80e0874" - "6573743138383100d90e08746573743138383200da0e08746573743138383300db0e08746573743138383400dc0e08" - "746573743138383500dd" - "0e08746573743138383600de0e08746573743138383700df0e08746573743138383800e00e08746573743138383900" - "e10e0874657374313839" - "3000e20e08746573743138393100e30e08746573743138393200e40e08746573743138393300e50e08746573743138" - "393400e60e0874657374" - "3138393500e70e08746573743138393600e80e08746573743138393700e90e08746573743138393800ea0e08746573" - "743138393900eb0e0874" - "6573743139303000ec0e08746573743139303100ed0e08746573743139303200ee0e08746573743139303300ef0e08" - "746573743139303400f0" - "0e08746573743139303500f10e08746573743139303600f20e08746573743139303700f30e08746573743139303800" - "f40e0874657374313930" - "3900f50e08746573743139313000f60e08746573743139313100f70e08746573743139313200f80e08746573743139" - "313300f90e0874657374" - "3139313400fa0e08746573743139313500fb0e08746573743139313600fc0e08746573743139313700fd0e08746573" - "743139313800fe0e0874" - "6573743139313900ff0e08746573743139323000800f08746573743139323100810f08746573743139323200820f08" - "74657374313932330083" - "0f08746573743139323400840f08746573743139323500850f08746573743139323600860f08746573743139323700" - "870f0874657374313932" - "3800880f08746573743139323900890f087465737431393330008a0f087465737431393331008b0f08746573743139" - "3332008c0f0874657374" - "31393333008d0f087465737431393334008e0f087465737431393335008f0f08746573743139333600900f08746573" - "743139333700910f0874" - "6573743139333800920f08746573743139333900930f08746573743139343000940f08746573743139343100950f08" - "74657374313934320096" - "0f08746573743139343300970f08746573743139343400980f08746573743139343500990f08746573743139343600" - "9a0f0874657374313934" - "37009b0f087465737431393438009c0f087465737431393439009d0f087465737431393530009e0f08746573743139" - "3531009f0f0874657374" - "3139353200a00f08746573743139353300a10f08746573743139353400a20f08746573743139353500a30f08746573" - "743139353600a40f0874" - "6573743139353700a50f08746573743139353800a60f08746573743139353900a70f08746573743139363000a80f08" - "746573743139363100a9" - "0f08746573743139363200aa0f08746573743139363300ab0f08746573743139363400ac0f08746573743139363500" - "ad0f0874657374313936" - "3600ae0f08746573743139363700af0f08746573743139363800b00f08746573743139363900b10f08746573743139" - "373000b20f0874657374" - "3139373100b30f08746573743139373200b40f08746573743139373300b50f08746573743139373400b60f08746573" - "743139373500b70f0874" - "6573743139373600b80f08746573743139373700b90f08746573743139373800ba0f08746573743139373900bb0f08" - "746573743139383000bc" - "0f08746573743139383100bd0f08746573743139383200be0f08746573743139383300bf0f08746573743139383400" - "c00f0874657374313938" - "3500c10f08746573743139383600c20f08746573743139383700c30f08746573743139383800c40f08746573743139" - "383900c50f0874657374" - "3139393000c60f08746573743139393100c70f08746573743139393200c80f08746573743139393300c90f08746573" - "743139393400ca0f0874" - "6573743139393500cb0f08746573743139393600cc0f08746573743139393700cd0f08746573743139393800ce0f08" - "746573743139393900cf" - "0f08746573743230303000d00f08746573743230303100d10f08746573743230303200d20f08746573743230303300" - "d30f0874657374323030" - "3400d40f08746573743230303500d50f08746573743230303600d60f08746573743230303700d70f08746573743230" - "303800d80f0874657374" - "3230303900d90f08746573743230313000da0f08746573743230313100db0f08746573743230313200dc0f08746573" - "743230313300dd0f0874" - "6573743230313400de0f08746573743230313500df0f08746573743230313600e00f08746573743230313700e10f08" - "746573743230313800e2" - "0f08746573743230313900e30f08746573743230323000e40f08746573743230323100e50f08746573743230323200" - "e60f0874657374323032" - "3300e70f08746573743230323400e80f08746573743230323500e90f08746573743230323600ea0f08746573743230" - "323700eb0f0874657374" - "3230323800ec0f08746573743230323900ed0f08746573743230333000ee0f08746573743230333100ef0f08746573" - "743230333200f00f0874" - "6573743230333300f10f08746573743230333400f20f08746573743230333500f30f08746573743230333600f40f08" - "746573743230333700f5" - "0f08746573743230333800f60f08746573743230333900f70f08746573743230343000f80f08746573743230343100" - "f90f0874657374323034" - "3200fa0f08746573743230343300fb0f08746573743230343400fc0f08746573743230343500fd0f08746573743230" - "343600fe0f0874657374" + "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" - "74323035310083100874" - "6573743230353200841008746573743230353300851008746573743230353400861008746573743230353500871008" - "74657374323035360088" - "10087465737432303537008910087465737432303538008a10087465737432303539008b1008746573743230363000" - "8c100874657374323036" - "31008d10087465737432303632008e10087465737432303633008f1008746573743230363400901008746573743230" - "36350091100874657374" - "3230363600921008746573743230363700931008746573743230363800941008746573743230363900951008746573" - "74323037300096100874" - "65737432303731009710087465737432303732009810087465737432303733009910087465737432303734009a1008" - "7465737432303735009b" - "10087465737432303736009c10087465737432303737009d10087465737432303738009e1008746573743230373900" - "9f100874657374323038" - "3000a01008746573743230383100a11008746573743230383200a21008746573743230383300a31008746573743230" - "383400a4100874657374" - "3230383500a51008746573743230383600a61008746573743230383700a71008746573743230383800a81008746573" - "743230383900a9100874" - "6573743230393000aa1008746573743230393100ab1008746573743230393200ac1008746573743230393300ad1008" - "746573743230393400ae" - "1008746573743230393500af1008746573743230393600b01008746573743230393700b11008746573743230393800" - "b2100874657374323039" - "3900b31008746573743231303000b41008746573743231303100b51008746573743231303200b61008746573743231" - "303300b7100874657374" - "3231303400b81008746573743231303500b91008746573743231303600ba1008746573743231303700bb1008746573" - "743231303800bc100874" - "6573743231303900bd1008746573743231313000be1008746573743231313100bf1008746573743231313200c01008" - "746573743231313300c1" - "1008746573743231313400c21008746573743231313500c31008746573743231313600c41008746573743231313700" - "c5100874657374323131" - "3800c61008746573743231313900c71008746573743231323000c81008746573743231323100c91008746573743231" - "323200ca100874657374" - "3231323300cb1008746573743231323400cc1008746573743231323500cd1008746573743231323600ce1008746573" - "743231323700cf100874" - "6573743231323800d01008746573743231323900d11008746573743231333000d21008746573743231333100d31008" - "746573743231333200d4" - "1008746573743231333300d51008746573743231333400d61008746573743231333500d71008746573743231333600" - "d8100874657374323133" - "3700d91008746573743231333800da1008746573743231333900db1008746573743231343000dc1008746573743231" - "343100dd100874657374" - "3231343200de1008746573743231343300df1008746573743231343400e01008746573743231343500e11008746573" - "743231343600e2100874" - "6573743231343700e31008746573743231343800e41008746573743231343900e51008746573743231353000e61008" - "746573743231353100e7" - "1008746573743231353200e81008746573743231353300e91008746573743231353400ea1008746573743231353500" - "eb100874657374323135" - "3600ec1008746573743231353700ed1008746573743231353800ee1008746573743231353900ef1008746573743231" - "363000f0100874657374" - "3231363100f11008746573743231363200f21008746573743231363300f31008746573743231363400f41008746573" - "743231363500f5100874" - "6573743231363600f61008746573743231363700f71008746573743231363800f81008746573743231363900f91008" - "746573743231373000fa" - "1008746573743231373100fb1008746573743231373200fc1008746573743231373300fd1008746573743231373400" - "fe100874657374323137" - "3500ff1008746573743231373600801108746573743231373700811108746573743231373800821108746573743231" - "37390083110874657374" - "3231383000841108746573743231383100851108746573743231383200861108746573743231383300871108746573" - "74323138340088110874" - "65737432313835008911087465737432313836008a11087465737432313837008b11087465737432313838008c1108" - "7465737432313839008d" - "11087465737432313930008e11087465737432313931008f1108746573743231393200901108746573743231393300" - "91110874657374323139" - "3400921108746573743231393500931108746573743231393600941108746573743231393700951108746573743231" - "39380096110874657374" - "32313939009711087465737432323030009811087465737432323031009911087465737432323032009a1108746573" - "7432323033009b110874" - "65737432323034009c11087465737432323035009d11087465737432323036009e11087465737432323037009f1108" - "746573743232303800a0" - "1108746573743232303900a11108746573743232313000a21108746573743232313100a31108746573743232313200" - "a4110874657374323231" - "3300a51108746573743232313400a61108746573743232313500a71108746573743232313600a81108746573743232" - "313700a9110874657374" - "3232313800aa1108746573743232313900ab1108746573743232323000ac1108746573743232323100ad1108746573" - "743232323200ae110874" - "6573743232323300af1108746573743232323400b01108746573743232323500b11108746573743232323600b21108" - "746573743232323700b3" - "1108746573743232323800b41108746573743232323900b51108746573743232333000b61108746573743232333100" - "b7110874657374323233" - "3200b81108746573743232333300b91108746573743232333400ba1108746573743232333500bb1108746573743232" - "333600bc110874657374" - "3232333700bd1108746573743232333800be1108746573743232333900bf1108746573743232343000c01108746573" - "743232343100c1110874" - "6573743232343200c21108746573743232343300c31108746573743232343400c41108746573743232343500c51108" - "746573743232343600c6" - "1108746573743232343700c71108746573743232343800c81108746573743232343900c91108746573743232353000" - "ca110874657374323235" - "3100cb1108746573743232353200cc1108746573743232353300cd1108746573743232353400ce1108746573743232" - "353500cf110874657374" - "3232353600d01108746573743232353700d11108746573743232353800d21108746573743232353900d31108746573" - "743232363000d4110874" - "6573743232363100d51108746573743232363200d61108746573743232363300d71108746573743232363400d81108" - "746573743232363500d9" - "1108746573743232363600da1108746573743232363700db1108746573743232363800dc1108746573743232363900" - "dd110874657374323237" + "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" - "373400e2110874657374" - "3232373500e31108746573743232373600e41108746573743232373700e51108746573743232373800e61108746573" - "743232373900e7110874" - "6573743232383000e81108746573743232383100e91108746573743232383200ea1108746573743232383300eb1108" - "746573743232383400ec" - "1108746573743232383500ed1108746573743232383600ee1108746573743232383700ef1108746573743232383800" - "f0110874657374323238" - "3900f11108746573743232393000f21108746573743232393100f31108746573743232393200f41108746573743232" - "393300f5110874657374" - "3232393400f61108746573743232393500f71108746573743232393600f81108746573743232393700f91108746573" - "743232393800fa110874" - "6573743232393900fb1108746573743233303000fc1108746573743233303100fd1108746573743233303200fe1108" - "746573743233303300ff" - "1108746573743233303400801208746573743233303500811208746573743233303600821208746573743233303700" - "83120874657374323330" - "3800841208746573743233303900851208746573743233313000861208746573743233313100871208746573743233" - "31320088120874657374" - "32333133008912087465737432333134008a12087465737432333135008b12087465737432333136008c1208746573" - "7432333137008d120874" - "65737432333138008e12087465737432333139008f1208746573743233323000901208746573743233323100911208" - "74657374323332320092" - "1208746573743233323300931208746573743233323400941208746573743233323500951208746573743233323600" - "96120874657374323332" - "37009712087465737432333238009812087465737432333239009912087465737432333330009a1208746573743233" - "3331009b120874657374" - "32333332009c12087465737432333333009d12087465737432333334009e12087465737432333335009f1208746573" - "743233333600a0120874" - "6573743233333700a11208746573743233333800a21208746573743233333900a31208746573743233343000a41208" - "746573743233343100a5" - "1208746573743233343200a61208746573743233343300a71208746573743233343400a81208746573743233343500" - "a9120874657374323334" - "3600aa1208746573743233343700ab1208746573743233343800ac1208746573743233343900ad1208746573743233" - "353000ae120874657374" - "3233353100af1208746573743233353200b01208746573743233353300b11208746573743233353400b21208746573" - "743233353500b3120874" - "6573743233353600b41208746573743233353700b51208746573743233353800b61208746573743233353900b71208" - "746573743233363000b8" - "1208746573743233363100b91208746573743233363200ba1208746573743233363300bb1208746573743233363400" - "bc120874657374323336" - "3500bd1208746573743233363600be1208746573743233363700bf1208746573743233363800c01208746573743233" - "363900c1120874657374" - "3233373000c21208746573743233373100c31208746573743233373200c41208746573743233373300c51208746573" - "743233373400c6120874" - "6573743233373500c71208746573743233373600c81208746573743233373700c91208746573743233373800ca1208" - "746573743233373900cb" - "1208746573743233383000cc1208746573743233383100cd1208746573743233383200ce1208746573743233383300" - "cf120874657374323338" - "3400d01208746573743233383500d11208746573743233383600d21208746573743233383700d31208746573743233" - "383800d4120874657374" - "3233383900d51208746573743233393000d61208746573743233393100d71208746573743233393200d81208746573" - "743233393300d9120874" - "6573743233393400da1208746573743233393500db1208746573743233393600dc1208746573743233393700dd1208" - "746573743233393800de" - "1208746573743233393900df1208746573743234303000e01208746573743234303100e11208746573743234303200" - "e2120874657374323430" - "3300e31208746573743234303400e41208746573743234303500e51208746573743234303600e61208746573743234" - "303700e7120874657374" - "3234303800e81208746573743234303900e91208746573743234313000ea1208746573743234313100eb1208746573" - "743234313200ec120874" - "6573743234313300ed1208746573743234313400ee1208746573743234313500ef1208746573743234313600f01208" - "746573743234313700f1" - "1208746573743234313800f21208746573743234313900f31208746573743234323000f41208746573743234323100" - "f5120874657374323432" - "3200f61208746573743234323300f71208746573743234323400f81208746573743234323500f91208746573743234" - "323600fa120874657374" - "3234323700fb1208746573743234323800fc1208746573743234323900fd1208746573743234333000fe1208746573" - "743234333100ff120874" - "6573743234333200801308746573743234333300811308746573743234333400821308746573743234333500831308" - "74657374323433360084" - "1308746573743234333700851308746573743234333800861308746573743234333900871308746573743234343000" - "88130874657374323434" - "31008913087465737432343432008a13087465737432343433008b13087465737432343434008c1308746573743234" - "3435008d130874657374" - "32343436008e13087465737432343437008f1308746573743234343800901308746573743234343900911308746573" - "74323435300092130874" - "6573743234353100931308746573743234353200941308746573743234353300951308746573743234353400961308" - "74657374323435350097" - "13087465737432343536009813087465737432343537009913087465737432343538009a1308746573743234353900" - "9b130874657374323436" - "30009c13087465737432343631009d13087465737432343632009e13087465737432343633009f1308746573743234" - "363400a0130874657374" - "3234363500a11308746573743234363600a21308746573743234363700a31308746573743234363800a41308746573" - "743234363900a5130874" - "6573743234373000a61308746573743234373100a71308746573743234373200a81308746573743234373300a91308" - "746573743234373400aa" - "1308746573743234373500ab1308746573743234373600ac1308746573743234373700ad1308746573743234373800" - "ae130874657374323437" - "3900af1308746573743234383000b01308746573743234383100b11308746573743234383200b21308746573743234" - "383300b3130874657374" - "3234383400b41308746573743234383500b51308746573743234383600b61308746573743234383700b71308746573" - "743234383800b8130874" - "6573743234383900b91308746573743234393000ba1308746573743234393100bb1308746573743234393200bc1308" - "746573743234393300bd" + "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" - "c1130874657374323439" - "3800c21308746573743234393900c31308746573743235303000c41308746573743235303100c51308746573743235" - "303200c6130874657374" - "3235303300c71308746573743235303400c81308746573743235303500c91308746573743235303600ca1308746573" - "743235303700cb130874" - "6573743235303800cc1308746573743235303900cd1308746573743235313000ce1308746573743235313100cf1308" - "746573743235313200d0" - "1308746573743235313300d11308746573743235313400d21308746573743235313500d31308746573743235313600" - "d4130874657374323531" - "3700d51308746573743235313800d61308746573743235313900d71308746573743235323000d81308746573743235" - "323100d9130874657374" - "3235323200da1308746573743235323300db1308746573743235323400dc1308746573743235323500dd1308746573" - "743235323600de130874" - "6573743235323700df1308746573743235323800e01308746573743235323900e11308746573743235333000e21308" - "746573743235333100e3" - "1308746573743235333200e41308746573743235333300e51308746573743235333400e61308746573743235333500" - "e7130874657374323533" - "3600e81308746573743235333700e91308746573743235333800ea1308746573743235333900eb1308746573743235" - "343000ec130874657374" - "3235343100ed1308746573743235343200ee1308746573743235343300ef1308746573743235343400f01308746573" - "743235343500f1130874" - "6573743235343600f21308746573743235343700f31308746573743235343800f41308746573743235343900f51308" - "746573743235353000f6" - "1308746573743235353100f71308746573743235353200f81308746573743235353300f91308746573743235353400" - "fa130874657374323535" - "3500fb1308746573743235353600fc1308746573743235353700fd1308746573743235353800fe1308746573743235" - "353900ff130874657374" - "3235363000801408746573743235363100811408746573743235363200821408746573743235363300831408746573" - "74323536340084140874" - "6573743235363500851408746573743235363600861408746573743235363700871408746573743235363800881408" - "74657374323536390089" - "14087465737432353730008a14087465737432353731008b14087465737432353732008c1408746573743235373300" - "8d140874657374323537" - "34008e14087465737432353735008f1408746573743235373600901408746573743235373700911408746573743235" - "37380092140874657374" - "3235373900931408746573743235383000941408746573743235383100951408746573743235383200961408746573" - "74323538330097140874" - "65737432353834009814087465737432353835009914087465737432353836009a14087465737432353837009b1408" - "7465737432353838009c" - "14087465737432353839009d14087465737432353930009e14087465737432353931009f1408746573743235393200" - "a0140874657374323539" - "3300a11408746573743235393400a21408746573743235393500a31408746573743235393600a41408746573743235" - "393700a5140874657374" - "3235393800a61408746573743235393900a71408746573743236303000a81408746573743236303100a91408746573" - "743236303200aa140874" - "6573743236303300ab1408746573743236303400ac1408746573743236303500ad1408746573743236303600ae1408" - "746573743236303700af" - "1408746573743236303800b01408746573743236303900b11408746573743236313000b21408746573743236313100" - "b3140874657374323631" - "3200b41408746573743236313300b51408746573743236313400b61408746573743236313500b71408746573743236" - "313600b8140874657374" - "3236313700b91408746573743236313800ba1408746573743236313900bb1408746573743236323000bc1408746573" - "743236323100bd140874" - "6573743236323200be1408746573743236323300bf1408746573743236323400c01408746573743236323500c11408" - "746573743236323600c2" - "1408746573743236323700c31408746573743236323800c41408746573743236323900c51408746573743236333000" - "c6140874657374323633" - "3100c71408746573743236333200c81408746573743236333300c91408746573743236333400ca1408746573743236" - "333500cb140874657374" - "3236333600cc1408746573743236333700cd1408746573743236333800ce1408746573743236333900cf1408746573" - "743236343000d0140874" - "6573743236343100d11408746573743236343200d21408746573743236343300d31408746573743236343400d41408" - "746573743236343500d5" - "1408746573743236343600d61408746573743236343700d71408746573743236343800d81408746573743236343900" - "d9140874657374323635" - "3000da1408746573743236353100db1408746573743236353200dc1408746573743236353300dd1408746573743236" - "353400de140874657374" - "3236353500df1408746573743236353600e01408746573743236353700e11408746573743236353800e21408746573" - "743236353900e3140874" - "6573743236363000e41408746573743236363100e51408746573743236363200e61408746573743236363300e71408" - "746573743236363400e8" - "1408746573743236363500e91408746573743236363600ea1408746573743236363700eb1408746573743236363800" - "ec140874657374323636" - "3900ed1408746573743236373000ee1408746573743236373100ef1408746573743236373200f01408746573743236" - "373300f1140874657374" - "3236373400f21408746573743236373500f31408746573743236373600f41408746573743236373700f51408746573" - "743236373800f6140874" - "6573743236373900f71408746573743236383000f81408746573743236383100f91408746573743236383200fa1408" - "746573743236383300fb" - "1408746573743236383400fc1408746573743236383500fd1408746573743236383600fe1408746573743236383700" - "ff140874657374323638" - "3800801508746573743236383900811508746573743236393000821508746573743236393100831508746573743236" - "39320084150874657374" - "3236393300851508746573743236393400861508746573743236393500871508746573743236393600881508746573" - "74323639370089150874" - "65737432363938008a15087465737432363939008b15087465737432373030008c15087465737432373031008d1508" - "7465737432373032008e" - "15087465737432373033008f1508746573743237303400901508746573743237303500911508746573743237303600" - "92150874657374323730" - "3700931508746573743237303800941508746573743237303900951508746573743237313000961508746573743237" - "31310097150874657374" - "32373132009815087465737432373133009915087465737432373134009a15087465737432373135009b1508746573" - "7432373136009c150874" + "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" - "746573743237323100a1" - "1508746573743237323200a21508746573743237323300a31508746573743237323400a41508746573743237323500" - "a5150874657374323732" - "3600a61508746573743237323700a71508746573743237323800a81508746573743237323900a91508746573743237" - "333000aa150874657374" - "3237333100ab1508746573743237333200ac1508746573743237333300ad1508746573743237333400ae1508746573" - "743237333500af150874" - "6573743237333600b01508746573743237333700b11508746573743237333800b21508746573743237333900b31508" - "746573743237343000b4" - "1508746573743237343100b51508746573743237343200b61508746573743237343300b71508746573743237343400" - "b8150874657374323734" - "3500b91508746573743237343600ba1508746573743237343700bb1508746573743237343800bc1508746573743237" - "343900bd150874657374" - "3237353000be1508746573743237353100bf1508746573743237353200c01508746573743237353300c11508746573" - "743237353400c2150874" - "6573743237353500c31508746573743237353600c41508746573743237353700c51508746573743237353800c61508" - "746573743237353900c7" - "1508746573743237363000c81508746573743237363100c91508746573743237363200ca1508746573743237363300" - "cb150874657374323736" - "3400cc1508746573743237363500cd1508746573743237363600ce1508746573743237363700cf1508746573743237" - "363800d0150874657374" - "3237363900d11508746573743237373000d21508746573743237373100d31508746573743237373200d41508746573" - "743237373300d5150874" - "6573743237373400d61508746573743237373500d71508746573743237373600d81508746573743237373700d91508" - "746573743237373800da" - "1508746573743237373900db1508746573743237383000dc1508746573743237383100dd1508746573743237383200" - "de150874657374323738" - "3300df1508746573743237383400e01508746573743237383500e11508746573743237383600e21508746573743237" - "383700e3150874657374" - "3237383800e41508746573743237383900e51508746573743237393000e61508746573743237393100e71508746573" - "743237393200e8150874" - "6573743237393300e91508746573743237393400ea1508746573743237393500eb1508746573743237393600ec1508" - "746573743237393700ed" - "1508746573743237393800ee1508746573743237393900ef1508746573743238303000f01508746573743238303100" - "f1150874657374323830" - "3200f21508746573743238303300f31508746573743238303400f41508746573743238303500f51508746573743238" - "303600f6150874657374" - "3238303700f71508746573743238303800f81508746573743238303900f91508746573743238313000fa1508746573" - "743238313100fb150874" - "6573743238313200fc1508746573743238313300fd1508746573743238313400fe1508746573743238313500ff1508" - "74657374323831360080" - "1608746573743238313700811608746573743238313800821608746573743238313900831608746573743238323000" - "84160874657374323832" - "3100851608746573743238323200861608746573743238323300871608746573743238323400881608746573743238" - "32350089160874657374" - "32383236008a16087465737432383237008b16087465737432383238008c16087465737432383239008d1608746573" - "7432383330008e160874" - "65737432383331008f1608746573743238333200901608746573743238333300911608746573743238333400921608" - "74657374323833350093" - "1608746573743238333600941608746573743238333700951608746573743238333800961608746573743238333900" - "97160874657374323834" - "30009816087465737432383431009916087465737432383432009a16087465737432383433009b1608746573743238" - "3434009c160874657374" - "32383435009d16087465737432383436009e16087465737432383437009f1608746573743238343800a01608746573" - "743238343900a1160874" - "6573743238353000a21608746573743238353100a31608746573743238353200a41608746573743238353300a51608" - "746573743238353400a6" - "1608746573743238353500a71608746573743238353600a81608746573743238353700a91608746573743238353800" - "aa160874657374323835" - "3900ab1608746573743238363000ac1608746573743238363100ad1608746573743238363200ae1608746573743238" - "363300af160874657374" - "3238363400b01608746573743238363500b11608746573743238363600b21608746573743238363700b31608746573" - "743238363800b4160874" - "6573743238363900b51608746573743238373000b61608746573743238373100b71608746573743238373200b81608" - "746573743238373300b9" - "1608746573743238373400ba1608746573743238373500bb1608746573743238373600bc1608746573743238373700" - "bd160874657374323837" - "3800be1608746573743238373900bf1608746573743238383000c01608746573743238383100c11608746573743238" - "383200c2160874657374" - "3238383300c31608746573743238383400c41608746573743238383500c51608746573743238383600c61608746573" - "743238383700c7160874" - "6573743238383800c81608746573743238383900c91608746573743238393000ca1608746573743238393100cb1608" - "746573743238393200cc" - "1608746573743238393300cd1608746573743238393400ce1608746573743238393500cf1608746573743238393600" - "d0160874657374323839" - "3700d11608746573743238393800d21608746573743238393900d31608746573743239303000d41608746573743239" - "303100d5160874657374" - "3239303200d61608746573743239303300d71608746573743239303400d81608746573743239303500d91608746573" - "743239303600da160874" - "6573743239303700db1608746573743239303800dc1608746573743239303900dd1608746573743239313000de1608" - "746573743239313100df" - "1608746573743239313200e01608746573743239313300e11608746573743239313400e21608746573743239313500" - "e3160874657374323931" - "3600e41608746573743239313700e51608746573743239313800e61608746573743239313900e71608746573743239" - "323000e8160874657374" - "3239323100e91608746573743239323200ea1608746573743239323300eb1608746573743239323400ec1608746573" - "743239323500ed160874" - "6573743239323600ee1608746573743239323700ef1608746573743239323800f01608746573743239323900f11608" - "746573743239333000f2" - "1608746573743239333100f31608746573743239333200f41608746573743239333300f51608746573743239333400" - "f6160874657374323933" - "3500f71608746573743239333600f81608746573743239333700f91608746573743239333800fa1608746573743239" - "333900fb160874657374" + "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" - "74323934340080170874" - "6573743239343500811708746573743239343600821708746573743239343700831708746573743239343800841708" - "74657374323934390085" - "1708746573743239353000861708746573743239353100871708746573743239353200881708746573743239353300" - "89170874657374323935" - "34008a17087465737432393535008b17087465737432393536008c17087465737432393537008d1708746573743239" - "3538008e170874657374" - "32393539008f1708746573743239363000901708746573743239363100911708746573743239363200921708746573" - "74323936330093170874" - "6573743239363400941708746573743239363500951708746573743239363600961708746573743239363700971708" - "74657374323936380098" - "17087465737432393639009917087465737432393730009a17087465737432393731009b1708746573743239373200" - "9c170874657374323937" - "33009d17087465737432393734009e17087465737432393735009f1708746573743239373600a01708746573743239" - "373700a1170874657374" - "3239373800a21708746573743239373900a31708746573743239383000a41708746573743239383100a51708746573" - "743239383200a6170874" - "6573743239383300a71708746573743239383400a81708746573743239383500a91708746573743239383600aa1708" - "746573743239383700ab" - "1708746573743239383800ac1708746573743239383900ad1708746573743239393000ae1708746573743239393100" - "af170874657374323939" - "3200b01708746573743239393300b11708746573743239393400b21708746573743239393500b31708746573743239" - "393600b4170874657374" - "3239393700b51708746573743239393800b61708746573743239393900b71708746573743330303000b81708746573" - "743330303100b9170874" - "6573743330303200ba1708746573743330303300bb1708746573743330303400bc1708746573743330303500bd1708" - "746573743330303600be" - "1708746573743330303700bf1708746573743330303800c01708746573743330303900c11708746573743330313000" - "c2170874657374333031" - "3100c31708746573743330313200c41708746573743330313300c51708746573743330313400c61708746573743330" - "313500c7170874657374" - "3330313600c81708746573743330313700c91708746573743330313800ca1708746573743330313900cb1708746573" - "743330323000cc170874" - "6573743330323100cd1708746573743330323200ce1708746573743330323300cf1708746573743330323400d01708" - "746573743330323500d1" - "1708746573743330323600d21708746573743330323700d31708746573743330323800d41708746573743330323900" - "d5170874657374333033" - "3000d61708746573743330333100d71708746573743330333200d81708746573743330333300d91708746573743330" - "333400da170874657374" - "3330333500db1708746573743330333600dc1708746573743330333700dd1708746573743330333800de1708746573" - "743330333900df170874" - "6573743330343000e01708746573743330343100e11708746573743330343200e21708746573743330343300e31708" - "746573743330343400e4" - "1708746573743330343500e51708746573743330343600e61708746573743330343700e71708746573743330343800" - "e8170874657374333034" - "3900e91708746573743330353000ea1708746573743330353100eb1708746573743330353200ec1708746573743330" - "353300ed170874657374" - "3330353400ee1708746573743330353500ef1708746573743330353600f01708746573743330353700f11708746573" - "743330353800f2170874" - "6573743330353900f31708746573743330363000f41708746573743330363100f51708746573743330363200f61708" - "746573743330363300f7" - "1708746573743330363400f81708746573743330363500f91708746573743330363600fa1708746573743330363700" - "fb170874657374333036" - "3800fc1708746573743330363900fd1708746573743330373000fe1708746573743330373100ff1708746573743330" - "37320080180874657374" - "3330373300811808746573743330373400821808746573743330373500831808746573743330373600841808746573" - "74333037370085180874" - "6573743330373800861808746573743330373900871808746573743330383000881808746573743330383100891808" - "7465737433303832008a" - "18087465737433303833008b18087465737433303834008c18087465737433303835008d1808746573743330383600" - "8e180874657374333038" - "37008f1808746573743330383800901808746573743330383900911808746573743330393000921808746573743330" - "39310093180874657374" - "3330393200941808746573743330393300951808746573743330393400961808746573743330393500971808746573" - "74333039360098180874" - "65737433303937009918087465737433303938009a18087465737433303939009b18087465737433313030009c1808" - "7465737433313031009d" - "18087465737433313032009e18087465737433313033009f1808746573743331303400a01808746573743331303500" - "a1180874657374333130" - "3600a21808746573743331303700a31808746573743331303800a41808746573743331303900a51808746573743331" - "313000a6180874657374" - "3331313100a71808746573743331313200a81808746573743331313300a91808746573743331313400aa1808746573" - "743331313500ab180874" - "6573743331313600ac1808746573743331313700ad1808746573743331313800ae1808746573743331313900af1808" - "746573743331323000b0" - "1808746573743331323100b11808746573743331323200b21808746573743331323300b31808746573743331323400" - "b4180874657374333132" - "3500b51808746573743331323600b61808746573743331323700b71808746573743331323800b81808746573743331" - "323900b9180874657374" - "3331333000ba1808746573743331333100bb1808746573743331333200bc1808746573743331333300bd1808746573" - "743331333400be180874" - "6573743331333500bf1808746573743331333600c01808746573743331333700c11808746573743331333800c21808" - "746573743331333900c3" - "1808746573743331343000c41808746573743331343100c51808746573743331343200c61808746573743331343300" - "c7180874657374333134" - "3400c81808746573743331343500c91808746573743331343600ca1808746573743331343700cb1808746573743331" - "343800cc180874657374" - "3331343900cd1808746573743331353000ce1808746573743331353100cf1808746573743331353200d01808746573" - "743331353300d1180874" - "6573743331353400d21808746573743331353500d31808746573743331353600d41808746573743331353700d51808" - "746573743331353800d6" - "1808746573743331353900d71808746573743331363000d81808746573743331363100d91808746573743331363200" - "da180874657374333136" + "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" - "363700df180874657374" - "3331363800e01808746573743331363900e11808746573743331373000e21808746573743331373100e31808746573" - "743331373200e4180874" - "6573743331373300e51808746573743331373400e61808746573743331373500e71808746573743331373600e81808" - "746573743331373700e9" - "1808746573743331373800ea1808746573743331373900eb1808746573743331383000ec1808746573743331383100" - "ed180874657374333138" - "3200ee1808746573743331383300ef1808746573743331383400f01808746573743331383500f11808746573743331" - "383600f2180874657374" - "3331383700f31808746573743331383800f41808746573743331383900f51808746573743331393000f61808746573" - "743331393100f7180874" - "6573743331393200f81808746573743331393300f91808746573743331393400fa1808746573743331393500fb1808" - "746573743331393600fc" - "1808746573743331393700fd1808746573743331393800fe1808746573743331393900ff1808746573743332303000" - "80190874657374333230" - "3100811908746573743332303200821908746573743332303300831908746573743332303400841908746573743332" - "30350085190874657374" - "3332303600861908746573743332303700871908746573743332303800881908746573743332303900891908746573" - "7433323130008a190874" - "65737433323131008b19087465737433323132008c19087465737433323133008d19087465737433323134008e1908" - "7465737433323135008f" - "1908746573743332313600901908746573743332313700911908746573743332313800921908746573743332313900" - "93190874657374333232" - "3000941908746573743332323100951908746573743332323200961908746573743332323300971908746573743332" - "32340098190874657374" - "33323235009919087465737433323236009a19087465737433323237009b19087465737433323238009c1908746573" - "7433323239009d190874" - "65737433323330009e19087465737433323331009f1908746573743332333200a01908746573743332333300a11908" - "746573743332333400a2" - "1908746573743332333500a31908746573743332333600a41908746573743332333700a51908746573743332333800" - "a6190874657374333233" - "3900a71908746573743332343000a81908746573743332343100a91908746573743332343200aa1908746573743332" - "343300ab190874657374" - "3332343400ac1908746573743332343500ad1908746573743332343600ae1908746573743332343700af1908746573" - "743332343800b0190874" - "6573743332343900b11908746573743332353000b21908746573743332353100b31908746573743332353200b41908" - "746573743332353300b5" - "1908746573743332353400b61908746573743332353500b71908746573743332353600b81908746573743332353700" - "b9190874657374333235" - "3800ba1908746573743332353900bb1908746573743332363000bc1908746573743332363100bd1908746573743332" - "363200be190874657374" - "3332363300bf1908746573743332363400c01908746573743332363500c11908746573743332363600c21908746573" - "743332363700c3190874" - "6573743332363800c41908746573743332363900c51908746573743332373000c61908746573743332373100c71908" - "746573743332373200c8" - "1908746573743332373300c91908746573743332373400ca1908746573743332373500cb1908746573743332373600" - "cc190874657374333237" - "3700cd1908746573743332373800ce1908746573743332373900cf1908746573743332383000d01908746573743332" - "383100d1190874657374" - "3332383200d21908746573743332383300d31908746573743332383400d41908746573743332383500d51908746573" - "743332383600d6190874" - "6573743332383700d71908746573743332383800d81908746573743332383900d91908746573743332393000da1908" - "746573743332393100db" - "1908746573743332393200dc1908746573743332393300dd1908746573743332393400de1908746573743332393500" - "df190874657374333239" - "3600e01908746573743332393700e11908746573743332393800e21908746573743332393900e31908746573743333" - "303000e4190874657374" - "3333303100e51908746573743333303200e61908746573743333303300e71908746573743333303400e81908746573" - "743333303500e9190874" - "6573743333303600ea1908746573743333303700eb1908746573743333303800ec1908746573743333303900ed1908" - "746573743333313000ee" - "1908746573743333313100ef1908746573743333313200f01908746573743333313300f11908746573743333313400" - "f2190874657374333331" - "3500f31908746573743333313600f41908746573743333313700f51908746573743333313800f61908746573743333" - "313900f7190874657374" - "3333323000f81908746573743333323100f91908746573743333323200fa1908746573743333323300fb1908746573" - "743333323400fc190874" - "6573743333323500fd1908746573743333323600fe1908746573743333323700ff1908746573743333323800801a08" - "74657374333332390081" - "1a08746573743333333000821a08746573743333333100831a08746573743333333200841a08746573743333333300" - "851a0874657374333333" - "3400861a08746573743333333500871a08746573743333333600881a08746573743333333700891a08746573743333" - "3338008a1a0874657374" - "33333339008b1a087465737433333430008c1a087465737433333431008d1a087465737433333432008e1a08746573" - "7433333433008f1a0874" - "6573743333343400901a08746573743333343500911a08746573743333343600921a08746573743333343700931a08" - "74657374333334380094" - "1a08746573743333343900951a08746573743333353000961a08746573743333353100971a08746573743333353200" - "981a0874657374333335" - "3300991a087465737433333534009a1a087465737433333535009b1a087465737433333536009c1a08746573743333" - "3537009d1a0874657374" - "33333538009e1a087465737433333539009f1a08746573743333363000a01a08746573743333363100a11a08746573" - "743333363200a21a0874" - "6573743333363300a31a08746573743333363400a41a08746573743333363500a51a08746573743333363600a61a08" - "746573743333363700a7" - "1a08746573743333363800a81a08746573743333363900a91a08746573743333373000aa1a08746573743333373100" - "ab1a0874657374333337" - "3200ac1a08746573743333373300ad1a08746573743333373400ae1a08746573743333373500af1a08746573743333" - "373600b01a0874657374" - "3333373700b11a08746573743333373800b21a08746573743333373900b31a08746573743333383000b41a08746573" - "743333383100b51a0874" - "6573743333383200b61a08746573743333383300b71a08746573743333383400b81a08746573743333383500b91a08" - "746573743333383600ba" + "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" - "be1a0874657374333339" - "3100bf1a08746573743333393200c01a08746573743333393300c11a08746573743333393400c21a08746573743333" - "393500c31a0874657374" - "3333393600c41a08746573743333393700c51a08746573743333393800c61a08746573743333393900c71a08746573" - "743334303000c81a0874" - "6573743334303100c91a08746573743334303200ca1a08746573743334303300cb1a08746573743334303400cc1a08" - "746573743334303500cd" - "1a08746573743334303600ce1a08746573743334303700cf1a08746573743334303800d01a08746573743334303900" - "d11a0874657374333431" - "3000d21a08746573743334313100d31a08746573743334313200d41a08746573743334313300d51a08746573743334" - "313400d61a0874657374" - "3334313500d71a08746573743334313600d81a08746573743334313700d91a08746573743334313800da1a08746573" - "743334313900db1a0874" - "6573743334323000dc1a08746573743334323100dd1a08746573743334323200de1a08746573743334323300df1a08" - "746573743334323400e0" - "1a08746573743334323500e11a08746573743334323600e21a08746573743334323700e31a08746573743334323800" - "e41a0874657374333432" - "3900e51a08746573743334333000e61a08746573743334333100e71a08746573743334333200e81a08746573743334" - "333300e91a0874657374" - "3334333400ea1a08746573743334333500eb1a08746573743334333600ec1a08746573743334333700ed1a08746573" - "743334333800ee1a0874" - "6573743334333900ef1a08746573743334343000f01a08746573743334343100f11a08746573743334343200f21a08" - "746573743334343300f3" - "1a08746573743334343400f41a08746573743334343500f51a08746573743334343600f61a08746573743334343700" - "f71a0874657374333434" - "3800f81a08746573743334343900f91a08746573743334353000fa1a08746573743334353100fb1a08746573743334" - "353200fc1a0874657374" - "3334353300fd1a08746573743334353400fe1a08746573743334353500ff1a08746573743334353600801b08746573" - "743334353700811b0874" - "6573743334353800821b08746573743334353900831b08746573743334363000841b08746573743334363100851b08" - "74657374333436320086" - "1b08746573743334363300871b08746573743334363400881b08746573743334363500891b08746573743334363600" - "8a1b0874657374333436" - "37008b1b087465737433343638008c1b087465737433343639008d1b087465737433343730008e1b08746573743334" - "3731008f1b0874657374" - "3334373200901b08746573743334373300911b08746573743334373400921b08746573743334373500931b08746573" - "743334373600941b0874" - "6573743334373700951b08746573743334373800961b08746573743334373900971b08746573743334383000981b08" - "74657374333438310099" - "1b087465737433343832009a1b087465737433343833009b1b087465737433343834009c1b08746573743334383500" - "9d1b0874657374333438" - "36009e1b087465737433343837009f1b08746573743334383800a01b08746573743334383900a11b08746573743334" - "393000a21b0874657374" - "3334393100a31b08746573743334393200a41b08746573743334393300a51b08746573743334393400a61b08746573" - "743334393500a71b0874" - "6573743334393600a81b08746573743334393700a91b08746573743334393800aa1b08746573743334393900ab1b08" - "746573743335303000ac" - "1b08746573743335303100ad1b08746573743335303200ae1b08746573743335303300af1b08746573743335303400" - "b01b0874657374333530" - "3500b11b08746573743335303600b21b08746573743335303700b31b08746573743335303800b41b08746573743335" - "303900b51b0874657374" - "3335313000b61b08746573743335313100b71b08746573743335313200b81b08746573743335313300b91b08746573" - "743335313400ba1b0874" - "6573743335313500bb1b08746573743335313600bc1b08746573743335313700bd1b08746573743335313800be1b08" - "746573743335313900bf" - "1b08746573743335323000c01b08746573743335323100c11b08746573743335323200c21b08746573743335323300" - "c31b0874657374333532" - "3400c41b08746573743335323500c51b08746573743335323600c61b08746573743335323700c71b08746573743335" - "323800c81b0874657374" - "3335323900c91b08746573743335333000ca1b08746573743335333100cb1b08746573743335333200cc1b08746573" - "743335333300cd1b0874" - "6573743335333400ce1b08746573743335333500cf1b08746573743335333600d01b08746573743335333700d11b08" - "746573743335333800d2" - "1b08746573743335333900d31b08746573743335343000d41b08746573743335343100d51b08746573743335343200" - "d61b0874657374333534" - "3300d71b08746573743335343400d81b08746573743335343500d91b08746573743335343600da1b08746573743335" - "343700db1b0874657374" - "3335343800dc1b08746573743335343900dd1b08746573743335353000de1b08746573743335353100df1b08746573" - "743335353200e01b0874" - "6573743335353300e11b08746573743335353400e21b08746573743335353500e31b08746573743335353600e41b08" - "746573743335353700e5" - "1b08746573743335353800e61b08746573743335353900e71b08746573743335363000e81b08746573743335363100" - "e91b0874657374333536" - "3200ea1b08746573743335363300eb1b08746573743335363400ec1b08746573743335363500ed1b08746573743335" - "363600ee1b0874657374" - "3335363700ef1b08746573743335363800f01b08746573743335363900f11b08746573743335373000f21b08746573" - "743335373100f31b0874" - "6573743335373200f41b08746573743335373300f51b08746573743335373400f61b08746573743335373500f71b08" - "746573743335373600f8" - "1b08746573743335373700f91b08746573743335373800fa1b08746573743335373900fb1b08746573743335383000" - "fc1b0874657374333538" - "3100fd1b08746573743335383200fe1b08746573743335383300ff1b08746573743335383400801c08746573743335" - "383500811c0874657374" - "3335383600821c08746573743335383700831c08746573743335383800841c08746573743335383900851c08746573" - "743335393000861c0874" - "6573743335393100871c08746573743335393200881c08746573743335393300891c087465737433353934008a1c08" - "7465737433353935008b" - "1c087465737433353936008c1c087465737433353937008d1c087465737433353938008e1c08746573743335393900" - "8f1c0874657374333630" - "3000901c08746573743336303100911c08746573743336303200921c08746573743336303300931c08746573743336" - "303400941c0874657374" - "3336303500951c08746573743336303600961c08746573743336303700971c08746573743336303800981c08746573" - "743336303900991c0874" + "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" - "7465737433363134009e" - "1c087465737433363135009f1c08746573743336313600a01c08746573743336313700a11c08746573743336313800" - "a21c0874657374333631" - "3900a31c08746573743336323000a41c08746573743336323100a51c08746573743336323200a61c08746573743336" - "323300a71c0874657374" - "3336323400a81c08746573743336323500a91c08746573743336323600aa1c08746573743336323700ab1c08746573" - "743336323800ac1c0874" - "6573743336323900ad1c08746573743336333000ae1c08746573743336333100af1c08746573743336333200b01c08" - "746573743336333300b1" - "1c08746573743336333400b21c08746573743336333500b31c08746573743336333600b41c08746573743336333700" - "b51c0874657374333633" - "3800b61c08746573743336333900b71c08746573743336343000b81c08746573743336343100b91c08746573743336" - "343200ba1c0874657374" - "3336343300bb1c08746573743336343400bc1c08746573743336343500bd1c08746573743336343600be1c08746573" - "743336343700bf1c0874" - "6573743336343800c01c08746573743336343900c11c08746573743336353000c21c08746573743336353100c31c08" - "746573743336353200c4" - "1c08746573743336353300c51c08746573743336353400c61c08746573743336353500c71c08746573743336353600" - "c81c0874657374333635" - "3700c91c08746573743336353800ca1c08746573743336353900cb1c08746573743336363000cc1c08746573743336" - "363100cd1c0874657374" - "3336363200ce1c08746573743336363300cf1c08746573743336363400d01c08746573743336363500d11c08746573" - "743336363600d21c0874" - "6573743336363700d31c08746573743336363800d41c08746573743336363900d51c08746573743336373000d61c08" - "746573743336373100d7" - "1c08746573743336373200d81c08746573743336373300d91c08746573743336373400da1c08746573743336373500" - "db1c0874657374333637" - "3600dc1c08746573743336373700dd1c08746573743336373800de1c08746573743336373900df1c08746573743336" - "383000e01c0874657374" - "3336383100e11c08746573743336383200e21c08746573743336383300e31c08746573743336383400e41c08746573" - "743336383500e51c0874" - "6573743336383600e61c08746573743336383700e71c08746573743336383800e81c08746573743336383900e91c08" - "746573743336393000ea" - "1c08746573743336393100eb1c08746573743336393200ec1c08746573743336393300ed1c08746573743336393400" - "ee1c0874657374333639" - "3500ef1c08746573743336393600f01c08746573743336393700f11c08746573743336393800f21c08746573743336" - "393900f31c0874657374" - "3337303000f41c08746573743337303100f51c08746573743337303200f61c08746573743337303300f71c08746573" - "743337303400f81c0874" - "6573743337303500f91c08746573743337303600fa1c08746573743337303700fb1c08746573743337303800fc1c08" - "746573743337303900fd" - "1c08746573743337313000fe1c08746573743337313100ff1c08746573743337313200801d08746573743337313300" - "811d0874657374333731" - "3400821d08746573743337313500831d08746573743337313600841d08746573743337313700851d08746573743337" - "313800861d0874657374" - "3337313900871d08746573743337323000881d08746573743337323100891d087465737433373232008a1d08746573" - "7433373233008b1d0874" - "65737433373234008c1d087465737433373235008d1d087465737433373236008e1d087465737433373237008f1d08" - "74657374333732380090" - "1d08746573743337323900911d08746573743337333000921d08746573743337333100931d08746573743337333200" - "941d0874657374333733" - "3300951d08746573743337333400961d08746573743337333500971d08746573743337333600981d08746573743337" - "333700991d0874657374" - "33373338009a1d087465737433373339009b1d087465737433373430009c1d087465737433373431009d1d08746573" - "7433373432009e1d0874" - "65737433373433009f1d08746573743337343400a01d08746573743337343500a11d08746573743337343600a21d08" - "746573743337343700a3" - "1d08746573743337343800a41d08746573743337343900a51d08746573743337353000a61d08746573743337353100" - "a71d0874657374333735" - "3200a81d08746573743337353300a91d08746573743337353400aa1d08746573743337353500ab1d08746573743337" - "353600ac1d0874657374" - "3337353700ad1d08746573743337353800ae1d08746573743337353900af1d08746573743337363000b01d08746573" - "743337363100b11d0874" - "6573743337363200b21d08746573743337363300b31d08746573743337363400b41d08746573743337363500b51d08" - "746573743337363600b6" - "1d08746573743337363700b71d08746573743337363800b81d08746573743337363900b91d08746573743337373000" - "ba1d0874657374333737" - "3100bb1d08746573743337373200bc1d08746573743337373300bd1d08746573743337373400be1d08746573743337" - "373500bf1d0874657374" - "3337373600c01d08746573743337373700c11d08746573743337373800c21d08746573743337373900c31d08746573" - "743337383000c41d0874" - "6573743337383100c51d08746573743337383200c61d08746573743337383300c71d08746573743337383400c81d08" - "746573743337383500c9" - "1d08746573743337383600ca1d08746573743337383700cb1d08746573743337383800cc1d08746573743337383900" - "cd1d0874657374333739" - "3000ce1d08746573743337393100cf1d08746573743337393200d01d08746573743337393300d11d08746573743337" - "393400d21d0874657374" - "3337393500d31d08746573743337393600d41d08746573743337393700d51d08746573743337393800d61d08746573" - "743337393900d71d0874" - "6573743338303000d81d08746573743338303100d91d08746573743338303200da1d08746573743338303300db1d08" - "746573743338303400dc" - "1d08746573743338303500dd1d08746573743338303600de1d08746573743338303700df1d08746573743338303800" - "e01d0874657374333830" - "3900e11d08746573743338313000e21d08746573743338313100e31d08746573743338313200e41d08746573743338" - "313300e51d0874657374" - "3338313400e61d08746573743338313500e71d08746573743338313600e81d08746573743338313700e91d08746573" - "743338313800ea1d0874" - "6573743338313900eb1d08746573743338323000ec1d08746573743338323100ed1d08746573743338323200ee1d08" - "746573743338323300ef" - "1d08746573743338323400f01d08746573743338323500f11d08746573743338323600f21d08746573743338323700" - "f31d0874657374333832" - "3800f41d08746573743338323900f51d08746573743338333000f61d08746573743338333100f71d08746573743338" - "333200f81d0874657374" + "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" - "743338333700fd1d0874" - "6573743338333800fe1d08746573743338333900ff1d08746573743338343000801e08746573743338343100811e08" - "74657374333834320082" - "1e08746573743338343300831e08746573743338343400841e08746573743338343500851e08746573743338343600" - "861e0874657374333834" - "3700871e08746573743338343800881e08746573743338343900891e087465737433383530008a1e08746573743338" - "3531008b1e0874657374" - "33383532008c1e087465737433383533008d1e087465737433383534008e1e087465737433383535008f1e08746573" - "743338353600901e0874" - "6573743338353700911e08746573743338353800921e08746573743338353900931e08746573743338363000941e08" - "74657374333836310095" - "1e08746573743338363200961e08746573743338363300971e08746573743338363400981e08746573743338363500" - "991e0874657374333836" - "36009a1e087465737433383637009b1e087465737433383638009c1e087465737433383639009d1e08746573743338" - "3730009e1e0874657374" - "33383731009f1e08746573743338373200a01e08746573743338373300a11e08746573743338373400a21e08746573" - "743338373500a31e0874" - "6573743338373600a41e08746573743338373700a51e08746573743338373800a61e08746573743338373900a71e08" - "746573743338383000a8" - "1e08746573743338383100a91e08746573743338383200aa1e08746573743338383300ab1e08746573743338383400" - "ac1e0874657374333838" - "3500ad1e08746573743338383600ae1e08746573743338383700af1e08746573743338383800b01e08746573743338" - "383900b11e0874657374" - "3338393000b21e08746573743338393100b31e08746573743338393200b41e08746573743338393300b51e08746573" - "743338393400b61e0874" - "6573743338393500b71e08746573743338393600b81e08746573743338393700b91e08746573743338393800ba1e08" - "746573743338393900bb" - "1e08746573743339303000bc1e08746573743339303100bd1e08746573743339303200be1e08746573743339303300" - "bf1e0874657374333930" - "3400c01e08746573743339303500c11e08746573743339303600c21e08746573743339303700c31e08746573743339" - "303800c41e0874657374" - "3339303900c51e08746573743339313000c61e08746573743339313100c71e08746573743339313200c81e08746573" - "743339313300c91e0874" - "6573743339313400ca1e08746573743339313500cb1e08746573743339313600cc1e08746573743339313700cd1e08" - "746573743339313800ce" - "1e08746573743339313900cf1e08746573743339323000d01e08746573743339323100d11e08746573743339323200" - "d21e0874657374333932" - "3300d31e08746573743339323400d41e08746573743339323500d51e08746573743339323600d61e08746573743339" - "323700d71e0874657374" - "3339323800d81e08746573743339323900d91e08746573743339333000da1e08746573743339333100db1e08746573" - "743339333200dc1e0874" - "6573743339333300dd1e08746573743339333400de1e08746573743339333500df1e08746573743339333600e01e08" - "746573743339333700e1" - "1e08746573743339333800e21e08746573743339333900e31e08746573743339343000e41e08746573743339343100" - "e51e0874657374333934" - "3200e61e08746573743339343300e71e08746573743339343400e81e08746573743339343500e91e08746573743339" - "343600ea1e0874657374" - "3339343700eb1e08746573743339343800ec1e08746573743339343900ed1e08746573743339353000ee1e08746573" - "743339353100ef1e0874" - "6573743339353200f01e08746573743339353300f11e08746573743339353400f21e08746573743339353500f31e08" - "746573743339353600f4" - "1e08746573743339353700f51e08746573743339353800f61e08746573743339353900f71e08746573743339363000" - "f81e0874657374333936" - "3100f91e08746573743339363200fa1e08746573743339363300fb1e08746573743339363400fc1e08746573743339" - "363500fd1e0874657374" - "3339363600fe1e08746573743339363700ff1e08746573743339363800801f08746573743339363900811f08746573" - "743339373000821f0874" - "6573743339373100831f08746573743339373200841f08746573743339373300851f08746573743339373400861f08" - "74657374333937350087" - "1f08746573743339373600881f08746573743339373700891f087465737433393738008a1f08746573743339373900" - "8b1f0874657374333938" - "30008c1f087465737433393831008d1f087465737433393832008e1f087465737433393833008f1f08746573743339" - "383400901f0874657374" - "3339383500911f08746573743339383600921f08746573743339383700931f08746573743339383800941f08746573" - "743339383900951f0874" - "6573743339393000961f08746573743339393100971f08746573743339393200981f08746573743339393300991f08" - "7465737433393934009a" - "1f087465737433393935009b1f087465737433393936009c1f087465737433393937009d1f08746573743339393800" - "9e1f0874657374333939" - "39009f1f08746573743430303000a01f08746573743430303100a11f08746573743430303200a21f08746573743430" - "303300a31f0874657374" - "3430303400a41f08746573743430303500a51f08746573743430303600a61f08746573743430303700a71f08746573" - "743430303800a81f0874" - "6573743430303900a91f08746573743430313000aa1f08746573743430313100ab1f08746573743430313200ac1f08" - "746573743430313300ad" - "1f08746573743430313400ae1f08746573743430313500af1f08746573743430313600b01f08746573743430313700" - "b11f0874657374343031" - "3800b21f08746573743430313900b31f08746573743430323000b41f08746573743430323100b51f08746573743430" - "323200b61f0874657374" - "3430323300b71f08746573743430323400b81f08746573743430323500b91f08746573743430323600ba1f08746573" - "743430323700bb1f0874" - "6573743430323800bc1f08746573743430323900bd1f08746573743430333000be1f08746573743430333100bf1f08" - "746573743430333200c0" - "1f08746573743430333300c11f08746573743430333400c21f08746573743430333500c31f08746573743430333600" - "c41f0874657374343033" - "3700c51f08746573743430333800c61f08746573743430333900c71f08746573743430343000c81f08746573743430" - "343100c91f0874657374" - "3430343200ca1f08746573743430343300cb1f08746573743430343400cc1f08746573743430343500cd1f08746573" - "743430343600ce1f0874" - "6573743430343700cf1f08746573743430343800d01f08746573743430343900d11f08746573743430353000d21f08" - "746573743430353100d3" - "1f08746573743430353200d41f08746573743430353300d51f08746573743430353400d61f08746573743430353500" - "d71f0874657374343035" + "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" - "363000dc1f0874657374" - "3430363100dd1f08746573743430363200de1f08746573743430363300df1f08746573743430363400e01f08746573" - "743430363500e11f0874" - "6573743430363600e21f08746573743430363700e31f08746573743430363800e41f08746573743430363900e51f08" - "746573743430373000e6" - "1f08746573743430373100e71f08746573743430373200e81f08746573743430373300e91f08746573743430373400" - "ea1f0874657374343037" - "3500eb1f08746573743430373600ec1f08746573743430373700ed1f08746573743430373800ee1f08746573743430" - "373900ef1f0874657374" - "3430383000f01f08746573743430383100f11f08746573743430383200f21f08746573743430383300f31f08746573" - "743430383400f41f0874" - "6573743430383500f51f08746573743430383600f61f08746573743430383700f71f08746573743430383800f81f08" - "746573743430383900f9" - "1f08746573743430393000fa1f08746573743430393100fb1f08746573743430393200fc1f08746573743430393300" - "fd1f0874657374343039" - "3400fe1f08746573743430393500ff1f08746573743430393600802008746573743430393700812008746573743430" - "39380082200874657374" - "3430393900832008746573743431303000842008746573743431303100852008746573743431303200862008746573" - "74343130330087200874" - "65737434313034008820087465737434313035008920087465737434313036008a20087465737434313037008b2008" - "7465737434313038008c" - "20087465737434313039008d20087465737434313130008e20087465737434313131008f2008746573743431313200" - "90200874657374343131" - "3300912008746573743431313400922008746573743431313500932008746573743431313600942008746573743431" - "31370095200874657374" - "3431313800962008746573743431313900972008746573743431323000982008746573743431323100992008746573" - "7434313232009a200874" - "65737434313233009b20087465737434313234009c20087465737434313235009d20087465737434313236009e2008" - "7465737434313237009f" - "2008746573743431323800a02008746573743431323900a12008746573743431333000a22008746573743431333100" - "a3200874657374343133" - "3200a42008746573743431333300a52008746573743431333400a62008746573743431333500a72008746573743431" - "333600a8200874657374" - "3431333700a92008746573743431333800aa2008746573743431333900ab2008746573743431343000ac2008746573" - "743431343100ad200874" - "6573743431343200ae2008746573743431343300af2008746573743431343400b02008746573743431343500b12008" - "746573743431343600b2" - "2008746573743431343700b32008746573743431343800b42008746573743431343900b52008746573743431353000" - "b6200874657374343135" - "3100b72008746573743431353200b82008746573743431353300b92008746573743431353400ba2008746573743431" - "353500bb200874657374" - "3431353600bc2008746573743431353700bd2008746573743431353800be2008746573743431353900bf2008746573" - "743431363000c0200874" - "6573743431363100c12008746573743431363200c22008746573743431363300c32008746573743431363400c42008" - "746573743431363500c5" - "2008746573743431363600c62008746573743431363700c72008746573743431363800c82008746573743431363900" - "c9200874657374343137" - "3000ca2008746573743431373100cb2008746573743431373200cc2008746573743431373300cd2008746573743431" - "373400ce200874657374" - "3431373500cf2008746573743431373600d02008746573743431373700d12008746573743431373800d22008746573" - "743431373900d3200874" - "6573743431383000d42008746573743431383100d52008746573743431383200d62008746573743431383300d72008" - "746573743431383400d8" - "2008746573743431383500d92008746573743431383600da2008746573743431383700db2008746573743431383800" - "dc200874657374343138" - "3900dd2008746573743431393000de2008746573743431393100df2008746573743431393200e02008746573743431" - "393300e1200874657374" - "3431393400e22008746573743431393500e32008746573743431393600e42008746573743431393700e52008746573" - "743431393800e6200874" - "6573743431393900e72008746573743432303000e82008746573743432303100e92008746573743432303200ea2008" - "746573743432303300eb" - "2008746573743432303400ec2008746573743432303500ed2008746573743432303600ee2008746573743432303700" - "ef200874657374343230" - "3800f02008746573743432303900f12008746573743432313000f22008746573743432313100f32008746573743432" - "313200f4200874657374" - "3432313300f52008746573743432313400f62008746573743432313500f72008746573743432313600f82008746573" - "743432313700f9200874" - "6573743432313800fa2008746573743432313900fb2008746573743432323000fc2008746573743432323100fd2008" - "746573743432323200fe" - "2008746573743432323300ff2008746573743432323400802108746573743432323500812108746573743432323600" - "82210874657374343232" - "3700832108746573743432323800842108746573743432323900852108746573743432333000862108746573743432" - "33310087210874657374" - "34323332008821087465737434323333008921087465737434323334008a21087465737434323335008b2108746573" - "7434323336008c210874" - "65737434323337008d21087465737434323338008e21087465737434323339008f2108746573743432343000902108" - "74657374343234310091" - "2108746573743432343200922108746573743432343300932108746573743432343400942108746573743432343500" - "95210874657374343234" - "3600962108746573743432343700972108746573743432343800982108746573743432343900992108746573743432" - "3530009a210874657374" - "34323531009b21087465737434323532009c21087465737434323533009d21087465737434323534009e2108746573" - "7434323535009f210874" - "6573743432353600a02108746573743432353700a12108746573743432353800a22108746573743432353900a32108" - "746573743432363000a4" - "2108746573743432363100a52108746573743432363200a62108746573743432363300a72108746573743432363400" - "a8210874657374343236" - "3500a92108746573743432363600aa2108746573743432363700ab2108746573743432363800ac2108746573743432" - "363900ad210874657374" - "3432373000ae2108746573743432373100af2108746573743432373200b02108746573743432373300b12108746573" - "743432373400b2210874" - "6573743432373500b32108746573743432373600b42108746573743432373700b52108746573743432373800b62108" - "746573743432373900b7" + "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" - "bb210874657374343238" - "3400bc2108746573743432383500bd2108746573743432383600be2108746573743432383700bf2108746573743432" - "383800c0210874657374" - "3432383900c12108746573743432393000c22108746573743432393100c32108746573743432393200c42108746573" - "743432393300c5210874" - "6573743432393400c62108746573743432393500c72108746573743432393600c82108746573743432393700c92108" - "746573743432393800ca" - "2108746573743432393900cb2108746573743433303000cc2108746573743433303100cd2108746573743433303200" - "ce210874657374343330" - "3300cf2108746573743433303400d02108746573743433303500d12108746573743433303600d22108746573743433" - "303700d3210874657374" - "3433303800d42108746573743433303900d52108746573743433313000d62108746573743433313100d72108746573" - "743433313200d8210874" - "6573743433313300d92108746573743433313400da2108746573743433313500db2108746573743433313600dc2108" - "746573743433313700dd" - "2108746573743433313800de2108746573743433313900df2108746573743433323000e02108746573743433323100" - "e1210874657374343332" - "3200e22108746573743433323300e32108746573743433323400e42108746573743433323500e52108746573743433" - "323600e6210874657374" - "3433323700e72108746573743433323800e82108746573743433323900e92108746573743433333000ea2108746573" - "743433333100eb210874" - "6573743433333200ec2108746573743433333300ed2108746573743433333400ee2108746573743433333500ef2108" - "746573743433333600f0" - "2108746573743433333700f12108746573743433333800f22108746573743433333900f32108746573743433343000" - "f4210874657374343334" - "3100f52108746573743433343200f62108746573743433343300f72108746573743433343400f82108746573743433" - "343500f9210874657374" - "3433343600fa2108746573743433343700fb2108746573743433343800fc2108746573743433343900fd2108746573" - "743433353000fe210874" - "6573743433353100ff2108746573743433353200802208746573743433353300812208746573743433353400822208" - "74657374343335350083" - "2208746573743433353600842208746573743433353700852208746573743433353800862208746573743433353900" - "87220874657374343336" - "30008822087465737434333631008922087465737434333632008a22087465737434333633008b2208746573743433" - "3634008c220874657374" - "34333635008d22087465737434333636008e22087465737434333637008f2208746573743433363800902208746573" - "74343336390091220874" - "6573743433373000922208746573743433373100932208746573743433373200942208746573743433373300952208" - "74657374343337340096" - "2208746573743433373500972208746573743433373600982208746573743433373700992208746573743433373800" - "9a220874657374343337" - "39009b22087465737434333830009c22087465737434333831009d22087465737434333832009e2208746573743433" - "3833009f220874657374" - "3433383400a02208746573743433383500a12208746573743433383600a22208746573743433383700a32208746573" - "743433383800a4220874" - "6573743433383900a52208746573743433393000a62208746573743433393100a72208746573743433393200a82208" - "746573743433393300a9" - "2208746573743433393400aa2208746573743433393500ab2208746573743433393600ac2208746573743433393700" - "ad220874657374343339" - "3800ae2208746573743433393900af2208746573743434303000b02208746573743434303100b12208746573743434" - "303200b2220874657374" - "3434303300b32208746573743434303400b42208746573743434303500b52208746573743434303600b62208746573" - "743434303700b7220874" - "6573743434303800b82208746573743434303900b92208746573743434313000ba2208746573743434313100bb2208" - "746573743434313200bc" - "2208746573743434313300bd2208746573743434313400be2208746573743434313500bf2208746573743434313600" - "c0220874657374343431" - "3700c12208746573743434313800c22208746573743434313900c32208746573743434323000c42208746573743434" - "323100c5220874657374" - "3434323200c62208746573743434323300c72208746573743434323400c82208746573743434323500c92208746573" - "743434323600ca220874" - "6573743434323700cb2208746573743434323800cc2208746573743434323900cd2208746573743434333000ce2208" - "746573743434333100cf" - "2208746573743434333200d02208746573743434333300d12208746573743434333400d22208746573743434333500" - "d3220874657374343433" - "3600d42208746573743434333700d52208746573743434333800d62208746573743434333900d72208746573743434" - "343000d8220874657374" - "3434343100d92208746573743434343200da2208746573743434343300db2208746573743434343400dc2208746573" - "743434343500dd220874" - "6573743434343600de2208746573743434343700df2208746573743434343800e02208746573743434343900e12208" - "746573743434353000e2" - "2208746573743434353100e32208746573743434353200e42208746573743434353300e52208746573743434353400" - "e6220874657374343435" - "3500e72208746573743434353600e82208746573743434353700e92208746573743434353800ea2208746573743434" - "353900eb220874657374" - "3434363000ec2208746573743434363100ed2208746573743434363200ee2208746573743434363300ef2208746573" - "743434363400f0220874" - "6573743434363500f12208746573743434363600f22208746573743434363700f32208746573743434363800f42208" - "746573743434363900f5" - "2208746573743434373000f62208746573743434373100f72208746573743434373200f82208746573743434373300" - "f9220874657374343437" - "3400fa2208746573743434373500fb2208746573743434373600fc2208746573743434373700fd2208746573743434" - "373800fe220874657374" - "3434373900ff2208746573743434383000802308746573743434383100812308746573743434383200822308746573" - "74343438330083230874" - "6573743434383400842308746573743434383500852308746573743434383600862308746573743434383700872308" - "74657374343438380088" - "23087465737434343839008923087465737434343930008a23087465737434343931008b2308746573743434393200" - "8c230874657374343439" - "33008d23087465737434343934008e23087465737434343935008f2308746573743434393600902308746573743434" - "39370091230874657374" - "3434393800922308746573743434393900932308746573743435303000942308746573743435303100952308746573" - "74343530320096230874" + "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" - "7465737434353037009b" - "23087465737434353038009c23087465737434353039009d23087465737434353130009e2308746573743435313100" - "9f230874657374343531" - "3200a02308746573743435313300a12308746573743435313400a22308746573743435313500a32308746573743435" - "313600a4230874657374" - "3435313700a52308746573743435313800a62308746573743435313900a72308746573743435323000a82308746573" - "743435323100a9230874" - "6573743435323200aa2308746573743435323300ab2308746573743435323400ac2308746573743435323500ad2308" - "746573743435323600ae" - "2308746573743435323700af2308746573743435323800b02308746573743435323900b12308746573743435333000" - "b2230874657374343533" - "3100b32308746573743435333200b42308746573743435333300b52308746573743435333400b62308746573743435" - "333500b7230874657374" - "3435333600b82308746573743435333700b92308746573743435333800ba2308746573743435333900bb2308746573" - "743435343000bc230874" - "6573743435343100bd2308746573743435343200be2308746573743435343300bf2308746573743435343400c02308" - "746573743435343500c1" - "2308746573743435343600c22308746573743435343700c32308746573743435343800c42308746573743435343900" - "c5230874657374343535" - "3000c62308746573743435353100c72308746573743435353200c82308746573743435353300c92308746573743435" - "353400ca230874657374" - "3435353500cb2308746573743435353600cc2308746573743435353700cd2308746573743435353800ce2308746573" - "743435353900cf230874" - "6573743435363000d02308746573743435363100d12308746573743435363200d22308746573743435363300d32308" - "746573743435363400d4" - "2308746573743435363500d52308746573743435363600d62308746573743435363700d72308746573743435363800" - "d8230874657374343536" - "3900d92308746573743435373000da2308746573743435373100db2308746573743435373200dc2308746573743435" - "373300dd230874657374" - "3435373400de2308746573743435373500df2308746573743435373600e02308746573743435373700e12308746573" - "743435373800e2230874" - "6573743435373900e32308746573743435383000e42308746573743435383100e52308746573743435383200e62308" - "746573743435383300e7" - "2308746573743435383400e82308746573743435383500e92308746573743435383600ea2308746573743435383700" - "eb230874657374343538" - "3800ec2308746573743435383900ed2308746573743435393000ee2308746573743435393100ef2308746573743435" - "393200f0230874657374" - "3435393300f12308746573743435393400f22308746573743435393500f32308746573743435393600f42308746573" - "743435393700f5230874" - "6573743435393800f62308746573743435393900f72308746573743436303000f82308746573743436303100f92308" - "746573743436303200fa" - "2308746573743436303300fb2308746573743436303400fc2308746573743436303500fd2308746573743436303600" - "fe230874657374343630" - "3700ff2308746573743436303800802408746573743436303900812408746573743436313000822408746573743436" - "31310083240874657374" - "3436313200842408746573743436313300852408746573743436313400862408746573743436313500872408746573" - "74343631360088240874" - "65737434363137008924087465737434363138008a24087465737434363139008b24087465737434363230008c2408" - "7465737434363231008d" - "24087465737434363232008e24087465737434363233008f2408746573743436323400902408746573743436323500" - "91240874657374343632" - "3600922408746573743436323700932408746573743436323800942408746573743436323900952408746573743436" - "33300096240874657374" - "34363331009724087465737434363332009824087465737434363333009924087465737434363334009a2408746573" - "7434363335009b240874" - "65737434363336009c24087465737434363337009d24087465737434363338009e24087465737434363339009f2408" - "746573743436343000a0" - "2408746573743436343100a12408746573743436343200a22408746573743436343300a32408746573743436343400" - "a4240874657374343634" - "3500a52408746573743436343600a62408746573743436343700a72408746573743436343800a82408746573743436" - "343900a9240874657374" - "3436353000aa2408746573743436353100ab2408746573743436353200ac2408746573743436353300ad2408746573" - "743436353400ae240874" - "6573743436353500af2408746573743436353600b02408746573743436353700b12408746573743436353800b22408" - "746573743436353900b3" - "2408746573743436363000b42408746573743436363100b52408746573743436363200b62408746573743436363300" - "b7240874657374343636" - "3400b82408746573743436363500b92408746573743436363600ba2408746573743436363700bb2408746573743436" - "363800bc240874657374" - "3436363900bd2408746573743436373000be2408746573743436373100bf2408746573743436373200c02408746573" - "743436373300c1240874" - "6573743436373400c22408746573743436373500c32408746573743436373600c42408746573743436373700c52408" - "746573743436373800c6" - "2408746573743436373900c72408746573743436383000c82408746573743436383100c92408746573743436383200" - "ca240874657374343638" - "3300cb2408746573743436383400cc2408746573743436383500cd2408746573743436383600ce2408746573743436" - "383700cf240874657374" - "3436383800d02408746573743436383900d12408746573743436393000d22408746573743436393100d32408746573" - "743436393200d4240874" - "6573743436393300d52408746573743436393400d62408746573743436393500d72408746573743436393600d82408" - "746573743436393700d9" - "2408746573743436393800da2408746573743436393900db2408746573743437303000dc2408746573743437303100" - "dd240874657374343730" - "3200de2408746573743437303300df2408746573743437303400e02408746573743437303500e12408746573743437" - "303600e2240874657374" - "3437303700e32408746573743437303800e42408746573743437303900e52408746573743437313000e62408746573" - "743437313100e7240874" - "6573743437313200e82408746573743437313300e92408746573743437313400ea2408746573743437313500eb2408" - "746573743437313600ec" - "2408746573743437313700ed2408746573743437313800ee2408746573743437313900ef2408746573743437323000" - "f0240874657374343732" - "3100f12408746573743437323200f22408746573743437323300f32408746573743437323400f42408746573743437" - "323500f5240874657374" + "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" - "743437333000fa240874" - "6573743437333100fb2408746573743437333200fc2408746573743437333300fd2408746573743437333400fe2408" - "746573743437333500ff" - "2408746573743437333600802508746573743437333700812508746573743437333800822508746573743437333900" - "83250874657374343734" - "3000842508746573743437343100852508746573743437343200862508746573743437343300872508746573743437" - "34340088250874657374" - "34373435008925087465737434373436008a25087465737434373437008b25087465737434373438008c2508746573" - "7434373439008d250874" - "65737434373530008e25087465737434373531008f2508746573743437353200902508746573743437353300912508" - "74657374343735340092" - "2508746573743437353500932508746573743437353600942508746573743437353700952508746573743437353800" - "96250874657374343735" - "39009725087465737434373630009825087465737434373631009925087465737434373632009a2508746573743437" - "3633009b250874657374" - "34373634009c25087465737434373635009d25087465737434373636009e25087465737434373637009f2508746573" - "743437363800a0250874" - "6573743437363900a12508746573743437373000a22508746573743437373100a32508746573743437373200a42508" - "746573743437373300a5" - "2508746573743437373400a62508746573743437373500a72508746573743437373600a82508746573743437373700" - "a9250874657374343737" - "3800aa2508746573743437373900ab2508746573743437383000ac2508746573743437383100ad2508746573743437" - "383200ae250874657374" - "3437383300af2508746573743437383400b02508746573743437383500b12508746573743437383600b22508746573" - "743437383700b3250874" - "6573743437383800b42508746573743437383900b52508746573743437393000b62508746573743437393100b72508" - "746573743437393200b8" - "2508746573743437393300b92508746573743437393400ba2508746573743437393500bb2508746573743437393600" - "bc250874657374343739" - "3700bd2508746573743437393800be2508746573743437393900bf2508746573743438303000c02508746573743438" - "303100c1250874657374" - "3438303200c22508746573743438303300c32508746573743438303400c42508746573743438303500c52508746573" - "743438303600c6250874" - "6573743438303700c72508746573743438303800c82508746573743438303900c92508746573743438313000ca2508" - "746573743438313100cb" - "2508746573743438313200cc2508746573743438313300cd2508746573743438313400ce2508746573743438313500" - "cf250874657374343831" - "3600d02508746573743438313700d12508746573743438313800d22508746573743438313900d32508746573743438" - "323000d4250874657374" - "3438323100d52508746573743438323200d62508746573743438323300d72508746573743438323400d82508746573" - "743438323500d9250874" - "6573743438323600da2508746573743438323700db2508746573743438323800dc2508746573743438323900dd2508" - "746573743438333000de" - "2508746573743438333100df2508746573743438333200e02508746573743438333300e12508746573743438333400" - "e2250874657374343833" - "3500e32508746573743438333600e42508746573743438333700e52508746573743438333800e62508746573743438" - "333900e7250874657374" - "3438343000e82508746573743438343100e92508746573743438343200ea2508746573743438343300eb2508746573" - "743438343400ec250874" - "6573743438343500ed2508746573743438343600ee2508746573743438343700ef2508746573743438343800f02508" - "746573743438343900f1" - "2508746573743438353000f22508746573743438353100f32508746573743438353200f42508746573743438353300" - "f5250874657374343835" - "3400f62508746573743438353500f72508746573743438353600f82508746573743438353700f92508746573743438" - "353800fa250874657374" - "3438353900fb2508746573743438363000fc2508746573743438363100fd2508746573743438363200fe2508746573" - "743438363300ff250874" - "6573743438363400802608746573743438363500812608746573743438363600822608746573743438363700832608" - "74657374343836380084" - "2608746573743438363900852608746573743438373000862608746573743438373100872608746573743438373200" - "88260874657374343837" - "33008926087465737434383734008a26087465737434383735008b26087465737434383736008c2608746573743438" - "3737008d260874657374" - "34383738008e26087465737434383739008f2608746573743438383000902608746573743438383100912608746573" - "74343838320092260874" - "6573743438383300932608746573743438383400942608746573743438383500952608746573743438383600962608" - "74657374343838370097" - "26087465737434383838009826087465737434383839009926087465737434383930009a2608746573743438393100" - "9b260874657374343839" - "32009c26087465737434383933009d26087465737434383934009e26087465737434383935009f2608746573743438" - "393600a0260874657374" - "3438393700a12608746573743438393800a22608746573743438393900a32608746573743439303000a42608746573" - "743439303100a5260874" - "6573743439303200a62608746573743439303300a72608746573743439303400a82608746573743439303500a92608" - "746573743439303600aa" - "2608746573743439303700ab2608746573743439303800ac2608746573743439303900ad2608746573743439313000" - "ae260874657374343931" - "3100af2608746573743439313200b02608746573743439313300b12608746573743439313400b22608746573743439" - "313500b3260874657374" - "3439313600b42608746573743439313700b52608746573743439313800b62608746573743439313900b72608746573" - "743439323000b8260874" - "6573743439323100b92608746573743439323200ba2608746573743439323300bb2608746573743439323400bc2608" - "746573743439323500bd" - "2608746573743439323600be2608746573743439323700bf2608746573743439323800c02608746573743439323900" - "c1260874657374343933" - "3000c22608746573743439333100c32608746573743439333200c42608746573743439333300c52608746573743439" - "333400c6260874657374" - "3439333500c72608746573743439333600c82608746573743439333700c92608746573743439333800ca2608746573" - "743439333900cb260874" - "6573743439343000cc2608746573743439343100cd2608746573743439343200ce2608746573743439343300cf2608" - "746573743439343400d0" - "2608746573743439343500d12608746573743439343600d22608746573743439343700d32608746573743439343800" - "d4260874657374343934" + "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" - "353300d9260874657374" - "3439353400da2608746573743439353500db2608746573743439353600dc2608746573743439353700dd2608746573" - "743439353800de260874" - "6573743439353900df2608746573743439363000e02608746573743439363100e12608746573743439363200e22608" - "746573743439363300e3" - "2608746573743439363400e42608746573743439363500e52608746573743439363600e62608746573743439363700" - "e7260874657374343936" - "3800e82608746573743439363900e92608746573743439373000ea2608746573743439373100eb2608746573743439" - "373200ec260874657374" - "3439373300ed2608746573743439373400ee2608746573743439373500ef2608746573743439373600f02608746573" - "743439373700f1260874" - "6573743439373800f22608746573743439373900f32608746573743439383000f42608746573743439383100f52608" - "746573743439383200f6" - "2608746573743439383300f72608746573743439383400f82608746573743439383500f92608746573743439383600" - "fa260874657374343938" - "3700fb2608746573743439383800fc2608746573743439383900fd2608746573743439393000fe2608746573743439" - "393100ff260874657374" - "3439393200802708746573743439393300812708746573743439393400822708746573743439393500832708746573" - "74343939360084270874" - "657374343939370085270874657374343939380086270874657374343939390087270ac2b80288270700200020016a" - "0b0700200020016a0b07" + "353300d92608746573743439353400da2608746573743439353500db2608746573743439353600dc26087465737434" + "39353700dd2608746573743439353800de2608746573743439353900df2608746573743439363000e0260874657374" + "3439363100e12608746573743439363200e22608746573743439363300e32608746573743439363400e42608746573" + "743439363500e52608746573743439363600e62608746573743439363700e72608746573743439363800e826087465" + "73743439363900e92608746573743439373000ea2608746573743439373100eb2608746573743439373200ec260874" + "6573743439373300ed2608746573743439373400ee2608746573743439373500ef2608746573743439373600f02608" + "746573743439373700f12608746573743439373800f22608746573743439373900f32608746573743439383000f426" + "08746573743439383100f52608746573743439383200f62608746573743439383300f72608746573743439383400f8" + "2608746573743439383500f92608746573743439383600fa2608746573743439383700fb2608746573743439383800" + "fc2608746573743439383900fd2608746573743439393000fe2608746573743439393100ff26087465737434393932" + "0080270874657374343939330081270874657374343939340082270874657374343939350083270874657374343939" + "360084270874657374343939370085270874657374343939380086270874657374343939390087270ac2b802882707" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" - "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" - "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" - "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" - "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" - "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" - "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" - "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" - "016a0b0700200020016a" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" - "6a0b0700200020016a0b" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" - "0b0700200020016a0b07" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" - "0700200020016a0b0700" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" - "00200020016a0b070020" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" - "200020016a0b07002000" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" + "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" - "0020016a0b0700200020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020" "016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07002000" - "20016a0b070020002001" - "6a0b0700200020016a0b0700200020016a0b0700200020016a0b"; + "20016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020" + "0020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700" + "200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b07" + "00200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b" + "0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a" + "0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b0700200020016a0b070020002001" + "6a0b"; extern std::string const opcReservedHex = "0061736d010000000105016000017f03030200000404017000010503010001060b027f0141000b7e0142000b071401" - "10616c6c5f696e737472" - "756374696f6e7300010907010041000b01000a53020400412a0b4c02017f017e010101010101010101010101010101" - "01010101010101010101" - "01010101010101010101010101010101010101010101010101010101010101010101010101010101010101410b0b0b" - "0a010041000b04746573" - "74"; + "10616c6c5f696e737472756374696f6e7300010907010041000b01000a53020400412a0b4c02017f017e0101010101" + "0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + "01010101010101010101010101010101410b0b0b0a010041000b0474657374"; + +extern std::string const impExpHex = + "0061736d0100000001100360027f7f017f6000017f60017f017f02330203656e760e6765745f6c65646765725f7371" + "6e000003656e76166765745f706172656e745f6c65646765725f686173680000030403010201050301000107310406" + "6d656d6f72790200096578705f66756e63310002096578705f66756e633200030c746573745f696d706f7274730004" + "0a2b03040041010b0700200041026c0b1c01027f4120410410001a41202802002100410041201001210120000b"; diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index ceace76fc0..b3b20a8f37 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -79,3 +79,5 @@ extern std::string const locals10kHex; extern std::string const functions5kHex; extern std::string const opcReservedHex; + +extern std::string const impExpHex; diff --git a/src/test/basics/Buffer_test.cpp b/src/test/basics/Buffer_test.cpp index 56f440b993..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; @@ -106,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); } @@ -129,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 @@ -140,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 @@ -151,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 @@ -163,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) } } @@ -241,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/IntrusiveShared_test.cpp b/src/test/basics/IntrusiveShared_test.cpp index 817cbb8c1d..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); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 8d879a4d2b..02a1e49232 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -199,9 +199,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } { bool caught = false; try @@ -291,9 +295,13 @@ public: } }; if (scale == MantissaRange::small) + { test(cSmall); + } else + { test(cLarge); + } } void @@ -314,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(); @@ -622,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)}; { diff --git a/src/test/basics/PerfLog_test.cpp b/src/test/basics/PerfLog_test.cpp index a2976301ef..ce0109c53d 100644 --- a/src/test/basics/PerfLog_test.cpp +++ b/src/test/basics/PerfLog_test.cpp @@ -87,7 +87,10 @@ class PerfLog_test : public beast::unit_test::suite { perf::PerfLog::Setup const setup{ withFile == WithFile::no ? "" : logFile(), logInterval()}; - return perf::make_PerfLog(setup, app_, j_, [this]() { return signalStop(); }); + return perf::make_PerfLog(setup, app_, j_, [this]() { + signalStop(); + return; + }); } // Block until the log file has grown in size, indicating that the @@ -451,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]); @@ -770,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]); @@ -793,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(); @@ -908,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 9712ec91c0..9693c6d181 100644 --- a/src/test/basics/Units_test.cpp +++ b/src/test/basics/Units_test.cpp @@ -31,7 +31,7 @@ private: auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); @@ -52,7 +52,7 @@ private: auto drops = mulDiv(baseFee, x, f); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT(drops.value() == 1000); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); @@ -73,7 +73,7 @@ private: auto drops = mulDiv(x, basefee, referencefee); BEAST_EXPECT(drops); - BEAST_EXPECT(drops.value() == 40); + BEAST_EXPECT(drops.value() == 40); // NOLINT(bugprone-unchecked-optional-access) BEAST_EXPECT((std::is_same_v< std::remove_reference_t::unit_type, unit::dropTag>)); 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 46e0c7615e..ca62ac02ef 100644 --- a/src/test/basics/base58_test.cpp +++ b/src/test/basics/base58_test.cpp @@ -270,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()); @@ -394,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) { @@ -407,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 0aa89bcecc..c8f931e2b5 100644 --- a/src/test/basics/base_uint_test.cpp +++ b/src/test/basics/base_uint_test.cpp @@ -142,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/beast/IPEndpoint_test.cpp b/src/test/beast/IPEndpoint_test.cpp index e0eedd4c24..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; 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/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_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/conditions/PreimageSha256_test.cpp b/src/test/conditions/PreimageSha256_test.cpp index c3508758a7..973f714ba4 100644 --- a/src/test/conditions/PreimageSha256_test.cpp +++ b/src/test/conditions/PreimageSha256_test.cpp @@ -17,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/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp index 0d023f270d..a096b82365 100644 --- a/src/test/consensus/Consensus_test.cpp +++ b/src/test/consensus/Consensus_test.cpp @@ -157,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); } @@ -193,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})); } } } @@ -244,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})); } } } @@ -297,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) { @@ -726,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 @@ -1100,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)); @@ -1118,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% @@ -1212,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)); @@ -1253,7 +1259,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()); } auto expectStalled = [this, &clog]( @@ -1301,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 @@ -1314,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 @@ -1340,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/LedgerTiming_test.cpp b/src/test/consensus/LedgerTiming_test.cpp index 62902d6a28..945f6b4ff0 100644 --- a/src/test/consensus/LedgerTiming_test.cpp +++ b/src/test/consensus/LedgerTiming_test.cpp @@ -29,11 +29,17 @@ class LedgerTiming_test : public beast::unit_test::suite 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 d03ee3950d..46c6936809 100644 --- a/src/test/consensus/NegativeUNL_test.cpp +++ b/src/test/consensus/NegativeUNL_test.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include namespace xrpl { @@ -835,9 +835,13 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite for (auto const& [n, score] : *scoreTable) { if (n == myId) + { BEAST_EXPECT(score == 256); + } else + { BEAST_EXPECT(score == 0); + } } } @@ -1304,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; @@ -1333,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) { @@ -1470,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 { @@ -1752,9 +1765,11 @@ 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 @@ -1790,7 +1805,7 @@ VerifyPubKeyAndSeq( return false; nUnlLedgerSeq.erase(it); } - return nUnlLedgerSeq.size() == 0; + return nUnlLedgerSeq.empty(); } std::size_t diff --git a/src/test/consensus/Validations_test.cpp b/src/test/consensus/Validations_test.cpp index f8458e090c..cf4498bd52 100644 --- a/src/test/consensus/Validations_test.cpp +++ b/src/test/consensus/Validations_test.cpp @@ -942,9 +942,13 @@ class Validations_test : public beast::unit_test::suite 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()); }; @@ -998,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 5cce837fb0..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; @@ -154,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!"); @@ -171,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!"); @@ -188,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!"); @@ -204,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 0ce457ec89..e9f7354482 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -284,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"); } @@ -434,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()); } } { @@ -533,7 +533,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -546,7 +546,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 0); try @@ -561,7 +561,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 255); try @@ -576,7 +576,7 @@ main error = e.what(); } - BEAST_EXPECT(error == ""); + BEAST_EXPECT(error.empty()); BEAST_EXPECT(c.NETWORK_ID == 10000); } @@ -1280,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()) { @@ -1289,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); } } @@ -1299,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"))); } @@ -1307,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"))); } } @@ -1335,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"); @@ -1427,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; @@ -1435,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 8289e305a0..bf6a5590e6 100644 --- a/src/test/core/JobQueue_test.cpp +++ b/src/test/core/JobQueue_test.cpp @@ -36,7 +36,7 @@ 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; + bool unprotected = false; BEAST_EXPECT(jQueue.addJob(jtCLIENT, "JobAddTest2", [&unprotected]() { unprotected = false; }) == false); @@ -118,7 +118,7 @@ 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; + bool unprotected = false; auto const coro = jQueue.postCoro( jtCLIENT, "PostCoroTest3", [&unprotected](std::shared_ptr const&) { unprotected = false; diff --git a/src/test/csf/BasicNetwork_test.cpp b/src/test/csf/BasicNetwork_test.cpp index eaef805f33..eee16c2ce1 100644 --- a/src/test/csf/BasicNetwork_test.cpp +++ b/src/test/csf/BasicNetwork_test.cpp @@ -33,8 +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); }); + } } else { @@ -51,9 +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); }); + } } } }; 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/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/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/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/Env_test.cpp b/src/test/jtx/Env_test.cpp index 6d151c27ce..bd40ac9a81 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -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, ""); @@ -533,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()); @@ -707,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; }(); @@ -721,7 +723,7 @@ 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)); }; { 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/PathSet.h b/src/test/jtx/PathSet.h index a363f9dff9..c522ed635e 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace xrpl { diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 529383c622..dc7682bb72 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -129,7 +129,7 @@ public: // TrustedPublisherServer must be accessed through a shared_ptr. // This constructor is only public so std::make_shared has access. - // The function`make_TrustedPublisherServer` should be used to create + // The function `make_TrustedPublisherServer` should be used to create // instances. // The `futures` member is expected to be structured as // effective / expiration time point pairs for use in version 2 UNLs @@ -249,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 } 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/impl/AMM.cpp b/src/test/jtx/impl/AMM.cpp index faf47259dc..6b2042efda 100644 --- a/src/test/jtx/impl/AMM.cpp +++ b/src/test/jtx/impl/AMM.cpp @@ -1,12 +1,13 @@ #include #include +#include #include #include #include #include -#include -#include +#include +#include namespace xrpl { namespace test { @@ -52,8 +53,6 @@ AMM::AMM( , log_(log) , doClose_(close) , lastPurchasePrice_(0) - , bidMin_() - , bidMax_() , msig_(ms) , fee_(fee) , ammAccount_(create(tfee, flags, seq, ter)) @@ -122,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) @@ -217,6 +220,7 @@ IOUAmount AMM::getLPTokensBalance(std::optional const& account) const { if (account) + { return accountHolds( *env_.current(), *account, @@ -224,6 +228,7 @@ AMM::getLPTokensBalance(std::optional const& account) const 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}; @@ -441,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); @@ -561,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); @@ -614,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 @@ -627,7 +652,8 @@ AMM::bid(BidArg const& arg) 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(); } } @@ -639,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) { @@ -657,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) @@ -689,22 +719,38 @@ AMM::submit( 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(); } @@ -719,7 +765,8 @@ AMM::expectAuctionSlot(auto&& cb) const 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 diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 01ba5069bf..ab8c1578a3 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -82,18 +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; @@ -130,11 +129,17 @@ 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}); @@ -264,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/Env.cpp b/src/test/jtx/impl/Env.cpp index 4dfd2f2b38..3217f67cbe 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -107,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 @@ -336,10 +342,14 @@ Env::parseResult(Json::Value const& jr) parsed.rpcCode.emplace(rpcSUCCESS); } else + { error(parsed, result); + } } else + { error(parsed, jr); + } return parsed; } @@ -362,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 @@ -409,7 +417,7 @@ 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 @@ -419,16 +427,15 @@ Env::postconditions( 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)); + transHuman(*jt.ter) + ")")); using namespace std::string_literals; bad = (jt.rpcCode && !test.expect( @@ -438,7 +445,7 @@ Env::postconditions( : "NO RESULT") + " (" + parsed.rpcMessage + "); Expected " + RPC::get_error_info(jt.rpcCode->first).token.c_str() + " (" + - jt.rpcCode->second + ")" + line)) || + 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 @@ -450,7 +457,7 @@ Env::postconditions( (!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)) || + jt.rpcException->second.value_or("n/a") + ")")) || bad; if (bad) { @@ -533,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 diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index df41ed39be..6b60744aad 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -35,13 +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()}; + } 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 302880c972..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) { @@ -157,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; } @@ -177,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) @@ -191,24 +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)); + } 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 @@ -216,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) @@ -226,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); } @@ -265,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); @@ -308,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); } diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 5e7a31adcd..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 { diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 84424be222..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,13 +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()}; + } 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 @@ -178,7 +180,9 @@ public: jp[jss::id] = 5; } else + { jp[jss::command] = cmd; + } auto const s = to_string(jp); ws_.write_some(true, buffer(s)); } diff --git a/src/test/jtx/impl/amount.cpp b/src/test/jtx/impl/amount.cpp index 4952334fe8..25fcb5c8f0 100644 --- a/src/test/jtx/impl/amount.cpp +++ b/src/test/jtx/impl/amount.cpp @@ -62,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(); diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index a136b19bdb..1c3cc0b3c3 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -66,7 +66,7 @@ doBalance( void balance::operator()(Env& env) const { - return std::visit( + std::visit( [&](auto const& issue) { doBalance(env, account_.id(), none_, value_, issue); }, value_.asset().value()); } 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/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/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 04860a5e3a..e5a7495a44 100644 --- a/src/test/jtx/impl/ledgerStateFixes.cpp +++ b/src/test/jtx/impl/ledgerStateFixes.cpp @@ -2,7 +2,7 @@ #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 4745b1a7a7..a6283bb6f9 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -77,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, @@ -145,7 +148,7 @@ 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( @@ -174,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; }); + } } } } @@ -245,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_) @@ -253,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) @@ -315,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); } @@ -360,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)); @@ -498,14 +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)); + } 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(); @@ -543,7 +594,7 @@ MPTTester::claw( 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(); 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/owners.cpp b/src/test/jtx/impl/owners.cpp index 53f2302143..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 { diff --git a/src/test/jtx/impl/permissioned_domains.cpp b/src/test/jtx/impl/permissioned_domains.cpp index fffcaa91a1..60d653e956 100644 --- a/src/test/jtx/impl/permissioned_domains.cpp +++ b/src/test/jtx/impl/permissioned_domains.cpp @@ -109,6 +109,7 @@ credentialsFromJson( obj = credential[jss::Credential]; auto const& issuer = obj[jss::Issuer]; auto const& credentialType = obj["CredentialType"]; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access): used only in tests auto blob = strUnHex(credentialType.asString()).value(); ret.push_back({human2Acc.at(issuer.asString()), std::string(blob.begin(), blob.end())}); } 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 59f8c822fb..9db79361eb 100644 --- a/src/test/jtx/impl/token.cpp +++ b/src/test/jtx/impl/token.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace xrpl { namespace test { 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 dfd7f0ba96..c2617f5073 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -286,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, @@ -296,6 +297,7 @@ claim_attestations( claimID, dst, signers[i])); + } return vec; } @@ -319,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, @@ -330,6 +333,7 @@ create_account_attestations( createCount, dst, signers[i])); + } return vec; } diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 785a9347dd..3f54e0c55f 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -2,7 +2,7 @@ #include -#include +#include #include #include 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/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/ledger/BookDirs_test.cpp b/src/test/ledger/BookDirs_test.cpp index 50f1bb46ba..d7841fe502 100644 --- a/src/test/ledger/BookDirs_test.cpp +++ b/src/test/ledger/BookDirs_test.cpp @@ -56,8 +56,10 @@ struct BookDirs_test : public beast::unit_test::suite { 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 8e7ba38c53..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 diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index 4be5827630..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 @@ -109,14 +110,14 @@ class PaymentSandbox_test : public beast::unit_test::suite 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); { 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) == @@ -153,7 +154,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { 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) == @@ -161,7 +162,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { 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) == @@ -224,7 +225,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { 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) == @@ -237,7 +238,7 @@ class PaymentSandbox_test : public beast::unit_test::suite 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) == @@ -246,7 +247,7 @@ class PaymentSandbox_test : public beast::unit_test::suite { 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) == @@ -314,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/View_test.cpp b/src/test/ledger/View_test.cpp index 80ecdba2d5..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 diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp index b537e3abb7..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 @@ -550,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: { @@ -620,8 +622,9 @@ public: using std::setw; int w = 8; for (auto const& test : tests) - if (w < test.first.size()) - w = test.first.size(); + { + w = std::max::size_type>(w, test.first.size()); + } log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << default_items << " Objects" << std::endl; { @@ -637,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--;) @@ -648,8 +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 << " " << to_string(config); log << ss.str() << std::endl; } @@ -689,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 578dd3670e..e5f281486b 100644 --- a/src/test/nodestore/import_test.cpp +++ b/src/test/nodestore/import_test.cpp @@ -339,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; @@ -367,8 +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())); + } void const* const key = it->key().data(); void const* const data = it->value().data(); auto const size = it->value().size(); @@ -406,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; @@ -465,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/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 3c787a4c72..f784a91350 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -176,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; diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index bf6d8aac1c..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 @@ -249,7 +250,7 @@ public: Validator& validator, PeerSPtr peer, Latency const& latency = {milliseconds(5), milliseconds(15)}) - : validator_(validator), peer_(peer), latency_(latency), up_(true) + : validator_(validator), peer_(peer), latency_(latency) { auto sp = peer_.lock(); assert(sp); @@ -294,7 +295,7 @@ private: Validator& validator_; PeerWPtr peer_; Latency latency_; - bool up_; + bool up_{true}; }; /** Simulate Validator */ @@ -404,7 +405,7 @@ public: } std::uint16_t - id() + id() const { return id_; } @@ -468,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) */ @@ -481,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: @@ -552,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")); @@ -600,8 +606,7 @@ public: for (auto& [id, _] : peers_) { (void)_; - if (id > maxId) - maxId = id; + maxId = std::max(id, maxId); } deletePeer(maxId, false); @@ -625,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 @@ -751,9 +756,13 @@ public: validators_.begin(), validators_.end(), [&](auto& v) { return v.id() == validatorId; }); assert(it != validators_.end()); if (enable) + { it->linkUp(peer); + } else + { it->linkDown(peer); + } } void @@ -900,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; @@ -939,9 +948,11 @@ 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; + } auto countingState = network_.overlay().isCountingState(validator); BEAST_EXPECT( countingState == false && @@ -967,7 +978,9 @@ protected: 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) @@ -1024,7 +1037,7 @@ protected: d > milliseconds(reduce_relay::IDLED).count() && network_.overlay().inState( *event.key_, reduce_relay::PeerState::Squelched) > 0 && - peers.find(event.peer_) != peers.end(); + peers.contains(event.peer_); } network_.overlay().deleteIdlePeers([&](PublicKey const& v, PeerWPtr const& ptr) { event.handled_ = true; @@ -1059,9 +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_; + } } bool @@ -1448,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 @@ -1518,18 +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; + } // 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; + } }); } diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index bff0bd82cb..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(); } @@ -210,7 +213,10 @@ 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(); @@ -248,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(); @@ -289,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(); } @@ -298,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_, @@ -322,13 +337,17 @@ private: if (ec == boost::asio::error::eof) { server_.test_.log << "[server] read: EOF" << std::endl; - return stream_.async_shutdown(bind_executor( + 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); @@ -350,7 +369,10 @@ private: { buf_.consume(bytes_transferred); if (ec) - return fail("write", ec); + { + fail("write", ec); + return; + } stream_.async_shutdown(bind_executor( strand_, std::bind( @@ -361,7 +383,10 @@ private: on_shutdown(error_code ec) { if (ec) - return fail("shutdown", ec); + { + fail("shutdown", ec); + return; + } socket_.close(); timer_.cancel(); } @@ -422,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(); @@ -463,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(); } @@ -472,7 +503,10 @@ private: on_connect(error_code ec) { if (ec) - return fail("connect", ec); + { + fail("connect", ec); + return; + } stream_.async_handshake( stream_type::client, bind_executor( @@ -485,7 +519,10 @@ private: on_handshake(error_code ec) { if (ec) - return fail("handshake", ec); + { + fail("handshake", ec); + return; + } write(buf_, "HELLO\n"); #if 1 @@ -512,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_, @@ -537,7 +577,10 @@ 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_, @@ -549,7 +592,10 @@ private: 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/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index e3facb2f76..11b5c95a4e 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -53,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(); + } } }; @@ -146,8 +154,7 @@ 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)) + ? 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())), diff --git a/src/test/peerfinder/Livecache_test.cpp b/src/test/peerfinder/Livecache_test.cpp index a69a300686..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}; diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index 2a9290ac3e..c39564c54c 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -123,7 +123,6 @@ public: 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); @@ -392,7 +391,7 @@ public: testcase(test); - std::string toLoad = ""; + std::string toLoad; int max = 0; if (maxPeers) { 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/MultiApiJson_test.cpp b/src/test/protocol/MultiApiJson_test.cpp index bbd6ec1a07..0da511b976 100644 --- a/src/test/protocol/MultiApiJson_test.cpp +++ b/src/test/protocol/MultiApiJson_test.cpp @@ -830,6 +830,7 @@ 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) { @@ -846,6 +847,7 @@ struct MultiApiJson_test : beast::unit_test::suite }; }(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) { diff --git a/src/test/protocol/PublicKey_test.cpp b/src/test/protocol/PublicKey_test.cpp index 9a7808db7e..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); @@ -370,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) } } } @@ -389,7 +390,7 @@ public: TokenType::NodePublic, "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::secp256k1); @@ -405,7 +406,7 @@ public: TokenType::NodePublic, "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(pk2); - BEAST_EXPECT(pk1 == *pk2); + BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::ed25519); 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/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 058df3ba82..0faa1946a9 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -169,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]); @@ -200,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]); @@ -351,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]); @@ -370,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]); @@ -399,7 +399,7 @@ 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( @@ -420,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; @@ -437,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 6838f557a9..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,7 +355,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); std::array expected = { 0x01, @@ -352,6 +376,7 @@ class STParsedJSON_test : public beast::unit_test::suite 0xAB, 0xCD, 0xEF}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128{expected}); } @@ -361,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); } @@ -371,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; }); @@ -436,11 +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}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency) == uint160{expected}); } // Valid lowercase hex string for UInt160 @@ -449,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); } @@ -459,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; }); @@ -516,11 +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}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192{expected}); } @@ -530,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); } @@ -540,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; }); @@ -608,12 +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}; + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256{expected}); } // Valid lowercase hex string for UInt256 @@ -624,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); } @@ -634,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; }); @@ -704,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 @@ -715,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 @@ -726,9 +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( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) obj.object->getFieldI32(sfLoanScale) == static_cast(maxUInt32)); + } } // Test with string value @@ -737,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 @@ -748,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) @@ -803,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); @@ -818,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 @@ -829,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); @@ -880,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()); @@ -893,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) @@ -959,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"); } @@ -971,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); } @@ -1043,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); } @@ -1054,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); } @@ -1064,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); @@ -1086,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); } @@ -1113,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); } @@ -1145,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)); } @@ -1155,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)); } @@ -1211,7 +1307,9 @@ 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); @@ -1240,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()); } @@ -1391,8 +1491,10 @@ 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"); @@ -1410,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]; @@ -1427,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]; @@ -1529,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]; @@ -1554,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]; @@ -1708,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)); } @@ -1718,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)); } @@ -1728,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)); } @@ -1738,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)); } @@ -1748,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)); } @@ -1757,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)); @@ -1809,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); } @@ -1856,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)); } @@ -1893,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)) { @@ -1965,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)); } @@ -2038,6 +2156,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == goodJson); } @@ -2062,6 +2181,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } @@ -2086,6 +2206,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } @@ -2194,6 +2315,7 @@ class STParsedJSON_test : public beast::unit_test::suite if (BEAST_EXPECT(parsed.object)) { std::string const& serialized( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) to_string(parsed.object->getJson(JsonOptions::none))); BEAST_EXPECT(serialized == expectedJson); } diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index 86e5e88d0a..93cfd16d3d 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1343,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; @@ -1474,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/SecretKey_test.cpp b/src/test/protocol/SecretKey_test.cpp index de21c7d88b..e42bec3363 100644 --- a/src/test/protocol/SecretKey_test.cpp +++ b/src/test/protocol/SecretKey_test.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -70,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); } @@ -95,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++) @@ -104,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: @@ -133,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++) { @@ -142,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: @@ -183,7 +191,7 @@ public: TokenType::NodePrivate, "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); BEAST_EXPECT(sk2); - BEAST_EXPECT(sk1 == *sk2); + BEAST_EXPECT(test::equal(sk1, *sk2)); // NOLINT(bugprone-unchecked-optional-access) } { @@ -193,7 +201,7 @@ public: 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 @@ -261,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)); } } } @@ -292,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) } } @@ -310,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 9f0ce68c46..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); } @@ -93,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) } } @@ -120,7 +120,7 @@ public: 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 @@ -162,7 +162,7 @@ public: 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 @@ -202,7 +202,7 @@ public: "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 @@ -241,7 +241,7 @@ public: "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 diff --git a/src/test/resource/Logic_test.cpp b/src/test/resource/Logic_test.cpp index de57c715bc..c555b92860 100644 --- a/src/test/resource/Logic_test.cpp +++ b/src/test/resource/Logic_test.cpp @@ -66,9 +66,13 @@ public: testDrop(beast::Journal j, bool limited) { if (limited) + { testcase("Limited warn/drop"); + } else + { testcase("Unlimited warn/drop"); + } TestLogic logic(j); @@ -90,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(); @@ -113,9 +125,13 @@ public: if (n == 0) { if (limited) + { fail("Loop count exceeded without dropping"); + } else + { pass(); + } return; } @@ -136,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 d3be04ff96..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 @@ -185,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)); } @@ -197,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), @@ -204,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}, @@ -212,6 +223,7 @@ public: std::nullopt, std::nullopt, ammAlice.ammAccount())); + } for (auto i = 0; i < 2; ++i) { std::unordered_set authAccounts = { diff --git a/src/test/rpc/AccountCurrencies_test.cpp b/src/test/rpc/AccountCurrencies_test.cpp index c317bfc31c..bb8ccf0a85 100644 --- a/src/test/rpc/AccountCurrencies_test.cpp +++ b/src/test/rpc/AccountCurrencies_test.cpp @@ -142,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 a67eef996d..518398f9ea 100644 --- a/src/test/rpc/AccountInfo_test.cpp +++ b/src/test/rpc/AccountInfo_test.cpp @@ -502,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; }; @@ -528,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 @@ -536,7 +537,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::array, 4> @@ -554,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 @@ -562,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{ @@ -573,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 { @@ -594,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/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 7c1877136d..435bbd37d5 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include @@ -305,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"); diff --git a/src/test/rpc/AccountOffers_test.cpp b/src/test/rpc/AccountOffers_test.cpp index 55e60f8caa..6b93e0570f 100644 --- a/src/test/rpc/AccountOffers_test.cpp +++ b/src/test/rpc/AccountOffers_test.cpp @@ -13,7 +13,7 @@ public: checkMarker(Json::Value const& val) { return val.isMember(jss::marker) && val[jss::marker].isString() && - val[jss::marker].asString().size() > 0; + !val[jss::marker].asString().empty(); } void diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 5015b960f9..a11f957628 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -62,19 +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()); - + } else if (metaNode.isMember(sfDeletedNode.jsonName)) + { deletedNodes.insert( metaNode[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + } else if (metaNode.isMember(sfModifiedNode.jsonName)) + { modifiedNodes.insert( metaNode[sfModifiedNode.jsonName][sfLedgerEntryType.jsonName].asString()); - + } else + { fail("Unexpected or unlabeled node type in metadata.", __FILE__, __LINE__); + } } BEAST_EXPECT(createdNodes == sane.created); @@ -138,7 +143,9 @@ class AccountTx_test : public beast::unit_test::suite (payment[jss::close_time_iso] == "2000-01-01T00:00:10Z"); } else + { return false; + } default: return false; @@ -177,18 +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)); + } 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)); + } p[jss::ledger_index_min] = 2; p[jss::ledger_index_max] = 1; @@ -204,10 +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)); + } p[jss::ledger_index_min] = env.current()->header().seq; BEAST_EXPECT(isErr( @@ -223,10 +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)); + } p[jss::ledger_index_max] = 3; BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p)))); @@ -278,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 @@ -288,10 +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)); + } } // test account non-string { @@ -320,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))}; @@ -328,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)); diff --git a/src/test/rpc/Book_test.cpp b/src/test/rpc/Book_test.cpp index c9d93b87a6..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 @@ -30,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)); diff --git a/src/test/rpc/DeliveredAmount_test.cpp b/src/test/rpc/DeliveredAmount_test.cpp index e024e249c3..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}; { @@ -265,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 @@ -287,7 +309,6 @@ 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; diff --git a/src/test/rpc/DepositAuthorized_test.cpp b/src/test/rpc/DepositAuthorized_test.cpp index 755609439c..87951e397c 100644 --- a/src/test/rpc/DepositAuthorized_test.cpp +++ b/src/test/rpc/DepositAuthorized_test.cpp @@ -246,9 +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::error) == !error.empty()); if (!error.empty()) @@ -398,24 +400,16 @@ public: { 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"); diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index a802aed482..0050429c99 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace xrpl { @@ -94,7 +95,8 @@ 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( @@ -168,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"); @@ -186,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 @@ -312,9 +347,12 @@ 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"); + } else + { BEAST_EXPECTS( (*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete && (!(*it)[jss::vetoed].isBool() || @@ -322,6 +360,7 @@ class Feature_test : public beast::unit_test::suite ((*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"); diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index b900afa178..5facd683a5 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -233,6 +233,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); @@ -247,6 +248,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); @@ -286,6 +288,7 @@ public: Oracle oracle( env, {.owner = oracles[i].first, + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) .documentID = asUInt(*oracles[i].second), .fee = baseFee}, false); diff --git a/src/test/rpc/KeyGeneration_test.cpp b/src/test/rpc/KeyGeneration_test.cpp index 3bffb9dc83..e4af2608b7 100644 --- a/src/test/rpc/KeyGeneration_test.cpp +++ b/src/test/rpc/KeyGeneration_test.cpp @@ -164,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 @@ -294,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); @@ -308,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); @@ -322,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); @@ -341,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); @@ -357,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); @@ -373,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); @@ -695,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); @@ -726,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); diff --git a/src/test/rpc/LedgerData_test.cpp b/src/test/rpc/LedgerData_test.cpp index e139ba24b4..472f5b2aa2 100644 --- a/src/test/rpc/LedgerData_test.cpp +++ b/src/test/rpc/LedgerData_test.cpp @@ -13,7 +13,7 @@ public: checkMarker(Json::Value const& val) { return val.isMember(jss::marker) && val[jss::marker].isString() && - val[jss::marker].asString().size() > 0; + !val[jss::marker].asString().empty(); } void @@ -198,8 +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)); + } } { // Closed ledger with binary form @@ -210,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; diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index 9d274b3549..eec114b71c 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -60,10 +60,8 @@ getFieldType(Json::StaticString fieldName) { 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 @@ -110,10 +108,12 @@ 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( @@ -122,11 +122,13 @@ class LedgerEntry_test : public beast::unit_test::suite "\", 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()); + } } std::vector @@ -175,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]); } @@ -286,10 +288,14 @@ class LedgerEntry_test : public beast::unit_test::suite 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); + } } auto tryField = [&](Json::Value fieldValue) -> void { correctRequest[fieldName] = fieldValue; @@ -491,9 +497,13 @@ class LedgerEntry_test : public beast::unit_test::suite 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."); + } } }); } @@ -2583,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: @@ -2643,7 +2650,9 @@ class LedgerEntry_XChain_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_EXPECT(jv[jss::error_message] == msg); + } } void @@ -2753,7 +2762,6 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, 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; diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index d45f50c657..2f4a63a090 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -30,10 +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() + "\""); + } } // Corrupt a valid address by replacing the 10th character with '!'. @@ -634,15 +636,19 @@ 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( diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index c436c1d7b1..54a2931ba5 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -235,8 +235,10 @@ public: { testBadInput(); for (auto user : {true, false}) + { for (auto problem : {true, false}) testBasic(user, problem); + } } }; diff --git a/src/test/rpc/NoRipple_test.cpp b/src/test/rpc/NoRipple_test.cpp index 27f13aa3e2..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"); + } } } } 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/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/ServerInfo_test.cpp b/src/test/rpc/ServerInfo_test.cpp index 9fdfe5e13b..5bb2c31757 100644 --- a/src/test/rpc/ServerInfo_test.cpp +++ b/src/test/rpc/ServerInfo_test.cpp @@ -85,7 +85,7 @@ admin = 127.0.0.1 (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())); } } diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index d1b1cd02fc..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,7 @@ class Simulate_test : public beast::unit_test::suite int const expectedSequence, XRPAmount const& expectedFee) { - return checkBasicReturnValidity( + checkBasicReturnValidity( result, tx, expectedSequence, expectedFee.jsonClipped().asString()); } @@ -87,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())) { @@ -136,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); } diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 7717f86b41..dcb5ab8afd 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -400,7 +400,7 @@ public: 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( @@ -818,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; @@ -998,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); } { @@ -1017,7 +1021,7 @@ public: return; IdxHashVec genesisFullHistoryVec; BEAST_EXPECT(env.syncClose()); - if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first)) + if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first)) return; /* @@ -1177,7 +1181,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*ws, tempVec, 100); + getTxHash(*ws, tempVec, 100, 1000ms); } auto count = mixedPayments(); @@ -1211,7 +1215,7 @@ public: { // take out existing txns from the stream IdxHashVec tempVec; - getTxHash(*wscLong, tempVec, 100); + getTxHash(*wscLong, tempVec, 100, 1000ms); } // repeat the payments many rounds diff --git a/src/test/rpc/Transaction_test.cpp b/src/test/rpc/Transaction_test.cpp index ebe3f3135d..2709900bf1 100644 --- a/src/test/rpc/Transaction_test.cpp +++ b/src/test/rpc/Transaction_test.cpp @@ -92,9 +92,13 @@ class Transaction_test : public beast::unit_test::suite 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. @@ -305,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), @@ -316,6 +321,7 @@ 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) { @@ -327,9 +333,13 @@ class Transaction_test : public beast::unit_test::suite 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. @@ -340,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), @@ -399,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)); @@ -594,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); @@ -614,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; }; @@ -670,7 +683,8 @@ 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) } } @@ -687,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; 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 829e2699a7..43ba188f6a 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -134,7 +134,7 @@ public: 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(); diff --git a/src/test/rpc/Version_test.cpp b/src/test/rpc/Version_test.cpp index c86bb8a660..7fa6720f83 100644 --- a/src/test/rpc/Version_test.cpp +++ b/src/test/rpc/Version_test.cpp @@ -42,10 +42,12 @@ 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; }; diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index dc92375428..178b85b3e4 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -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())); @@ -132,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, @@ -195,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; } @@ -211,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; } @@ -286,6 +288,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en "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( @@ -293,6 +296,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "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( @@ -300,6 +304,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "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( @@ -314,6 +319,7 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en : "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"); } @@ -418,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); @@ -428,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; @@ -509,8 +516,10 @@ 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 = + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) env.app().config().section("port_rpc").get("password").value(); // try with the correct user/pass, but not encoded @@ -538,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; @@ -596,15 +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 @@ -615,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; @@ -636,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; @@ -748,7 +765,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en response resp; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + 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; @@ -784,7 +810,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en // but status does not indicate a problem doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + 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; @@ -795,7 +830,16 @@ 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); + 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; @@ -849,7 +893,16 @@ class ServerStatus_test : public beast::unit_test::suite, public beast::test::en response resp; doRequest( - yield, makeHTTPRequest(*ip_ws, *port_ws, "", {}), *ip_ws, *port_ws, false, resp, ec); + 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; @@ -888,7 +941,16 @@ 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); + 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; @@ -898,7 +960,16 @@ 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); + 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 f7250de7fa..bce8bced60 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -100,8 +100,8 @@ 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{}; @@ -110,7 +110,7 @@ public: Handoff onHandoff( Session& session, - http_request_type&& request, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; @@ -121,9 +121,13 @@ public: { session.write(std::string("Hello, world!\n")); if (beast::rfc2616::is_keep_alive(session.request())) + { session.complete(); + } else + { session.close(true); + } } void @@ -232,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)); } @@ -268,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 @@ -308,8 +312,8 @@ 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{}; @@ -318,7 +322,7 @@ public: Handoff onHandoff( Session& session, - http_request_type&& request, + http_request_type const& request, boost::asio::ip::tcp::endpoint remote_address) { return Handoff{}; 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 81305c4dab..a8f3a478b2 100644 --- a/src/test/shamap/SHAMapSync_test.cpp +++ b/src/test/shamap/SHAMapSync_test.cpp @@ -107,7 +107,7 @@ 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()); diff --git a/src/test/shamap/SHAMap_test.cpp b/src/test/shamap/SHAMap_test.cpp index 1eb050178c..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,9 +199,13 @@ public: sMap.dump(); if (backed) + { testcase("build/tear backed"); + } else + { testcase("build/tear unbacked"); + } { constexpr std::array keys{ uint256( @@ -269,9 +281,13 @@ public: } if (backed) + { testcase("iterate backed"); + } else + { testcase("iterate unbacked"); + } { constexpr std::array keys{ diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp index 3838f128f0..239564bd7c 100644 --- a/src/test/unit_test/multi_runner.cpp +++ b/src/test/unit_test/multi_runner.cpp @@ -108,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) @@ -234,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 (...) { 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 index f01bd91bbf..483cf37fe2 100644 --- a/src/tests/libxrpl/basics/MallocTrim.cpp +++ b/src/tests/libxrpl/basics/MallocTrim.cpp @@ -89,7 +89,7 @@ TEST(parseStatmRSSkB, standard_format) // Test empty string { - std::string statm = ""; + std::string statm; long result = parseStatmRSSkB(statm); EXPECT_EQ(result, -1); } 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 8fef0d61a3..72f2d081c7 100644 --- a/src/tests/libxrpl/basics/Slice.cpp +++ b/src/tests/libxrpl/basics/Slice.cpp @@ -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/json/Value.cpp b/src/tests/libxrpl/json/Value.cpp index cbca7c88a8..a7b7387ba1 100644 --- a/src/tests/libxrpl/json/Value.cpp +++ b/src/tests/libxrpl/json/Value.cpp @@ -761,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) @@ -1348,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/net/HTTPClient.cpp b/src/tests/libxrpl/net/HTTPClient.cpp index 1e93d47c3c..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}; @@ -173,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) @@ -183,61 +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 = { @@ -262,7 +289,6 @@ TEST(HTTPClient, case_insensitive_content_length) bool testCompleted = runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError); - // Verify results EXPECT_TRUE(testCompleted); EXPECT_FALSE(resultError); @@ -271,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"; @@ -292,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 @@ -312,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}; 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 98dccc0089..5a3d53dae0 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -78,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); @@ -384,7 +382,10 @@ RCLConsensus::Adaptor::onAccept( bool const validating) { app_.getJobQueue().addJob( - jtACCEPT, "AcceptLedger", [=, this, cj = std::move(consensusJson)]() mutable { + 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 @@ -408,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; @@ -497,7 +498,7 @@ RCLConsensus::Adaptor::doAccept( std::move(accepted), [curr = built.seq(), j = app_.journal("CensorshipDetector"), &failed]( uint256 const& id, LedgerIndex seq) { - if (failed.count(id)) + if (failed.contains(id)) return true; auto const wait = curr - seq; @@ -584,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, @@ -663,9 +668,13 @@ RCLConsensus::Adaptor::notify( 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()); @@ -673,7 +682,7 @@ RCLConsensus::Adaptor::notify( 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; @@ -725,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)}; 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/RCLValidations.cpp b/src/xrpld/app/consensus/RCLValidations.cpp index 60e02f53b4..a968acb1a7 100644 --- a/src/xrpld/app/consensus/RCLValidations.cpp +++ b/src/xrpld/app/consensus/RCLValidations.cpp @@ -205,14 +205,18 @@ handleNewValidation( }(); if (outcome == ValStatus::conflicting) + { 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() << "]"; + } } } diff --git a/src/xrpld/app/ledger/AcceptedLedger.cpp b/src/xrpld/app/ledger/AcceptedLedger.cpp index 339c5f57c7..1da70702bf 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.cpp +++ b/src/xrpld/app/ledger/AcceptedLedger.cpp @@ -10,8 +10,10 @@ AcceptedLedger::AcceptedLedger(std::shared_ptr const& ledger) : auto insertAll = [&](auto const& txns) { for (auto const& item : txns) + { transactions_.emplace_back( std::make_unique(ledger, item.first, item.second)); + } }; transactions_.reserve(256); diff --git a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp index 575a1a1460..48080aaedb 100644 --- a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp +++ b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp @@ -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) diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index dfb90fa5c2..636410e57a 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -605,9 +605,13 @@ Ledger::setup() if (src) { if (src->native()) + { dest = src->xrp(); + } else + { ret = false; + } } }; assign(fees_.base, baseFeeXRP); @@ -632,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&) @@ -807,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()) @@ -883,7 +895,7 @@ Ledger::updateSkipList() auto sle = peek(k); std::vector hashes; - bool created; + bool created = false; if (!sle) { sle = std::make_shared(k); @@ -901,16 +913,20 @@ Ledger::updateSkipList() 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); @@ -928,9 +944,13 @@ Ledger::updateSkipList() sle->setFieldV256(sfHashes, STVector256(hashes)); sle->setFieldU32(sfLastLedgerSequence, prevIndex); if (created) + { rawInsert(sle); + } else + { rawReplace(sle); + } } bool @@ -1040,7 +1060,7 @@ Ledger::invariants() const std::shared_ptr loadLedgerHelper(LedgerHeader const& info, Application& app, bool acquire) { - bool loaded; + bool loaded = false; auto ledger = std::make_shared( info, loaded, acquire, app.config(), app.getNodeFamily(), app.journal("Ledger")); diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index f2e2afb3e6..5ff5555fff 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -337,9 +337,11 @@ 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); @@ -350,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."; diff --git a/src/xrpld/app/ledger/LedgerReplayTask.h b/src/xrpld/app/ledger/LedgerReplayTask.h index 028e7ba0bd..030121b240 100644 --- a/src/xrpld/app/ledger/LedgerReplayTask.h +++ b/src/xrpld/app/ledger/LedgerReplayTask.h @@ -78,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/OrderBookDBImpl.cpp b/src/xrpld/app/ledger/OrderBookDBImpl.cpp index a4eb0fb1ca..2691935911 100644 --- a/src/xrpld/app/ledger/OrderBookDBImpl.cpp +++ b/src/xrpld/app/ledger/OrderBookDBImpl.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include namespace xrpl { @@ -52,12 +52,16 @@ OrderBookDBImpl::setup(std::shared_ptr const& ledger) if (pathSearchMax_ != 0) { if (standalone_) + { update(ledger); + } else + { registry_.getJobQueue().addJob( jtUPDATE_PF, "OrderBookUpd" + std::to_string(ledger->seq()), [this, ledger]() { update(ledger); }); + } } } @@ -111,14 +115,22 @@ OrderBookDBImpl::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; } @@ -167,14 +179,22 @@ OrderBookDBImpl::addOrderBook(Book const& book) 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 @@ -198,9 +218,13 @@ OrderBookDBImpl::getBooksByTakerPays(Issue const& issue, std::optional }; if (!domain) + { getBooks(allBooks_, issue); + } else + { getBooks(domainBooks_, std::make_pair(issue, *domain)); + } } return ret; @@ -306,11 +330,17 @@ OrderBookDBImpl::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/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp index c4f8f9e60a..5ad72c7a69 100644 --- a/src/xrpld/app/ledger/detail/BuildLedger.cpp +++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp @@ -181,10 +181,12 @@ 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(); + } else JLOG(j.debug()) << "Applied " << applied << " transactions. " << "Total transactions in ledger (including Inner Batch): " diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index 4dd8ab887e..9f7312ea34 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -145,9 +145,13 @@ InboundLedger::checkLocal() if (!isDone()) { if (mLedger) + { tryDB(mLedger->stateMap().family().db()); + } else + { tryDB(app_.getNodeFamily().db()); + } if (failed_ || complete_) { done(); @@ -184,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); @@ -435,7 +441,9 @@ InboundLedger::done() self->app_.getLedgerMaster().tryAdvance(); } else + { self->app_.getInboundLedgers().logFailure(self->hash_, self->mSeq); + } }); } @@ -461,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(); } @@ -562,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. @@ -602,7 +616,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->stateMap().isValid()) + { failed_ = true; + } else { mHaveState = true; @@ -628,10 +644,8 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) mPeerSet->sendRequest(tmGL, peer); return; } - else - { - JLOG(journal_.trace()) << "All AS nodes filtered"; - } + + JLOG(journal_.trace()) << "All AS nodes filtered"; } } } @@ -667,7 +681,9 @@ InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) if (nodes.empty()) { if (!mLedger->txMap().isValid()) + { failed_ = true; + } else { mHaveTransactions = true; @@ -692,10 +708,8 @@ 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"; } } } @@ -823,11 +837,13 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) 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())}; + } return { mLedger->stateMap(), SHAMapHash{mLedger->header().accountHash}, @@ -872,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) { @@ -1056,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"; @@ -1131,9 +1149,13 @@ struct PeerDataCounts while (i != counts.end()) { if (i->second < thresh) + { i = counts.erase(i); + } else + { ++i; + } } } diff --git a/src/xrpld/app/ledger/detail/InboundLedgers.cpp b/src/xrpld/app/ledger/detail/InboundLedgers.cpp index e17437d64f..be7577a2c3 100644 --- a/src/xrpld/app/ledger/detail/InboundLedgers.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedgers.cpp @@ -174,8 +174,10 @@ public: // 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(); }); + } return true; } @@ -290,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; + } } } @@ -301,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; @@ -340,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); diff --git a/src/xrpld/app/ledger/detail/InboundTransactions.cpp b/src/xrpld/app/ledger/detail/InboundTransactions.cpp index b6f4c961c1..f27f2c33f0 100644 --- a/src/xrpld/app/ledger/detail/InboundTransactions.cpp +++ b/src/xrpld/app/ledger/detail/InboundTransactions.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -50,7 +51,6 @@ 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)) @@ -168,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(); } @@ -203,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; + } } } } @@ -227,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; diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp index ad9c6a277d..3052f787dc 100644 --- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp +++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp @@ -93,7 +93,9 @@ public: std::lock_guard lock(mutex_); if (maxRange_ == 0) + { map["status"] = "idle"; + } else { map["status"] = "running"; @@ -356,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 e22351fc77..cb2e2e57ea 100644 --- a/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp +++ b/src/xrpld/app/ledger/detail/LedgerDeltaAcquire.cpp @@ -145,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_) @@ -185,14 +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 diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index 64bdf04df1..42d7f2ec6e 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -283,7 +283,9 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) app_.getOPs().setAmendmentWarned(); } else + { app_.getOPs().clearAmendmentWarned(); + } } } } @@ -350,10 +352,12 @@ 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() / 2; + } if (ledger->header().seq > maxSeq) { @@ -520,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; } @@ -544,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 @@ -555,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; + } } } @@ -577,9 +589,13 @@ LedgerMaster::getEarliestFetch() std::uint32_t e = getClosedLedger()->header().seq; if (e > fetch_depth_) + { e -= fetch_depth_; + } else + { e = 0; + } return e; } @@ -927,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()); @@ -1063,10 +1079,7 @@ LedgerMaster::consensusBuilt( class valSeq { public: - valSeq() : valCount_(0), ledgerSeq_(0) - { - ; - } + valSeq() = default; void mergeValidation(LedgerIndex seq) @@ -1078,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 @@ -1097,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 @@ -1112,6 +1126,7 @@ LedgerMaster::consensusBuilt( maxLedger = v.first; } } + } if (maxSeq > mValidLedgerSeq) { @@ -1224,8 +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); + } } // Did we acquire the next ledger we need to publish? @@ -1750,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; @@ -1771,7 +1788,7 @@ LedgerMaster::fetchForHistory( } 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(); @@ -1853,7 +1870,9 @@ LedgerMaster::doAdvance(std::unique_lock& sl) JLOG(m_journal.trace()) << "advanceThread should acquire"; } else + { missing = std::nullopt; + } } if (missing) { @@ -2136,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/LedgerReplayMsgHandler.cpp b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp index 73fcf77e4e..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 @@ -136,7 +137,7 @@ LedgerReplayMsgHandler::processProofPathResponse( return false; } - if (auto item = static_cast(node.get())->peekItem()) + if (auto item = safe_downcast(node.get())->peekItem()) { replayer_.gotSkipList(info, item); return true; diff --git a/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp b/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp index 42db911830..b878c840dd 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayTask.cpp @@ -67,7 +67,7 @@ LedgerReplayTask::LedgerReplayTask( InboundLedgers& inboundLedgers, LedgerReplayer& replayer, std::shared_ptr& skipListAcquirer, - TaskParameter&& parameter) + TaskParameter const& parameter) : TimeoutCounter( app, parameter.finishHash_, @@ -183,7 +183,9 @@ LedgerReplayTask::tryAdvance(ScopedLockType& sl) parent_ = l; } else + { return; + } } complete_ = true; @@ -248,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 f649eb4d95..553ff91174 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayer.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayer.cpp @@ -98,8 +98,12 @@ LedgerReplayer::createDeltas(std::shared_ptr task) { auto skipListItem = std::find(parameter.skipList_.begin(), parameter.skipList_.end(), parameter.startHash_); - if (skipListItem == parameter.skipList_.end() || - ++skipListItem == parameter.skipList_.end()) + 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_; @@ -213,7 +217,9 @@ LedgerReplayer::sweep() it = subTasks.erase(it); } else + { ++it; + } } }; removeCannotLocked(skipLists_); diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index 038a5ae742..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 @@ -74,7 +75,9 @@ void fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info) { if (!closed) + { json[jss::closed] = false; + } else { json[jss::closed] = true; @@ -120,11 +123,13 @@ 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}); + } // If applicable, insert mpt issuance id RPC::insertMPTokenIssuanceID( @@ -154,11 +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}); + } // If applicable, insert mpt issuance id RPC::insertMPTokenIssuanceID( @@ -233,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())); + } } } @@ -266,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); + } } } @@ -279,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); @@ -318,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/TransactionAcquire.cpp b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp index 8cc26b1da6..18c5066064 100644 --- a/src/xrpld/app/ledger/detail/TransactionAcquire.cpp +++ b/src/xrpld/app/ledger/detail/TransactionAcquire.cpp @@ -6,6 +6,7 @@ #include +#include #include namespace xrpl { @@ -129,9 +130,13 @@ TransactionAcquire::trigger(std::shared_ptr const& peer) if (nodes.empty()) { if (mMap->isValid()) + { complete_ = true; + } else + { failed_ = true; + } done(); return; @@ -183,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()) { @@ -234,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/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 3e3d87dcd5..048ef0d9b4 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -92,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) { } @@ -278,11 +278,17 @@ public: // 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_), @@ -600,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); @@ -823,7 +829,7 @@ public: } bool - initNodeStore() + initNodeStore() const { if (config_->doImport) { @@ -1346,7 +1352,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()); @@ -1572,7 +1579,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; @@ -1867,7 +1876,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); diff --git a/src/xrpld/app/main/GRPCServer.cpp b/src/xrpld/app/main/GRPCServer.cpp index c6b5c91e14..a9810ae22b 100644 --- a/src/xrpld/app/main/GRPCServer.cpp +++ b/src/xrpld/app/main/GRPCServer.cpp @@ -205,9 +205,11 @@ Role GRPCServerImpl::CallData::getRole(bool isUnlimited) { if (isUnlimited) + { return Role::IDENTIFIED; - else - return Role::USER; + } + + return Role::USER; } template @@ -383,8 +385,8 @@ GRPCServerImpl::handleRpcs() 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. diff --git a/src/xrpld/app/main/LoadManager.cpp b/src/xrpld/app/main/LoadManager.cpp index 5d2bd9a81e..f0fc4085ac 100644 --- a/src/xrpld/app/main/LoadManager.cpp +++ b/src/xrpld/app/main/LoadManager.cpp @@ -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) { } diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 32d246f493..03f8076ed1 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -61,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; @@ -69,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) { @@ -103,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" @@ -195,8 +199,10 @@ public: operator()(beast::unit_test::suite_info const& s) { for (auto& sel : selectors_) + { if (sel(s)) return true; + } return false; } @@ -277,8 +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); + } int bad_child_exits = 0; int terminated_child_exits = 0; @@ -305,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 @@ -468,13 +474,13 @@ 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; std::cout << "Git commit hash: " << xrpl::git::getCommitHash() << std::endl; @@ -493,57 +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"))); + configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone")); - if (vm.count("vacuum")) + if (vm.contains("vacuum")) { if (config->standalone()) { @@ -609,21 +611,21 @@ run(int argc, char** argv) } } - if (vm.count("start")) + if (vm.contains("start")) { 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 = StartUpType::REPLAY; - if (vm.count("trap_tx_hash")) + if (vm.contains("trap_tx_hash")) { uint256 tmp = {}; auto hash = vm["trap_tx_hash"].as(); @@ -641,25 +643,27 @@ run(int argc, char** argv) } } else + { config->START_UP = StartUpType::LOAD; + } } - else if (vm.count("ledgerfile")) + else if (vm.contains("ledgerfile")) { config->START_LEDGER = vm["ledgerfile"].as(); 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 = 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 == StartUpType::LOAD) || (config->START_UP == StartUpType::REPLAY)) { @@ -670,14 +674,14 @@ run(int argc, char** argv) 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) @@ -689,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 @@ -705,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 { @@ -732,15 +738,19 @@ 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 @@ -760,7 +770,7 @@ 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 = diff --git a/src/xrpld/app/main/NodeIdentity.cpp b/src/xrpld/app/main/NodeIdentity.cpp index 3019caeb31..bc2c943b10 100644 --- a/src/xrpld/app/main/NodeIdentity.cpp +++ b/src/xrpld/app/main/NodeIdentity.cpp @@ -12,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); @@ -37,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/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 0c2eaadaf8..eb0ce5617b 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -200,9 +200,13 @@ FeeVoteImpl::doVoting( { auto const vote = field->xrp(); if (isLegalAmountSigned(vote)) + { value.addVote(vote); + } else + { value.noVote(); + } } else { @@ -230,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 { diff --git a/src/xrpld/app/misc/NegativeUNLVote.cpp b/src/xrpld/app/misc/NegativeUNLVote.cpp index 6673785b61..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); } @@ -175,7 +175,7 @@ NegativeUNLVote::buildScoreTable( 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()]; } } @@ -195,24 +195,20 @@ NegativeUNLVote::buildScoreTable( << " The reliability measurement could be wrong."; return {}; } - else if ( - myValidationCount > negativeUNLMinLocalValsToVote && + 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, @@ -225,7 +221,7 @@ NegativeUNLVote::findAllCandidates( std::size_t negativeListed = 0; for (auto const& n : unl) { - if (negUnl.count(n)) + if (negUnl.contains(n)) ++negativeListed; } bool const result = negativeListed < maxNegativeListed; @@ -246,8 +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); @@ -256,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); @@ -277,7 +273,7 @@ NegativeUNLVote::findAllCandidates( { for (auto const& n : negUnl) { - if (!unl.count(n)) + if (!unl.contains(n)) { candidates.toReEnableCandidates.push_back(n); } @@ -292,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 95646af034..601ac7d060 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.h +++ b/src/xrpld/app/misc/NegativeUNLVote.h @@ -172,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 017be50a20..29be555568 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -43,11 +43,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -165,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 @@ -648,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) { } }; @@ -717,7 +714,7 @@ private: std::optional const validatorPK_; std::optional const validatorMasterPK_; - ConsensusPhase mLastConsensusPhase; + ConsensusPhase mLastConsensusPhase{ConsensusPhase::open}; LedgerMaster& m_ledgerMaster; @@ -759,7 +756,7 @@ private: DispatchState mDispatchState = DispatchState::none; std::vector mTransactions; - StateAccounting accounting_{}; + StateAccounting accounting_; std::set pendingValidations_; std::mutex validationsMutex_; @@ -1019,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) { @@ -1231,9 +1232,13 @@ NetworkOPsImp::processTransaction( return; if (bLocal) + { doTransactionSync(transaction, bUnlimited, failType); + } else + { doTransactionAsync(transaction, bUnlimited, failType); + } } void @@ -1294,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(); })) @@ -1349,7 +1354,9 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) } if (mTransactions.empty()) + { mTransactions.swap(transactions); + } else { mTransactions.reserve(mTransactions.size() + transactions.size()); @@ -1378,7 +1385,7 @@ NetworkOPsImp::transactionBatch() if (mDispatchState == DispatchState::running) return; - while (mTransactions.size()) + while (!mTransactions.empty()) { apply(lock); } @@ -1449,7 +1456,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) registry_.getHashRouter().setFlags(e.transaction->getID(), HashRouterFlags::BAD); #ifdef DEBUG - if (e.result != tesSUCCESS) + if (!isTesSuccess(e.result)) { std::string token, human; @@ -1462,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); @@ -1613,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()); @@ -1639,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 { @@ -1811,7 +1820,9 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint switchLedgers = false; } else + { networkClosed = closedLedger; + } if (!switchLedgers) return false; @@ -1819,8 +1830,10 @@ NetworkOPsImp::checkLastClosedLedger(Overlay::PeerSequence const& peerList, uint auto consensus = m_ledgerMaster.getLedgerByHash(closedLedger); if (!consensus) + { consensus = registry_.getInboundLedgers().acquire( closedLedger, 0, InboundLedger::Reason::CONSENSUS); + } if (consensus && (!m_ledgerMaster.canBeCurrent(consensus) || @@ -1872,9 +1885,13 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr const& newLC auto const lastVal = registry_.getLedgerMaster().getValidatedLedger(); std::optional rules; if (lastVal) + { rules = makeRulesGivenLedger(*lastVal, registry_.app().config().features); + } else + { rules.emplace(registry_.app().config().features); + } registry_.openLedger().accept( registry_.app(), *rules, @@ -2194,7 +2211,9 @@ NetworkOPsImp::pubServer() jvObj[jss::load_factor_fee_reference] = f.em->referenceFeeLevel.jsonClipped(); } else + { jvObj[jss::load_factor] = f.loadFactorServer; + } mLastFeeSummary = f; @@ -2428,9 +2447,13 @@ 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(registry_.app(), val, source, bypassAccept, m_journal); } @@ -2545,6 +2568,9 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (admin) { + // 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: @@ -2569,10 +2595,14 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (!human) { if (when) + { info[jss::validator_list_expires] = safe_cast(when->time_since_epoch().count()); + } else + { info[jss::validator_list_expires] = 0; + } } else { @@ -2592,9 +2622,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) x[jss::expiration] = to_string(*when); if (*when > registry_.timeKeeper().now()) + { x[jss::status] = "active"; + } else + { x[jss::status] = "expired"; + } } } else @@ -2727,22 +2761,30 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } if (escalationMetrics.openLedgerFeeLevel != escalationMetrics.referenceFeeLevel && (admin || loadFactorFeeEscalation != loadFactor)) + { info[jss::load_factor_fee_escalation] = escalationMetrics.openLedgerFeeLevel.decimalFromReference( escalationMetrics.referenceFeeLevel); + } if (escalationMetrics.minProcessingFeeLevel != escalationMetrics.referenceFeeLevel) + { info[jss::load_factor_fee_queue] = escalationMetrics.minProcessingFeeLevel.decimalFromReference( escalationMetrics.referenceFeeLevel); + } } bool valid = false; auto lpClosed = m_ledgerMaster.getValidatedLedger(); if (lpClosed) + { valid = true; + } else + { lpClosed = m_ledgerMaster.getClosedLedger(); + } if (lpClosed) { @@ -2802,15 +2844,23 @@ 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); @@ -2986,7 +3036,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) ++it; } else + { it = mStreamMaps[sLedger].erase(it); + } } } @@ -3004,7 +3056,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) ++it; } else + { it = mStreamMaps[sBookChanges].erase(it); + } } } @@ -3199,7 +3253,9 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sTransactions].erase(it); + } } it = mStreamMaps[sRTTransactions].begin(); @@ -3216,7 +3272,9 @@ NetworkOPsImp::pubValidatedTransaction( ++it; } else + { it = mStreamMaps[sRTTransactions].erase(it); + } } } @@ -3261,7 +3319,9 @@ NetworkOPsImp::pubAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } @@ -3279,7 +3339,9 @@ NetworkOPsImp::pubAccountTransaction( ++iAccepted; } else + { it = simiIt->second.erase(it); + } } } @@ -3393,7 +3455,9 @@ NetworkOPsImp::pubProposedAccountTransaction( ++iProposed; } else + { it = simiIt->second.erase(it); + } } } } @@ -3408,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, @@ -3623,7 +3689,8 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) switch (dbType) { case Sqlite: { - auto db = static_cast(®istry_.getRelationalDatabase()); + auto db = + safe_downcast(®istry_.getRelationalDatabase()); RelationalDatabase::AccountTxPageOptions options{ accountId, minLedger, maxLedger, marker, 0, true}; return db->newestAccountTxPage(options); @@ -3761,10 +3828,8 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo) << " done, found last tx."; return; } - else - { - sendMultiApiJson(jvTx, false); - } + + sendMultiApiJson(jvTx, false); } if (marker) @@ -3924,7 +3989,9 @@ bool NetworkOPsImp::subBook(InfoSub::ref isrListener, Book const& book) { if (auto listeners = registry_.getOrderBookDB().makeBookListeners(book)) + { listeners->addSubscriber(isrListener); + } else { // LCOV_EXCL_START @@ -4188,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); @@ -4235,7 +4302,7 @@ NetworkOPsImp::getBookPage( std::shared_ptr sleOfferDir; uint256 offerIndex; - unsigned int uBookEntry; + unsigned int uBookEntry = 0; STAmount saDirRate; auto const rate = transferRate(view, book.out.account); @@ -4251,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) { diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index 47e9f0ef52..13746369f5 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -97,7 +97,7 @@ SHAMapStoreImp::SHAMapStoreImp( { // 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)) @@ -143,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(); @@ -248,7 +248,9 @@ SHAMapStoreImp::run() validatedLedger = std::move(newLedger_); } else + { continue; + } } LedgerIndex const validatedSeq = validatedLedger->header().seq; @@ -385,15 +387,21 @@ SHAMapStoreImp::dbPaths() ++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) || + 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"); @@ -428,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; } diff --git a/src/xrpld/app/misc/ValidatorSite.h b/src/xrpld/app/misc/ValidatorSite.h index 9c2d45fb6d..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 @@ -224,7 +224,7 @@ private: /// lock over sites_mutex_ required std::shared_ptr processRedirect( - detail::response_type& res, + detail::response_type const& res, std::size_t siteIdx, std::lock_guard const&); diff --git a/src/xrpld/app/misc/detail/AccountTxPaging.cpp b/src/xrpld/app/misc/detail/AccountTxPaging.cpp index 63457f00ac..4e79b53ed0 100644 --- a/src/xrpld/app/misc/detail/AccountTxPaging.cpp +++ b/src/xrpld/app/misc/detail/AccountTxPaging.cpp @@ -27,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.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/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 196584fe6f..2b7005f4cc 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -40,8 +40,10 @@ parseSection(Section const& section) uint256 id; if (!id.parseHex(match[1])) + { Throw( "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]"); + } names.push_back(std::make_pair(id, match[2])); } @@ -357,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_; @@ -370,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 @@ -477,11 +479,7 @@ AmendmentTableImpl::AmendmentTableImpl( Section const& enabled, Section const& vetoed, beast::Journal journal) - : lastUpdateSeq_(0) - , majorityTime_(majorityTime) - , unsupportedEnabled_(false) - , j_(journal) - , db_(registry.getWalletDB()) + : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB()) { std::lock_guard lock(mutex_); @@ -529,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 @@ -545,18 +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]."; } } @@ -749,7 +745,7 @@ AmendmentTableImpl::doValidation(std::set const& enabled) const for (auto const& e : amendmentMap_) { if (e.second.supported && e.second.vote == AmendmentVote::up && - (enabled.count(e.first) == 0)) + (!enabled.contains(e.first))) { amendments.push_back(e.first); JLOG(j_.info()) << "Voting for amendment " << e.second.name; @@ -931,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; diff --git a/src/xrpld/app/misc/detail/Transaction.cpp b/src/xrpld/app/misc/detail/Transaction.cpp index fae26873fb..35b49504d5 100644 --- a/src/xrpld/app/misc/detail/Transaction.cpp +++ b/src/xrpld/app/misc/detail/Transaction.cpp @@ -53,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; } diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 718b468ff0..020fa501d5 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -386,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; } @@ -690,7 +696,7 @@ TxQ::apply( // 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 @@ -718,9 +724,11 @@ TxQ::apply( 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. @@ -762,9 +770,11 @@ TxQ::apply( 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()}}; }(); @@ -928,7 +938,7 @@ TxQ::apply( // // 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 @@ -949,9 +959,13 @@ TxQ::apply( if (txSeqProx.isSeq()) { if (txSeqProx < acctSeqProx) + { return {tefPAST_SEQ, false}; - else if (txSeqProx > acctSeqProx) + } + if (txSeqProx > acctSeqProx) + { return {terPRE_SEQ, false}; + } } } else if (!replacedTxIter) @@ -1301,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; + } } } @@ -1393,9 +1411,13 @@ TxQ::accept(Application& app, OpenView& view) 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); @@ -1406,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>()) { @@ -1447,7 +1473,9 @@ TxQ::accept(Application& app, OpenView& view) } } else + { ++candidateIter; + } } } else @@ -1462,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 @@ -1532,12 +1564,14 @@ TxQ::nextQueuableSeqImpl( 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. @@ -1798,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) diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 73db2f8bdb..386b96387e 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -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; @@ -508,34 +508,29 @@ splitMessageParts( 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 @@ -608,14 +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]] @@ -648,15 +641,17 @@ ValidatorList::buildValidatorListMessages( 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()) @@ -668,9 +663,11 @@ ValidatorList::buildValidatorListMessages( 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. @@ -693,10 +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( @@ -728,11 +730,13 @@ ValidatorList::sendValidatorList( 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(); + } else { XRPL_ASSERT( @@ -835,7 +839,7 @@ 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) @@ -955,13 +959,15 @@ ValidatorList::applyLists( 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; @@ -1017,9 +1023,13 @@ ValidatorList::updatePublisherList( { // Decrement list count for removed keys if (keyListings_[*iOld] <= 1) + { keyListings_.erase(*iOld); + } else + { --keyListings_[*iOld]; + } ++iOld; } else @@ -1038,7 +1048,7 @@ 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"; @@ -1099,7 +1109,7 @@ 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 && @@ -1122,15 +1132,17 @@ ValidatorList::applyList( (result == ListDisposition::accepted || result == ListDisposition::expired); if (accepted) + { 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 @@ -1282,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; @@ -1324,14 +1336,23 @@ ValidatorList::verify( 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 @@ -1343,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 { @@ -1362,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 @@ -1385,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; } @@ -1394,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; } @@ -1411,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; } @@ -1444,9 +1466,13 @@ ValidatorList::removePublisherList( continue; if (iVal->second <= 1) + { keyListings_.erase(iVal); + } else + { --iVal->second; + } } iList->second.current.list.clear(); @@ -1458,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 @@ -1489,9 +1515,13 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const { (void)sequence; if (check.validFrom <= chainedExpiration) + { chainedExpiration = check.validUntil; + } else + { break; + } } // Earliest @@ -1501,7 +1531,7 @@ ValidatorList::expires(ValidatorList::shared_lock const&) const } } - if (localPublisherList.list.size() > 0) + if (!localPublisherList.list.empty()) { PublisherList collection = localPublisherList; // Unfetched @@ -1550,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 @@ -1951,7 +1985,7 @@ ValidatorList::updateTrusted( { for (auto const& k : trustedMasterKeys_) { - if (negativeUNL_.count(k)) + if (negativeUNL_.contains(k)) --effectiveUnlSize; } hash_set negUnlNodeIDs; @@ -1961,7 +1995,7 @@ ValidatorList::updateTrusted( } for (auto const& nid : seenValidators) { - if (negUnlNodeIDs.count(nid)) + if (negUnlNodeIDs.contains(nid)) --seenSize; } } @@ -1977,7 +2011,7 @@ ValidatorList::updateTrusted( << " 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(); @@ -2031,12 +2065,10 @@ ValidatorList::negativeUNLFilter(std::vector>&& va 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 c4077a1b8b..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,7 +63,6 @@ ValidatorSite::Site::Site(std::string uri) , redirCount{0} , refreshInterval{default_refresh_interval} , nextRefresh{clock_type::now()} - , lastRequestEndpoint{} , lastRequestSuccessful{false} { } @@ -301,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"; @@ -367,7 +370,7 @@ ValidatorSite::parseJsonResponse( 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) { @@ -453,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."; @@ -496,7 +499,7 @@ 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_}; 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 index a0e63dd67e..0cc61f0730 100644 --- a/src/xrpld/app/misc/detail/setup_HashRouter.cpp +++ b/src/xrpld/app/misc/detail/setup_HashRouter.cpp @@ -18,17 +18,21 @@ setup_HashRouter(Config const& config) 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) diff --git a/src/xrpld/app/paths/AMMLiquidity.h b/src/xrpld/app/paths/AMMLiquidity.h index 5c3ff2a8e6..71b8dbb12a 100644 --- a/src/xrpld/app/paths/AMMLiquidity.h +++ b/src/xrpld/app/paths/AMMLiquidity.h @@ -4,9 +4,9 @@ #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/PathRequest.cpp b/src/xrpld/app/paths/PathRequest.cpp index c6d0f59ff0..f82a207552 100644 --- a/src/xrpld/app/paths/PathRequest.cpp +++ b/src/xrpld/app/paths/PathRequest.cpp @@ -406,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; @@ -461,9 +459,13 @@ PathRequest::getPathFinder( 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); } @@ -565,7 +567,7 @@ 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()); @@ -652,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) { diff --git a/src/xrpld/app/paths/PathRequest.h b/src/xrpld/app/paths/PathRequest.h index fde499a312..0ffc6c6e2c 100644 --- a/src/xrpld/app/paths/PathRequest.h +++ b/src/xrpld/app/paths/PathRequest.h @@ -139,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 35a6c7aa48..61db1e58ef 100644 --- a/src/xrpld/app/paths/PathRequests.cpp +++ b/src/xrpld/app/paths/PathRequests.cpp @@ -95,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)) diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/app/paths/Pathfinder.cpp index 254db35ea5..c777fcb2f7 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/app/paths/Pathfinder.cpp @@ -95,7 +95,7 @@ struct PathCost }; using PathCostList = std::vector; -static PathTable mPathTable; +PathTable mPathTable; std::string pathTypeToString(Pathfinder::PathType const& type) @@ -260,7 +260,7 @@ Pathfinder::findPaths(int searchLevel, std::function const& continue // 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 @@ -348,7 +348,7 @@ Pathfinder::getPathLiquidity( 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); @@ -491,10 +491,10 @@ Pathfinder::rankPaths( if (!currentPath.empty()) { STAmount liquidity; - uint64_t uQuality; + uint64_t uQuality = 0; auto const resultCode = getPathLiquidity(currentPath, saMinDstAmount, liquidity, uQuality); - if (resultCode != tesSUCCESS) + if (!isTesSuccess(resultCode)) { JLOG(j_.debug()) << "findPaths: dropping : " << transToken(resultCode) << ": " << currentPath.getJson(JsonOptions::none); @@ -574,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 @@ -1020,9 +1032,13 @@ Pathfinder::addLink( 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) @@ -1090,7 +1106,9 @@ Pathfinder::addLink( addUniquePath(mCompletePaths, newPath); } else + { incompletePaths.push_back(newPath); + } } else if (!currentPath.hasSeen( book.out.account, book.out.currency, book.out.account)) @@ -1158,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 9916facdc2..ac3e28e579 100644 --- a/src/xrpld/app/paths/RippleLineCache.cpp +++ b/src/xrpld/app/paths/RippleLineCache.cpp @@ -76,7 +76,7 @@ RippleLineCache::getRippleLines(AccountID const& accountID, LineDirection direct { 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(); diff --git a/src/xrpld/app/paths/TrustLine.cpp b/src/xrpld/app/paths/TrustLine.cpp index 6c54aa52d7..963e1402be 100644 --- a/src/xrpld/app/paths/TrustLine.cpp +++ b/src/xrpld/app/paths/TrustLine.cpp @@ -1,5 +1,6 @@ #include +#include #include #include diff --git a/src/xrpld/app/paths/detail/AMMLiquidity.cpp b/src/xrpld/app/paths/detail/AMMLiquidity.cpp index 3ffc86d1ba..72ee8eb261 100644 --- a/src/xrpld/app/paths/detail/AMMLiquidity.cpp +++ b/src/xrpld/app/paths/detail/AMMLiquidity.cpp @@ -80,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 @@ -108,14 +114,12 @@ 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 @@ -166,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 @@ -175,13 +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 = + 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) @@ -192,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) { diff --git a/src/xrpld/app/paths/detail/BookStep.cpp b/src/xrpld/app/paths/detail/BookStep.cpp index 4124fdfa30..e0053956a8 100644 --- a/src/xrpld/app/paths/detail/BookStep.cpp +++ b/src/xrpld/app/paths/detail/BookStep.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,7 +14,7 @@ #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 { @@ -223,6 +227,8 @@ private: // whichever is a better quality. std::optional tipOfferQualityF(ReadView const& view) const; + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -240,7 +246,11 @@ class BookPaymentStep : public BookStep> public: explicit BookPaymentStep() = default; - using BookStep>::BookStep; + BookPaymentStep(StrandContext const& ctx, Issue const& in, Issue const& out) + : BookStep>(ctx, in, out) + { + } + using BookStep>::qualityUpperBound; using typename BookStep>::OfferType; @@ -484,11 +494,14 @@ public: // when calculating the upper bound quality and the quality function // because single path AMM's offer quality is not constant. if (!rules.enabled(fixAMMv1_1)) + { return ofrQ; - else if ( - offerType == OfferType::CLOB || + } + if (offerType == OfferType::CLOB || (this->ammLiquidity_ && this->ammLiquidity_->multiPath())) + { return ofrQ; + } auto rate = [&](AccountID const& id) { if (isXRP(id) || id == this->strandDst_) @@ -667,9 +680,13 @@ BookStep::forEachOffer( // Note that offer.quality() returns a (non-optional) Quality. So // ofrQ is always safe to use below this point in the lambda. if (!ofrQ) + { ofrQ = offer.quality(); + } else if (*ofrQ != offer.quality()) + { return false; + } if (static_cast(this)->limitSelfCrossQuality( strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted)) @@ -697,8 +714,10 @@ BookStep::forEachOffer( if (auto const key = offer.key()) offers.permRmOffer(*key); if (!offerAttempted) + { // Change quality only if no previous offers were tried. ofrQ = std::nullopt; + } // Returning true causes offers.step() to delete the offer. return true; } @@ -804,7 +823,7 @@ BookStep::consumeOffer( { auto const dr = offer.send(sb, book_.in.account, offer.owner(), toSTAmount(ofrAmt.in, book_.in), j_); - if (dr != tesSUCCESS) + if (!isTesSuccess(dr)) Throw(dr); } @@ -813,7 +832,7 @@ BookStep::consumeOffer( { auto const cr = offer.send(sb, offer.owner(), book_.out.account, toSTAmount(ownerGives, book_.out), j_); - if (cr != tesSUCCESS) + if (!isTesSuccess(cr)) Throw(cr); } @@ -874,24 +893,34 @@ auto BookStep::tipOfferQuality(ReadView const& view) const -> std::optional> { - if (auto const res = tip(view); !res) + auto const res = tip(view); + if (!res) + { return std::nullopt; - else if (auto const q = std::get_if(&(*res))) + } + if (auto const q = std::get_if(&(*res))) + { return std::make_pair(*q, OfferType::CLOB); - else - return std::make_pair(std::get>(*res).quality(), OfferType::AMM); + } + + return std::make_pair(std::get>(*res).quality(), OfferType::AMM); } template std::optional BookStep::tipOfferQualityF(ReadView const& view) const { - if (auto const res = tip(view); !res) + auto const res = tip(view); + if (!res) + { return std::nullopt; - else if (auto const q = std::get_if(&(*res))) + } + if (auto const q = std::get_if(&(*res))) + { return QualityFunction{*q, QualityFunction::CLOBLikeTag{}}; - else - return std::get>(*res).getQualityFunc(); + } + + return std::get>(*res).getQualityFunc(); } template @@ -947,33 +976,31 @@ BookStep::revImp( // we need to consume the offer return true; } - else - { - auto ofrAdjAmt = ofrAmt; - auto stpAdjAmt = stpAmt; - auto ownerGivesAdj = ownerGives; - limitStepOut( - offer, - ofrAdjAmt, - stpAdjAmt, - ownerGivesAdj, - transferRateIn, - transferRateOut, - remainingOut); - remainingOut = beast::zero; - savedIns.insert(stpAdjAmt.in); - savedOuts.insert(remainingOut); - result.in = sum(savedIns); - result.out = out; - this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj); - // Explicitly check whether the offer is funded. Given that we have - // (stpAmt.out > remainingOut), it's natural to assume the offer - // will still be funded after consuming remainingOut but that is - // not always the case. If the mantissas of two IOU amounts differ - // by less than ten, then subtracting them leaves a zero. - return offer.fully_consumed(); - } + auto ofrAdjAmt = ofrAmt; + auto stpAdjAmt = stpAmt; + auto ownerGivesAdj = ownerGives; + limitStepOut( + offer, + ofrAdjAmt, + stpAdjAmt, + ownerGivesAdj, + transferRateIn, + transferRateOut, + remainingOut); + remainingOut = beast::zero; + savedIns.insert(stpAdjAmt.in); + savedOuts.insert(remainingOut); + result.in = sum(savedIns); + result.out = out; + this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj); + + // Explicitly check whether the offer is funded. Given that we have + // (stpAmt.out > remainingOut), it's natural to assume the offer + // will still be funded after consuming remainingOut but that is + // not always the case. If the mantissas of two IOU amounts differ + // by less than ten, then subtracting them leaves a zero. + return offer.fully_consumed(); }; { @@ -1329,7 +1356,7 @@ make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out) ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/xrpld/app/paths/detail/DirectStep.cpp b/src/xrpld/app/paths/detail/DirectStep.cpp index 09ad2fcd67..0c8693eef4 100644 --- a/src/xrpld/app/paths/detail/DirectStep.cpp +++ b/src/xrpld/app/paths/detail/DirectStep.cpp @@ -1,8 +1,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -66,7 +67,7 @@ protected: std::pair qualities(ReadView const& sb, DebtDirection srcDebtDir, StrandDirection strandDir) const; -public: +private: DirectStepI( StrandContext const& ctx, AccountID const& src, @@ -81,6 +82,7 @@ public: { } +public: AccountID const& src() const { @@ -195,6 +197,8 @@ private: } return false; } + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -209,7 +213,15 @@ private: class DirectIPaymentStep : public DirectStepI { public: - using DirectStepI::DirectStepI; + DirectIPaymentStep( + StrandContext const& ctx, + AccountID const& src, + AccountID const& dst, + Currency const& c) + : DirectStepI(ctx, src, dst, c) + { + } + using DirectStepI::check; bool @@ -252,7 +264,15 @@ public: class DirectIOfferCrossingStep : public DirectStepI { public: - using DirectStepI::DirectStepI; + DirectIOfferCrossingStep( + StrandContext const& ctx, + AccountID const& src, + AccountID const& dst, + Currency const& c) + : DirectStepI(ctx, src, dst, c) + { + } + using DirectStepI::check; bool @@ -316,18 +336,20 @@ DirectIPaymentStep::quality(ReadView const& sb, QualityDirection qDir) const { // compute dst quality in if (this->dst_ < this->src_) + { return sfLowQualityIn; - else - return sfHighQualityIn; + } + + return sfHighQualityIn; } - else + + // compute src quality out + if (this->src_ < this->dst_) { - // compute src quality out - if (this->src_ < this->dst_) - return sfLowQualityOut; - else - return sfHighQualityOut; + return sfLowQualityOut; } + + return sfHighQualityOut; }(); if (!sle->isFieldPresent(field)) @@ -737,15 +759,13 @@ DirectStepI::qualities( { return qualitiesSrcRedeems(sb); } - else - { - auto const prevStepDebtDirection = [&] { - if (prevStep_) - return prevStep_->debtDirection(sb, strandDir); - return DebtDirection::issues; - }(); - return qualitiesSrcIssues(sb, prevStepDebtDirection); - } + + auto const prevStepDebtDirection = [&] { + if (prevStep_) + return prevStep_->debtDirection(sb, strandDir); + return DebtDirection::issues; + }(); + return qualitiesSrcIssues(sb, prevStepDebtDirection); } template @@ -803,7 +823,7 @@ DirectStepI::check(StrandContext const& ctx) const if (!(ctx.isLast && ctx.isFirst)) { auto const ter = checkFreeze(ctx.view, src_, dst_, currency_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; } @@ -814,7 +834,7 @@ DirectStepI::check(StrandContext const& ctx) const if (auto prevSrc = ctx.prevStep->directStepSrcAcct()) { auto const ter = checkNoRipple(ctx.view, *prevSrc, src_, dst_, currency_, j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; } } @@ -897,7 +917,7 @@ make_DirectStepI( ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/xrpld/app/paths/detail/PaySteps.cpp b/src/xrpld/app/paths/detail/PaySteps.cpp index 7bbcb2a72c..9087647aeb 100644 --- a/src/xrpld/app/paths/detail/PaySteps.cpp +++ b/src/xrpld/app/paths/detail/PaySteps.cpp @@ -264,9 +264,13 @@ toStrand( auto const next = &normPath[i + 1]; if (cur->isAccount()) + { curIssue.account = cur->getAccountID(); + } else if (cur->hasIssuer()) + { curIssue.account = cur->getIssuerID(); + } if (cur->hasCurrency()) { @@ -283,7 +287,7 @@ toStrand( JLOG(j.trace()) << "Inserting implied account"; auto msr = make_DirectStepI( ctx(), cur->getAccountID(), curIssue.account, curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); impliedPE.emplace( @@ -298,7 +302,7 @@ toStrand( JLOG(j.trace()) << "Inserting implied account before offer"; auto msr = make_DirectStepI( ctx(), cur->getAccountID(), curIssue.account, curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); impliedPE.emplace( @@ -314,21 +318,19 @@ toStrand( { if (i != normPath.size() - 2) return {temBAD_PATH, Strand{}}; - else - { - // Last step. insert xrp endpoint step - auto msr = make_XRPEndpointStep(ctx(), next->getAccountID()); - if (msr.first != tesSUCCESS) - return {msr.first, Strand{}}; - result.push_back(std::move(msr.second)); - } + + // Last step. insert xrp endpoint step + auto msr = make_XRPEndpointStep(ctx(), next->getAccountID()); + if (!isTesSuccess(msr.first)) + return {msr.first, Strand{}}; + result.push_back(std::move(msr.second)); } else { JLOG(j.trace()) << "Inserting implied account after offer"; auto msr = make_DirectStepI( ctx(), curIssue.account, next->getAccountID(), curIssue.currency); - if (msr.first != tesSUCCESS) + if (!isTesSuccess(msr.first)) return {msr.first, Strand{}}; result.push_back(std::move(msr.second)); } @@ -346,8 +348,10 @@ toStrand( } auto s = toStep(ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curIssue); - if (s.first == tesSUCCESS) + if (isTesSuccess(s.first)) + { result.emplace_back(std::move(s.second)); + } else { JLOG(j.debug()) << "toStep failed: " << s.first; @@ -457,7 +461,7 @@ toStrands( auto const ter = sp.first; auto& strand = sp.second; - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) { JLOG(j.trace()) << "failed to add default path"; if (isTemMalformed(ter) || paths.empty()) @@ -501,7 +505,7 @@ toStrands( auto ter = sp.first; auto& strand = sp.second; - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) { lastFailTer = ter; JLOG(j.trace()) << "failed to add path: ter: " << ter diff --git a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp b/src/xrpld/app/paths/detail/XRPEndpointStep.cpp index ceb166d6ec..620d901f22 100644 --- a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp +++ b/src/xrpld/app/paths/detail/XRPEndpointStep.cpp @@ -1,8 +1,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -37,12 +38,12 @@ private: return EitherAmount(*cache_); } -public: XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) : acc_(acc), isLast_(ctx.isLast), j_(ctx.j) { } +public: AccountID const& acc() const { @@ -135,6 +136,8 @@ private: } return false; } + + friend TDerived; }; //------------------------------------------------------------------------------ @@ -149,7 +152,10 @@ private: class XRPEndpointPaymentStep : public XRPEndpointStep { public: - using XRPEndpointStep::XRPEndpointStep; + XRPEndpointPaymentStep(StrandContext const& ctx, AccountID const& acc) + : XRPEndpointStep(ctx, acc) + { + } XRPAmount xrpLiquid(ReadView& sb) const @@ -238,7 +244,7 @@ XRPEndpointStep::revImp( auto& sender = isLast_ ? xrpAccount() : acc_; auto& receiver = isLast_ ? acc_ : xrpAccount(); auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {XRPAmount{beast::zero}, XRPAmount{beast::zero}}; cache_.emplace(result); @@ -261,7 +267,7 @@ XRPEndpointStep::fwdImp( auto& sender = isLast_ ? xrpAccount() : acc_; auto& receiver = isLast_ ? acc_ : xrpAccount(); auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {XRPAmount{beast::zero}, XRPAmount{beast::zero}}; cache_.emplace(result); @@ -327,7 +333,7 @@ XRPEndpointStep::check(StrandContext const& ctx) const auto& src = isLast_ ? xrpAccount() : acc_; auto& dst = isLast_ ? acc_ : xrpAccount(); auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency()); - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return ter; auto const issuesIndex = isLast_ ? 0 : 1; @@ -375,7 +381,7 @@ make_XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) ter = paymentStep->check(ctx); r = std::move(paymentStep); } - if (ter != tesSUCCESS) + if (!isTesSuccess(ter)) return {ter, nullptr}; return {tesSUCCESS, std::move(r)}; diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp index 9425cd9159..09b4ebc241 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.cpp +++ b/src/xrpld/app/rdb/backend/detail/Node.cpp @@ -74,8 +74,8 @@ makeLedgerDBs( { // Check if AccountTransactions has primary key std::string cid, name, type; - std::size_t notnull, dflt_value, pk; - soci::indicator ind; + std::size_t notnull = 0, dflt_value = 0, pk = 0; + soci::indicator ind = soci::i_null; soci::statement st = (tx->getSession().prepare << ("PRAGMA table_info(AccountTransactions);"), soci::into(cid), @@ -97,8 +97,8 @@ makeLedgerDBs( return {std::move(lgr), std::move(tx), true}; } - else - return {std::move(lgr), {}, true}; + + return {std::move(lgr), {}, true}; } std::optional @@ -136,7 +136,7 @@ deleteBeforeLedgerSeq(soci::session& session, TableType type, LedgerIndex ledger std::size_t getRows(soci::session& session, TableType type) { - std::size_t rows; + std::size_t rows = 0; session << "SELECT COUNT(*) AS rows " "FROM " << to_string(type) << ";", @@ -148,7 +148,7 @@ getRows(soci::session& session, TableType type) RelationalDatabase::CountMinMax getRowsMinMax(soci::session& session, TableType type) { - RelationalDatabase::CountMinMax res; + RelationalDatabase::CountMinMax res{}; session << "SELECT COUNT(*) AS rows, " "MIN(LedgerSeq) AS first, " "MAX(LedgerSeq) AS last " @@ -282,7 +282,9 @@ saveValidatedLedger( for (auto const& account : accts) { if (!first) + { sql += ", ('"; + } else { sql += "('"; @@ -543,7 +545,7 @@ getHashesByIndex(soci::session& session, LedgerIndex minSeq, LedgerIndex maxSeq, sql.append(std::to_string(maxSeq)); sql.append(";"); - std::uint64_t ls; + std::uint64_t ls = 0; std::string lh; // SOCI requires boost::optional (not std::optional) as the parameter. boost::optional ph; @@ -587,7 +589,7 @@ getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, i boost::optional ledgerSeq; boost::optional status; soci::blob sociRawTxnBlob(session); - soci::indicator rti; + soci::indicator rti = soci::i_null; Blob rawTxn; soci::statement st = @@ -600,9 +602,13 @@ getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, i while (st.fetch()) { if (soci::i_ok == rti) + { convert(sociRawTxnBlob, rawTxn); + } else + { rawTxn.clear(); + } if (auto trans = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app)) { @@ -645,7 +651,7 @@ transactionsSQL( constexpr std::uint32_t NONBINARY_PAGE_LENGTH = 200; constexpr std::uint32_t BINARY_PAGE_LENGTH = 500; - std::uint32_t numberOfResults; + std::uint32_t numberOfResults = 0; if (count) { @@ -665,8 +671,8 @@ transactionsSQL( numberOfResults = options.limit; } - std::string maxClause = ""; - std::string minClause = ""; + std::string maxClause; + std::string minClause; if (options.maxLedger) { @@ -683,13 +689,16 @@ transactionsSQL( std::string sql; if (count) + { sql = boost::str( boost::format( "SELECT %s FROM AccountTransactions " "WHERE Account = '%s' %s %s LIMIT %u, %u;") % selection % toBase58(options.account) % maxClause % minClause % options.offset % numberOfResults); + } else + { sql = boost::str( boost::format( "SELECT %s FROM " @@ -702,6 +711,7 @@ transactionsSQL( selection % toBase58(options.account) % maxClause % minClause % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % options.offset % numberOfResults); + } JLOG(j.trace()) << "txSQL query: " << sql; return sql; } @@ -746,7 +756,7 @@ getAccountTxs( false, false, j); - if (sql == "") + if (sql.empty()) return {ret, 0}; int total = 0; @@ -755,7 +765,7 @@ getAccountTxs( boost::optional ledgerSeq; boost::optional status; soci::blob sociTxnBlob(session), sociTxnMetaBlob(session); - soci::indicator rti, tmi; + soci::indicator rti = soci::i_null, tmi = soci::i_null; Blob rawTxn, txnMeta; soci::statement st = @@ -769,14 +779,22 @@ getAccountTxs( while (st.fetch()) { if (soci::i_ok == rti) + { convert(sociTxnBlob, rawTxn); + } else + { rawTxn.clear(); + } if (soci::i_ok == tmi) + { convert(sociTxnMetaBlob, txnMeta); + } else + { txnMeta.clear(); + } auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app); @@ -862,7 +880,7 @@ getAccountTxsB( true /*binary*/, false, j); - if (sql == "") + if (sql.empty()) return {ret, 0}; int total = 0; @@ -872,7 +890,7 @@ getAccountTxsB( boost::optional ledgerSeq; boost::optional status; soci::blob sociTxnBlob(session), sociTxnMetaBlob(session); - soci::indicator rti, tmi; + soci::indicator rti = soci::i_null, tmi = soci::i_null; soci::statement st = (session.prepare << sql, @@ -953,13 +971,17 @@ accountTxPage( bool lookingForMarker = options.marker.has_value(); - std::uint32_t numberOfResults; + std::uint32_t numberOfResults = 0; if (options.limit == 0 || options.limit == UINT32_MAX || (options.limit > page_length && !options.bAdmin)) + { numberOfResults = page_length; + } else + { numberOfResults = options.limit; + } // As an account can have many thousands of transactions, there is a limit // placed on the amount of transactions returned. If the limit is reached @@ -1041,7 +1063,7 @@ accountTxPage( boost::optional status; soci::blob txnData(session); soci::blob txnMeta(session); - soci::indicator dataPresent, metaPresent; + soci::indicator dataPresent = soci::i_null, metaPresent = soci::i_null; soci::statement st = (session.prepare << sql, @@ -1062,7 +1084,9 @@ accountTxPage( lookingForMarker = false; } else + { continue; + } } else if (numberOfResults == 0) { @@ -1072,17 +1096,25 @@ accountTxPage( } if (dataPresent == soci::i_ok) + { convert(txnData, rawData); + } else + { rawData.clear(); + } if (metaPresent == soci::i_ok) + { convert(txnMeta, rawMeta); + } else + { rawMeta.clear(); + } // Work around a bug that could leave the metadata missing - if (rawMeta.size() == 0) + if (rawMeta.empty()) onUnsavedLedger(ledgerSeq.value_or(0)); // `rawData` and `rawMeta` will be used after they are moved. @@ -1151,7 +1183,7 @@ getTransaction( Blob rawTxn, rawMeta; { soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session); - soci::indicator txn, meta; + soci::indicator txn = soci::i_null, meta = soci::i_null; session << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, txn), soci::into(sociRawMetaBlob, meta); @@ -1164,7 +1196,7 @@ getTransaction( if (!got_data) { uint64_t count = 0; - soci::indicator rti; + soci::indicator rti = soci::i_null; session << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE " "LedgerSeq BETWEEN " @@ -1230,16 +1262,16 @@ dbHasSpace(soci::session& session, Config const& config, beast::Journal j) } static auto const pageSize = [&] { - std::uint32_t ps; + std::uint32_t ps = 0; session << "PRAGMA page_size;", soci::into(ps); return ps; }(); static auto const maxPages = [&] { - std::uint32_t mp; + std::uint32_t mp = 0; session << "PRAGMA max_page_count;", soci::into(mp); return mp; }(); - std::uint32_t pageCount; + std::uint32_t pageCount = 0; session << "PRAGMA page_count;", soci::into(pageCount); std::uint32_t freePages = maxPages - pageCount; std::uint64_t freeSpace = safe_cast(freePages) * pageSize; diff --git a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp index 1b15fcf52e..4f5365ea01 100644 --- a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp +++ b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp @@ -400,7 +400,8 @@ SQLiteDatabase::oldestAccountTxPage(AccountTxPageOptions const& options) auto onTransaction = [&ret, &app = registry_.app()]( std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { - convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); + convertBlobsToTxResult( + ret, ledger_index, status, std::move(rawTxn), std::move(rawMeta), app); }; if (existsTransaction()) @@ -428,7 +429,8 @@ SQLiteDatabase::newestAccountTxPage(AccountTxPageOptions const& options) auto onTransaction = [&ret, &app = registry_.app()]( std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { - convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); + convertBlobsToTxResult( + ret, ledger_index, status, std::move(rawTxn), std::move(rawMeta), app); }; if (existsTransaction()) diff --git a/src/xrpld/app/rdb/detail/PeerFinder.cpp b/src/xrpld/app/rdb/detail/PeerFinder.cpp index cfab6f764a..d619d4bb85 100644 --- a/src/xrpld/app/rdb/detail/PeerFinder.cpp +++ b/src/xrpld/app/rdb/detail/PeerFinder.cpp @@ -83,7 +83,7 @@ updatePeerFinderDB(soci::session& session, int currentSchemaVersion, beast::Jour " PeerFinder_BootstrapCache_Next " " ( address ); "; - std::size_t count; + std::size_t count = 0; session << "SELECT COUNT(*) FROM PeerFinder_BootstrapCache;", soci::into(count); std::vector list; @@ -91,7 +91,7 @@ updatePeerFinderDB(soci::session& session, int currentSchemaVersion, beast::Jour { list.reserve(count); std::string s; - int valence; + int valence = 0; soci::statement st = (session.prepare << "SELECT " " address, " @@ -187,7 +187,7 @@ void readPeerFinderDB(soci::session& session, std::function const& func) { std::string s; - int valence; + int valence = 0; soci::statement st = (session.prepare << "SELECT " " address, " diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 716825a40f..48fc41b94d 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -51,7 +51,7 @@ namespace detail { [[nodiscard]] std::uint64_t getMemorySize() { - if (struct sysinfo si; sysinfo(&si) == 0) + if (struct sysinfo si{}; sysinfo(&si) == 0) return static_cast(si.totalram) * si.mem_unit; return 0; @@ -154,7 +154,7 @@ parseIniFile(std::string const& strInput, bool const bTrim) boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n")); // Set the default Section name. - std::string strSection = SECTION_DEFAULT_NAME; + std::string strSection = SECTION_DEFAULT_NAME; // NOLINT(readability-redundant-string-init) // Initialize the default Section. secResult[strSection] = IniFileSections::mapped_type(); @@ -364,9 +364,13 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand // load() may have set a new value for the dataDir std::string const dbPath(legacy("database_path")); if (!dbPath.empty()) + { dataDir = boost::filesystem::path(dbPath); + } else if (RUN_STANDALONE) + { dataDir.clear(); + } } if (!dataDir.empty()) @@ -386,7 +390,6 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand if (RUN_STANDALONE) LEDGER_HISTORY = 0; - std::string ledgerTxDbType; Section ledgerTxTablesSection = section("ledger_tx_tables"); get_if_exists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES); @@ -494,13 +497,21 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_)) { if (strTemp == "main") + { NETWORK_ID = 0; + } else if (strTemp == "testnet") + { NETWORK_ID = 1; + } else if (strTemp == "devnet") + { NETWORK_ID = 2; + } else + { NETWORK_ID = beast::lexicalCastThrow(strTemp); + } } if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_)) @@ -517,8 +528,10 @@ Config::loadFromString(std::string const& fileContents) { peers_in_max = beast::lexicalCastThrow(strTemp); if (*peers_in_max > 1000) + { Throw("Invalid value specified in [" SECTION_PEERS_IN_MAX "] section; the value must be less or equal than 1000"); + } } std::optional peers_out_max{}; @@ -526,15 +539,19 @@ Config::loadFromString(std::string const& fileContents) { peers_out_max = beast::lexicalCastThrow(strTemp); if (*peers_out_max < 10 || *peers_out_max > 1000) + { Throw("Invalid value specified in [" SECTION_PEERS_OUT_MAX "] section; the value must be in range 10-1000"); + } } // if one section is configured then the other must be configured too if ((peers_in_max && !peers_out_max) || (peers_out_max && !peers_in_max)) + { Throw("Both sections [" SECTION_PEERS_IN_MAX "]" "and [" SECTION_PEERS_OUT_MAX "] must be configured"); + } if (peers_in_max && peers_out_max) { @@ -546,17 +563,29 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_)) { if (boost::iequals(strTemp, "tiny")) + { NODE_SIZE = 0; + } else if (boost::iequals(strTemp, "small")) + { NODE_SIZE = 1; + } else if (boost::iequals(strTemp, "medium")) + { NODE_SIZE = 2; + } else if (boost::iequals(strTemp, "large")) + { NODE_SIZE = 3; + } else if (boost::iequals(strTemp, "huge")) + { NODE_SIZE = 4; + } else + { NODE_SIZE = std::min(4, beast::lexicalCastThrow(strTemp)); + } } if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_)) @@ -574,32 +603,50 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_)) { if (boost::iequals(strTemp, "all")) + { RELAY_UNTRUSTED_VALIDATIONS = 1; + } else if (boost::iequals(strTemp, "trusted")) + { RELAY_UNTRUSTED_VALIDATIONS = 0; + } else if (boost::iequals(strTemp, "drop_untrusted")) + { RELAY_UNTRUSTED_VALIDATIONS = -1; + } else + { Throw("Invalid value specified in [" SECTION_RELAY_VALIDATIONS "] section"); + } } if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_)) { if (boost::iequals(strTemp, "all")) + { RELAY_UNTRUSTED_PROPOSALS = 1; + } else if (boost::iequals(strTemp, "trusted")) + { RELAY_UNTRUSTED_PROPOSALS = 0; + } else if (boost::iequals(strTemp, "drop_untrusted")) + { RELAY_UNTRUSTED_PROPOSALS = -1; + } else + { Throw("Invalid value specified in [" SECTION_RELAY_PROPOSALS "] section"); + } } if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN)) + { Throw("Cannot have both [" SECTION_VALIDATION_SEED "] and [" SECTION_VALIDATOR_TOKEN "] config sections"); + } if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_)) NETWORK_QUORUM = beast::lexicalCastThrow(strTemp); @@ -614,24 +661,35 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_)) { if (boost::iequals(strTemp, "full")) + { LEDGER_HISTORY = std::numeric_limits::max(); + } else if (boost::iequals(strTemp, "none")) + { LEDGER_HISTORY = 0; + } else + { LEDGER_HISTORY = beast::lexicalCastThrow(strTemp); + } } if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_)) { if (boost::iequals(strTemp, "none")) + { FETCH_DEPTH = 0; + } else if (boost::iequals(strTemp, "full")) + { FETCH_DEPTH = std::numeric_limits::max(); + } else + { FETCH_DEPTH = beast::lexicalCastThrow(strTemp); + } - if (FETCH_DEPTH < 10) - FETCH_DEPTH = 10; + FETCH_DEPTH = std::max(FETCH_DEPTH, 10); } // By default, validators don't have pathfinding enabled, unless it is @@ -656,8 +714,10 @@ Config::loadFromString(std::string const& fileContents) SWEEP_INTERVAL = beast::lexicalCastThrow(strTemp); if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600) + { Throw("Invalid " SECTION_SWEEP_INTERVAL ": must be between 10 and 600 inclusive"); + } } if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_)) @@ -665,8 +725,10 @@ Config::loadFromString(std::string const& fileContents) WORKERS = beast::lexicalCastThrow(strTemp); if (WORKERS < 1 || WORKERS > 1024) + { Throw("Invalid " SECTION_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_)) @@ -674,8 +736,10 @@ Config::loadFromString(std::string const& fileContents) IO_WORKERS = beast::lexicalCastThrow(strTemp); if (IO_WORKERS < 1 || IO_WORKERS > 1024) + { Throw("Invalid " SECTION_IO_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_)) @@ -683,8 +747,10 @@ Config::loadFromString(std::string const& fileContents) PREFETCH_WORKERS = beast::lexicalCastThrow(strTemp); if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024) + { Throw("Invalid " SECTION_PREFETCH_WORKERS ": must be between 1 and 1024 inclusive."); + } } if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_)) @@ -704,18 +770,26 @@ Config::loadFromString(std::string const& fileContents) // VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = // // sec.value_or("vp_base_squelch_enable", true); // if (sec.exists("vp_base_squelch_enable") && sec.exists("vp_enable")) + { Throw("Invalid " SECTION_REDUCE_RELAY " cannot specify both vp_base_squelch_enable and vp_enable " "options. " "vp_enable was deprecated and replaced by " "vp_base_squelch_enable"); + } if (sec.exists("vp_base_squelch_enable")) + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.value_or("vp_base_squelch_enable", false); + } else if (sec.exists("vp_enable")) + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.value_or("vp_enable", false); + } else + { VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = false; + } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// ///////////////////// !!TEMPORARY CODE BLOCK!! /////////////////////// @@ -725,9 +799,11 @@ Config::loadFromString(std::string const& fileContents) VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS = sec.value_or("vp_base_squelch_max_selected_peers", 5); if (VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS < 3) + { Throw("Invalid " SECTION_REDUCE_RELAY " vp_base_squelch_max_selected_peers must be " "greater than or equal to 3"); + } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false); @@ -735,10 +811,12 @@ Config::loadFromString(std::string const& fileContents) TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20); TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25); if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 || TX_REDUCE_RELAY_MIN_PEERS < 10) + { Throw("Invalid " SECTION_REDUCE_RELAY ", tx_min_peers must be greater than or equal to 10" ", tx_relay_percentage must be greater than or equal to 10 " "and less than or equal to 100"); + } } if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_)) @@ -777,9 +855,11 @@ Config::loadFromString(std::string const& fileContents) } if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800}) + { Throw( "Invalid value 'max_unknown_time' in " SECTION_OVERLAY ": the time must be between 300 and 1800 seconds, inclusive."); + } try { @@ -805,24 +885,36 @@ Config::loadFromString(std::string const& fileContents) boost::regex const re("^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$"); boost::smatch match; if (!boost::regex_match(strTemp, match, re)) + { Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME ", must be: [0-9]+ [minutes|hours|days|weeks]"); + } std::uint32_t duration = beast::lexicalCastThrow(match[1].str()); if (boost::iequals(match[2], "minutes")) + { AMENDMENT_MAJORITY_TIME = minutes(duration); + } else if (boost::iequals(match[2], "hours")) + { AMENDMENT_MAJORITY_TIME = hours(duration); + } else if (boost::iequals(match[2], "days")) + { AMENDMENT_MAJORITY_TIME = days(duration); + } else if (boost::iequals(match[2], "weeks")) + { AMENDMENT_MAJORITY_TIME = weeks(duration); + } if (AMENDMENT_MAJORITY_TIME < minutes(15)) + { Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME ", the minimum amount of time an amendment must hold a " "majority is 15 minutes"); + } } if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_)) @@ -846,25 +938,30 @@ Config::loadFromString(std::string const& fileContents) validatorsFile = strTemp; if (validatorsFile.empty()) + { Throw("Invalid path specified in [" SECTION_VALIDATORS_FILE "]"); + } if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty()) validatorsFile = CONFIG_DIR / validatorsFile; if (!boost::filesystem::exists(validatorsFile)) + { Throw( "The file specified in [" SECTION_VALIDATORS_FILE "] " "does not exist: " + validatorsFile.string()); - + } else if ( !boost::filesystem::is_regular_file(validatorsFile) && !boost::filesystem::is_symlink(validatorsFile)) + { Throw( "Invalid file specified in [" SECTION_VALIDATORS_FILE "]: " + validatorsFile.string()); + } } else if (!CONFIG_DIR.empty()) { @@ -873,11 +970,15 @@ Config::loadFromString(std::string const& fileContents) if (!validatorsFile.empty()) { if (!boost::filesystem::exists(validatorsFile)) + { validatorsFile.clear(); + } else if ( !boost::filesystem::is_regular_file(validatorsFile) && !boost::filesystem::is_symlink(validatorsFile)) + { validatorsFile.clear(); + } } } @@ -922,6 +1023,7 @@ Config::loadFromString(std::string const& fileContents) section(SECTION_VALIDATOR_LIST_THRESHOLD).append(*valListThreshold); if (!entries && !valKeyEntries && !valListKeys) + { Throw( "The file specified in [" SECTION_VALIDATORS_FILE "] " @@ -933,19 +1035,24 @@ Config::loadFromString(std::string const& fileContents) "]" " section: " + validatorsFile.string()); + } } VALIDATOR_LIST_THRESHOLD = [&]() -> std::optional { auto const& listThreshold = section(SECTION_VALIDATOR_LIST_THRESHOLD); if (listThreshold.lines().empty()) + { return std::nullopt; - else if (listThreshold.values().size() == 1) + } + if (listThreshold.values().size() == 1) { auto strTemp = listThreshold.values()[0]; auto const listThreshold = beast::lexicalCastThrow(strTemp); if (listThreshold == 0) + { return std::nullopt; // NOTE: Explicitly ask for computed - else if (listThreshold > section(SECTION_VALIDATOR_LIST_KEYS).values().size()) + } + if (listThreshold > section(SECTION_VALIDATOR_LIST_KEYS).values().size()) { Throw( "Value in config section " @@ -954,12 +1061,10 @@ Config::loadFromString(std::string const& fileContents) } return listThreshold; } - else - { - Throw( - "Config section " - "[" SECTION_VALIDATOR_LIST_THRESHOLD "] should contain single value only"); - } + + Throw( + "Config section " + "[" SECTION_VALIDATOR_LIST_THRESHOLD "] should contain single value only"); }(); // Consolidate [validator_keys] and [validators] @@ -978,9 +1083,13 @@ Config::loadFromString(std::string const& fileContents) for (auto const& s : part.values()) { if (auto const f = getRegisteredFeature(s)) + { features.insert(*f); + } else + { Throw("Unknown feature: " + s + " in config file."); + } } } @@ -1051,13 +1160,13 @@ setup_FeeVote(Section const& section) { FeeSetup setup; { - std::uint64_t temp; + std::uint64_t temp = 0; if (set(temp, "reference_fee", section) && temp <= std::numeric_limits::max()) setup.reference_fee = temp; } { - std::uint32_t temp; + std::uint32_t temp = 0; if (set(temp, "account_reserve", section)) setup.account_reserve = temp; if (set(temp, "owner_reserve", section)) diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index ac0743e936..78aee006f1 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -51,7 +51,10 @@ void ConnectAttempt::stop() { if (!strand_.running_in_this_thread()) - return boost::asio::post(strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); + { + boost::asio::post(strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); + return; + } if (!socket_.is_open()) return; @@ -65,7 +68,10 @@ void ConnectAttempt::run() { if (!strand_.running_in_this_thread()) - return boost::asio::post(strand_, std::bind(&ConnectAttempt::run, shared_from_this())); + { + boost::asio::post(strand_, std::bind(&ConnectAttempt::run, shared_from_this())); + return; + } JLOG(journal_.debug()) << "run: connecting to " << remote_endpoint_; @@ -115,9 +121,10 @@ ConnectAttempt::tryAsyncShutdown() if (currentStep_ != ConnectionStep::TcpConnect && currentStep_ != ConnectionStep::TlsHandshake) { setTimer(ConnectionStep::ShutdownStarted); - return stream_.async_shutdown(bind_executor( + stream_.async_shutdown(bind_executor( strand_, std::bind(&ConnectAttempt::onShutdown, shared_from_this(), std::placeholders::_1))); + return; } close(); @@ -160,7 +167,7 @@ ConnectAttempt::close() cancelTimer(); error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) } void @@ -197,7 +204,8 @@ ConnectAttempt::setTimer(ConnectionStep step) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer (global): " << ex.what(); - return close(); + close(); + return; } } @@ -240,7 +248,8 @@ ConnectAttempt::setTimer(ConnectionStep step) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer (step " << stepToString(step) << "): " << ex.what(); - return close(); + close(); + return; } } @@ -272,7 +281,8 @@ ConnectAttempt::onTimer(error_code ec) // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); - return close(); + close(); + return; } // Determine which timer expired by checking their expiry times @@ -304,9 +314,13 @@ ConnectAttempt::onConnect(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onConnect", ec); + fail("onConnect", ec); + return; } if (!socket_.is_open()) @@ -315,10 +329,16 @@ ConnectAttempt::onConnect(error_code ec) // check if connection has really been established socket_.local_endpoint(ec); if (ec) - return fail("onConnect", ec); + { + fail("onConnect", ec); + return; + } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -340,25 +360,38 @@ ConnectAttempt::onHandshake(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onHandshake", ec); + fail("onHandshake", ec); + return; } auto const local_endpoint = socket_.local_endpoint(ec); if (ec) - return fail("onHandshake", ec); + { + fail("onHandshake", ec); + return; + } setTimer(ConnectionStep::HttpWrite); // check if we connected to ourselves if (!overlay_.peerFinder().onConnected( slot_, beast::IPAddressConversion::from_asio(local_endpoint))) - return fail("Self connection"); + { + fail("Self connection"); + return; + } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return shutdown(); // makeSharedValue logs + { + shutdown(); + return; // makeSharedValue logs + } req_ = makeRequest( !overlay_.peerFinder().config().peerPrivate, @@ -376,7 +409,10 @@ ConnectAttempt::onHandshake(error_code ec) app_); if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -396,13 +432,20 @@ ConnectAttempt::onWrite(error_code ec) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onWrite", ec); + fail("onWrite", ec); + return; } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } ioPending_ = true; @@ -429,17 +472,25 @@ ConnectAttempt::onRead(error_code ec) if (ec == boost::asio::error::eof) { JLOG(journal_.debug()) << "EOF"; - return shutdown(); + shutdown(); + return; } if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onRead", ec); + fail("onRead", ec); + return; } if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } processResponse(); } @@ -457,7 +508,8 @@ ConnectAttempt::processResponse() { JLOG(journal_.warn()) << "Unable to upgrade to peer protocol: " << response_.result() << " (" << response_.reason() << ")"; - return shutdown(); + shutdown(); + return; } // Parse response body to determine if this is a redirect or other @@ -465,8 +517,10 @@ ConnectAttempt::processResponse() std::string responseBody; responseBody.reserve(boost::asio::buffer_size(response_.body().data())); for (auto const buffer : response_.body().data()) + { responseBody.append( static_cast(buffer.data()), boost::asio::buffer_size(buffer)); + } Json::Value json; Json::Reader reader; @@ -481,12 +535,16 @@ ConnectAttempt::processResponse() << " failed to upgrade to peer protocol: " << response_.result() << " (" << response_.reason() << ")"; - return shutdown(); + shutdown(); + return; } Json::Value const& peerIps = json["peer-ips"]; if (!peerIps.isArray()) - return fail("processResponse: invalid peer-ips format"); + { + fail("processResponse: invalid peer-ips format"); + return; + } // Extract and validate peer endpoints std::vector redirectEndpoints; @@ -506,7 +564,8 @@ ConnectAttempt::processResponse() // Notify PeerFinder about the redirect redirectEndpoints may be empty overlay_.peerFinder().onRedirects(remote_endpoint_, redirectEndpoints); - return fail("processResponse: failed to connect to peer: redirected"); + fail("processResponse: failed to connect to peer: redirected"); + return; } // Just because our peer selected a particular protocol version doesn't @@ -520,12 +579,18 @@ ConnectAttempt::processResponse() negotiatedProtocol = pvs[0]; if (!negotiatedProtocol) - return fail("processResponse: Unable to negotiate protocol version"); + { + fail("processResponse: Unable to negotiate protocol version"); + return; + } } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return shutdown(); // makeSharedValue logs + { + shutdown(); + return; // makeSharedValue logs + } try { @@ -553,14 +618,18 @@ ConnectAttempt::processResponse() { std::stringstream ss; ss << "Outbound Connect Attempt " << remote_endpoint_ << " " << to_string(result); - return fail(ss.str()); + fail(ss.str()); + return; } if (!socket_.is_open()) return; if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto const peer = std::make_shared( app_, @@ -578,7 +647,8 @@ ConnectAttempt::processResponse() } catch (std::exception const& e) { - return fail(std::string("Handshake failure (") + e.what() + ")"); + fail(std::string("Handshake failure (") + e.what() + ")"); + return; } } diff --git a/src/xrpld/overlay/detail/Handshake.cpp b/src/xrpld/overlay/detail/Handshake.cpp index c463a31257..e9ad25bcc4 100644 --- a/src/xrpld/overlay/detail/Handshake.cpp +++ b/src/xrpld/overlay/detail/Handshake.cpp @@ -211,7 +211,7 @@ verifyHandshake( if (auto const iter = headers.find("Network-ID"); iter != headers.end()) { - std::uint32_t nid; + std::uint32_t nid = 0; if (!beast::lexicalCastChecked(nid, iter->value())) throw std::runtime_error("Invalid peer network identifier"); @@ -223,7 +223,7 @@ verifyHandshake( if (auto const iter = headers.find("Network-Time"); iter != headers.end()) { auto const netTime = [str = iter->value()]() -> TimeKeeper::time_point { - TimeKeeper::duration::rep val; + TimeKeeper::duration::rep val = 0; if (beast::lexicalCastChecked(val, str)) return TimeKeeper::time_point{TimeKeeper::duration{val}}; @@ -300,9 +300,11 @@ verifyHandshake( throw std::runtime_error("Invalid Local-IP"); if (beast::IP::is_public(remote) && remote != local_ip) + { throw std::runtime_error( "Incorrect Local-IP: " + remote.to_string() + " instead of " + local_ip.to_string()); + } } if (auto const iter = headers.find("Remote-IP"); iter != headers.end()) @@ -318,9 +320,11 @@ verifyHandshake( // We know our public IP and peer reports our connection came // from some other IP. if (remote_ip != public_ip) + { throw std::runtime_error( "Incorrect Remote-IP: " + public_ip.to_string() + " instead of " + remote_ip.to_string()); + } } } diff --git a/src/xrpld/overlay/detail/Message.cpp b/src/xrpld/overlay/detail/Message.cpp index 510bc24fe2..754545f04a 100644 --- a/src/xrpld/overlay/detail/Message.cpp +++ b/src/xrpld/overlay/detail/Message.cpp @@ -58,6 +58,8 @@ Message::compress() bool const compressible = [&] { if (messageBytes <= 70) return false; + + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (type) { case protocol::mtMANIFESTS: @@ -104,7 +106,9 @@ Message::compress() setHeader(bufferCompressed_.data(), compressedSize, type, Algorithm::LZ4, messageBytes); } else + { bufferCompressed_.resize(0); + } } } @@ -186,10 +190,12 @@ Message::getBuffer(Compressed tryCompressed) std::call_once(once_flag_, &Message::compress, this); - if (bufferCompressed_.size() > 0) + if (!bufferCompressed_.empty()) + { return bufferCompressed_; - else - return buffer_; + } + + return buffer_; } int diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index 55f45e727e..d9077686ec 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace xrpl { namespace CrawlOptions { @@ -479,9 +481,13 @@ OverlayImpl::start() for (auto const& addr : addresses) { if (addr.port() == 0) + { ips.push_back(to_string(addr.at_port(DEFAULT_PEER_PORT))); + } else + { ips.push_back(to_string(addr)); + } } std::string const base("config: "); @@ -501,9 +507,13 @@ OverlayImpl::start() for (auto& addr : addresses) { if (addr.port() == 0) + { ips.emplace_back(addr.address(), DEFAULT_PEER_PORT); + } else + { ips.emplace_back(addr); + } } if (!ips.empty()) @@ -634,8 +644,10 @@ OverlayImpl::onManifests( } if (!relay.list().empty()) + { for_each([m2 = std::make_shared(relay, protocol::mtMANIFESTS)]( - std::shared_ptr&& p) { p->send(m2); }); + std::shared_ptr const& p) { p->send(m2); }); + } } void @@ -667,16 +679,16 @@ OverlayImpl::limit() } Json::Value -OverlayImpl::getOverlayInfo() +OverlayImpl::getOverlayInfo() const { using namespace std::chrono; Json::Value jv; - auto& av = jv["active"] = Json::Value(Json::arrayValue); + auto& av = jv[jss::active] = Json::Value(Json::arrayValue); - for_each([&](std::shared_ptr&& sp) { + for_each([&](std::shared_ptr const& sp) { auto& pv = av.append(Json::Value(Json::objectValue)); pv[jss::public_key] = base64_encode(sp->getNodePublic().data(), sp->getNodePublic().size()); - pv[jss::type] = sp->slot()->inbound() ? "in" : "out"; + pv[jss::type] = sp->slot()->inbound() ? jss::in : jss::out; pv[jss::uptime] = static_cast(duration_cast(sp->uptime()).count()); if (sp->crawl()) { @@ -688,18 +700,20 @@ OverlayImpl::getOverlayInfo() } else { - pv[jss::port] = std::to_string(sp->getRemoteAddress().port()); + pv[jss::port] = sp->getRemoteAddress().port(); } } { auto version{sp->getVersion()}; if (!version.empty()) + { // Could move here if Json::value supported moving from strings pv[jss::version] = std::string{version}; + } } - std::uint32_t minSeq, maxSeq; + std::uint32_t minSeq = 0, maxSeq = 0; sp->ledgerRange(minSeq, maxSeq); if (minSeq != 0 || maxSeq != 0) pv[jss::complete_ledgers] = std::to_string(minSeq) + "-" + std::to_string(maxSeq); @@ -825,7 +839,7 @@ OverlayImpl::processValidatorList(http_request_type const& req, Handoff& handoff // return the most recent validator list for that key. constexpr std::string_view prefix("/vl/"); - if (!req.target().starts_with(prefix.data()) || !setup_.vlEnabled) + if (!req.target().starts_with(prefix) || !setup_.vlEnabled) return false; std::uint32_t version = 1; @@ -868,20 +882,18 @@ OverlayImpl::processValidatorList(http_request_type const& req, Handoff& handoff // 404 not found return fail(boost::beast::http::status::not_found); } - else if (!*vl) + if (!*vl) { return fail(boost::beast::http::status::bad_request); } - else - { - msg.result(boost::beast::http::status::ok); - msg.body() = *vl; + msg.result(boost::beast::http::status::ok); - msg.prepare_payload(); - handoff.response = std::make_shared(msg); - return true; - } + msg.body() = *vl; + + msg.prepare_payload(); + handoff.response = std::make_shared(msg); + return true; } bool @@ -907,36 +919,41 @@ OverlayImpl::processHealth(http_request_type const& req, Handoff& handoff) std::string server_state = info[jss::server_state].asString(); auto load_factor = info[jss::load_factor_server].asDouble() / info[jss::load_base].asDouble(); - enum { healthy, warning, critical }; - int health = healthy; - auto set_health = [&health](int state) { - if (health < state) - health = state; - }; + enum class HealthState { healthy, warning, critical }; + auto health = HealthState::healthy; + auto set_health = [&health](HealthState state) { health = std::max(health, state); }; msg.body()[jss::info] = Json::objectValue; if (last_validated_ledger_age >= 7 || last_validated_ledger_age < 0) { msg.body()[jss::info][jss::validated_ledger] = last_validated_ledger_age; if (last_validated_ledger_age < 20) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (amendment_blocked) { msg.body()[jss::info][jss::amendment_blocked] = true; - set_health(critical); + set_health(HealthState::critical); } if (number_peers <= 7) { msg.body()[jss::info][jss::peers] = number_peers; if (number_peers != 0) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (!(server_state == "full" || server_state == "validating" || server_state == "proposing")) @@ -944,30 +961,36 @@ OverlayImpl::processHealth(http_request_type const& req, Handoff& handoff) msg.body()[jss::info][jss::server_state] = server_state; if (server_state == "syncing" || server_state == "tracking" || server_state == "connected") { - set_health(warning); + set_health(HealthState::warning); } else - set_health(critical); + { + set_health(HealthState::critical); + } } if (load_factor > 100) { msg.body()[jss::info][jss::load_factor] = load_factor; if (load_factor < 1000) - set_health(warning); + { + set_health(HealthState::warning); + } else - set_health(critical); + { + set_health(HealthState::critical); + } } switch (health) { - case healthy: + case HealthState::healthy: msg.result(boost::beast::http::status::ok); break; - case warning: + case HealthState::warning: msg.result(boost::beast::http::status::service_unavailable); break; - case critical: + case HealthState::critical: msg.result(boost::beast::http::status::internal_server_error); break; } @@ -991,7 +1014,7 @@ OverlayImpl::getActivePeers() const Overlay::PeerSequence ret; ret.reserve(size()); - for_each([&ret](std::shared_ptr&& sp) { ret.emplace_back(std::move(sp)); }); + for_each([&ret](std::shared_ptr const& sp) { ret.emplace_back(std::move(sp)); }); return ret; } @@ -1021,10 +1044,14 @@ OverlayImpl::getActivePeers( if (!reduceRelayEnabled) ++disabled; - if (toSkip.count(id) == 0) + if (!toSkip.contains(id)) + { ret.emplace_back(std::move(p)); + } else if (reduceRelayEnabled) + { ++enabledInSkip; + } } } @@ -1034,7 +1061,7 @@ OverlayImpl::getActivePeers( void OverlayImpl::checkTracking(std::uint32_t index) { - for_each([index](std::shared_ptr&& sp) { sp->checkTracking(index); }); + for_each([index](std::shared_ptr const& sp) { sp->checkTracking(index); }); } std::shared_ptr @@ -1070,7 +1097,7 @@ void OverlayImpl::broadcast(protocol::TMProposeSet& m) { auto const sm = std::make_shared(m, protocol::mtPROPOSE_LEDGER); - for_each([&](std::shared_ptr&& p) { p->send(sm); }); + for_each([&](std::shared_ptr const& p) { p->send(sm); }); } std::set @@ -1079,8 +1106,8 @@ OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey cons if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { auto const sm = std::make_shared(m, protocol::mtPROPOSE_LEDGER, validator); - for_each([&](std::shared_ptr&& p) { - if (toSkip->find(p->id()) == toSkip->end()) + for_each([&](std::shared_ptr const& p) { + if (!toSkip->contains(p->id())) p->send(sm); }); return *toSkip; @@ -1092,7 +1119,7 @@ void OverlayImpl::broadcast(protocol::TMValidation& m) { auto const sm = std::make_shared(m, protocol::mtVALIDATION); - for_each([sm](std::shared_ptr&& p) { p->send(sm); }); + for_each([sm](std::shared_ptr const& p) { p->send(sm); }); } std::set @@ -1101,8 +1128,8 @@ OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey cons if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { auto const sm = std::make_shared(m, protocol::mtVALIDATION, validator); - for_each([&](std::shared_ptr&& p) { - if (toSkip->find(p->id()) == toSkip->end()) + for_each([&](std::shared_ptr const& p) { + if (!toSkip->contains(p->id())) p->send(sm); }); return *toSkip; @@ -1297,7 +1324,7 @@ OverlayImpl::sendEndpoints() } void -OverlayImpl::sendTxQueue() +OverlayImpl::sendTxQueue() const { for_each([](auto const& p) { if (p->txReduceRelayEnabled()) @@ -1347,17 +1374,23 @@ OverlayImpl::updateSlotAndSquelch( return; if (!strand_.running_in_this_thread()) - return post( + { + post( strand_, // Must capture copies of reference parameters (i.e. key, validator) [this, key = key, validator = validator, peers = std::move(peers), type]() mutable { updateSlotAndSquelch(key, validator, std::move(peers), type); }); + return; + } + for (auto id : peers) + { slots_.updateSlotAndSquelch(key, validator, id, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); }); + } } void @@ -1371,12 +1404,17 @@ OverlayImpl::updateSlotAndSquelch( return; if (!strand_.running_in_this_thread()) - return post( - strand_, - // Must capture copies of reference parameters (i.e. key, validator) - [this, key = key, validator = validator, peer, type]() { - updateSlotAndSquelch(key, validator, peer, type); - }); + { + { + post( + strand_, + // Must capture copies of reference parameters (i.e. key, validator) + [this, key = key, validator = validator, peer, type]() { + updateSlotAndSquelch(key, validator, peer, type); + }); + } + return; + } slots_.updateSlotAndSquelch(key, validator, peer, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); @@ -1387,7 +1425,10 @@ void OverlayImpl::deletePeer(Peer::id_t id) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&OverlayImpl::deletePeer, this, id)); + { + post(strand_, std::bind(&OverlayImpl::deletePeer, this, id)); + return; + } slots_.deletePeer(id, true); } @@ -1396,7 +1437,10 @@ void OverlayImpl::deleteIdlePeers() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this)); + { + post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this)); + return; + } slots_.deleteIdlePeers(); } diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 9e891e6e8a..b77b4e69aa 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -474,7 +474,7 @@ private: Controlled through the config section [crawl] overlay=[0|1] */ Json::Value - getOverlayInfo(); + getOverlayInfo() const; /** Returns information about the local server. Reported through the /crawl API @@ -522,7 +522,7 @@ private: /** Send once a second transactions' hashes aggregated by peers. */ void - sendTxQueue(); + sendTxQueue() const; /** Check if peers stopped relaying messages * and if slots stopped receiving messages from the validator */ diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index dc7c350697..7ed8c45453 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -134,7 +134,10 @@ void PeerImp::run() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::run, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::run, shared_from_this())); + return; + } auto parseLedgerHash = [](std::string_view value) -> std::optional { if (uint256 ret; ret.parseHex(value)) @@ -177,9 +180,13 @@ PeerImp::run() } if (inbound_) + { doAccept(); + } else + { doProtocolStart(); + } // Anything else that needs to be done with the connection should be // done in doProtocolStart @@ -189,7 +196,10 @@ void PeerImp::stop() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::stop, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::stop, shared_from_this())); + return; + } if (!socket_.is_open()) return; @@ -209,14 +219,20 @@ void PeerImp::send(std::shared_ptr const& m) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); + { + post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); + return; + } if (!socket_.is_open()) return; // we are in progress of closing the connection if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto validator = m->getValidatorKey(); if (validator && !squelch_.expireSquelch(*validator)) @@ -273,7 +289,10 @@ void PeerImp::sendTxQueue() { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::sendTxQueue, shared_from_this())); + { + post(strand_, std::bind(&PeerImp::sendTxQueue, shared_from_this())); + return; + } if (!txQueue_.empty()) { @@ -291,7 +310,10 @@ void PeerImp::addTxQueue(uint256 const& hash) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::addTxQueue, shared_from_this(), hash)); + { + post(strand_, std::bind(&PeerImp::addTxQueue, shared_from_this(), hash)); + return; + } if (txQueue_.size() == reduce_relay::MAX_TX_QUEUE_SIZE) { @@ -307,7 +329,10 @@ void PeerImp::removeTxQueue(uint256 const& hash) { if (!strand_.running_in_this_thread()) - return post(strand_, std::bind(&PeerImp::removeTxQueue, shared_from_this(), hash)); + { + post(strand_, std::bind(&PeerImp::removeTxQueue, shared_from_this(), hash)); + return; + } auto removed = txQueue_.erase(hash); JLOG(p_journal_.trace()) << "removeTxQueue " << removed; @@ -366,8 +391,10 @@ PeerImp::json() ret[jss::cluster] = true; if (auto const n = name(); !n.empty()) + { // Could move here if Json::Value supported moving from a string ret[jss::name] = n; + } } if (auto const d = domain(); !d.empty()) @@ -392,7 +419,7 @@ PeerImp::json() ret[jss::uptime] = static_cast(std::chrono::duration_cast(uptime()).count()); - std::uint32_t minSeq, maxSeq; + std::uint32_t minSeq = 0, maxSeq = 0; ledgerRange(minSeq, maxSeq); if ((minSeq != 0) || (maxSeq != 0)) @@ -545,10 +572,13 @@ void PeerImp::fail(std::string const& reason) { if (!strand_.running_in_this_thread()) - return post( + { + post( strand_, std::bind( (void (Peer::*)(std::string const&))&PeerImp::fail, shared_from_this(), reason)); + return; + } if (!socket_.is_open()) return; @@ -635,7 +665,7 @@ PeerImp::close() cancelTimer(); error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) overlay_.incPeerDisconnect(); @@ -658,7 +688,8 @@ PeerImp::setTimer(std::chrono::seconds interval) catch (std::exception const& ex) { JLOG(journal_.error()) << "setTimer: " << ex.what(); - return shutdown(); + shutdown(); + return; } timer_.async_wait(bind_executor( @@ -691,7 +722,8 @@ PeerImp::onTimer(error_code const& ec) // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); - return close(); + close(); + return; } // the timer expired before the shutdown completed @@ -699,11 +731,15 @@ PeerImp::onTimer(error_code const& ec) if (shutdown_) { JLOG(journal_.debug()) << "onTimer: shutdown timer expired"; - return close(); + close(); + return; } if (large_sendq_++ >= Tuning::sendqIntervals) - return fail("Large send queue"); + { + fail("Large send queue"); + return; + } if (auto const t = tracking_.load(); !inbound_ && t != Tracking::converged) { @@ -718,13 +754,17 @@ PeerImp::onTimer(error_code const& ec) (t == Tracking::unknown && (duration > app_.config().MAX_UNKNOWN_TIME))) { overlay_.peerFinder().on_failure(slot_); - return fail("Not useful"); + fail("Not useful"); + return; } } // Already waiting for PONG if (lastPingSeq_) - return fail("Ping Timeout"); + { + fail("Ping Timeout"); + return; + } lastPingTime_ = clock_type::now(); lastPingSeq_ = rand_int(); @@ -761,14 +801,20 @@ PeerImp::doAccept() // a shutdown was initiated before the handshake, there is nothing to do if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); // This shouldn't fail since we already computed // the shared value successfully in OverlayImpl if (!sharedValue) - return fail("makeSharedValue: Unexpected failure"); + { + fail("makeSharedValue: Unexpected failure"); + return; + } JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_); @@ -811,12 +857,22 @@ PeerImp::doAccept() if (!socket_.is_open()) return; if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (ec) - return fail("onWriteResponse", ec); + { + fail("onWriteResponse", ec); + return; + } if (write_buffer->size() == bytes_transferred) - return doProtocolStart(); - return fail("Failed to write header"); + { + doProtocolStart(); + return; + } + fail("Failed to write header"); + return; })); } @@ -842,7 +898,10 @@ PeerImp::doProtocolStart() { // a shutdown was initiated before the handshare, there is nothing to do if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } onReadMessage(error_code(), 0); @@ -895,17 +954,25 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) if (ec == boost::asio::error::eof) { JLOG(journal_.debug()) << "EOF"; - return shutdown(); + shutdown(); + return; } if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onReadMessage", ec); + fail("onReadMessage", ec); + return; } // we started shutdown, no reason to process further data if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (auto stream = journal_.trace()) { @@ -921,7 +988,7 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) while (read_buffer_.size() > 0) { - std::size_t bytes_consumed; + std::size_t bytes_consumed = 0; using namespace std::chrono_literals; std::tie(bytes_consumed, ec) = perf::measureDurationAndLog( @@ -936,7 +1003,10 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) // the error_code is produced by invokeProtocolMessage // it could be due to a bad message if (ec) - return fail("onReadMessage", ec); + { + fail("onReadMessage", ec); + return; + } if (bytes_consumed == 0) break; @@ -946,7 +1016,10 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) // check if a shutdown was initiated while processing messages if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } readPending_ = true; @@ -978,9 +1051,13 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) if (ec) { if (ec == boost::asio::error::operation_aborted) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } - return fail("onWriteMessage", ec); + fail("onWriteMessage", ec); + return; } if (auto stream = journal_.trace()) @@ -995,7 +1072,10 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) send_queue_.pop(); if (shutdown_) - return tryAsyncShutdown(); + { + tryAsyncShutdown(); + return; + } if (!send_queue_.empty()) { @@ -1003,7 +1083,7 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onWriteMessage : shutdown started"); // Timeout on writes only - return boost::asio::async_write( + boost::asio::async_write( stream_, boost::asio::buffer(send_queue_.front()->getBuffer(compressionEnabled_)), bind_executor( @@ -1013,6 +1093,7 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + return; } } @@ -1122,9 +1203,13 @@ PeerImp::onMessage(std::shared_ptr const& m) std::lock_guard sl(recentLock_); if (latency_) + { latency_ = (*latency_ * 7 + rtt) / 8; + } else + { latency_ = rtt; + } } return; @@ -1315,7 +1400,7 @@ PeerImp::handleTransaction( } // LCOV_EXCL_STOP - HashRouterFlags flags; + HashRouterFlags flags = HashRouterFlags::UNDEFINED; constexpr std::chrono::seconds tx_interval = 10s; if (!app_.getHashRouter().shouldProcess(txID, id_, flags, tx_interval)) @@ -1330,7 +1415,9 @@ PeerImp::handleTransaction( // Erase only if the server has seen this tx. If the server has not // seen this tx then the tx could not has been queued for this peer. else if (eraseTxQueue && txReduceRelayEnabled()) + { removeTxQueue(txID); + } overlay_.reportInboundTraffic( TrafficCount::category::transaction_duplicate, Message::messageSize(*m)); @@ -1402,7 +1489,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // Verify ledger info type if (itype < protocol::liBASE || itype > protocol::liTS_CANDIDATE) - return badData("Invalid ledger info type"); + { + badData("Invalid ledger info type"); + return; + } auto const ltype = [&m]() -> std::optional<::protocol::TMLedgerType> { if (m->has_ltype()) @@ -1413,21 +1503,31 @@ PeerImp::onMessage(std::shared_ptr const& m) if (itype == protocol::liTS_CANDIDATE) { if (!m->has_ledgerhash()) - return badData("Invalid TX candidate set, missing TX set hash"); + { + badData("Invalid TX candidate set, missing TX set hash"); + return; + } } else if ( !m->has_ledgerhash() && !m->has_ledgerseq() && !(ltype && *ltype == protocol::ltCLOSED)) { - return badData("Invalid request"); + badData("Invalid request"); + return; } // Verify ledger type if (ltype && (*ltype < protocol::ltACCEPTED || *ltype > protocol::ltCLOSED)) - return badData("Invalid ledger type"); + { + badData("Invalid ledger type"); + return; + } // Verify ledger hash if (m->has_ledgerhash() && !stringIsUint256Sized(m->ledgerhash())) - return badData("Invalid ledger hash"); + { + badData("Invalid ledger hash"); + return; + } // Verify ledger sequence if (m->has_ledgerseq()) @@ -1439,7 +1539,8 @@ PeerImp::onMessage(std::shared_ptr const& m) if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s && ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } @@ -1447,25 +1548,35 @@ PeerImp::onMessage(std::shared_ptr const& m) if (itype != protocol::liBASE) { if (m->nodeids_size() <= 0) - return badData("Invalid ledger node IDs"); + { + badData("Invalid ledger node IDs"); + return; + } for (auto const& nodeId : m->nodeids()) { if (deserializeSHAMapNodeID(nodeId) == std::nullopt) - return badData("Invalid SHAMap node ID"); + { + badData("Invalid SHAMap node ID"); + return; + } } } // Verify query type if (m->has_querytype() && m->querytype() != protocol::qtINDIRECT) - return badData("Invalid query type"); + { + badData("Invalid query type"); + return; + } // Verify query depth if (m->has_querydepth()) { if (m->querydepth() > Tuning::maxQueryDepth || itype == protocol::liBASE) { - return badData("Invalid query depth"); + badData("Invalid query depth"); + return; } } @@ -1496,9 +1607,13 @@ PeerImp::onMessage(std::shared_ptr const& m) if (reply.has_error()) { if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) + { peer->charge(Resource::feeMalformedRequest, "proof_path_request"); + } else + { peer->charge(Resource::feeRequestNoReply, "proof_path_request"); + } } else { @@ -1542,9 +1657,13 @@ PeerImp::onMessage(std::shared_ptr const& m) if (reply.has_error()) { if (reply.error() == protocol::TMReplyError::reBAD_REQUEST) + { peer->charge(Resource::feeMalformedRequest, "replay_delta_request"); + } else + { peer->charge(Resource::feeRequestNoReply, "replay_delta_request"); + } } else { @@ -1579,7 +1698,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // Verify ledger hash if (!stringIsUint256Sized(m->ledgerhash())) - return badData("Invalid ledger hash"); + { + badData("Invalid ledger hash"); + return; + } // Verify ledger sequence { @@ -1588,7 +1710,8 @@ PeerImp::onMessage(std::shared_ptr const& m) { if (ledgerSeq != 0) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } else @@ -1598,26 +1721,32 @@ PeerImp::onMessage(std::shared_ptr const& m) if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s && ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10) { - return badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + badData("Invalid ledger sequence " + std::to_string(ledgerSeq)); + return; } } } // Verify ledger info type if (m->type() < protocol::liBASE || m->type() > protocol::liTS_CANDIDATE) - return badData("Invalid ledger info type"); + { + badData("Invalid ledger info type"); + return; + } // Verify reply error if (m->has_error() && (m->error() < protocol::reNO_LEDGER || m->error() > protocol::reBAD_REQUEST)) { - return badData("Invalid reply error"); + badData("Invalid reply error"); + return; } // Verify ledger nodes. if (m->nodes_size() <= 0 || m->nodes_size() > Tuning::hardMaxReplyNodes) { - return badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size())); + badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size())); + return; } // If there is a request cookie, attempt to relay the message @@ -1770,7 +1899,9 @@ PeerImp::onMessage(std::shared_ptr const& m) { std::lock_guard sl(recentLock_); if (!last_status_.has_newstatus() || m->has_newstatus()) + { last_status_ = *m; + } else { // preserve old status @@ -1856,7 +1987,7 @@ PeerImp::onMessage(std::shared_ptr const& m) checkTracking(m->ledgerseq(), app_.getLedgerMaster().getValidLedgerIndex()); } - app_.getOPs().pubPeerStatus([=, this]() -> Json::Value { + app_.getOPs().pubPeerStatus([m, this]() -> Json::Value { Json::Value j = Json::objectValue; if (m->has_newstatus()) @@ -1933,7 +2064,7 @@ PeerImp::onMessage(std::shared_ptr const& m) void PeerImp::checkTracking(std::uint32_t validationSeq) { - std::uint32_t serverSeq; + std::uint32_t serverSeq = 0; { // Extract the sequence number of the highest // ledger this peer has @@ -2227,7 +2358,7 @@ PeerImp::onMessage(std::shared_ptr const& m fee_.update(Resource::feeUselessData, "unsupported peer"); return; } - else if (m->version() < 2) + if (m->version() < 2) { JLOG(p_journal_.debug()) << "ValidatorListCollection: received invalid validator list " @@ -2312,8 +2443,10 @@ PeerImp::onMessage(std::shared_ptr const& m) // peer receives within IDLED seconds since the message has been // relayed. if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED) + { overlay_.updateSlotAndSquelch( key, val->getSignerPublic(), id_, protocol::mtVALIDATION); + } // increase duplicate validations received overlay_.reportInboundTraffic( @@ -2483,7 +2616,9 @@ PeerImp::onMessage(std::shared_ptr const& m) JLOG(p_journal_.debug()) << "GetObj: Late fetch pack for " << pLSeq; } else + { progress = true; + } } } @@ -2584,11 +2719,13 @@ PeerImp::onMessage(std::shared_ptr const& m) overlay_.addTxMetrics(m->transactions_size()); for (std::uint32_t i = 0; i < m->transactions_size(); ++i) + { handleTransaction( std::shared_ptr( m->mutable_transactions(i), [](protocol::TMTransaction*) {}), false, true); + } } void @@ -2596,7 +2733,10 @@ PeerImp::onMessage(std::shared_ptr const& m) { using on_message_fn = void (PeerImp::*)(std::shared_ptr const&); if (!strand_.running_in_this_thread()) - return post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m)); + { + post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m)); + return; + } if (!m->has_validatorpubkey()) { @@ -2621,9 +2761,13 @@ PeerImp::onMessage(std::shared_ptr const& m) std::uint32_t duration = m->has_squelchduration() ? m->squelchduration() : 0; if (!m->squelch()) + { squelch_.removeSquelch(key); + } else if (!squelch_.addSquelch(key, std::chrono::seconds{duration})) + { fee_.update(Resource::feeInvalidData, "squelch duration"); + } JLOG(p_journal_.debug()) << "onMessage: TMSquelch " << slice << " " << id() << " " << duration; } @@ -2879,12 +3023,16 @@ PeerImp::checkPropose( return; } - bool relay; + bool relay = false; if (isTrusted) + { relay = app_.getOPs().processTrustedProposal(peerPos); + } else + { relay = app_.config().RELAY_UNTRUSTED_PROPOSALS == 1 || cluster(); + } if (relay) { @@ -2895,11 +3043,13 @@ PeerImp::checkPropose( auto haveMessage = app_.overlay().relay(*packet, peerPos.suppressionID(), peerPos.publicKey()); if (!haveMessage.empty()) + { overlay_.updateSlotAndSquelch( peerPos.suppressionID(), peerPos.publicKey(), std::move(haveMessage), protocol::mtPROPOSE_LEDGER); + } } } @@ -3235,7 +3385,8 @@ PeerImp::processLedgerRequest(std::shared_ptr const& m) // Add requested node data to reply if (m->nodeids_size() > 0) { - auto const queryDepth{m->has_querydepth() ? m->querydepth() : (isHighLatency() ? 2 : 1)}; + std::uint32_t const defaultDepth = isHighLatency() ? 2 : 1; + auto const queryDepth{m->has_querydepth() ? m->querydepth() : defaultDepth}; std::vector> data; @@ -3347,9 +3498,13 @@ PeerImp::getScore(bool haveItem) const } if (latency) + { score -= latency->count() * spLatency; + } else + { score -= spNoLatency; + } return score; } diff --git a/src/xrpld/overlay/detail/ProtocolVersion.cpp b/src/xrpld/overlay/detail/ProtocolVersion.cpp index 3163ba55e0..29bff996a9 100644 --- a/src/xrpld/overlay/detail/ProtocolVersion.cpp +++ b/src/xrpld/overlay/detail/ProtocolVersion.cpp @@ -81,8 +81,8 @@ parseProtocolVersions(boost::beast::string_view const& value) if (boost::regex_match(s, m, re)) { - std::uint16_t major; - std::uint16_t minor; + std::uint16_t major = 0; + std::uint16_t minor = 0; if (!beast::lexicalCastChecked(major, std::string(m[1]))) continue; diff --git a/src/xrpld/overlay/detail/TrafficCount.cpp b/src/xrpld/overlay/detail/TrafficCount.cpp index f782d7cf0a..2ce32b4468 100644 --- a/src/xrpld/overlay/detail/TrafficCount.cpp +++ b/src/xrpld/overlay/detail/TrafficCount.cpp @@ -36,16 +36,22 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->type() == protocol::liTS_CANDIDATE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_tsc_get : TrafficCount::category::ld_tsc_share; + } if (msg->type() == protocol::liTX_NODE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_txn_get : TrafficCount::category::ld_txn_share; + } if (msg->type() == protocol::liAS_NODE) + { return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_asn_get : TrafficCount::category::ld_asn_share; + } return (inbound && !msg->has_requestcookie()) ? TrafficCount::category::ld_get : TrafficCount::category::ld_share; @@ -54,16 +60,22 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->itype() == protocol::liTS_CANDIDATE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_tsc_share : TrafficCount::category::gl_tsc_get; + } if (msg->itype() == protocol::liTX_NODE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_txn_share : TrafficCount::category::gl_txn_get; + } if (msg->itype() == protocol::liAS_NODE) + { return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_asn_share : TrafficCount::category::gl_asn_get; + } return (inbound || msg->has_requestcookie()) ? TrafficCount::category::gl_share : TrafficCount::category::gl_get; @@ -72,28 +84,40 @@ TrafficCount::categorize( if (auto msg = dynamic_cast(&message)) { if (msg->type() == protocol::TMGetObjectByHash::otLEDGER) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_ledger : TrafficCount::category::get_hash_ledger; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTION) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_tx : TrafficCount::category::get_hash_tx; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTION_NODE) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_txnode : TrafficCount::category::get_hash_txnode; + } if (msg->type() == protocol::TMGetObjectByHash::otSTATE_NODE) + { return (msg->query() == inbound) ? TrafficCount::category::share_hash_asnode : TrafficCount::category::get_hash_asnode; + } if (msg->type() == protocol::TMGetObjectByHash::otCAS_OBJECT) + { return (msg->query() == inbound) ? TrafficCount::category::share_cas_object : TrafficCount::category::get_cas_object; + } if (msg->type() == protocol::TMGetObjectByHash::otFETCH_PACK) + { return (msg->query() == inbound) ? TrafficCount::category::share_fetch_pack : TrafficCount::category::get_fetch_pack; + } if (msg->type() == protocol::TMGetObjectByHash::otTRANSACTIONS) return TrafficCount::category::get_transactions; diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index ab53068fca..b850a10975 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -74,7 +74,7 @@ struct Config /** Write the configuration into a property stream */ void - onWrite(beast::PropertyStream::Map& map); + onWrite(beast::PropertyStream::Map& map) const; /** Make PeerFinder::Config from configuration parameters * @param config server's configuration diff --git a/src/xrpld/peerfinder/detail/Bootcache.cpp b/src/xrpld/peerfinder/detail/Bootcache.cpp index af9a1ad57b..d07ec444a4 100644 --- a/src/xrpld/peerfinder/detail/Bootcache.cpp +++ b/src/xrpld/peerfinder/detail/Bootcache.cpp @@ -4,6 +4,8 @@ #include +#include + namespace xrpl { namespace PeerFinder { @@ -131,8 +133,7 @@ Bootcache::on_success(beast::IP::Endpoint const& endpoint) else { Entry entry(result.first->right); - if (entry.valence() < 0) - entry.valence() = 0; + entry.valence() = std::max(entry.valence(), 0); ++entry.valence(); m_map.erase(result.first); result = m_map.insert(value_type(endpoint, entry)); @@ -156,8 +157,7 @@ Bootcache::on_failure(beast::IP::Endpoint const& endpoint) else { Entry entry(result.first->right); - if (entry.valence() > 0) - entry.valence() = 0; + entry.valence() = std::min(entry.valence(), 0); --entry.valence(); m_map.erase(result.first); result = m_map.insert(value_type(endpoint, entry)); diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index 58246cd198..cc74018fc0 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -1,6 +1,8 @@ #include #include +#include + namespace xrpl { namespace PeerFinder { @@ -51,7 +53,7 @@ Config::applyTuning() } void -Config::onWrite(beast::PropertyStream::Map& map) +Config::onWrite(beast::PropertyStream::Map& map) const { map["max_peers"] = maxPeers; map["out_peers"] = outPeers; @@ -81,8 +83,7 @@ Config::makeConfig( if (cfg.PEERS_MAX != 0) config.maxPeers = cfg.PEERS_MAX; - if (config.maxPeers < Tuning::minOutCount) - config.maxPeers = Tuning::minOutCount; + config.maxPeers = std::max(config.maxPeers, Tuning::minOutCount); config.outPeers = config.calcOutPeers(); // Calculate the number of outbound peers we want. If we dont want @@ -93,9 +94,13 @@ Config::makeConfig( // Calculate the largest number of inbound connections we could // take. if (config.maxPeers >= config.outPeers) + { config.inPeers = config.maxPeers - config.outPeers; + } else + { config.inPeers = 0; + } } else { diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index d04e09441c..70f082c0d5 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -33,8 +33,7 @@ public: beast::Journal journal, BasicConfig const& config, beast::insight::Collector::ptr const& collector) - : Manager() - , io_context_(io_context) + : io_context_(io_context) , work_(std::in_place, boost::asio::make_work_guard(io_context_)) , m_clock(clock) , m_journal(journal) diff --git a/src/xrpld/perflog/detail/PerfLogImp.cpp b/src/xrpld/perflog/detail/PerfLogImp.cpp index d1267953e4..fc9ef70830 100644 --- a/src/xrpld/perflog/detail/PerfLogImp.cpp +++ b/src/xrpld/perflog/detail/PerfLogImp.cpp @@ -257,8 +257,10 @@ void PerfLogImp::report() { if (!logFile_) + { // If logFile_ is not writable do no further work. return; + } auto const present = system_clock::now(); if (present < lastLog_ + setup_.logInterval) @@ -345,9 +347,13 @@ PerfLogImp::rpcEnd(std::string const& method, std::uint64_t const requestId, boo } std::lock_guard lock(counter->second.mutex); if (finish) + { ++counter->second.value.finished; + } else + { ++counter->second.value.errored; + } counter->second.value.duration += std::chrono::duration_cast(steady_clock::now() - startTime); } @@ -437,7 +443,7 @@ PerfLogImp::rotate() void PerfLogImp::start() { - if (setup_.perfLog.size()) + if (!setup_.perfLog.empty()) thread_ = std::thread(&PerfLogImp::run, this); } @@ -463,7 +469,7 @@ setup_PerfLog(Section const& section, boost::filesystem::path const& configDir) PerfLog::Setup setup; std::string perfLog; set(perfLog, "perf_log", section); - if (perfLog.size()) + if (!perfLog.empty()) { setup.perfLog = boost::filesystem::path(perfLog); if (setup.perfLog.is_relative()) @@ -472,7 +478,7 @@ setup_PerfLog(Section const& section, boost::filesystem::path const& configDir) } } - std::uint64_t logInterval; + std::uint64_t logInterval = 0; if (get_if_exists(section, "log_interval", logInterval)) setup.logInterval = std::chrono::seconds(logInterval); return setup; diff --git a/src/xrpld/rpc/ServerHandler.h b/src/xrpld/rpc/ServerHandler.h index af7ec4bf1c..74f59756b0 100644 --- a/src/xrpld/rpc/ServerHandler.h +++ b/src/xrpld/rpc/ServerHandler.h @@ -182,7 +182,7 @@ private: Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress, - Output&&, + Output const&, std::shared_ptr coro, std::string_view forwardedFor, std::string_view user); @@ -192,7 +192,7 @@ private: }; ServerHandler::Setup -setup_ServerHandler(Config const& c, std::ostream&& log); +setup_ServerHandler(Config const& c, std::ostream& log); std::unique_ptr make_ServerHandler( diff --git a/src/xrpld/rpc/detail/DeliveredAmount.cpp b/src/xrpld/rpc/detail/DeliveredAmount.cpp index 7cb90eaa98..3e89edc1f9 100644 --- a/src/xrpld/rpc/detail/DeliveredAmount.cpp +++ b/src/xrpld/rpc/detail/DeliveredAmount.cpp @@ -34,7 +34,7 @@ getDeliveredAmount( if (auto const& deliveredAmount = transactionMeta.getDeliveredAmount(); deliveredAmount.has_value()) { - return *deliveredAmount; + return deliveredAmount; } if (serializedTx->isFieldPresent(sfAmount)) diff --git a/src/xrpld/rpc/detail/Handler.cpp b/src/xrpld/rpc/detail/Handler.cpp index ec970b107e..04cf0420b7 100644 --- a/src/xrpld/rpc/detail/Handler.cpp +++ b/src/xrpld/rpc/detail/Handler.cpp @@ -42,9 +42,13 @@ handle(JsonContext& context, Object& object) auto status = handler.check(); if (status) + { status.inject(object); + } else + { handler.writeResult(object); + } return status; } @@ -172,9 +176,11 @@ private: { if (overlappingApiVersion( table_.equal_range(entry.name_), entry.minApiVer_, entry.maxApiVer_)) + { LogicError( std::string("Handler for ") + entry.name_ + " overlaps with an existing handler"); + } table_.insert({entry.name_, entry}); } @@ -232,9 +238,11 @@ private: table_.equal_range(HandlerImpl::name), HandlerImpl::minApiVer, HandlerImpl::maxApiVer)) + { LogicError( std::string("Handler for ") + HandlerImpl::name + " overlaps with an existing handler"); + } table_.insert({HandlerImpl::name, handlerFrom()}); } diff --git a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp index f818c72b59..ef8bc448ee 100644 --- a/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp +++ b/src/xrpld/rpc/detail/MPTokenIssuanceID.cpp @@ -17,7 +17,7 @@ canHaveMPTokenIssuanceID( return false; // if the transaction failed nothing could have been delivered. - if (transactionMeta.getResultTER() != tesSUCCESS) + if (!isTesSuccess(transactionMeta.getResultTER())) return false; return true; diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp index 134cbb34f8..cd36208294 100644 --- a/src/xrpld/rpc/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -116,7 +116,7 @@ private: jvResult[jss::currency] = strCurrency; - if (strIssuer.length()) + if (!strIssuer.empty()) { // Could confirm issuer is a valid Ripple address. jvResult[jss::issuer] = strIssuer; @@ -124,11 +124,9 @@ private: return jvResult; } - else - { - return RPC::make_param_error( - std::string("Invalid currency/issuer '") + strCurrencyIssuer + "'"); - } + + return RPC::make_param_error( + std::string("Invalid currency/issuer '") + strCurrencyIssuer + "'"); } static bool @@ -212,6 +210,7 @@ private: // account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] // [count] [descending] Json::Value + // NOLINTNEXTLINE(readability-make-member-function-const) parseAccountTransactions(Json::Value const& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -298,19 +297,15 @@ private: { return jvTakerPays; } - else - { - jvRequest[jss::taker_pays] = jvTakerPays; - } + + jvRequest[jss::taker_pays] = jvTakerPays; if (isRpcError(jvTakerGets)) { return jvTakerGets; } - else - { - jvRequest[jss::taker_gets] = jvTakerGets; - } + + jvRequest[jss::taker_gets] = jvTakerGets; if (jvParams.size() >= 3) { @@ -366,9 +361,13 @@ private: std::string input = jvParams[0u].asString(); if (input.find_first_not_of("0123456789") == std::string::npos) + { jvRequest["can_delete"] = jvParams[0u].asUInt(); + } else + { jvRequest["can_delete"] = input; + } return jvRequest; } @@ -447,11 +446,17 @@ private: // determines whether an amendment is vetoed - so "reject" means // that jss::vetoed is true. if (boost::iequals(action, "reject")) + { jvRequest[jss::vetoed] = Json::Value(true); + } else if (boost::iequals(action, "accept")) + { jvRequest[jss::vetoed] = Json::Value(false); + } else + { return rpcError(rpcINVALID_PARAMS); + } } return jvRequest; @@ -967,8 +972,7 @@ private: return jvRequest; } - else if ( - (jvParams.size() >= 2 || bOffline) && reader.parse(jvParams[1u].asString(), txJSON)) + if ((jvParams.size() >= 2 || bOffline) && reader.parse(jvParams[1u].asString(), txJSON)) { // Signing or submitting tx_json. Json::Value jvRequest{Json::objectValue}; @@ -1055,9 +1059,13 @@ private: } if (jvParams[0u].asString().length() == 16) + { jvRequest[jss::ctid] = jvParams[0u].asString(); + } else + { jvRequest[jss::transaction] = jvParams[0u].asString(); + } return jvRequest; } @@ -1123,9 +1131,13 @@ private: if (param[0] != 'r') { if (param.size() == 64) + { jvRequest[jss::ledger_hash] = param; + } else + { jvRequest[jss::ledger_index] = param; + } if (size <= index) return RPC::make_param_error("Invalid hotwallet"); @@ -1355,10 +1367,12 @@ struct RPCCallImp // Receive reply if (strData.empty()) + { Throw( "no response from server. Please " "ensure that the rippled server is running in another " "process."); + } // Parse reply JLOG(j.debug()) << "RPC reply: " << strData << std::endl; @@ -1435,9 +1449,13 @@ rpcCmdToJson( }; if (jvRequest.isObject()) + { insert_api_version(jvRequest); + } else if (jvRequest.isArray()) + { std::for_each(jvRequest.begin(), jvRequest.end(), insert_api_version); + } JLOG(j.trace()) << "RPC Request: " << jvRequest << std::endl; return jvRequest; @@ -1476,8 +1494,8 @@ rpcClient( xrpl::ServerHandler::Setup setup; try { - setup = setup_ServerHandler( - config, beast::logstream{logs.journal("HTTPClient").warn()}); + beast::logstream rpcCallLog{logs.journal("HTTPClient").warn()}; + setup = setup_ServerHandler(config, rpcCallLog); } catch (std::exception const&) // NOLINT(bugprone-empty-catch) { @@ -1500,7 +1518,9 @@ rpcClient( jvRequest["admin_password"] = setup.client.admin_password; if (jvRequest.isObject()) + { jvParams.append(jvRequest); + } else if (jvRequest.isArray()) { for (Json::UInt i = 0; i < jvRequest.size(); ++i) @@ -1516,9 +1536,12 @@ rpcClient( setup.client.user, setup.client.password, "", - jvRequest.isMember(jss::method) // Allow parser to rewrite method. - ? jvRequest[jss::method].asString() - : jvRequest.isArray() ? "batch" : args[0], + // Allow parser to rewrite method. + [&]() -> std::string { + if (jvRequest.isMember(jss::method)) + return jvRequest[jss::method].asString(); + return jvRequest.isArray() ? "batch" : args[0]; + }(), jvParams, // Parsed, execute. setup.client.secure != 0, // Use SSL config.quiet(), @@ -1557,11 +1580,17 @@ rpcClient( { jvOutput[jss::status] = "error"; if (jvOutput.isMember(jss::error_code)) + { nRet = std::stoi(jvOutput[jss::error_code].asString()); + } else if (jvOutput[jss::error].isMember(jss::error_code)) + { nRet = std::stoi(jvOutput[jss::error][jss::error_code].asString()); + } else + { nRet = rpcBAD_SYNTAX; + } } // YYY We could have a command line flag for single line output for diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index 2066c8a5e9..840ebd5946 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -215,11 +215,9 @@ doCommand(RPC::JsonContext& context, Json::Value& result) return ret; } - else - { - auto ret = callMethod(context, method, handler->name_, result); - return ret; - } + + auto ret = callMethod(context, method, handler->name_, result); + return ret; } return rpcUNKNOWN_COMMAND; diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index 14e1108955..832ff778e1 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -24,9 +24,13 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID) if (sle->getType() == ltRIPPLE_STATE) { if (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) + { return sle->getFieldU64(sfLowNode); - else if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID) + } + if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID) + { return sle->getFieldU64(sfHighNode); + } } if (!sle->isFieldPresent(sfOwnerNode)) @@ -46,7 +50,7 @@ isRelatedToAccount( return (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) || (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID); } - else if (sle->isFieldPresent(sfAccount)) + if (sle->isFieldPresent(sfAccount)) { // If there's an sfAccount present, also test the sfDestination, if // present. This will match objects such as Escrows (ltESCROW), Payment @@ -57,12 +61,12 @@ isRelatedToAccount( return sle->getAccountID(sfAccount) == accountID || (sle->isFieldPresent(sfDestination) && sle->getAccountID(sfDestination) == accountID); } - else if (sle->getType() == ltSIGNER_LIST) + if (sle->getType() == ltSIGNER_LIST) { Keylet const accountSignerList = keylet::signers(accountID); return sle->key() == accountSignerList.key; } - else if (sle->getType() == ltNFTOKEN_OFFER) + if (sle->getType() == ltNFTOKEN_OFFER) { // Do not check the sfDestination field. NFToken Offers are NOT added to // the Destination account's directory. @@ -234,9 +238,13 @@ keypairForSignature(Json::Value const& params, Json::Value& error, unsigned int if (!keyType) { if (apiVersion > 1u) + { error = RPC::make_error(rpcBAD_KEY_TYPE); + } else + { error = RPC::invalid_field_error(jss::key_type); + } return {}; } @@ -279,7 +287,9 @@ keypairForSignature(Json::Value const& params, Json::Value& error, unsigned int if (!seed) { if (has_key_type) + { seed = getSeedFromRPC(params, error); + } else { if (!params[jss::secret].isString()) diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp index ddf78425bd..54ff515894 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -55,7 +55,7 @@ ledgerFromIndex( if (index == "closed") return getLedger(ledger, LedgerShortcut::Closed, context); - std::uint32_t iVal; + std::uint32_t iVal = 0; if (!beast::lexicalCastChecked(iVal, index)) return {rpcINVALID_PARAMS, expected_field_message(fieldName, "string or number")}; @@ -78,10 +78,12 @@ ledgerFromRequest(T& ledger, JsonContext const& context) // while `ledger` is still supported, it is deprecated // and therefore shouldn't be mentioned in the error message if (hasLedger) + { return { rpcINVALID_PARAMS, "Exactly one of 'ledger', 'ledger_hash', or " "'ledger_index' can be specified."}; + } return { rpcINVALID_PARAMS, "Exactly one of 'ledger_hash' or " @@ -97,9 +99,11 @@ ledgerFromRequest(T& ledger, JsonContext const& context) return {rpcINVALID_PARAMS, expected_field_message(jss::ledger, "string or number")}; } if (legacyLedger.isString() && legacyLedger.asString().size() == 64) + { return ledgerFromHash(ledger, legacyLedger, context, jss::ledger); - else - return ledgerFromIndex(ledger, legacyLedger, context, jss::ledger); + } + + return ledgerFromIndex(ledger, legacyLedger, context, jss::ledger); } if (hasHash) @@ -182,17 +186,15 @@ ledgerFromSpecifier( { return getLedger(ledger, LedgerShortcut::Validated, context); } - else + + if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || + shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED) { - if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || - shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED) - { - return getLedger(ledger, LedgerShortcut::Current, context); - } - else if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) - { - return getLedger(ledger, LedgerShortcut::Closed, context); - } + return getLedger(ledger, LedgerShortcut::Current, context); + } + if (shortcut == org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) + { + return getLedger(ledger, LedgerShortcut::Closed, context); } } } diff --git a/src/xrpld/rpc/detail/RPCSub.cpp b/src/xrpld/rpc/detail/RPCSub.cpp index a0e29f68e2..233981001a 100644 --- a/src/xrpld/rpc/detail/RPCSub.cpp +++ b/src/xrpld/rpc/detail/RPCSub.cpp @@ -26,26 +26,37 @@ public: , m_io_context(io_context) , m_jobQueue(jobQueue) , mUrl(strUrl) - , mSSL(false) , mUsername(strUsername) , mPassword(strPassword) - , mSending(false) , j_(logs.journal("RPCSub")) , logs_(logs) { parsedURL pUrl; if (!parseUrl(pUrl, strUrl)) + { Throw("Failed to parse url."); + } else if (pUrl.scheme == "https") + { mSSL = true; + } else if (pUrl.scheme != "http") + { Throw("Only http and https is supported."); + } mSeq = 1; mIp = pUrl.domain; - mPort = (!pUrl.port) ? (mSSL ? 443 : 80) : *pUrl.port; + if (!pUrl.port) + { + mPort = mSSL ? 443 : 80; + } + else + { + mPort = *pUrl.port; + } mPath = pUrl.path; JLOG(j_.info()) << "RPCCall::fromNetwork sub: ip=" << mIp << " port=" << mPort @@ -97,7 +108,7 @@ private: sendThread() { Json::Value jvEvent; - bool bSend; + bool bSend = false; do { @@ -159,14 +170,14 @@ private: std::string mUrl; std::string mIp; std::uint16_t mPort; - bool mSSL; + bool mSSL{false}; std::string mUsername; std::string mPassword; std::string mPath; int mSeq; // Next id to allocate. - bool mSending; // Sending thread is active. + bool mSending{false}; // Sending thread is active. std::deque> mDeque; diff --git a/src/xrpld/rpc/detail/Role.cpp b/src/xrpld/rpc/detail/Role.cpp index e040a55c69..499787cece 100644 --- a/src/xrpld/rpc/detail/Role.cpp +++ b/src/xrpld/rpc/detail/Role.cpp @@ -83,7 +83,7 @@ requestRole( if (ipAllowed(remoteIp.address(), port.secure_gateway_nets_v4, port.secure_gateway_nets_v6)) { - if (user.size()) + if (!user.empty()) return Role::IDENTIFIED; return Role::PROXY; } @@ -137,9 +137,11 @@ extractIpAddrFromField(std::string_view field) { std::size_t const firstNonSpace = ret.find_first_not_of(' '); if (firstNonSpace == std::string_view::npos) + { // We know there's at least one leading space. So if we got // npos, then it must be all spaces. Return empty string_view. return {}; + } ret = ret.substr(firstNonSpace); } @@ -151,9 +153,11 @@ extractIpAddrFromField(std::string_view field) { std::size_t const lastNonSpace = ret.find_last_not_of(" \r\n"); if (lastNonSpace == std::string_view::npos) + { // We know there's at least one leading space. So if we // got npos, then it must be all spaces. return {}; + } ret = ret.substr(0, lastNonSpace + 1); } diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 5be41a5a23..f5187d6285 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -363,9 +363,13 @@ void logDuration(Json::Value const& request, T const& duration, beast::Journal& journal) { using namespace std::chrono_literals; - auto const level = (duration >= 10s) ? journal.error() - : (duration >= 1s) ? journal.warn() - : journal.debug(); + auto const level = [&]() { + if (duration >= 10s) + return journal.error(); + if (duration >= 1s) + return journal.warn(); + return journal.debug(); + }(); JLOG(level) << "RPC request processing duration = " << std::chrono::duration_cast(duration).count() @@ -535,9 +539,13 @@ ServerHandler::processSession( }()); if (beast::rfc2616::is_keep_alive(session->request())) + { session->complete(); + } else + { session->close(true); + } } static Json::Value @@ -561,7 +569,7 @@ ServerHandler::processRequest( Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress, - Output&& output, + Output const& output, std::shared_ptr coro, std::string_view forwardedFor, std::string_view user) @@ -643,8 +651,10 @@ ServerHandler::processRequest( auto role = Role::FORBID; auto required = Role::FORBID; if (jsonRPC.isMember(jss::method) && jsonRPC[jss::method].isString()) + { required = RPC::roleRequired( apiVersion, app_.config().BETA_RPC_API, jsonRPC[jss::method].asString()); + } if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() && jsonRPC[jss::params].size() > 0 && jsonRPC[jss::params][Json::UInt(0)].isObjectOrNull()) @@ -749,8 +759,9 @@ ServerHandler::processRequest( { params = jsonRPC[jss::params]; if (!params) + { params = Json::Value(Json::objectValue); - + } else if (!params.isArray() || params.size() != 1) { usage.charge(Resource::feeMalformedRPC); @@ -909,9 +920,13 @@ ServerHandler::processRequest( if (params.isMember(jss::id)) r[jss::id] = params[jss::id]; if (batch) + { reply.append(std::move(r)); + } else + { reply = std::move(r); + } if (reply.isMember(jss::result) && reply[jss::result].isMember(jss::result)) { @@ -957,9 +972,13 @@ ServerHandler::processRequest( { static int const maxSize = 10000; if (response.size() <= maxSize) + { stream << "Reply: " << response; + } else + { stream << "Reply: " << response.substr(0, maxSize); + } } HTTPReply(httpStatus, response, output, rpcJ); @@ -981,10 +1000,9 @@ ServerHandler::statusResponse(http_request_type const& request) const if (app_.serverOkay(reason)) { msg.result(boost::beast::http::status::ok); - msg.body() = "" + systemName() + - " Test page for rippled

" + systemName() + - " Test

This page shows rippled http(s) " - "connectivity is working.

"; + msg.body() = "Test page for " + systemName() + + "

Test

This page shows " + systemName() + + " http(s) connectivity is working.

"; } else { @@ -1010,10 +1028,14 @@ ServerHandler::Setup::makeContexts() if (p.secure()) { if (p.ssl_key.empty() && p.ssl_cert.empty() && p.ssl_chain.empty()) + { p.context = make_SSLContext(p.ssl_ciphers); + } else + { p.context = make_SSLContextAuthed(p.ssl_key, p.ssl_cert, p.ssl_chain, p.ssl_ciphers); + } } else { @@ -1114,9 +1136,13 @@ parse_Ports(Config const& config, std::ostream& log) // Remove the peer protocol, and if that would // leave the port empty, remove the port as well if (p.erase("peer") && p.empty()) + { it = result.erase(it); + } else + { ++it; + } } } else @@ -1144,15 +1170,22 @@ setup_Client(ServerHandler::Setup& setup) { decltype(setup.ports)::const_iterator iter; for (iter = setup.ports.cbegin(); iter != setup.ports.cend(); ++iter) + { if (iter->protocol.count("http") > 0 || iter->protocol.count("https") > 0) break; + } if (iter == setup.ports.cend()) return; setup.client.secure = iter->protocol.count("https") > 0; - setup.client.ip = beast::IP::is_unspecified(iter->ip) ? - // VFALCO HACK! to make localhost work - (iter->ip.is_v6() ? "::1" : "127.0.0.1") - : iter->ip.to_string(); + if (beast::IP::is_unspecified(iter->ip)) + { + // VFALCO HACK! to make localhost work + setup.client.ip = iter->ip.is_v6() ? "::1" : "127.0.0.1"; + } + else + { + setup.client.ip = iter->ip.to_string(); + } setup.client.port = iter->port; setup.client.user = iter->user; setup.client.password = iter->password; @@ -1176,7 +1209,7 @@ setup_Overlay(ServerHandler::Setup& setup) } ServerHandler::Setup -setup_ServerHandler(Config const& config, std::ostream&& log) +setup_ServerHandler(Config const& config, std::ostream& log) { ServerHandler::Setup setup; setup.ports = parse_Ports(config, log); diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 8afe3612fe..80c608dbbf 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -72,7 +72,7 @@ public: bool validMultiSign() const { - return isMultiSigning() && multiSignPublicKey_ && multiSignature_.size(); + return isMultiSigning() && multiSignPublicKey_ && !multiSignature_.empty(); } // Don't call this method unless isMultiSigning() returns true. @@ -179,11 +179,15 @@ checkPayment( if (tx_json.isMember(jss::Amount)) { if (tx_json[jss::DeliverMax] != tx_json[jss::Amount]) + { return RPC::make_error( rpcINVALID_PARAMS, "Cannot specify differing 'Amount' and 'DeliverMax'"); + } } else + { tx_json[jss::Amount] = tx_json[jss::DeliverMax]; + } tx_json.removeMember(jss::DeliverMax); } @@ -204,12 +208,16 @@ checkPayment( return RPC::invalid_field_error("tx_json.Destination"); if (params.isMember(jss::build_path) && ((doPath == false) || amount.holds())) + { return RPC::make_error( rpcINVALID_PARAMS, "Field 'build_path' not allowed in this context."); + } if (tx_json.isMember(jss::Paths) && params.isMember(jss::build_path)) + { return RPC::make_error( rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'build_path'"); + } std::optional domain; if (tx_json.isMember(sfDomainID.jsonName)) @@ -220,10 +228,8 @@ checkPayment( { return RPC::make_error(rpcDOMAIN_MALFORMED, "Unable to parse 'DomainID'."); } - else - { - domain = num; - } + + domain = num; } if (!tx_json.isMember(jss::Paths) && params.isMember(jss::build_path)) @@ -338,9 +344,13 @@ checkTxJsonFields( if (verify && !config.standalone() && (validatedLedgerAge > Tuning::maxValidatedLedgerAge)) { if (apiVersion == 1) + { ret.first = rpcError(rpcNO_CURRENT); + } else + { ret.first = rpcError(rpcNOT_SYNCED); + } return ret; } @@ -525,8 +535,10 @@ transactionPreProcessImpl( if (verify) { if (!sle) + { // XXX Ignore transactions for accounts not created. return rpcError(rpcSRC_ACT_NOT_FOUND); + } JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : " << toBase58(srcAddressID); @@ -592,8 +604,10 @@ transactionPreProcessImpl( { // If the target object doesn't exist, make one. if (!parsed.object->isFieldPresent(*signatureTarget)) + { parsed.object->setFieldObject( *signatureTarget, STObject{*signatureTemplate, *signatureTarget}); + } sigObject = &parsed.object->peekFieldObject(*signatureTarget); } sigObject->setFieldVL( @@ -666,8 +680,10 @@ transactionConstructImpl( // Check the signature if that's called for. auto sttxNew = std::make_shared(sit); if (!app.checkSigs()) + { forceValidity( app.getHashRouter(), sttxNew->getTransactionID(), Validity::SigGoodOnly); + } if (checkValidity(app.getHashRouter(), *sttxNew, rules).first != Validity::Valid) { ret.first = RPC::make_error(rpcINTERNAL, "Invalid signature."); @@ -714,7 +730,9 @@ transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion) jvResult[jss::hash] = to_string(tpTrans->getID()); } else + { jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::none); + } RPC::insertDeliverMax( jvResult[jss::tx_json], tpTrans->getSTransaction()->getTxnType(), apiVersion); @@ -880,9 +898,11 @@ checkFee( { mult = request[jss::fee_mult_max].asInt(); if (mult < 0) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_mult_max, "a positive integer")); + } } else { @@ -896,9 +916,11 @@ checkFee( { div = request[jss::fee_div_max].asInt(); if (div <= 0) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_div_max, "a positive integer")); + } } else { @@ -1024,8 +1046,10 @@ checkMultiSignFields(Json::Value const& jvRequest) // Account. if (!jvRequest.isMember(jss::signature_target) && !tx_json[sfSigningPubKey.getJsonName()].asString().empty()) + { return RPC::make_error( rpcINVALID_PARAMS, "When multi-signing 'tx_json.SigningPubKey' must be empty."); + } return Json::Value(); } diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/AMMInfo.cpp index 3b843c9410..42e44f004d 100644 --- a/src/xrpld/rpc/handlers/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/AMMInfo.cpp @@ -2,11 +2,12 @@ #include #include +#include #include #include #include #include -#include +#include #include @@ -74,17 +75,25 @@ doAMMInfo(RPC::JsonContext& context) if (params.isMember(jss::asset)) { if (auto const i = getIssue(params[jss::asset], context.j)) + { issue1 = *i; + } else + { return Unexpected(i.error()); + } } if (params.isMember(jss::asset2)) { if (auto const i = getIssue(params[jss::asset2], context.j)) + { issue2 = *i; + } else + { return Unexpected(i.error()); + } } if (params.isMember(jss::amm_account)) @@ -175,7 +184,7 @@ doAMMInfo(RPC::JsonContext& context) "xrpl::doAMMInfo : auction slot is set"); if (amm->isFieldPresent(sfAuctionSlot)) { - auto const& auctionSlot = static_cast(amm->peekAtField(sfAuctionSlot)); + auto const& auctionSlot = safe_downcast(amm->peekAtField(sfAuctionSlot)); if (auctionSlot.isFieldPresent(sfAccount)) { Json::Value auction; @@ -203,11 +212,15 @@ doAMMInfo(RPC::JsonContext& context) } if (!isXRP(asset1Balance)) + { ammResult[jss::asset_frozen] = isFrozen(*ledger, ammAccountID, issue1.currency, issue1.account); + } if (!isXRP(asset2Balance)) + { ammResult[jss::asset2_frozen] = isFrozen(*ledger, ammAccountID, issue2.currency, issue2.account); + } result[jss::amm] = std::move(ammResult); if (!result.isMember(jss::ledger_index) && !result.isMember(jss::ledger_hash)) diff --git a/src/xrpld/rpc/handlers/AccountChannels.cpp b/src/xrpld/rpc/handlers/AccountChannels.cpp index 062a2b69a0..c418386a71 100644 --- a/src/xrpld/rpc/handlers/AccountChannels.cpp +++ b/src/xrpld/rpc/handlers/AccountChannels.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -81,7 +82,7 @@ doAccountChannels(RPC::JsonContext& context) if (!strDst.empty() && !raDstAccount) return rpcError(rpcACT_MALFORMED); - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountInfo.cpp b/src/xrpld/rpc/handlers/AccountInfo.cpp index e88da097f7..15a6786e22 100644 --- a/src/xrpld/rpc/handlers/AccountInfo.cpp +++ b/src/xrpld/rpc/handlers/AccountInfo.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -84,7 +85,9 @@ doAccountInfo(RPC::JsonContext& context) strIdent = params[jss::ident].asString(); } else + { return RPC::missing_field_error(jss::account); + } std::shared_ptr ledger; auto result = RPC::lookupLedger(ledger, context); @@ -150,12 +153,16 @@ doAccountInfo(RPC::JsonContext& context) acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second); if (ledger->rules().enabled(featureClawback)) + { acctFlags[allowTrustLineClawbackFlag.first.data()] = sleAccepted->isFlag(allowTrustLineClawbackFlag.second); + } if (ledger->rules().enabled(featureTokenEscrow)) + { acctFlags[allowTrustLineLockingFlag.first.data()] = sleAccepted->isFlag(allowTrustLineLockingFlag.second); + } result[jss::account_flags] = std::move(acctFlags); @@ -300,7 +307,9 @@ doAccountInfo(RPC::JsonContext& context) jvQueueData[jss::max_spend_drops_total] = to_string(totalSpend); } else + { jvQueueData[jss::txn_count] = 0u; + } result[jss::queue_data] = std::move(jvQueueData); } diff --git a/src/xrpld/rpc/handlers/AccountLines.cpp b/src/xrpld/rpc/handlers/AccountLines.cpp index bb61e6f6c0..b3b93c0eb1 100644 --- a/src/xrpld/rpc/handlers/AccountLines.cpp +++ b/src/xrpld/rpc/handlers/AccountLines.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -98,7 +99,7 @@ doAccountLines(RPC::JsonContext& context) return result; } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context)) return *err; @@ -191,9 +192,13 @@ doAccountLines(RPC::JsonContext& context) if (visitData.ignoreDefault) { if (sleCur->getFieldAmount(sfLowLimit).getIssuer() == visitData.accountID) + { ignore = !(sleCur->getFieldU32(sfFlags) & lsfLowReserve); + } else + { ignore = !(sleCur->getFieldU32(sfFlags) & lsfHighReserve); + } } if (!ignore && count <= limit) diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index 9dcdf7bdf6..cd67391da8 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include @@ -53,7 +53,7 @@ doAccountNFTs(RPC::JsonContext& context) if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountNFTokens, context)) return *err; @@ -144,9 +144,13 @@ doAccountNFTs(RPC::JsonContext& context) } if (auto npm = (*cp)[~sfNextPageMin]) + { cp = ledger->read(Keylet(ltNFTOKEN_PAGE, *npm)); + } else + { cp = nullptr; + } } if (markerSet && !markerFound) @@ -229,9 +233,13 @@ getAccountObjects( jvObjects.append(cp->getJson(JsonOptions::none)); auto const npm = (*cp)[~sfNextPageMin]; if (npm) + { cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm)); + } else + { cp = nullptr; + } if (--mlimit == 0) { @@ -429,13 +437,13 @@ doAccountObjects(RPC::JsonContext& context) rpcStatus.inject(result); return result; } - else if (type != ltANY) + if (type != ltANY) { typeFilter = std::vector({type}); } } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountObjects, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountOffers.cpp b/src/xrpld/rpc/handlers/AccountOffers.cpp index 130decad04..86ab140b03 100644 --- a/src/xrpld/rpc/handlers/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/AccountOffers.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ doAccountOffers(RPC::JsonContext& context) if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::accountOffers, context)) return *err; diff --git a/src/xrpld/rpc/handlers/AccountTx.cpp b/src/xrpld/rpc/handlers/AccountTx.cpp index a6dcbb7b9d..d7bffe5d95 100644 --- a/src/xrpld/rpc/handlers/AccountTx.cpp +++ b/src/xrpld/rpc/handlers/AccountTx.cpp @@ -59,7 +59,7 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) return LedgerRange{min, max}; } - else if (params.isMember(jss::ledger_hash)) + if (params.isMember(jss::ledger_hash)) { auto& hashValue = params[jss::ledger_hash]; if (!hashValue.isString()) @@ -78,21 +78,29 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) } return hash; } - else if (params.isMember(jss::ledger_index)) + if (params.isMember(jss::ledger_index)) { LedgerSpecifier ledger; if (params[jss::ledger_index].isNumeric()) + { ledger = params[jss::ledger_index].asUInt(); + } else { std::string ledgerStr = params[jss::ledger_index].asString(); if (ledgerStr == "current" || ledgerStr.empty()) + { ledger = LedgerShortcut::Current; + } else if (ledgerStr == "closed") + { ledger = LedgerShortcut::Closed; + } else if (ledgerStr == "validated") + { ledger = LedgerShortcut::Validated; + } else { RPC::Status status{rpcINVALID_PARAMS, "ledger_index string malformed"}; @@ -108,8 +116,8 @@ parseLedgerArgs(RPC::Context& context, Json::Value const& params) std::variant getLedgerRange(RPC::Context& context, std::optional const& ledgerSpecifier) { - std::uint32_t uValidatedMin; - std::uint32_t uValidatedMax; + std::uint32_t uValidatedMin = 0; + std::uint32_t uValidatedMax = 0; bool bValidated = context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax); if (!bValidated) @@ -298,7 +306,9 @@ populateJsonResponse( jvObj[jss::close_time_iso] = to_string_iso(*closeTime); } else + { jvObj[json_tx] = txn->getJson(JsonOptions::include_date); + } auto const& sttx = txn->getSTransaction(); RPC::insertDeliverMax(jvObj[json_tx], sttx->getTxnType(), context.apiVersion); @@ -404,10 +414,8 @@ doAccountTxJson(RPC::JsonContext& context) { return *jv; } - else - { - args.ledger = std::get>(parseRes); - } + + args.ledger = std::get>(parseRes); if (params.isMember(jss::marker)) { diff --git a/src/xrpld/rpc/handlers/BlackList.cpp b/src/xrpld/rpc/handlers/BlackList.cpp index 102041b8d0..86abe53686 100644 --- a/src/xrpld/rpc/handlers/BlackList.cpp +++ b/src/xrpld/rpc/handlers/BlackList.cpp @@ -11,9 +11,11 @@ doBlackList(RPC::JsonContext& context) { auto& rm = context.app.getResourceManager(); if (context.params.isMember(jss::threshold)) + { return rm.getJson(context.params[jss::threshold].asInt()); - else - return rm.getJson(); + } + + return rm.getJson(); } } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/BookOffers.cpp b/src/xrpld/rpc/handlers/BookOffers.cpp index 070d7988de..ec53473821 100644 --- a/src/xrpld/rpc/handlers/BookOffers.cpp +++ b/src/xrpld/rpc/handlers/BookOffers.cpp @@ -83,12 +83,16 @@ doBookOffers(RPC::JsonContext& context) return RPC::expected_field_error("taker_pays.issuer", "string"); if (!to_issuer(pay_issuer, taker_pays[jss::issuer].asString())) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer."); + } if (pay_issuer == noAccount()) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer account one."); + } } else { @@ -96,14 +100,18 @@ doBookOffers(RPC::JsonContext& context) } if (isXRP(pay_currency) && !isXRP(pay_issuer)) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for " "XRP currency specification."); + } if (!isXRP(pay_currency) && isXRP(pay_issuer)) + { return RPC::make_error( rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."); + } AccountID get_issuer; @@ -113,12 +121,16 @@ doBookOffers(RPC::JsonContext& context) return RPC::expected_field_error("taker_gets.issuer", "string"); if (!to_issuer(get_issuer, taker_gets[jss::issuer].asString())) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."); + } if (get_issuer == noAccount()) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one."); + } } else { @@ -126,14 +138,18 @@ doBookOffers(RPC::JsonContext& context) } if (isXRP(get_currency) && !isXRP(get_issuer)) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for " "XRP currency specification."); + } if (!isXRP(get_currency) && isXRP(get_issuer)) + { return RPC::make_error( rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."); + } std::optional takerID; if (context.params.isMember(jss::taker)) @@ -155,10 +171,8 @@ doBookOffers(RPC::JsonContext& context) { return RPC::make_error(rpcDOMAIN_MALFORMED, "Unable to parse domain."); } - else - { - domain = num; - } + + domain = num; } if (pay_currency == get_currency && pay_issuer == get_issuer) @@ -167,7 +181,7 @@ doBookOffers(RPC::JsonContext& context) return RPC::make_error(rpcBAD_MARKET); } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::bookOffers, context)) return *err; diff --git a/src/xrpld/rpc/handlers/Connect.cpp b/src/xrpld/rpc/handlers/Connect.cpp index c76ebed546..292cd50cbb 100644 --- a/src/xrpld/rpc/handlers/Connect.cpp +++ b/src/xrpld/rpc/handlers/Connect.cpp @@ -33,12 +33,16 @@ doConnect(RPC::JsonContext& context) return rpcError(rpcINVALID_PARAMS); } - int iPort; + int iPort = 0; if (context.params.isMember(jss::port)) + { iPort = context.params[jss::port].asInt(); + } else + { iPort = DEFAULT_PEER_PORT; + } auto const ip_str = context.params[jss::ip].asString(); auto ip = beast::IP::Endpoint::from_string(ip_str); diff --git a/src/xrpld/rpc/handlers/DepositAuthorized.cpp b/src/xrpld/rpc/handlers/DepositAuthorized.cpp index 7bad908c51..080b136964 100644 --- a/src/xrpld/rpc/handlers/DepositAuthorized.cpp +++ b/src/xrpld/rpc/handlers/DepositAuthorized.cpp @@ -1,8 +1,8 @@ #include #include -#include #include +#include #include #include #include @@ -27,8 +27,10 @@ doDepositAuthorized(RPC::JsonContext& context) if (!params.isMember(jss::source_account)) return RPC::missing_field_error(jss::source_account); if (!params[jss::source_account].isString()) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::source_account, "a string")); + } auto srcID = parseBase58(params[jss::source_account].asString()); if (!srcID) @@ -39,8 +41,10 @@ doDepositAuthorized(RPC::JsonContext& context) if (!params.isMember(jss::destination_account)) return RPC::missing_field_error(jss::destination_account); if (!params[jss::destination_account].isString()) + { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::destination_account, "a string")); + } auto dstID = parseBase58(params[jss::destination_account].asString()); if (!dstID) @@ -84,7 +88,7 @@ doDepositAuthorized(RPC::JsonContext& context) RPC::expected_field_message( jss::credentials, "is non-empty array of CredentialID(hash256)")); } - else if (creds.size() > maxCredentialsArraySize) + if (creds.size() > maxCredentialsArraySize) { return RPC::make_error( rpcINVALID_PARAMS, RPC::expected_field_message(jss::credentials, "array too long")); @@ -151,8 +155,10 @@ doDepositAuthorized(RPC::JsonContext& context) // not set, then the deposit should be fine. bool depositAuthorized = true; if (reqAuth) + { depositAuthorized = ledger->exists(keylet::depositPreauth(dstAcct, srcAcct)) || (credentialsPresent && ledger->exists(keylet::depositPreauth(dstAcct, sorted))); + } result[jss::source_account] = params[jss::source_account].asString(); result[jss::destination_account] = params[jss::destination_account].asString(); diff --git a/src/xrpld/rpc/handlers/Feature1.cpp b/src/xrpld/rpc/handlers/Feature1.cpp index bd1e501506..24ff0d62b8 100644 --- a/src/xrpld/rpc/handlers/Feature1.cpp +++ b/src/xrpld/rpc/handlers/Feature1.cpp @@ -61,9 +61,13 @@ doFeature(RPC::JsonContext& context) return rpcError(rpcNO_PERMISSION); if (context.params[jss::vetoed].asBool()) + { table.veto(feature); + } else + { table.unVeto(feature); + } } Json::Value jvReply = table.getJson(feature, isAdmin); diff --git a/src/xrpld/rpc/handlers/GatewayBalances.cpp b/src/xrpld/rpc/handlers/GatewayBalances.cpp index a2176ab388..f7acbc77e6 100644 --- a/src/xrpld/rpc/handlers/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/GatewayBalances.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -173,7 +174,7 @@ doGatewayBalances(RPC::JsonContext& context) // A positive balance means the cold wallet has an asset // (unusual) - if (hotWallets.count(peer) > 0) + if (hotWallets.contains(peer)) { // This is a specified hot wallet hotBalances[peer].push_back(-rs->getBalance()); diff --git a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/GetAggregatePrice.cpp index eaeff767cd..cf327258a3 100644 --- a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/GetAggregatePrice.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ static void iteratePriceData( RPC::JsonContext& context, std::shared_ptr const& sle, - std::function&& f) + std::function const& f) { using Meta = std::shared_ptr; constexpr std::uint8_t maxHistory = 3; @@ -87,8 +88,8 @@ iteratePriceData( if (isNew && history == 1) return; - oracle = isNew ? &static_cast(node.peekAtField(sfNewFields)) - : &static_cast(node.peekAtField(sfFinalFields)); + oracle = isNew ? &safe_downcast(node.peekAtField(sfNewFields)) + : &safe_downcast(node.peekAtField(sfFinalFields)); break; } } @@ -148,7 +149,7 @@ doGetAggregatePrice(RPC::JsonContext& context) // support positive int, uint, and a number represented as a string auto validUInt = [](Json::Value const& params, Json::StaticString const& field) { auto const& jv = params[field]; - std::uint32_t v; + std::uint32_t v = 0; return jv.isUInt() || (jv.isInt() && jv.asInt() >= 0) || (jv.isString() && beast::lexicalCastChecked(v, jv.asString())); }; @@ -217,6 +218,12 @@ doGetAggregatePrice(RPC::JsonContext& context) return result; } + // Get the ledger + std::shared_ptr ledger; + result = RPC::lookupLedger(ledger, context); + if (!ledger) + return result; // LCOV_EXCL_LINE + // Collect the dataset into bimap keyed by lastUpdateTime and // STAmount (Number is int64 and price is uint64) Prices prices; @@ -237,11 +244,6 @@ doGetAggregatePrice(RPC::JsonContext& context) return result; } - std::shared_ptr ledger; - result = RPC::lookupLedger(ledger, context); - if (!ledger) - return result; // LCOV_EXCL_LINE - auto const sle = ledger->read(keylet::oracle(*account, *documentID)); iteratePriceData(context, sle, [&](STObject const& node) { auto const& series = node.getFieldArray(sfPriceDataSeries); @@ -283,8 +285,8 @@ doGetAggregatePrice(RPC::JsonContext& context) if (auto const threshold = std::get(timeThreshold)) { // threshold defines an acceptable range {max,min} of lastUpdateTime as - // {latestTime, latestTime - threshold}, the prices with lastUpdateTime - // greater than (latestTime - threshold) are erased. + // {latestTime, latestTime - threshold}. Prices with lastUpdateTime + // less than (latestTime - threshold) are erased (outdated prices). auto const oldestTime = prices.left.rbegin()->first; auto const upperBound = latestTime > threshold ? (latestTime - threshold) : oldestTime; if (upperBound > oldestTime) diff --git a/src/xrpld/rpc/handlers/LedgerData.cpp b/src/xrpld/rpc/handlers/LedgerData.cpp index 308f4b4436..733d1c99c6 100644 --- a/src/xrpld/rpc/handlers/LedgerData.cpp +++ b/src/xrpld/rpc/handlers/LedgerData.cpp @@ -140,14 +140,14 @@ doLedgerDataGrpc(RPC::GRPCContext& con { startKey = *key; } - else if (request.marker().size() != 0) + else if (!request.marker().empty()) { grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "marker malformed"}; return {response, errorStatus}; } auto e = ledger->sles.end(); - if (request.end_marker().size() != 0) + if (!request.end_marker().empty()) { auto const key = uint256::fromVoidChecked(request.end_marker()); diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index e4e8f52fd7..640369c04b 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -70,9 +70,11 @@ parseIndex(Json::Value const& params, Json::StaticString const fieldName, unsign if (index == jss::nunl) return keylet::negativeUNL().key; if (index == jss::hashes) + { // Note this only finds the "short" skip list. Use "hashes":index to // get the long list. return keylet::skip().key; + } } return parseObjectID(params, fieldName, "hex string"); } @@ -532,8 +534,10 @@ parseMPTokenIssuance( { auto const mptIssuanceID = LedgerEntryHelpers::parse(params); if (!mptIssuanceID) + { return LedgerEntryHelpers::invalidFieldError( "malformedMPTokenIssuance", fieldName, "Hash192"); + } return keylet::mptIssuance(*mptIssuanceID).key; } @@ -901,8 +905,8 @@ doLedgerEntry(RPC::JsonContext& context) // this exception return an invalidParam error. return RPC::make_error(rpcINVALID_PARAMS); } - else - throw; + + throw; } // Return the computed index regardless of whether the node exists. diff --git a/src/xrpld/rpc/handlers/LedgerHandler.cpp b/src/xrpld/rpc/handlers/LedgerHandler.cpp index 6d188f8520..43c9780bb0 100644 --- a/src/xrpld/rpc/handlers/LedgerHandler.cpp +++ b/src/xrpld/rpc/handlers/LedgerHandler.cpp @@ -215,11 +215,17 @@ doLedgerGrpc(RPC::GRPCContext& context) obj->set_data(inDesired->data(), inDesired->size()); } if (inBase && inDesired) + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED); + } else if (inBase && !inDesired) + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED); + } else + { obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED); + } auto const blob = inDesired ? inDesired->slice() : inBase->slice(); auto const objectType = static_cast(blob[1] << 8 | blob[2]); diff --git a/src/xrpld/rpc/handlers/LogLevel.cpp b/src/xrpld/rpc/handlers/LogLevel.cpp index 2596cd6c70..2bc1beb7d4 100644 --- a/src/xrpld/rpc/handlers/LogLevel.cpp +++ b/src/xrpld/rpc/handlers/LogLevel.cpp @@ -52,9 +52,13 @@ doLogLevel(RPC::JsonContext& context) std::string partition(context.params[jss::partition].asString()); if (boost::iequals(partition, "base")) + { context.app.logs().threshold(severity); + } else + { context.app.logs().get(partition).threshold(severity); + } return Json::objectValue; } diff --git a/src/xrpld/rpc/handlers/NFTOffers.cpp b/src/xrpld/rpc/handlers/NFTOffers.cpp index 51f75bfe4c..5fe2e3bede 100644 --- a/src/xrpld/rpc/handlers/NFTOffers.cpp +++ b/src/xrpld/rpc/handlers/NFTOffers.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,7 @@ appendNftOfferJson( static Json::Value enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const& directory) { - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::nftOffers, context)) return *err; diff --git a/src/xrpld/rpc/handlers/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/NoRippleCheck.cpp index e17a437efc..73f51fca1a 100644 --- a/src/xrpld/rpc/handlers/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/NoRippleCheck.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -55,12 +56,16 @@ doNoRippleCheck(RPC::JsonContext& context) { std::string const role = params["role"].asString(); if (role == "gateway") + { roleGateway = true; + } else if (role != "user") + { return RPC::invalid_field_error("role"); + } } - unsigned int limit; + unsigned int limit = 0; if (auto err = readLimitField(limit, RPC::Tuning::noRippleCheck, context)) return *err; diff --git a/src/xrpld/rpc/handlers/PayChanClaim.cpp b/src/xrpld/rpc/handlers/PayChanClaim.cpp index 44312ee336..b24a241147 100644 --- a/src/xrpld/rpc/handlers/PayChanClaim.cpp +++ b/src/xrpld/rpc/handlers/PayChanClaim.cpp @@ -29,8 +29,10 @@ doChannelAuthorize(RPC::JsonContext& context) auto const& params(context.params); for (auto const& p : {jss::channel_id, jss::amount}) + { if (!params.isMember(p)) return RPC::missing_field_error(p); + } // Compatibility if a key type isn't specified. If it is, the // keypairForSignature code will validate parameters and return @@ -92,8 +94,10 @@ doChannelVerify(RPC::JsonContext& context) { auto const& params(context.params); for (auto const& p : {jss::public_key, jss::channel_id, jss::amount, jss::signature}) + { if (!params.isMember(p)) return RPC::missing_field_error(p); + } std::optional pk; { @@ -125,7 +129,7 @@ doChannelVerify(RPC::JsonContext& context) std::uint64_t const drops = *optDrops; auto sig = strUnHex(params[jss::signature].asString()); - if (!sig || !sig->size()) + if (!sig || sig->empty()) return rpcError(rpcINVALID_PARAMS); Serializer msg; diff --git a/src/xrpld/rpc/handlers/Peers.cpp b/src/xrpld/rpc/handlers/Peers.cpp index 1be136a5f8..b21efc01fb 100644 --- a/src/xrpld/rpc/handlers/Peers.cpp +++ b/src/xrpld/rpc/handlers/Peers.cpp @@ -27,9 +27,13 @@ doPeers(RPC::JsonContext& context) auto const s = p[jss::track].asString(); if (s == "diverged") + { p["sanity"] = "insane"; + } else if (s == "unknown") + { p["sanity"] = "unknown"; + } } } } @@ -53,8 +57,10 @@ doPeers(RPC::JsonContext& context) json[jss::fee] = static_cast(node.getLoadFee()) / ref; if (node.getReportTime() != NetClock::time_point{}) + { json[jss::age] = (node.getReportTime() >= now) ? 0 : (now - node.getReportTime()).count(); + } }); return jvResult; diff --git a/src/xrpld/rpc/handlers/Ping.cpp b/src/xrpld/rpc/handlers/Ping.cpp index cc3c558cd9..4e9b18c4c9 100644 --- a/src/xrpld/rpc/handlers/Ping.cpp +++ b/src/xrpld/rpc/handlers/Ping.cpp @@ -22,7 +22,7 @@ doPing(RPC::JsonContext& context) case Role::IDENTIFIED: ret[jss::role] = "identified"; ret[jss::username] = std::string{context.headers.user}; - if (context.headers.forwardedFor.size()) + if (!context.headers.forwardedFor.empty()) ret[jss::ip] = std::string{context.headers.forwardedFor}; break; case Role::PROXY: diff --git a/src/xrpld/rpc/handlers/ServerDefinitions.cpp b/src/xrpld/rpc/handlers/ServerDefinitions.cpp index e153065ea9..24abd69a02 100644 --- a/src/xrpld/rpc/handlers/ServerDefinitions.cpp +++ b/src/xrpld/rpc/handlers/ServerDefinitions.cpp @@ -63,9 +63,11 @@ ServerDefinitions::translate(std::string const& inp) { if (contains("512") || contains("384") || contains("256") || contains("192") || contains("160") || contains("128")) + { return replace("UINT", "Hash"); - else - return replace("UINT", "UInt"); + } + + return replace("UINT", "UInt"); } static std::unordered_map const replacements{ @@ -102,7 +104,9 @@ ServerDefinitions::translate(std::string const& inp) out += token; } else + { out += token; + } if (pos == inpToProcess.size()) break; inpToProcess = inpToProcess.substr(pos + 1); @@ -217,7 +221,7 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue} for (auto const& [code, field] : xrpl::SField::getKnownCodeToField()) { - if (field->fieldName == "") + if (field->fieldName.empty()) continue; Json::Value innerObj = Json::objectValue; diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/Simulate.cpp index 5703597beb..1d46f72425 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/Simulate.cpp @@ -126,7 +126,7 @@ autofillTx(Json::Value& tx_json, RPC::JsonContext& context) } if (auto error = autofillSignature(tx_json)) - return *error; + return error; if (!tx_json.isMember(jss::Sequence)) { diff --git a/src/xrpld/rpc/handlers/Submit.cpp b/src/xrpld/rpc/handlers/Submit.cpp index b8cca3670a..cac7259a00 100644 --- a/src/xrpld/rpc/handlers/Submit.cpp +++ b/src/xrpld/rpc/handlers/Submit.cpp @@ -55,7 +55,7 @@ doSubmit(RPC::JsonContext& context) auto ret = strUnHex(context.params[jss::tx_blob].asString()); - if (!ret || !ret->size()) + if (!ret || ret->empty()) return rpcError(rpcINVALID_PARAMS); SerialIter sitTrans(makeSlice(*ret)); @@ -76,8 +76,10 @@ doSubmit(RPC::JsonContext& context) { if (!context.app.checkSigs()) + { forceValidity( context.app.getHashRouter(), stTx->getTransactionID(), Validity::SigGoodOnly); + } auto [validity, reason] = checkValidity( context.app.getHashRouter(), *stTx, context.ledgerMaster.getCurrentLedger()->rules()); if (validity != Validity::Valid) diff --git a/src/xrpld/rpc/handlers/Subscribe.cpp b/src/xrpld/rpc/handlers/Subscribe.cpp index 8fbf5a917e..6e8d9dbaa3 100644 --- a/src/xrpld/rpc/handlers/Subscribe.cpp +++ b/src/xrpld/rpc/handlers/Subscribe.cpp @@ -281,10 +281,8 @@ doSubscribe(RPC::JsonContext& context) { return rpcError(rpcDOMAIN_MALFORMED); } - else - { - book.domain = domain; - } + + book.domain = domain; } if (!isConsistent(book)) diff --git a/src/xrpld/rpc/handlers/TransactionEntry.cpp b/src/xrpld/rpc/handlers/TransactionEntry.cpp index 8cd1120aad..36f53130fa 100644 --- a/src/xrpld/rpc/handlers/TransactionEntry.cpp +++ b/src/xrpld/rpc/handlers/TransactionEntry.cpp @@ -59,8 +59,10 @@ doTransactionEntry(RPC::JsonContext& context) jvResult[jss::hash] = to_string(sttx->getTransactionID()); if (!lpLedger->open()) + { jvResult[jss::ledger_hash] = to_string(context.ledgerMaster.getHashBySeq(lpLedger->seq())); + } bool const validated = context.ledgerMaster.isValidated(*lpLedger); @@ -73,7 +75,9 @@ doTransactionEntry(RPC::JsonContext& context) } } else + { jvResult[jss::tx_json] = sttx->getJson(JsonOptions::none); + } RPC::insertDeliverMax(jvResult[jss::tx_json], sttx->getTxnType(), context.apiVersion); diff --git a/src/xrpld/rpc/handlers/Tx.cpp b/src/xrpld/rpc/handlers/Tx.cpp index 84f2a6c618..482b7e3bf1 100644 --- a/src/xrpld/rpc/handlers/Tx.cpp +++ b/src/xrpld/rpc/handlers/Tx.cpp @@ -42,7 +42,7 @@ struct TxResult std::optional ctid; std::optional closeTime; std::optional ledgerHash; - TxSearched searchedAll; + TxSearched searchedAll = TxSearched::unknown; }; struct TxArgs @@ -153,7 +153,7 @@ doTxHelp(RPC::Context& context, TxArgs args) uint32_t netID = context.app.getNetworkIDService().getNetworkID(); if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL) - result.ctid = RPC::encodeCTID(lgrSeq, (uint32_t)txnIdx, (uint32_t)netID); + result.ctid = RPC::encodeCTID(lgrSeq, txnIdx, netID); } } @@ -192,7 +192,9 @@ populateJsonResponse( constexpr auto optionsJson = JsonOptions::include_date | JsonOptions::disable_API_prior_V2; if (args.binary) + { response[jss::tx_blob] = result.txn->getJson(optionsJson, true); + } else { response[jss::tx_json] = result.txn->getJson(optionsJson); @@ -258,8 +260,10 @@ doTxJson(RPC::JsonContext& context) TxArgs args; if (context.params.isMember(jss::transaction) && context.params.isMember(jss::ctid)) + { // specifying both is ambiguous return rpcError(rpcINVALID_PARAMS); + } if (context.params.isMember(jss::transaction)) { @@ -286,7 +290,9 @@ doTxJson(RPC::JsonContext& context) args.ctid = {lgr_seq, txn_idx}; } else + { return rpcError(rpcINVALID_PARAMS); + } args.binary = context.params.isMember(jss::binary) && context.params[jss::binary].asBool(); diff --git a/src/xrpld/rpc/handlers/Unsubscribe.cpp b/src/xrpld/rpc/handlers/Unsubscribe.cpp index 15da0d1d6a..824d57203c 100644 --- a/src/xrpld/rpc/handlers/Unsubscribe.cpp +++ b/src/xrpld/rpc/handlers/Unsubscribe.cpp @@ -165,8 +165,7 @@ doUnsubscribe(RPC::JsonContext& context) return rpcError(rpcSRC_CUR_MALFORMED); } // Parse optional issuer. - else if ( - ((taker_pays.isMember(jss::issuer)) && + if (((taker_pays.isMember(jss::issuer)) && (!taker_pays[jss::issuer].isString() || !to_issuer(book.in.account, taker_pays[jss::issuer].asString()))) // Don't allow illegal issuers. @@ -186,8 +185,7 @@ doUnsubscribe(RPC::JsonContext& context) return rpcError(rpcDST_AMT_MALFORMED); } // Parse optional issuer. - else if ( - ((taker_gets.isMember(jss::issuer)) && + if (((taker_gets.isMember(jss::issuer)) && (!taker_gets[jss::issuer].isString() || !to_issuer(book.out.account, taker_gets[jss::issuer].asString()))) // Don't allow illegal issuers. @@ -211,10 +209,8 @@ doUnsubscribe(RPC::JsonContext& context) { return rpcError(rpcDOMAIN_MALFORMED); } - else - { - book.domain = domain; - } + + book.domain = domain; } context.netOps.unsubBook(ispSub->getSeq(), book); diff --git a/src/xrpld/rpc/handlers/VaultInfo.cpp b/src/xrpld/rpc/handlers/VaultInfo.cpp index b17a16cc4a..4a704e0b0b 100644 --- a/src/xrpld/rpc/handlers/VaultInfo.cpp +++ b/src/xrpld/rpc/handlers/VaultInfo.cpp @@ -35,8 +35,7 @@ parseVault(Json::Value const& params, Json::Value& jvResult) RPC::inject_error(rpcACT_MALFORMED, jvResult); return std::nullopt; } - else if ( - !(params[jss::seq].isInt() || params[jss::seq].isUInt()) || + if (!(params[jss::seq].isInt() || params[jss::seq].isUInt()) || params[jss::seq].asDouble() <= 0.0 || params[jss::seq].asDouble() > double(Json::Value::maxUInt)) { diff --git a/src/xrpld/rpc/handlers/WalletPropose.cpp b/src/xrpld/rpc/handlers/WalletPropose.cpp index 273d829670..a5005ce701 100644 --- a/src/xrpld/rpc/handlers/WalletPropose.cpp +++ b/src/xrpld/rpc/handlers/WalletPropose.cpp @@ -76,9 +76,13 @@ walletPropose(Json::Value const& params) // to detect such keys to avoid user confusion. { if (params.isMember(jss::passphrase)) + { seed = RPC::parseRippleLibSeed(params[jss::passphrase]); + } else if (params.isMember(jss::seed)) + { seed = RPC::parseRippleLibSeed(params[jss::seed]); + } if (seed) { @@ -142,15 +146,19 @@ walletPropose(Json::Value const& params) // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (estimate_entropy(passphrase) < 80.0) + { obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; + } else + { obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; + } } }